All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.ogema.channelmapperv2.impl.ChannelMapperImpl Maven / Gradle / Ivy

The newest version!
/**
 * Copyright 2011-2018 Fraunhofer-Gesellschaft zur Förderung der angewandten Wissenschaften e.V.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.ogema.channelmapperv2.impl;

import java.lang.ref.WeakReference;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;

import org.apache.felix.scr.annotations.Activate;
import org.apache.felix.scr.annotations.Component;
import org.apache.felix.scr.annotations.Service;
import org.ogema.channelmapperv2.ChannelMapper;
import org.ogema.channelmapperv2.config.ChannelMapperConfigPattern;
import org.ogema.channelmapperv2.config.ChannelMapperConfiguration;
import org.ogema.core.application.Application;
import org.ogema.core.application.ApplicationManager;
import org.ogema.core.channelmanager.ChannelAccess;
import org.ogema.core.channelmanager.ChannelConfiguration.Direction;
import org.ogema.core.channelmanager.driverspi.ChannelLocator;
import org.ogema.core.logging.OgemaLogger;
import org.ogema.core.model.ResourceList;
import org.ogema.core.model.simple.SingleValueResource;
import org.ogema.core.resourcemanager.AccessPriority;
import org.ogema.core.resourcemanager.CompoundResourceEvent;
import org.ogema.core.resourcemanager.pattern.PatternChangeListener;
import org.ogema.core.resourcemanager.pattern.PatternListener;
import org.ogema.core.resourcemanager.transaction.ResourceTransaction;
import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceRegistration;

/**
 * By means of the ChannelMapper, it is possible to map channels to resources without any HighLevel-Driver.
 */
@Component(immediate = true) // need to reload existing configurations
@Service(Application.class)
public class ChannelMapperImpl implements Application, ChannelMapper,PatternListener, PatternChangeListener {

	/*
	 * Map
	 */
	final ConcurrentMap resourceMappings = new ConcurrentHashMap<>();
	private final ConcurrentMap channelMappings = new ConcurrentHashMap<>();
	private volatile ResourceList configs;
	protected volatile OgemaLogger logger;
	private volatile ApplicationManager appMan;
	private volatile ChannelAccess ca;
	// this is only needed if we get a mapping for a driver that has not been registered yet
	private volatile WeakReference timer = null;
	private volatile BundleContext ctx;
	private ServiceRegistration sreg;
	
	ScheduledExecutorService getTimer() {
		ScheduledExecutorService t;
		WeakReference timer = this.timer;
		if (timer != null) {
			t = timer.get();
			if (t != null)  
				return t;
		}
		synchronized (this) {
			timer = this.timer;
			if (timer != null) {
				t = timer.get();
				if (t != null)
					return t;
			}
			t = Executors.newSingleThreadScheduledExecutor();
			this.timer = new WeakReference(t);
			return t;
		}
	}

	/*********************************
	 * 
	 * ChannelMapper implementation
	 * 
	 *********************************/

	@Override
	public void mapChannelToResource(ChannelLocator channel, SingleValueResource target, Direction direction,
			long samplingPeriod, float scalingFactor, float valueOffset) {
		Objects.requireNonNull(ca);
		Objects.requireNonNull(channel);
		Objects.requireNonNull(target);
		Objects.requireNonNull(direction);
		ChannelMapperConfiguration config = configs.add();
		ResourceTransaction trans = appMan.getResourceAccess().createResourceTransaction();
		trans.create(config.channelLocator());
		trans.create(config.channelLocator().driverId());
		trans.create(config.channelLocator().interfaceId());
		trans.create(config.channelLocator().deviceAddress());
		trans.create(config.channelLocator().parameters());
		trans.create(config.channelLocator().channelAddress());
		trans.setString(config.channelLocator().driverId(), channel.getDeviceLocator().getDriverName());
		trans.setString(config.channelLocator().deviceAddress(), channel.getDeviceLocator().getDeviceAddress());
		trans.setString(config.channelLocator().interfaceId(), channel.getDeviceLocator().getInterfaceName());
		trans.setString(config.channelLocator().parameters(), channel.getDeviceLocator().getParameters());
		trans.setString(config.channelLocator().channelAddress(), channel.getChannelAddress());
		trans.setAsReference(config.target(), target);
		trans.create(config.direction());
		trans.setString(config.direction(), direction.name());
		trans.create(config.samplingInterval());
		trans.setTime(config.samplingInterval(), samplingPeriod);
		trans.create(config.scalingFactor());
		trans.setFloat(config.scalingFactor(), (float) scalingFactor); 
		trans.create(config.valueOffset());
		trans.setFloat(config.valueOffset(), (float) valueOffset);
		trans.activate(config, false, true);		
		trans.commit();
	}


	@Override
	public void unmapChannel(ChannelLocator channel) {
		unmapChannel(channel, null);
	}

	@Override
	public void unmapChannel(final ChannelLocator channel, final SingleValueResource target) {
		ChannelController cc = channelMappings.get(channel);
		if (cc == null) {
			logger.warn("Channel not found, cannot remove it: {}", channel);
			return;
		}
		if (target != null && !cc.pattern.target.equalsLocation(target))
			return;
		cc.pattern.model.delete(); //  will trigger a patternUnavailable callback
	}
	
	@Override
	public void patternAvailable(ChannelMapperConfigPattern pattern) {
		logger.info("New channel mapper configuration found: {}",pattern.model);
		newPattern(pattern);
		appMan.getResourcePatternAccess().addPatternChangeListener(pattern, this, ChannelMapperConfigPattern.class);
		// TODO register channel with channel access
 	}
	
	private void newPattern(ChannelMapperConfigPattern pattern) {
		String path = pattern.model.getPath();
		if (resourceMappings.containsKey(path)) {
			logger.warn("Got a second callback for an existing mapping configuration " + pattern.model);
			return;
		}
		final ChannelController cc;
		try {
			cc = new ChannelController(pattern, ca, this);
		} catch (SecurityException e) {
			logger.warn("Channel configuration could not be created, permission denied: {}",pattern.model,e);
			// We need to delete the configuration, otherwise the channel will be created after the next system restart with this app's permissions.
			// This problem is difficult to overcome, otherwise.
			pattern.model.delete(); 
			return;
		}
		resourceMappings.put(path, cc);
		channelMappings.put(cc.channelLocator, cc);
	}
	
	@Override
	public void patternUnavailable(ChannelMapperConfigPattern pattern) {
		logger.info("Channel mapper configuration removed: {}",pattern.model);
		appMan.getResourcePatternAccess().removePatternChangeListener(pattern, this);
		patternGone(pattern);
	}
	
	private void patternGone(ChannelMapperConfigPattern pattern) {
		ChannelController cc = resourceMappings.remove(pattern.model.getPath());
		if (cc == null)
			return;
		cc.close();
		channelMappings.remove(cc.channelLocator);
	}
	
	@Override
	public void patternChanged(ChannelMapperConfigPattern instance, List> changes) {
		logger.info("Channel mapper configuration has changed: {}",instance.model);
		patternGone(instance);
		newPattern(instance);
	}
	
	@Activate
	protected void activate(BundleContext ctx) {
		this.ctx = ctx;
	}

	@SuppressWarnings("unchecked")
	@Override
	public void start(ApplicationManager appManager) {
		Objects.requireNonNull(ctx);
		this.logger = appManager.getLogger();
		this.appMan = appManager;
		this.ca = appManager.getChannelAccess();
		this.configs = appManager.getResourceManagement().createResource("channelMapperConfigurations", ResourceList.class);
		configs.setElementType(ChannelMapperConfiguration.class);
		configs.activate(false);
		appManager.getResourcePatternAccess().addPatternDemand(ChannelMapperConfigPattern.class, this, AccessPriority.PRIO_LOWEST);
		this.sreg = ctx.registerService(ChannelMapper.class, this, null);
	}
	
	// delete everything but the persistent configurations
	@Override
	public void stop(AppStopReason reason) {
		final ServiceRegistration sreg = this.sreg;
		if (sreg != null) {
			try {
				sreg.unregister();
			} catch (Exception ignore) {}
			this.sreg = null;
		}
		if (appMan != null) 
			appMan.getResourcePatternAccess().removePatternDemand(ChannelMapperConfigPattern.class, this);
		WeakReference timer = this.timer;
		if (timer != null) {
			ScheduledExecutorService t = timer.get();
			if (t != null) {
				t.shutdownNow();
			}
			this.timer = null;
		}
		for (ChannelController cc: resourceMappings.values()) {
			if (appMan != null)
				appMan.getResourcePatternAccess().removePatternChangeListener(cc.pattern, this);
			cc.close();
		}
		this.logger = null;
		this.appMan = null;
		this.ca = null;
		this.resourceMappings.clear();
		this.channelMappings.clear();
		this.configs = null;
	}
	
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy