/* * Dog - Network Driver * * Copyright (c) 2012-2014 Dario Bonino and Luigi De Russis * * 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 it.polito.elite.dog.drivers.echelon.ilon100.network; import it.polito.elite.dog.core.library.util.LogHelper; import it.polito.elite.dog.drivers.echelon.ilon100.network.info.DataPointInfo; import it.polito.elite.dog.drivers.echelon.ilon100.network.interfaces.EchelonIlon100Network; import java.rmi.RemoteException; import java.util.Dictionary; import java.util.HashMap; import java.util.HashSet; import java.util.Hashtable; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import org.osgi.framework.BundleContext; import org.osgi.framework.Constants; import org.osgi.framework.ServiceRegistration; import org.osgi.service.cm.ConfigurationException; import org.osgi.service.cm.ManagedService; import org.osgi.service.log.LogService; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.Dp_Data; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.E_LonString; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.E_xSelect; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.Item; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.Item_Coll; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.Item_Data; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.message.Item_DataColl; import com.echelon.wsdl.web_services_ns.ilon100.v4_0.wsdl.ILON100PortTypeProxy; /** * The network driver for Echelon networks using the iLon 100 gateway as access * point. * * @author <a href="mailto:dario.bonino@polito.it">Dario Bonino</a> * @see <a href="http://elite.polito.it">http://elite.polito.it</a> * */ public class EchelonIlon100DriverImpl implements ManagedService, EchelonIlon100Network { /** the default getAll xSelect to use for discovery **/ public static final String XSELECT_ALL = "//Item[@xsi:type=\"Dp_Cfg\"]"; // a reference to the bundle context private BundleContext bundleContext; // the service registration handle private ServiceRegistration<?> regServiceEchelonIlon100; // the driver logger private LogHelper logger; // the log identifier, unique for the class public static String logId = "[EchelonIlon100DriverImpl]: "; // map associating data points to drivers private Map<DataPointInfo, EchelonIlon100DriverInstance> datapoint2Driver; // the inverse map...for polling private Map<EchelonIlon100DriverInstance, Map<String, DataPointInfo>> driver2Datapoint; // the endpoint-to-datapoint association used for polling... private Map<String, Map<String, DataPointInfo>> endpoint2Datapoint; // the baseline pollingTime adopted if no endpoint-specific setting is given private int pollingTimeMillis = 5000; // default value // the driver poller... private EchelonIlon100Poller poller; public EchelonIlon100DriverImpl(BundleContext bundleContext) { // init the logger this.logger = new LogHelper(bundleContext); // store a reference to the bundle context this.bundleContext = bundleContext; // init the map between datapoints and drivers this.datapoint2Driver = new ConcurrentHashMap<DataPointInfo, EchelonIlon100DriverInstance>(); // init the reverse map this.driver2Datapoint = new ConcurrentHashMap<EchelonIlon100DriverInstance, Map<String, DataPointInfo>>(); // init the endpoint2Datapoint map this.endpoint2Datapoint = new ConcurrentHashMap<String, Map<String, DataPointInfo>>(); // register this bundle as a managed service, i.e. as a bundle able to // be configured through a proper configuration file this.registerManagedService(); } /** * Unregisters the driver from the OSGi framework */ public void unRegister() { // stop the poller if (this.poller != null) { this.poller.setRunnable(false); } // unregister if (this.regServiceEchelonIlon100 != null) { this.regServiceEchelonIlon100.unregister(); } } /*** * Register this class as a Managed Service */ private void registerManagedService() { Hashtable<String, Object> propManagedService = new Hashtable<String, Object>(); propManagedService.put(Constants.SERVICE_PID, this.bundleContext.getBundle().getSymbolicName()); this.bundleContext.registerService(ManagedService.class.getName(), this, propManagedService); } @SuppressWarnings("rawtypes") @Override public void updated(Dictionary properties) throws ConfigurationException { // get the bundle configuration parameters if (properties != null) { // try to get the baseline polling time String pollingTimeAsString = (String) properties.get("pollingTimeMillis"); // trim leading and trailing spaces pollingTimeAsString = pollingTimeAsString.trim(); // check not null if (pollingTimeAsString != null) { // parse the string this.pollingTimeMillis = Integer.valueOf(pollingTimeAsString); } } // in any case, as the polling time has a default, init the poller // thread and start it this.poller = new EchelonIlon100Poller(this); // start the poller poller.start(); // log the driver start this.logger.log(LogService.LOG_INFO, EchelonIlon100DriverImpl.logId + "Started the driver poller thread, ready to handle datapoint sampling, reading and setting..."); // register the service // register the driver service if not already registered if (this.regServiceEchelonIlon100 == null) this.regServiceEchelonIlon100 = this.bundleContext.registerService(EchelonIlon100Network.class.getName(), this, null); } /** * @return the datapoint2Driver */ public Map<DataPointInfo, EchelonIlon100DriverInstance> getDatapoint2Driver() { return datapoint2Driver; } /** * @return the driver2Datapoint */ public Map<EchelonIlon100DriverInstance, Map<String, DataPointInfo>> getDriver2Datapoint() { return driver2Datapoint; } /** * Returns the set of connected end point addresses. * * @return a {@link Set}<{@link String}> of end point addresses to which the * driver is currently connected */ public Set<String> getConnectedEndpoints() { return this.endpoint2Datapoint.keySet(); } /** * Returns the set of datapoints connected to the given endpoint address. * * @param endpoint * The endpoint address. * @return The connected datapoints as a {@link Set} of * {@link DataPointInfo} instances. */ public Map<String, DataPointInfo> getEndpointDatapoints(String endpoint) { return this.endpoint2Datapoint.get(endpoint); } /** * Returns the polling time, in milliseconds, currently set for this driver. * * @return The polling time in milliseconds. */ public int getPollingTimeMillis() { return this.pollingTimeMillis; } /** * @return the logger */ protected LogHelper getLogger() { return logger; } /****************************************************************************** * * * Echelon Ilon 100 Network implementation * * *******************************************************************************/ @Override public void read(DataPointInfo dataPointInfo) { this.readDP(dataPointInfo); // get the driver currently handling the datapoint EchelonIlon100DriverInstance driver = this.datapoint2Driver.get(dataPointInfo); // dispatch the new datapoint value driver.newMessageFromHouse(dataPointInfo); } public void readDP(DataPointInfo dataPointInfo) { // Create a proxy towards the DataPoint endpoint ILON100PortTypeProxy proxy = new ILON100PortTypeProxy(dataPointInfo.getiLonServerEndpoint()); // instantiate the member object Item_Coll itemColl = new Item_Coll(); // the item to query Item itemToQuery = new Item(); // set the DP name itemToQuery.setUCPTname(dataPointInfo.getiLonId()); // add the item to the item query itemColl.setItem(new Item[] { itemToQuery }); // read the datapoint value try { Item_DataColl dataPointValueC = proxy.read(itemColl); // get the only value if (dataPointValueC != null) { // get the item data of this datapoint Dp_Data itemData = (Dp_Data) dataPointValueC.getItem(0); // fill the return value try { dataPointInfo.setValue(Double.parseDouble(itemData.getUCPTvalue(0).get_value())); } catch (NumberFormatException e) { // log the error this.logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "Attempt to read currently not supported data, skipping..."); } } else // log the error this.logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "No data available for: " + dataPointInfo.getiLonId()); } catch (RemoteException e) { // log the error this.logger.log(LogService.LOG_ERROR, EchelonIlon100DriverImpl.logId + "Unable to retrieve data for the given datapoints:" + e); } } @Override public void readAll(Map<String, DataPointInfo> allDatapoints) { // Prepare a variable to store the end point associated to the given // group, if more than one end point is found, only the first occurrence // will be considered. String endpoint = null; // instantiate the member object Item_Coll itemColl = new Item_Coll(); // the array of data points to get value from Item[] itemsToQuery = new Item[allDatapoints.size()]; int i = 0; for (String dpIlonId : allDatapoints.keySet()) { // the item to query Item itemToQuery = new Item(); // set the DP name itemToQuery.setUCPTname(dpIlonId); // add the item to query itemsToQuery[i] = itemToQuery; // add the first endpoint if (i == 0) endpoint = allDatapoints.get(dpIlonId).getiLonServerEndpoint(); i++; } itemColl.setItem(itemsToQuery); if (endpoint != null) { // Create a proxy towards the DataPoint endpoint ILON100PortTypeProxy proxy = new ILON100PortTypeProxy(endpoint); // read the datapoint value try { Item_DataColl dataPointValueC = proxy.read(itemColl); // get the datapoint values if (dataPointValueC != null) { // convert the data values collection into a more "usable" // array Item_Data[] dataPointValues = dataPointValueC.getItem(); // iterate over the available data point values, consider // the // one-valued only variables // TODO: improve the data point "extraction" to address also // multiple-valued data points for (i = 0; i < dataPointValues.length; i++) { // get the item data of this datapoint Dp_Data itemData = (Dp_Data) dataPointValueC.getItem(i); try { // extract the corresponding data point from the // given list DataPointInfo dp = allDatapoints.get(itemData.getUCPTname()); if (dp != null) { // try to convert the value to a double double value = Double.parseDouble(itemData.getUCPTvalue(0).get_value()); // update the datapoint value dp.setValue(value); // log the update logger.log(LogService.LOG_DEBUG, EchelonIlon100DriverImpl.logId + "Updated datapoint " + dp); // get the specific driver associated to the // current datapoint EchelonIlon100DriverInstance drv = this.datapoint2Driver.get(dp); // notify the new datapoint value drv.newMessageFromHouse(dp); } else { logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "Found unmatched data from ILon server..."); } } catch (NumberFormatException e) { // log the error this.logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "Attempt to read currently not supported data, skipping..."); } } } else // log the error this.logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "No data available for the given datapoints "); } catch (RemoteException e) { // log the error this.logger.log(LogService.LOG_ERROR, EchelonIlon100DriverImpl.logId + "Unable to retrieve data for the given datapoints:" + e); } } else { this.logger.log(LogService.LOG_WARNING, "No endpoint given for readAll, gracefully failing..."); } } @Override // TODO: Must be tested!!! public void write(DataPointInfo dataPointInfo) { // Create a proxy towards the DataPoint endpoint ILON100PortTypeProxy proxy = new ILON100PortTypeProxy(dataPointInfo.getiLonServerEndpoint()); // instantiate the member object Item_DataColl itemColl = new Item_DataColl(); // the item to query Dp_Data itemToWrite = new Dp_Data(); // set the DP name itemToWrite.setUCPTname(dataPointInfo.getiLonId()); itemToWrite.setUCPTvalue(new E_LonString[] { new E_LonString("" + dataPointInfo.getValue()) }); // add the item to the item query itemColl.setItem(new Item_Data[] { itemToWrite }); // read the datapoint value try { proxy.write(itemColl); } catch (RemoteException e) { // log the error this.logger.log(LogService.LOG_ERROR, EchelonIlon100DriverImpl.logId + "Unable to write data for the given datapoints:" + e); } } @Override public Set<DataPointInfo> discoverDatapoints(String endpointAddress) { // init the data point set HashSet<DataPointInfo> dataPoints = new HashSet<DataPointInfo>(); // if xSelect is null, fall back to the default getAll query and log a // warning String xSelect = EchelonIlon100DriverImpl.XSELECT_ALL; // Create a proxy towards the DataPoint endpoint ILON100PortTypeProxy proxy = new ILON100PortTypeProxy(endpointAddress); // prepare the list query to the iLon100 server E_xSelect dataPointSelectionMsg = new E_xSelect(); dataPointSelectionMsg.setXSelect(xSelect); // ------------- List the data points try { // list all the available data points (needed for requesting data // about datapoints) Item_Coll allDataPointsCollection = proxy.list(dataPointSelectionMsg); // read data on the available data points Item_DataColl allDataPointValuesCollection = proxy.read(allDataPointsCollection); // convert the data values collection into a more "usable" array Item_Data[] allDatapointValues = allDataPointValuesCollection.getItem(); // iterate over the available data point values, consider the // one-valued only variables // TODO: improve the data point "extraction" to address also // multiple-valued data points for (int i = 0; i < allDatapointValues.length; i++) { // get the data point data Dp_Data itemData = (Dp_Data) allDatapointValues[i]; // get the datapoint data value E_LonString[] itemDataValues = itemData.getUCPTvalue(); // check if monoValued otherwise log a warning if (itemDataValues.length > 1) { // log the warning and skip this.logger.log(LogService.LOG_WARNING, EchelonIlon100DriverImpl.logId + "Detected un-supported multiple-valued datapoint, skipping..."); } else { // valid data point, create a corresponding DataPoint object DataPointInfo dp = new DataPointInfo(itemData.getUCPTname(), itemData.getUCPTaliasName(), itemDataValues[0].getUnit(), endpointAddress); // add the new data point dataPoints.add(dp); // log the addition this.logger.log(LogService.LOG_INFO, EchelonIlon100DriverImpl.logId + "Found new datapoint " + dp); } } } catch (RemoteException e) { // log the error this.logger.log(LogService.LOG_ERROR, EchelonIlon100DriverImpl.logId + "Unable to retrieve data for the given datapoints:" + e); } return dataPoints; } @Override public void addDriver(DataPointInfo datapoint, EchelonIlon100DriverInstance driver) { // adds a given datapoint-driver association this.datapoint2Driver.put(datapoint, driver); // fills the reverse map Map<String, DataPointInfo> driverDatapoints = this.driver2Datapoint.get(driver); if (driverDatapoints == null) { // create the new set of data points associated to the given driver driverDatapoints = new HashMap<String, DataPointInfo>(); this.driver2Datapoint.put(driver, driverDatapoints); } driverDatapoints.put(datapoint.getiLonId(), datapoint); // fill the endpoint to datapoint map Map<String, DataPointInfo> endpointDatapoints = this.endpoint2Datapoint.get(datapoint.getiLonServerEndpoint()); if (endpointDatapoints == null) { // create the new entry endpointDatapoints = new HashMap<String, DataPointInfo>(); this.endpoint2Datapoint.put(datapoint.getiLonServerEndpoint(), endpointDatapoints); } // add the datapoint entry endpointDatapoints.put(datapoint.getiLonId(), datapoint); } @Override public void removeDriver(DataPointInfo datapoint) { // removes a given datapoint-driver association EchelonIlon100DriverInstance drv = this.datapoint2Driver.remove(datapoint); if (drv != null) { // removes the datapoint from the corresponding set Map<String, DataPointInfo> driverDatapoints = this.driver2Datapoint.get(drv); driverDatapoints.remove(datapoint.getiLonId()); // if after removal the set is empty, removes the reverse map entry if (driverDatapoints.isEmpty()) this.driver2Datapoint.remove(drv); } // remove the datapoint entry from the endpoint to datapoint map Map<String, DataPointInfo> endpointDatapoints = this.endpoint2Datapoint.get(datapoint.getiLonServerEndpoint()); if (endpointDatapoints != null) { // create the new entry endpointDatapoints.remove(datapoint.getiLonId()); // if it is the last entry in the set remove the map entry if (endpointDatapoints.isEmpty()) this.endpoint2Datapoint.remove(datapoint.getiLonServerEndpoint()); } } @Override public void removeDriver(EchelonIlon100DriverInstance driver) { // removes a given driver-datapoint association Map<String, DataPointInfo> driverDatapoints = this.driver2Datapoint.remove(driver); // remove the datpoint-to-driver and the endpoint-to-datapoint // associations if (driverDatapoints != null) { for (DataPointInfo dp : driverDatapoints.values()) { // remove the datapoint-to-driver associations this.datapoint2Driver.remove(dp); // remove the datapoints from the endpoint/datapoint association Map<String, DataPointInfo> endpointDatapoints = this.endpoint2Datapoint.get(dp.getiLonServerEndpoint()); if (endpointDatapoints != null) { // create the new entry endpointDatapoints.remove(dp.getiLonId()); // if it is the last entry in the set remove the map entry if (endpointDatapoints.isEmpty()) this.endpoint2Datapoint.remove(dp.getiLonServerEndpoint()); } } } } }