Java tutorial
/** * This file is part of JEMMA - http://jemma.energy-home.org * (C) Copyright 2013 Telecom Italia (http://www.telecomitalia.it) * * JEMMA is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License (LGPL) version 3 * or later as published by the Free Software Foundation, which accompanies * this distribution and is available at http://www.gnu.org/licenses/lgpl.html * * JEMMA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License (LGPL) for more details. * */ package org.energy_home.jemma.ah.internal.zigbee; import org.energy_home.jemma.ah.cluster.zigbee.general.IdentifyQueryResponse; import org.energy_home.jemma.ah.hac.lib.ext.INetworkManager; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; import java.io.Serializable; import java.math.BigInteger; import java.net.URL; import java.util.ArrayList; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Dictionary; import java.util.Enumeration; import java.util.HashMap; import java.util.Hashtable; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Properties; import java.util.Set; import java.util.Vector; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.eclipse.equinox.internal.util.timer.Timer; import org.eclipse.equinox.internal.util.timer.TimerListener; import org.energy_home.jemma.ah.zigbee.IZclFrame; import org.energy_home.jemma.ah.zigbee.ZCL; import org.energy_home.jemma.ah.zigbee.ZclFrame; import org.energy_home.jemma.ah.zigbee.ZigBeeDevice; import org.energy_home.jemma.ah.zigbee.ZigBeeDeviceListener; import org.energy_home.jemma.ah.zigbee.ZigBeeMngrService; import org.energy_home.jemma.ah.zigbee.zcl.ZclException; import org.energy_home.jemma.ah.zigbee.zcl.ZclValidationException; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclApplianceControlClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclApplianceEventsAndAlertsClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclApplianceIdentificationClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclApplianceStatisticsClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclMeterIdentificationClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.eh.ZclPowerProfileClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclBasicClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclBasicServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclIdentifyClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclIdentifyQueryResponse; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclIdentifyServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclOnOffClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclOnOffServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclPartitionServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclTimeServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.measurement.ZclIlluminanceMeasurementClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.measurement.ZclOccupancySensingClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.measurement.ZclRelativeHumidityMeasurementClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.measurement.ZclTemperatureMeasurementClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.metering.ZclSimpleMeteringClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.metering.ZclSimpleMeteringServer; import org.energy_home.jemma.ah.zigbee.zcl.cluster.security.ZclIASZoneClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.zll.ZclLightLinkColorControlClient; import org.energy_home.jemma.ah.zigbee.zcl.cluster.general.ZclLevelControlClient; import org.energy_home.jemma.zgd.APSMessageListener; import org.energy_home.jemma.zgd.GatewayConstants; import org.energy_home.jemma.zgd.GatewayEventListener; import org.energy_home.jemma.zgd.GatewayException; import org.energy_home.jemma.zgd.GatewayInterface; import org.energy_home.jemma.zgd.jaxb.APSMessage; import org.energy_home.jemma.zgd.jaxb.APSMessageEvent; import org.energy_home.jemma.zgd.jaxb.Address; import org.energy_home.jemma.zgd.jaxb.Binding; import org.energy_home.jemma.zgd.jaxb.BindingList; import org.energy_home.jemma.zgd.jaxb.Device; import org.energy_home.jemma.zgd.jaxb.NodeDescriptor; import org.energy_home.jemma.zgd.jaxb.NodeServices; import org.energy_home.jemma.zgd.jaxb.ServiceDescriptor; import org.energy_home.jemma.zgd.jaxb.SimpleDescriptor; import org.energy_home.jemma.zgd.jaxb.Status; import org.energy_home.jemma.zgd.jaxb.TxOptions; import org.energy_home.jemma.zgd.jaxb.WSNNode; import org.energy_home.jemma.zgd.jaxb.NodeServices.ActiveEndpoints; import org.osgi.framework.ServiceReference; import org.osgi.framework.ServiceRegistration; import org.osgi.service.component.ComponentContext; import org.osgi.service.event.Event; import org.osgi.service.event.EventAdmin; import edu.emory.mathcs.backport.java.util.concurrent.locks.ReentrantReadWriteLock; class InstallationStatus implements Serializable { private static final long serialVersionUID = 3982442253260584361L; public static final int ANNOUNCEMENT_RECEIVED = 1; public static final int WAITING_FOR_SERVICES = 2; public static final int ACTIVE_ENDPOINTS_RETRIEVED = 3; public static final int WAITING_FOR_SERVICE_DESCRIPTOR = 4; public static final int WAITING_FOR_NODE_DESCRIPTOR = 5; public static final int INSTALLED = 6; public static final int SERVICE_DESCRIPTOR_RETRIEVED = 7; private int status = 0; private Address address; private int retryCounter = 5; private NodeDescriptor nodeDescriptor; private int currentEpIndex = -1; private long time; private HashMap activeEpsMap = new HashMap(); private NodeServices services; public InstallationStatus(Address a) { this.address = a; } public int getStatus() { return status; } public Address getAddress() { return this.address; } public void setCurrentService(int i) { this.currentEpIndex = i; } public int getRetryCounter() { return retryCounter--; } public void resetRetryCounter() { retryCounter = 4; } public void setStatus(int status) { this.status = status; } public void setNodeDescriptor(NodeDescriptor node) { this.nodeDescriptor = node; } public void setNodeServices(NodeServices services) { this.services = services; } public NodeServices getNodeServices() { return this.services; } public int getCurrentService() { return this.currentEpIndex; } public NodeDescriptor getNodeDescriptor() { return this.nodeDescriptor; } public long getTime() { return time; } public void refreshTime() { this.time = System.currentTimeMillis(); } public ServiceDescriptor getServiceDescriptor(short endPoint) { return (ServiceDescriptor) this.activeEpsMap.get(new Short(endPoint)); } public void putServiceDescriptor(short endPoint, ServiceDescriptor service) { this.activeEpsMap.put(new Short(endPoint), service); } public void addServiceDescriptor(short endPoint, ServiceDescriptor service) { this.activeEpsMap.put(new Short(endPoint), service); } public String toString() { return "InstallationStatus [ieee=" + ZigBeeManagerImpl.getIeeeAddressHex(this.getAddress()) + ", status: " + this.status + "]"; } } class ActiveEpInstallationStatus { short activeEndPoint; ServiceDescriptor service; } public class ZigBeeManagerImpl implements TimerListener, APSMessageListener, GatewayEventListener, ZigBeeMngrService, INetworkManager { private Timer timer; private Vector zigbeeDevices = new Vector(); // private Hashtable ieee2service = new Hashtable(); private Hashtable ieee2devices = new Hashtable(); private Hashtable ieee2sr = new Hashtable(); /** * Used to track the status of a ZigBee device installation phase. This is a * dictionary indexed on ieee address { String ieeeAddress, * InstallationStatus status } */ private Hashtable devicesUnderInstallation = new Hashtable(); private LinkedList discoveredNodesQueue = new LinkedList(); private LinkedList inProcessNode = new LinkedList(); private Hashtable installedDevices = new Hashtable(); private boolean enableRxTxLogs = true; private boolean enableLockingLogs = false; private boolean enableNotifyFrameLogs = false; private boolean enableDiscoveryLogs = true; private boolean enableDsLogs = false; private boolean enableAllClusters = false; private boolean enableEnergyAtHomeClusters = true; private static final int JGalReconnectTimer = 2; private static final int discoveryTimer = 3; private static final int permitJoinAllTimer = 4; private static final int galCommandTimer = 5; private GatewayInterface gateway; // FIXME: currently we use EP 8 because of a problem in the GAL private short localEndpoint = 1; protected long callbackId = -1; SimpleDescriptor sd = null; private long timeout = 7000; private int timeoutOffset = 2; int galRunning = 0; // for accessing the data structures private ReentrantReadWriteLock rwLock = new ReentrantReadWriteLock(); private Object sLock = new Object(); // for DS bind and unbind methods private ComponentContext ctxt; /** * By default use the nvram. */ private boolean useNVMNetworkSetting = true; private boolean handleMultipleEps = true; /** * enable/disable caching in memory of the information related to the discovered ZigBee * Nodes (Node Descriptor, Active EPs and Simple Descriptors). If this variable is * enabled, the node InstallationStatus instance is put in an Hashtable indexed on * the ZigBee node IEEE address. */ private boolean cacheDiscoveryInfos = true; /** * enable/disable persistence on the cached InstallationStatus objects. * This flag is meaningful only if cacheDiscoveryInfos is true. */ private boolean dumpDiscoveryInfos = true; /** * If false, only the sleeping end-devices descriptors are made persistent * and retrieved back on startup. * This flag doesn't have any effect if dumpDiscoveryInfos is false. */ private boolean dumpAllDevices = false; private Properties properties; private EventAdmin eventAdmin; private static final Log log = LogFactory.getLog(ZigBeeManagerImpl.class); public static final String propertyFilename = "it.telecomitalia.ah.zigbee.properties"; private String propertiesFilename = "."; private ZigBeeManagerProperties cmProps = new ZigBeeManagerProperties(); /** * The name of the file used to store the cache of discovered devices. */ private String cacheFilename = "cache.dump"; private File cacheFile = null; protected void activate(ComponentContext ctxt, Map props) { rwLock.writeLock().lock(); try { this.ctxt = ctxt; if (enableDsLogs) log.debug("activated"); this.propertiesFilename = this.ctxt.getBundleContext().getProperty("osgi.instance.area") + propertyFilename; this.cacheFile = this.ctxt.getBundleContext().getDataFile(cacheFilename); update(props); handleBundleUpgrade(); if (cacheDiscoveryInfos) { loadDiscoveredDevicesDb(); } if (!isGalRunning()) { this.bindGal(); } } finally { rwLock.writeLock().unlock(); } } private void handleBundleUpgrade() { this.loadProperties(); } private void update(Map props) { if (enableDsLogs) log.debug("received configuration"); boolean enableLqi = cmProps.isLqiEnabled(); cmProps.update(props); if (cmProps.isLqiEnabled() != enableLqi) { if (isGalRunning()) { if (cmProps.isLqiEnabled()) { timerStart(discoveryTimer, cmProps.getInitialDiscoveryDelay()); } else { timerCancel(discoveryTimer); } } if (enableDsLogs) log.debug("updated enableLqi to '" + cmProps.isLqiEnabled() + "'"); } } protected void deactivate(ComponentContext ctxt) { rwLock.writeLock().lock(); try { if (enableDsLogs) log.debug("deactivated"); cancelAllTimers(); try { unbindGal(); } catch (Exception e) { log.error("error unbinding gal", e); } if (cacheDiscoveryInfos) { dumpDiscoveredDevicesDb(false); } } finally { rwLock.writeLock().unlock(); } } protected void modified(ComponentContext ctxt, Map props) { rwLock.writeLock().lock(); try { update(props); } finally { rwLock.writeLock().unlock(); } } protected void setTimer(Timer timer) { synchronized (sLock) { this.timer = timer; } } protected void unsetTimer(Timer timer) { synchronized (sLock) { this.timer = null; } } private void timerStart(int event, int timePeriod) { synchronized (sLock) { Timer time = (Timer) getTimer(); time.notifyAfter(this, timePeriod, event); } } private Timer getTimer() { return timer; } public void setEventAdmin(EventAdmin eventAdmin) { synchronized (sLock) { this.eventAdmin = eventAdmin; } } public synchronized void unsetEventAdmin(EventAdmin eventAdmin) { synchronized (sLock) { if (this.eventAdmin == eventAdmin) this.eventAdmin = null; } } private void timerCancel(int event) { synchronized (sLock) { Timer time = (Timer) getTimer(); time.removeListener(this, event); } } private void cancelAllTimers() { timerCancel(discoveryTimer); timerCancel(JGalReconnectTimer); timerCancel(permitJoinAllTimer); timerCancel(galCommandTimer); } private void printAPSMessageEvent(APSMessageEvent msg) { String s = ""; s += getIeeeAddressHex(msg.getSourceAddress()) + ":" + " Thr " + Thread.currentThread().getId() + ": notifyAPSMessage()" + " " + msg.getClusterID() + " "; s += Hex.byteToHex(msg.getData(), 0); log.debug(s); } private boolean trackNode; private String galIeeeAddress; /** * Utility method for retrieving the ieee address from the Address class * * @param address * The Address class instance * * @return A string representing the hex representation (16 Hex digits) of * the IEEE address. */ public static final String getIeeeAddressHex(Address address) { if (address == null) return null; if (address.getIeeeAddress() != null) { String ieee = address.getIeeeAddress().toString(16).toUpperCase(); ieee = Utils.padding[ieee.length()] + ieee; return ieee; } else return null; } public static final String getNodePid(Address address) { if (address == null) return null; if (address.getIeeeAddress() != null) { String ieee = address.getIeeeAddress().toString(16).toUpperCase(); ieee = Utils.padding[ieee.length()] + ieee; return ieee; } else return null; } protected static String getIeeeAddress(Address a) { if (a.getIeeeAddress() != null) return a.getIeeeAddress().toString(); else return null; } /** * Called when a message has been received from ZigBee */ public void notifyAPSMessage(APSMessageEvent msg) { if (enableNotifyFrameLogs) this.printAPSMessageEvent(msg); // forward the message to the peer device Address srcAddress = msg.getSourceAddress(); String nodePid = getNodePid(srcAddress); if (nodePid == null) { log.debug("message discarded because the src node ieee address is not present"); return; } if ((log != null) && (enableNotifyFrameLogs)) { log.debug(getIeeeAddressHex(srcAddress) + ": Thr " + Thread.currentThread().getId() + ": messageReceived()"); } if (msg.getDestinationEndpoint() == 0xFF) { handleBroadcastMessages(msg); return; } rwLock.readLock().lock(); if (enableLockingLogs) { if (rwLock.getReadLockCount() > 1) { log.debug("Thr: " + Thread.currentThread().getId() + ": There are multiple read lock" + rwLock.getReadLockCount()); } } // Drop messages that doesn't belong to the exported clusters if (enableNotifyFrameLogs) this.printAPSMessageEvent(msg); try { Vector devices = (Vector) ieee2devices.get(nodePid); ZclFrame zclFrame = new ZclFrame(msg.getData()); int clusterID = msg.getClusterID(); if (!checkGatewaySimpleDescriptor(clusterID, zclFrame)) { // FIXME: qui dovremmo dare un errore differente a seconda se il comando' e' generale e manufacturer specific IZclFrame zclResponseFrame = this.getDefaultResponse(zclFrame, ZCL.UNSUP_CLUSTER_COMMAND); log.error("APS message coming from clusterID 0x" + Hex.toHexString(clusterID, 4) + ". This clusterId is not supported by the gateway"); this.post(msg, zclResponseFrame); return; } if (devices != null) { Iterator it = devices.iterator(); boolean epFound = false; while (it.hasNext()) { ZigBeeDeviceImpl device = (ZigBeeDeviceImpl) it.next(); if (device.getEp() == msg.getSourceEndpoint()) { if (enableNotifyFrameLogs) { log.debug("notifyZclFrame() : Thr " + Thread.currentThread().getId() + " " + msg.getClusterID() + " message to ep " + device.getEp()); } try { device.notifyZclFrame((short) msg.getClusterID(), zclFrame); } catch (ZclException e) { if (!zclFrame.isDefaultResponseDisabled()) { IZclFrame zclResponseFrame = this.getDefaultResponse(zclFrame, e.getStatusCode()); this.post(msg, zclResponseFrame); log.error(getIeeeAddressHex(srcAddress) + ": messageReceived(): Sent to device a default response with status code " + e.getStatusCode()); } } if (enableNotifyFrameLogs) { log.debug("after notifyZclFrame() : Thr " + Thread.currentThread().getId() + " " + msg.getClusterID() + " message to ep " + device.getEp()); } epFound = true; break; } } if (!epFound && log.isDebugEnabled()) log.error("not found any matching ep for the incoming message"); } else { IZclFrame zclResponseFrame; InstallationStatus installationStatus = this.getInstallingDevice(srcAddress); if (installationStatus != null) { log.error(getIeeeAddressHex(srcAddress) + ": received a message from a node that is not installed. Reply with TIMEOUT"); zclResponseFrame = this.getDefaultResponse(zclFrame, 0x94); } else { log.error("received a message from an unknown node " + getIeeeAddressHex(srcAddress) + " . Reply with TIMEOUT"); zclResponseFrame = this.getDefaultResponse(zclFrame, 0x94); } this.post(msg, zclResponseFrame); } } finally { if (enableLockingLogs) { log.debug("Thr: " + Thread.currentThread().getId() + ": unlocking and read lock count is: " + rwLock.getReadLockCount()); } rwLock.readLock().unlock(); } if (enableNotifyFrameLogs) { log.debug(getIeeeAddressHex(msg.getSourceAddress()) + ": " + " Thr " + Thread.currentThread().getId() + ": leave notifyAPSMessage()"); } } private boolean checkGatewaySimpleDescriptor(int clusterID, IZclFrame zclFrame) { if (sd != null) { List clusters; if (zclFrame.isClientToServer()) { clusters = sd.getApplicationInputCluster(); } else { clusters = sd.getApplicationOutputCluster(); } if ((clusters == null) || (clusters != null) && (!clusters.contains(clusterID))) return false; return true; } return false; } private IZclFrame getDefaultResponse(IZclFrame zclFrame, int statusCode) { IZclFrame responseZclFrame = zclFrame.createResponseFrame(2); responseZclFrame.setCommandId(ZCL.ZclDefaultRsp); responseZclFrame.appendUInt8(zclFrame.getCommandId()); responseZclFrame.setFrameType(IZclFrame.GENERAL_COMMAND); responseZclFrame.appendUInt8(statusCode); return responseZclFrame; } public void inquiryCompleted(int inquiryStatus) { log.debug("inquiryCompleted: not used!"); } public void nodeDiscovered(Status status, WSNNode node) { rwLock.writeLock().lock(); try { if (status.getCode() != GatewayConstants.SUCCESS) { log.error("called nodeDiscovered with status different from SUCCESS, message is '" + status.getMessage() + "'"); return; } Address a = node.getAddress(); if (enableDiscoveryLogs) log.info(getIeeeAddressHex(a) + ": node discovered"); // skip the coordinator if (a.getNetworkAddress().intValue() == 0) { this.galIeeeAddress = getIeeeAddressHex(a); if (enableDiscoveryLogs) log.debug("discovered node with address 0. Skipping it"); return; } nodeDiscovered(a); } finally { rwLock.writeLock().unlock(); } } private void nodeDiscovered(Address a) { if (enableDiscoveryLogs) this.printTables(); String nodePid = getNodePid(a); Vector devices = (Vector) this.getDevices(nodePid); if (devices == null) { // This is a new node if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": announcement from a new node"); // starts installation process InstallationStatus installationStatus = this.getInstallingDevice(a); if (installationStatus == null) { if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": discovered new device ... installing it"); installationStatus = this.addInstallingDevice(a); installationStatus.refreshTime(); installationStatus.setStatus(InstallationStatus.ANNOUNCEMENT_RECEIVED); this.discoveredNodesQueue.addLast(installationStatus); } else { if (installationStatus.getStatus() == InstallationStatus.ANNOUNCEMENT_RECEIVED) { if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": duplicate announcement"); long age = System.currentTimeMillis() - installationStatus.getTime(); if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": the announcement has an age of " + age + " ms"); if (age > 20000) { if (!this.discoveredNodesQueue.contains(installationStatus)) { this.discoveredNodesQueue.addLast(installationStatus); } else { log.error(getIeeeAddressHex(a) + ": too old ... restartarting discovery"); } } } else { if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": discovery process in progress for device "); } } this.handleNextDiscoveredNode(); } else { if (enableDiscoveryLogs) log.info(getIeeeAddressHex(a) + ": received announcement from an already known node"); // notifies all devices Iterator it = devices.iterator(); while (it.hasNext()) { ZigBeeDeviceImpl device = (ZigBeeDeviceImpl) it.next(); device.announce(); } } } private void printTables() { Collection nodes = getNodes(); log.debug("devices (" + nodes.size() + "):"); for (Iterator iterator = nodes.iterator(); iterator.hasNext();) { Vector devices = (Vector) iterator.next(); for (Iterator iterator2 = devices.iterator(); iterator2.hasNext();) { ZigBeeDeviceImpl device = (ZigBeeDeviceImpl) iterator2.next(); if (enableDiscoveryLogs) log.debug("\t" + getIeeeAddressHex(device.getServiceDescriptor().getAddress())); } } log.debug("inProcessNode (" + inProcessNode.size() + "):"); for (Iterator iterator = inProcessNode.iterator(); iterator.hasNext();) { log.debug("\t" + ((InstallationStatus) iterator.next()).toString()); } log.debug("discoveredNodesQueue(" + discoveredNodesQueue.size() + "):"); for (Iterator iterator = discoveredNodesQueue.iterator(); iterator.hasNext();) { log.debug("\t" + ((InstallationStatus) iterator.next()).toString()); } log.debug("devicesUnderInstallation (" + devicesUnderInstallation.size() + "):"); for (Iterator iterator = devicesUnderInstallation.values().iterator(); iterator.hasNext();) { log.debug("\t" + ((InstallationStatus) iterator.next()).toString()); } log.debug("installedDevices (" + installedDevices.size() + "):"); for (Iterator iterator = installedDevices.values().iterator(); iterator.hasNext();) { log.debug("\t" + ((InstallationStatus) iterator.next()).toString()); } } private void startNodeDiscoveryProcess(InstallationStatus installationStatus) { Address a = installationStatus.getAddress(); String nodePid = getNodePid(installationStatus.getAddress()); Vector devices = (Vector) this.getDevices(nodePid); if (devices == null) { try { if (enableDiscoveryLogs) { log.debug(getIeeeAddressHex(a) + ": beginning device discovery"); } installationStatus.setStatus(InstallationStatus.WAITING_FOR_NODE_DESCRIPTOR); gateway.getNodeDescriptor(timeout, a); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); if (enableDiscoveryLogs) { log.debug(getIeeeAddressHex(a) + ": called getNodeDescriptor()"); } } catch (Exception e) { this.terminateDeviceDiscovery(installationStatus); this.handleNextDiscoveredNode(); } } } public void servicesDiscovered(Status status, NodeServices services) { if (status.getCode() != GatewayConstants.SUCCESS) { rwLock.writeLock().lock(); try { this.timerCancel(galCommandTimer); // in case of failure services is null and there is no way to // retrieve the Address so try to guess it. InstallationStatus installingDevice = this .getInstallingDevice(InstallationStatus.WAITING_FOR_SERVICES); Address a = installingDevice.getAddress(); if (installingDevice != null) { log.error(getIeeeAddressHex(a) + ": servicesDiscovered callback returned error code " + status.getCode() + "'. Guessed address '" + getIeeeAddressHex(a)); // retries until retry counter goes to 0 if (installingDevice.getRetryCounter() > 0) { try { log.debug(getIeeeAddressHex(a) + ": retry startServiceDiscovery()"); gateway.startServiceDiscovery(timeout, a); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); return; } catch (Exception e) { log.error("exception in startServiceDiscovery() ", e); } } // abort installation of this node log.error(getIeeeAddressHex(a) + ": too many retries for getting services"); this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); return; } } finally { rwLock.writeLock().unlock(); } return; } Address a = services.getAddress(); synchronized (rwLock) { this.timerCancel(galCommandTimer); InstallationStatus installingDevice = this.getInstallingDevice(a); if (installingDevice == null) { log.error(getIeeeAddressHex(a) + ": unsolicited serviceDiscovered()"); this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); return; } if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(installingDevice.getAddress()) + ": discovered " + services.getActiveEndpoints().size() + " endpoint(s)"); if ((services.getActiveEndpoints().size() > 1) && (!handleMultipleEps)) { log.warn("sorry but currently (in this version) we handle only the first one!"); } installingDevice.setNodeServices(services); installingDevice.setStatus(InstallationStatus.ACTIVE_ENDPOINTS_RETRIEVED); installingDevice.resetRetryCounter(); List activeEndpoints = services.getActiveEndpoints(); for (int i = 0; i < activeEndpoints.size(); i++) { installingDevice.setCurrentService(i); ActiveEndpoints ep = (ActiveEndpoints) activeEndpoints.get(i); try { installingDevice.setStatus(InstallationStatus.WAITING_FOR_SERVICE_DESCRIPTOR); if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": getting Service Descriptor for EP " + ep.getEndPoint()); gateway.getServiceDescriptor(timeout, a, ep.getEndPoint()); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); } catch (Exception e) { this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); } break; } } } public void serviceDescriptorRetrieved(Status status, ServiceDescriptor service) { rwLock.writeLock().lock(); try { this.timerCancel(galCommandTimer); if (status.getCode() != GatewayConstants.SUCCESS) { // in case of failure services is null and there is no way to // retrieve the Address so try to guess it. InstallationStatus installingDevice = this .getInstallingDevice(InstallationStatus.WAITING_FOR_SERVICE_DESCRIPTOR); if (installingDevice != null) { Address a = installingDevice.getAddress(); log.error(getIeeeAddressHex(a) + ": serviceDescriptorRetrieved callback returned error code " + status.getCode() + "'. Guessed address '" + getIeeeAddressHex(a)); // retries until retry counter goes to 0 if (installingDevice.getRetryCounter() > 0) { try { int i = installingDevice.getCurrentService(); if (i >= 0) { NodeServices services = installingDevice.getNodeServices(); ActiveEndpoints ep = (ActiveEndpoints) services.getActiveEndpoints().get(i); if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": getting Service Descriptor for EP " + ep.getEndPoint()); gateway.getServiceDescriptor(timeout, a, ep.getEndPoint()); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); return; } else { log.fatal(getIeeeAddressHex(a) + ": wrong ep index stored into InstallationStatus. Abort installation of this node"); } } catch (Exception e) { log.error(getIeeeAddressHex(a) + ": exception in startServiceDiscovery(). Abort installation of this node", e); } } else { int i = installingDevice.getCurrentService(); if (i >= 0) { NodeServices services = installingDevice.getNodeServices(); ActiveEndpoints ep = (ActiveEndpoints) services.getActiveEndpoints().get(i); log.error(getIeeeAddressHex(a) + ": too many retries for serviceDescriptor for ep " + ep.getEndPoint() + ". Abort installation of this node"); } } this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); return; } else { log.fatal( "unable to find an associated installation status: unsolicited serviceDescriptorRetrieved()"); } return; } Address a = service.getAddress(); String ieeeAddress = getIeeeAddressHex(a); InstallationStatus installingDevice = this.getInstallingDevice(a); installingDevice.addServiceDescriptor(service.getEndPoint(), service); if (handleMultipleEps) { int retrievedServiceIndex = installingDevice.getCurrentService(); installingDevice.resetRetryCounter(); List activeEndpoints = installingDevice.getNodeServices().getActiveEndpoints(); retrievedServiceIndex++; if (retrievedServiceIndex < activeEndpoints.size()) { try { installingDevice.setCurrentService(retrievedServiceIndex); installingDevice.setStatus(InstallationStatus.WAITING_FOR_SERVICE_DESCRIPTOR); ActiveEndpoints ep = (ActiveEndpoints) activeEndpoints.get(retrievedServiceIndex); if (enableDiscoveryLogs) log.debug(getIeeeAddressHex(a) + ": getting Service Descriptor for EP " + ep.getEndPoint()); gateway.getServiceDescriptor(timeout, a, ep.getEndPoint()); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); return; } catch (Exception e) { this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); } } } installingDevice.setStatus(InstallationStatus.INSTALLED); try { this.finalizeNode(installingDevice); } catch (Exception e) { log.error("exception", e); if (cacheDiscoveryInfos) { // dump to file the currently discovered devices descriptors this.updateDiscoveredDevicesDb(ieeeAddress, installingDevice); } this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); return; } if (cacheDiscoveryInfos) { // dump to file the currently discovered devices descriptors this.updateDiscoveredDevicesDb(ieeeAddress, installingDevice); } this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); } finally { rwLock.writeLock().unlock(); } } /** * Stores the newly discovered infos into a persistent storage * * @param ieeeAddress * @param installingDevice */ private void updateDiscoveredDevicesDb(String ieeeAddress, InstallationStatus installingDevice) { InstallationStatus installationStatus = (InstallationStatus) this.installedDevices.get(ieeeAddress); if (installationStatus == null) { this.installedDevices.put(ieeeAddress, installingDevice); if (dumpDiscoveryInfos) { // dump on filesystem only the sleeping end devices. dumpDiscoveredDevicesDb(dumpAllDevices); } } } private void finalizeNode(InstallationStatus installingDevice) { NodeServices nodeServices = installingDevice.getNodeServices(); List activeEndpoints = nodeServices.getActiveEndpoints(); Address a = nodeServices.getAddress(); String nodePid = getNodePid(a); String[] endPoints = new String[activeEndpoints.size()]; for (int i = 0; i < activeEndpoints.size(); i++) { ActiveEndpoints ep = (ActiveEndpoints) activeEndpoints.get(i); ServiceDescriptor service = installingDevice.getServiceDescriptor(ep.getEndPoint()); endPoints[i] = service.getSimpleDescriptor().getApplicationProfileIdentifier() + "." + service.getSimpleDescriptor().getApplicationDeviceIdentifier() + "." + new Short(service.getEndPoint()); } for (int i = 0; i < activeEndpoints.size(); i++) { ActiveEndpoints ep = (ActiveEndpoints) activeEndpoints.get(i); ServiceDescriptor service = installingDevice.getServiceDescriptor(ep.getEndPoint()); if (service == null) { log.error(getIeeeAddressHex(installingDevice.getAddress()) + ": Service descriptor is null while finalizing ep " + ep.getEndPoint() + ": skip it!"); continue; } ZigBeeDevice device = createDevice(installingDevice, service, endPoints); if (device != null) { // add the device to our db Vector devices = this.getDevices(nodePid); if (devices == null) { devices = new Vector(); this.ieee2devices.put(nodePid, devices); } devices.add(device); } } } private void handleNextDiscoveredNode() { if (inProcessNode.size() > 0) { // still discovering node properties return; } InstallationStatus is = null; try { is = (InstallationStatus) this.discoveredNodesQueue.removeFirst(); inProcessNode.addLast(is); this.startNodeDiscoveryProcess(is); } catch (NoSuchElementException e) { if (enableDiscoveryLogs) log.debug("installation queue is empty"); return; } } private Vector getDevices(String nodePid) { return (Vector) this.ieee2devices.get(nodePid); } protected Collection getNodes() { return ieee2devices.values(); } /** * Creates a new service representing the newly detected ZigBee device. This * OSGi Device follows the Device Admin sepcification. * * @param node * @param ep * @return */ private ZigBeeDevice createDevice(InstallationStatus installingDevice, ServiceDescriptor service, String[] endPoints) { Hashtable deviceProps = new Hashtable(); NodeDescriptor node = installingDevice.getNodeDescriptor(); int deviceId = service.getSimpleDescriptor().getApplicationDeviceIdentifier().intValue(); int profileId = service.getSimpleDescriptor().getApplicationProfileIdentifier().intValue(); String ieeeAddr = getIeeeAddress(service.getAddress()); if (enableDiscoveryLogs) { log.debug("new node detected ieeeAddr = '" + ieeeAddr + "', "); log.debug("profileId = '" + Integer.toString(profileId) + "', "); log.debug("deviceId = '" + Integer.toString(deviceId) + "', "); } if (node == null) { log.fatal("here node should not be null"); } if (enableDiscoveryLogs) log.debug("manufacturerCode = '" + node.getManufacturerCode() + "', "); deviceProps.put(org.osgi.service.device.Constants.DEVICE_CATEGORY, "ZigBee"); deviceProps.put(org.osgi.service.device.Constants.DEVICE_SERIAL, ieeeAddr); deviceProps.put(org.osgi.framework.Constants.SERVICE_PID, ieeeAddr); deviceProps.put("zigbee.device.ep.id", new Short(service.getEndPoint())); deviceProps.put("zigbee.device.profile.id", new Integer(profileId)); deviceProps.put("zigbee.device.device.id", new Integer(deviceId)); deviceProps.put("zigbee.device.eps", endPoints); deviceProps.put("zigbee.device.eps.number", new Integer(endPoints.length)); deviceProps.put("zigbee.device.manufacturer.id", node.getManufacturerCode()); ZigBeeDeviceImpl device = new ZigBeeDeviceImpl(this, timer, installingDevice.getNodeServices(), node, service); // this registration starts the driver location process! ServiceRegistration deviceServiceReg = ctxt.getBundleContext().registerService(ZigBeeDevice.class.getName(), device, deviceProps); Vector deviceRegs = null; String nodePid = getNodePid(service.getAddress()); synchronized (ieee2sr) { deviceRegs = (Vector) ieee2sr.get(nodePid); if (deviceRegs == null) { deviceRegs = new Vector(); ieee2sr.put(nodePid, deviceRegs); } } deviceRegs.add(deviceServiceReg); return device; } private boolean add(ZigBeeDevice hacDevice) { zigbeeDevices.add(hacDevice); return true; } protected boolean post(ZigBeeDevice device, short profileId, short clusterId, IZclFrame zclFrame) { if (gateway == null) { log.error("post(): jgal not bound"); return false; } APSMessage msg = new APSMessage(); ServiceDescriptor service = device.getServiceDescriptor(); // patch to clean the network address, otherwise the GAL uses it, // instead of ieee address // TODO: move this patch when the service descriptor is assigned to // the // device. Address a = service.getAddress(); a.setNetworkAddress(null); if (enableRxTxLogs) log.debug(getIeeeAddressHex(a) + ": sending message"); msg.setDestinationAddressMode(new Long(GatewayConstants.EXTENDED_ADDRESS_MODE)); msg.setDestinationAddress(service.getAddress()); msg.setDestinationEndpoint(service.getEndPoint()); msg.setSourceEndpoint(localEndpoint); msg.setClusterID(clusterId & 0xffff); msg.setProfileID(new Integer(profileId & 0xffff)); msg.setData(zclFrame.getData()); TxOptions tx = new TxOptions(); tx.setAcknowledged(true); tx.setPermitFragmentation(false); tx.setSecurityEnabled(false); tx.setUseNetworkKey(true); msg.setTxOptions(tx); msg.setRadius((short) 10); try { gateway.sendAPSMessage(msg); } catch (IOException e) { log.error("IOException, message not sent :" + e.getMessage()); return false; } catch (GatewayException e) { log.error("GatewayException, message not sent :" + e.getMessage()); return false; } catch (Exception e) { log.error("Exception, message not sent :" + e.getMessage(), e); return false; } return true; } protected boolean post(APSMessageEvent srcMsgEvent, IZclFrame zclFrame) { if (gateway == null) { log.error("post(): jgal not bound"); return false; } APSMessage msg = new APSMessage(); msg.setDestinationAddressMode(new Long(GatewayConstants.EXTENDED_ADDRESS_MODE)); msg.setDestinationAddress(srcMsgEvent.getSourceAddress()); msg.setDestinationEndpoint(srcMsgEvent.getSourceEndpoint()); msg.setSourceEndpoint(localEndpoint); msg.setClusterID(srcMsgEvent.getClusterID() & 0xffff); msg.setProfileID(srcMsgEvent.getProfileID()); msg.setData(zclFrame.getData()); TxOptions tx = new TxOptions(); tx.setAcknowledged(true); tx.setPermitFragmentation(false); tx.setSecurityEnabled(false); tx.setUseNetworkKey(true); msg.setTxOptions(tx); msg.setRadius((short) 10); try { gateway.sendAPSMessage(msg); } catch (IOException e) { log.error("IOException, message not sent :" + e.getMessage()); return false; } catch (GatewayException e) { log.error("GatewayException, message not sent :" + e.getMessage()); return false; } catch (Exception e) { log.error("Exception, message not sent :" + e.getMessage(), e); return false; } log.debug("Thread " + Thread.currentThread().getId() + ": message sent"); return true; } public void noDriverFound(ZigBeeDevice device) { log.error("no driver found for device " + device.getIeeeAddress()); } public void attach(ZigBeeDevice device) { // a driver has been attached to the device. try { add(device); } catch (Exception e) { log.error("element not present in intstalling Devices list"); } } public void timer(int event) { switch (event) { case JGalReconnectTimer: synchronized (sLock) { rwLock.writeLock().lock(); boolean galBound = false; try { galBound = bindGal(); } finally { rwLock.writeLock().unlock(); } if (!galBound) { tryReconnectToJGal(cmProps.getReconnectToJGalDelay()); } } break; case discoveryTimer: synchronized (sLock) { if (gateway != null) { try { log.debug("started discovery"); int discoveryTimeout = ((cmProps.getDiscoveryDelay() - 2) > 10 ? 10 : (cmProps.getDiscoveryDelay() - 2)) * 1000; gateway.startNodeDiscovery(discoveryTimeout, GatewayConstants.DISCOVERY_LQI); } catch (Exception e) { tryReconnectToJGal(cmProps.getReconnectToJGalDelay()); break; } if (cmProps.getDiscoveryDelay() > 0) timerStart(discoveryTimer, cmProps.getDiscoveryDelay()); } } break; case permitJoinAllTimer: rwLock.writeLock().lock(); try { timerCancel(permitJoinAllTimer); this.terminateDeviceDiscoveryForJoinedDevices(); this.postEvent("ah/zigbee/CLOSE_NETWORK", null); } finally { rwLock.writeLock().unlock(); } break; case galCommandTimer: rwLock.writeLock().lock(); log.warn("galCommandTimer expired"); // if this timer expires, it means that the GAL was not sending a // calback for node descriptor or service discriptor or active // endpoints. We need to start to process a new node. try { if (this.inProcessNode.size() == 0) { log.fatal("galCommandTimer expired but no nodes are in the inProcessNode queue"); // TESTME: we start the discovery process on a new node. this.handleNextDiscoveredNode(); break; } // try to recover InstallationStatus installingDevice = (InstallationStatus) this.inProcessNode.getFirst(); log.error(getIeeeAddressHex(installingDevice.getAddress()) + ": no response from jgal. Try to recover."); Status status = new Status(); switch (installingDevice.getStatus()) { case InstallationStatus.WAITING_FOR_NODE_DESCRIPTOR: // Simulates the callback with GatewayConstants.TIMEOUT error status.setCode((short) GatewayConstants.TIMEOUT); this.nodeDescriptorRetrieved(status, null); break; case InstallationStatus.WAITING_FOR_SERVICES: // Simulates the callback with GatewayConstants.TIMEOUT error status.setCode((short) GatewayConstants.TIMEOUT); this.servicesDiscovered(status, null); break; case InstallationStatus.WAITING_FOR_SERVICE_DESCRIPTOR: // Simulates the callback with GatewayConstants.TIMEOUT error status.setCode((short) GatewayConstants.TIMEOUT); this.serviceDescriptorRetrieved(status, null); break; default: log.debug("no actions to recover!"); this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); } } finally { rwLock.writeLock().unlock(); } break; } } protected void availStateUpdated(ZigBeeDevice device, int availState) { if (availState == ZigBeeDeviceImpl.Disconnected) { log.debug("device " + device.getIeeeAddress() + " is unreachable"); } else if (availState == ZigBeeDeviceImpl.Connected) { log.debug("device " + device.getIeeeAddress() + " is now reachable"); } } protected ZigBeeDeviceListener getService(ServiceReference driverRef) { return (ZigBeeDeviceListener) ctxt.getBundleContext().getService(driverRef); } protected void setGatewayInterface(GatewayInterface r) { synchronized (sLock) { gateway = r; } } protected void unsetGatewayInterface(GatewayInterface r) { synchronized (sLock) { if (r == gateway) { gateway = null; } } } public void gatewayStartResult(Status status) { if (status.getCode() == 0) { if (log != null) log.info("zigbee network up and running."); synchronized (sLock) { this.galRunning = 2; if (!getUseNVM()) this.setUseNVM(true); if (cmProps.isLqiEnabled()) timerStart(discoveryTimer, cmProps.getInitialDiscoveryDelay()); // register any cached node finalizeNodes(); } } else { if (status.getCode() == GatewayConstants.NETWORK_FAILURE) { synchronized (sLock) { log.error("ZigBeeGateway started with status code: NETWORK_FAILURE"); } } else { log.info("ZigBeeGateway started with status code " + status.getCode()); } } } public void dongleResetResult(Status status) { if (status.getCode() == GatewayConstants.SUCCESS) { synchronized (sLock) { try { if (gateway == null) { log.warn("dongleResetResult(): gateway is null"); return; } // configure local endpoint try { gateway.clearEndpoint(localEndpoint); } catch (Exception e) { log.error("exception in clearEndpoint of endpoint " + localEndpoint + " " + e.getMessage()); } // TODO the following input clusters have to be configurable // from Config Admin or props file // FIXME: this simple descriptor MUST be set to null when the gal is detached. sd = new SimpleDescriptor(); sd.setEndPoint(new Short(localEndpoint)); sd.setApplicationDeviceVersion(new Short((short) 0x01)); sd.setApplicationDeviceIdentifier(new Integer(0x0050)); // ESP sd.setApplicationProfileIdentifier(new Integer(0x0104)); // HA List outputClusters = sd.getApplicationOutputCluster(); List inputClusters = sd.getApplicationInputCluster(); // TODO the following input clusters have to be configurable // from Config Admin or props file outputClusters.add(new Integer(ZclSimpleMeteringClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclMeterIdentificationClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclPowerProfileClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclApplianceStatisticsClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclApplianceControlClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclApplianceIdentificationClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclApplianceEventsAndAlertsClient.CLUSTER_ID)); if (enableEnergyAtHomeClusters) { // This is the list of Client side clusters supported by E@H outputClusters.add(new Integer(ZclBasicClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclIdentifyClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclOnOffClient.CLUSTER_ID)); } if (enableAllClusters) { outputClusters.add(new Integer(ZclOccupancySensingClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclIASZoneClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclTemperatureMeasurementClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclIlluminanceMeasurementClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclRelativeHumidityMeasurementClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclLightLinkColorControlClient.CLUSTER_ID)); outputClusters.add(new Integer(ZclLevelControlClient.CLUSTER_ID)); outputClusters.add(new Integer(0x0201)); //Thermostat cluster } // This is the list of Server side clusters supported by E@H inputClusters.add(new Integer(ZclBasicServer.CLUSTER_ID)); inputClusters.add(new Integer(ZclIdentifyServer.CLUSTER_ID)); inputClusters.add(new Integer(ZclTimeServer.CLUSTER_ID)); if (enableEnergyAtHomeClusters) { inputClusters.add(new Integer(ZclSimpleMeteringServer.CLUSTER_ID)); inputClusters.add(new Integer(ZclPartitionServer.CLUSTER_ID)); } if (enableAllClusters) { inputClusters.add(new Integer(ZclOnOffServer.CLUSTER_ID)); } localEndpoint = gateway.configureEndpoint(100, sd); // start discovery announcement gateway.startNodeDiscovery(0, GatewayConstants.DISCOVERY_ANNOUNCEMENTS); // subscribe liveness gateway.subscribeNodeRemoval(0, GatewayConstants.DISCOVERY_FRESHNESS | GatewayConstants.DISCOVERY_LEAVE); // register local callback this.callbackId = gateway.createAPSCallback(localEndpoint, this); if (this.callbackId == -1) { log.fatal("createAPSCallback returned -1"); } // start gateway device gateway.startGatewayDevice(0); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } } else { log.error("dongleResetResult returned error code " + status.getCode()); } } public void leaveResult(Status status) { rwLock.writeLock().lock(); rwLock.writeLock().unlock(); } public void permitJoinResult(Status status) { synchronized (sLock) { log.debug("t" + 5); log.debug("permit join returned status " + status.getCode()); if (status.getCode() == 0) { this.postEvent("ah/zigbee/OPEN_NETWORK", null); } } } private void tryReconnectToJGal(int delay) { timerStart(JGalReconnectTimer, delay); if (log != null) log.info("retry to reconnect to ZigbeeGatewayDevice in " + delay + " s"); } private boolean bindGal() { if ((this.gateway == null) || (this.timer == null)) { return false; } String testevent = this.ctxt.getBundleContext().getProperty("it.telecomitalia.ah.zigbee.zcl.testevent"); try { if (Boolean.parseBoolean(testevent)) { // enables only those clusters meaningful for the HA 1.2 Testevent this.enableAllClusters = false; this.enableEnergyAtHomeClusters = false; } else { // enables all clusters this.enableAllClusters = true; this.enableEnergyAtHomeClusters = true; } } finally { // do nothing } this.terminateDeviceDiscoveryAll(); try { gateway.setGatewayEventListener(this); } catch (Exception e1) { // TODO Auto-generated catch block e1.printStackTrace(); // FIXME: what I have to do here? } try { if (getUseNVM()) { // use the non volatile memory ram log.info("starting zigbee gateway. Use NVRAM"); gateway.resetDongle(0, GatewayConstants.RESET_USE_NVMEMORY); } else { log.info("starting zigbee gateway. Don't use NVRAM"); gateway.resetDongle(0, GatewayConstants.RESET_COMMISSIONING_ASSOCIATION); } } catch (IOException e) { log.error("exception when starting zigbee gateway: " + e.getMessage()); return false; } catch (GatewayException e) { log.error("exception when starting zigbee gateway: " + e.getMessage()); return false; } catch (Exception e) { log.error("exception when starting zigbee gateway: " + e.getMessage()); return false; } galRunning = 1; // connected! return true; } private boolean unbindGal() { if (enableDsLogs) log.debug("unbindGal"); this.terminateDeviceDiscoveryAll(); try { gateway.setGatewayEventListener(null); gateway.startNodeDiscovery(0, GatewayConstants.DISCOVERY_STOP); } catch (Exception e) { log.error("exception while calling startNodeDiscovery() or stopping node discovery: " + e.getMessage()); } try { if (callbackId != -1) gateway.deleteCallback(this.callbackId); callbackId = -1; } catch (Exception e) { log.error(e); } unregisterAllDevices(); galRunning = 0; return true; } private void unregisterAllDevices() { Set keys = ieee2sr.keySet(); for (Iterator iterator = keys.iterator(); iterator.hasNext();) { try { String nodePid = (String) iterator.next(); Vector deviceRegs = (Vector) this.ieee2sr.get(nodePid); if (deviceRegs != null) { this.ieee2devices.remove(nodePid); for (Iterator iterator2 = deviceRegs.iterator(); iterator2.hasNext();) { ServiceRegistration deviceReg = (ServiceRegistration) iterator2.next(); deviceReg.unregister(); } } } catch (ConcurrentModificationException e) { e.printStackTrace(); return; } } ieee2sr.clear(); } public void nodeDescriptorRetrieved(Status status, NodeDescriptor node) { // guess the installing device because the node descriptor doesn't // contain the address of the device rwLock.writeLock().lock(); try { this.timerCancel(galCommandTimer); if (gateway == null) { log.warn("in nodeDescriptorRetrieved() detected that gateway has been removed"); return; } InstallationStatus installingDevice = this .getInstallingDevice(InstallationStatus.WAITING_FOR_NODE_DESCRIPTOR); if (installingDevice == null) { log.warn("received a node descriptor from an unsolicited node"); this.handleNextDiscoveredNode(); return; } String nodePid = getNodePid(installingDevice.getAddress()); String nodeIeeeAddressHex = getIeeeAddressHex(installingDevice.getAddress()); if (status.getCode() != 0) { log.error(nodeIeeeAddressHex + ": nodeDescriptorRetrieved callback returned error code " + status.getCode() + "'. Guessed pid '" + nodePid); if (installingDevice.getRetryCounter() > 0) { try { gateway.getNodeDescriptor(timeout, installingDevice.getAddress()); log.debug(nodeIeeeAddressHex + ": called getNodeDescriptor()"); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); return; } catch (Exception e) { log.error(e); } } // abort installation of this node log.error(nodeIeeeAddressHex + ": too many retries for getting node descriptor"); this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); return; } if (enableDiscoveryLogs) log.debug(nodeIeeeAddressHex + ": retrieved node descriptor"); // update the state installingDevice.setStatus(InstallationStatus.WAITING_FOR_SERVICES); installingDevice.resetRetryCounter(); installingDevice.setNodeDescriptor(node); try { if (enableDiscoveryLogs) log.debug(nodeIeeeAddressHex + ": startServiceDiscovery()"); gateway.startServiceDiscovery(timeout, installingDevice.getAddress()); timerStart(galCommandTimer, (int) (timeout / 1000) + timeoutOffset); } catch (Exception e) { this.terminateDeviceDiscovery(installingDevice); this.handleNextDiscoveredNode(); } } finally { rwLock.writeLock().unlock(); } } protected Dictionary getConfiguration() { Dictionary config = new Hashtable(); config.put("it.telecomitalia.ah.adapter.zigbee.lqi", cmProps.isLqiEnabled() + ""); config.put("it.telecomitalia.ah.adapter.zigbee.reconnect", cmProps.getReconnectToJGalDelay() + ""); config.put("it.telecomitalia.ah.adapter.zigbee.discovery.delay", cmProps.getDiscoveryDelay() + ""); config.put("it.telecomitalia.ah.adapter.zigbee.discovery.initialdelay", cmProps.getInitialDiscoveryDelay() + ""); return config; } public void nodeRemoved(Status status, WSNNode node) { rwLock.writeLock().lock(); try { // notifies the node // TODO: if the node has short address 0 it means that the dongle is // crashed. if (status.getCode() == 0) { String nodePid = getNodePid(node.getAddress()); Vector deviceRegs = (Vector) this.ieee2sr.get(nodePid); if (deviceRegs != null) { log.debug(getIeeeAddressHex(node.getAddress()) + ": node has been removed"); this.ieee2sr.remove(nodePid); this.ieee2devices.remove(nodePid); for (Iterator iterator = deviceRegs.iterator(); iterator.hasNext();) { ServiceRegistration deviceReg = (ServiceRegistration) iterator.next(); deviceReg.unregister(); } } else { log.warn(nodePid + ": unknown node has been removed"); } if (cacheDiscoveryInfos) { this.installedDevices.remove(nodePid); if (dumpDiscoveryInfos) { this.dumpDiscoveredDevicesDb(dumpAllDevices); } } } } finally { rwLock.writeLock().unlock(); } } protected boolean isGalRunning() { return this.galRunning == 2; } public void permitJoin(short duration) throws Exception { rwLock.writeLock().lock(); try { if (gateway == null) { throw new Exception("zgd not started"); } gateway.permitJoinAll(timeout, duration); if (duration == 0) { this.postEvent("ah/zigbee/CLOSE_NETWORK", null); this.terminateDeviceDiscoveryForJoinedDevices(); this.handleNextDiscoveredNode(); } else { timerCancel(permitJoinAllTimer); this.postEvent("ah/zigbee/OPEN_NETWORK", null); timerStart(permitJoinAllTimer, duration); } } catch (Exception e) { log.error("Exception in PermitJoin()", e); throw e; } finally { rwLock.writeLock().unlock(); } } /** * Stops the discovery process for all those devices that were not in the * network */ private void terminateDeviceDiscoveryAll() { this.timerCancel(galCommandTimer); // send leave to any new node under processing for (Iterator iterator = this.inProcessNode.iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); // FIXME: Devo farlo per tutti? this.terminateDeviceDiscovery(installationStatus); } // the following device are not under processing for (Iterator iterator = this.discoveredNodesQueue.iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); this.terminateDeviceDiscovery(installationStatus); } if (inProcessNode.size() > 0) { log.fatal("inProcessNode is not empty!"); } if (discoveredNodesQueue.size() > 0) { log.fatal("discoveredNodesQueue is not empty!"); } if (devicesUnderInstallation.size() > 0) { log.fatal("devicesUnderInstallation is not empty!"); } this.inProcessNode.clear(); this.discoveredNodesQueue.clear(); this.devicesUnderInstallation.clear(); } // remove from our list (and send a leave) only those devices just entered // because the network has been opened private void terminateDeviceDiscoveryForJoinedDevices() { // send leave to any new node under processing for (Iterator iterator = this.inProcessNode.iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); if (this.hasJoined(installationStatus)) { this.timerCancel(galCommandTimer); this.terminateDeviceDiscovery(installationStatus); } } // the following device are not under processing. for (Iterator iterator = this.discoveredNodesQueue.iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); if (this.hasJoined(installationStatus)) { this.terminateDeviceDiscovery(installationStatus); } } } /** * Given an InstallationStatus remove the device from any queue. * * @param installationStatus */ private void terminateDeviceDiscovery(InstallationStatus installationStatus) { String nodePid = getNodePid(installationStatus.getAddress()); if (hasJoined(installationStatus)) { try { log.debug("in terminateDeviceDiscovery() sending leave to node " + getIeeeAddressHex(installationStatus.getAddress())); gateway.leave(100, installationStatus.getAddress()); } catch (Exception e) { log.error(e); } } this.devicesUnderInstallation.remove(nodePid); this.discoveredNodesQueue.remove(installationStatus); this.inProcessNode.remove(installationStatus); } private boolean hasJoined(InstallationStatus installationStatus) { return false; } private InstallationStatus addInstallingDevice(Address a) { rwLock.writeLock().lock(); try { String nodePid = getNodePid(a); InstallationStatus installationStatus = new InstallationStatus(a); this.devicesUnderInstallation.put(nodePid, installationStatus); return installationStatus; } finally { rwLock.writeLock().unlock(); } } private InstallationStatus getInstallingDevice(Address a) { synchronized (rwLock) { String nodePid = getNodePid(a); return (InstallationStatus) this.devicesUnderInstallation.get(nodePid); } } private InstallationStatus getInstallingDevice(int deviceStatus) { Enumeration keys = this.devicesUnderInstallation.keys(); while (keys.hasMoreElements()) { String nodePid = (String) keys.nextElement(); InstallationStatus installationStatus = (InstallationStatus) this.devicesUnderInstallation.get(nodePid); if (installationStatus.getStatus() == deviceStatus) { // this.devicesUnderInstallation.remove(nodeIeeeAddress); // log.debug("error in getting EPs, removing node '" + // nodeIeeeAddress + "' from installing devices"); return installationStatus; } } return null; } /** * The following method is called when a broadcast message arrives. It * handles the IdentifyQuery message. FIXME: this must not be here but the * IdentifyQuery must be handled by the driver. * * @param msg */ private void handleBroadcastMessages(APSMessageEvent msg) { ZclFrame zclFrame = new ZclFrame(msg.getData()); int commandId = zclFrame.getCommandId(); if (zclFrame.isServerToClient()) { log.error("invalid direction field in broadcast message"); return; } IZclFrame zclResponseFrame = null; switch (commandId) { case 1: IdentifyQueryResponse r = new IdentifyQueryResponse(0xFFFF); int size; try { size = ZclIdentifyQueryResponse.zclSize(r); zclResponseFrame = zclFrame.createResponseFrame(size); zclResponseFrame.setCommandId(0); ZclIdentifyQueryResponse.zclSerialize(zclResponseFrame, r); } catch (ZclValidationException e) { log.error(e); } break; } if (zclResponseFrame != null) { APSMessage responseMsg = new APSMessage(); if (log.isDebugEnabled()) log.debug("Gateway" + ": Sync T > 0x" + Hex.toHexString(msg.getClusterID() & 0xffff, 2) + "(clusterId) " + zclResponseFrame.toString()); log.debug("sending message to node " + getIeeeAddressHex(msg.getSourceAddress())); responseMsg.setDestinationAddressMode(msg.getSourceAddressMode()); responseMsg.setDestinationAddress(msg.getSourceAddress()); responseMsg.setDestinationEndpoint(msg.getSourceEndpoint()); responseMsg.setSourceEndpoint(localEndpoint); responseMsg.setClusterID(msg.getClusterID() & 0xffff); responseMsg.setProfileID(msg.getProfileID() & 0xffff); responseMsg.setData(zclResponseFrame.getData()); TxOptions tx = new TxOptions(); tx.setAcknowledged(true); tx.setPermitFragmentation(false); tx.setSecurityEnabled(false); tx.setUseNetworkKey(true); responseMsg.setTxOptions(tx); responseMsg.setRadius((short) 10); try { gateway.sendAPSMessage(responseMsg); } catch (IOException e) { log.error("IOException, message not sent :" + e.getMessage()); } catch (GatewayException e) { log.error("GatewayException, message not sent :" + e.getMessage()); } catch (Exception e) { log.error("Exception, message not sent :" + e.getMessage(), e); } } } private void unregisterDevice(String nodePid) { Vector deviceRegs = (Vector) this.ieee2sr.get(nodePid); if (deviceRegs != null) { this.ieee2sr.remove(nodePid); this.ieee2devices.remove(nodePid); for (Iterator iterator = deviceRegs.iterator(); iterator.hasNext();) { ServiceRegistration deviceReg = (ServiceRegistration) iterator.next(); deviceReg.unregister(); } } if (cacheDiscoveryInfos) { InstallationStatus installationStatus = (InstallationStatus) this.installedDevices.remove(nodePid); if (this.enableDiscoveryLogs && installationStatus != null) log.debug(nodePid + ": removed Node from installedDevices table"); if (dumpDiscoveryInfos && (installationStatus != null)) { this.dumpDiscoveredDevicesDb(dumpAllDevices); } } } public void removeDevice(String nodePid) throws Exception { rwLock.writeLock().lock(); try { Vector devices = (Vector) ieee2devices.get(nodePid); if (devices == null || devices.size() == 0) return; log.debug(nodePid + ": deleting node with ieee address "); log.debug("in terminateDeviceDiscovery() sending leave to node " + nodePid); try { gateway.leave(timeout, ((ZigBeeDevice) devices.get(0)).getServiceDescriptor().getAddress()); } catch (Exception e) { log.error(nodePid + ": exception in leave(): " + e.getMessage()); } this.unregisterDevice(nodePid); } finally { rwLock.writeLock().unlock(); } } protected void remove(ZigBeeDevice device) throws Exception { ServiceDescriptor service = device.getServiceDescriptor(); this.removeDevice(getIeeeAddressHex(service.getAddress())); } public void enableNVM() { synchronized (sLock) { this.setUseNVM(true); } } public void disableNVM() { synchronized (sLock) { this.setUseNVM(false); } } public boolean getNVMStatus() { synchronized (sLock) { return this.getUseNVM(); } } private void setUseNVM(boolean useNVM) { log.debug("setUseNVM to " + useNVM); this.useNVMNetworkSetting = useNVM; this.saveProperties(); } private boolean getUseNVM() { log.debug("returned UseNVM: " + this.useNVMNetworkSetting); return this.useNVMNetworkSetting; } private boolean useDataFileDir = false; private void loadDiscoveredDevicesDb() { List sleepingEndDevices = new ArrayList(); ObjectInputStream in = null; try { in = new ObjectInputStream(new FileInputStream(cacheFile)); sleepingEndDevices = (ArrayList) in.readObject(); in.close(); for (Iterator iterator = sleepingEndDevices.iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); Address a = installationStatus.getAddress(); String ieeeAddress = getIeeeAddressHex(a); installedDevices.put(ieeeAddress, installationStatus); } } catch (FileNotFoundException e) { log.error("cache file not found"); } catch (Exception e) { log.error("exeption reading cache dump. Wrong format?", e); } finally { if (in != null) { try { in.close(); } catch (IOException e) { log.error("IOException closing cache dump", e); } } } } /** * Dump to the filesystem the set of devices that have been discovered since * now If the saveAll flag is false, only sleeping end devices are actually * stored. */ private void dumpDiscoveredDevicesDb(boolean saveAll) { if (enableDiscoveryLogs) { if (saveAll) log.debug("Dump persistently ALL discovered devices"); else log.debug("Dump persistently SLEEPING discovered devices"); } ObjectOutputStream out = null; List devicesToDump = new ArrayList(); try { for (Iterator iterator = this.installedDevices.values().iterator(); iterator.hasNext();) { InstallationStatus installationStatus = (InstallationStatus) iterator.next(); if (installationStatus.getStatus() == InstallationStatus.INSTALLED) { if (!installationStatus.getNodeDescriptor().getMACCapabilityFlag().isReceiverOnWhenIdle() || saveAll) { devicesToDump.add(installationStatus); } } } out = new ObjectOutputStream(new FileOutputStream(cacheFile)); out.writeObject(devicesToDump); out.close(); } catch (IOException e) { log.error("IOException writing cache dump", e); } catch (Exception e) { log.error("exception writing cache dump", e); } finally { if (out != null) { try { out.close(); } catch (IOException e) { log.error("IOException closing cache dump", e); } } } } private void finalizeNodes() { Iterator it = installedDevices.values().iterator(); while (it.hasNext()) { InstallationStatus installationStatus = (InstallationStatus) it.next(); try { finalizeNode(installationStatus); } catch (Exception e) { log.error("exception while finalizing Node " + getIeeeAddress(installationStatus.getAddress())); } } } private void loadProperties() { if (this.properties != null) return; this.properties = new Properties(); File propertiesFile; try { if (useDataFileDir) propertiesFile = this.ctxt.getBundleContext().getDataFile(propertyFilename); else { URL url = new URL(propertiesFilename); propertiesFile = new File(url.getFile()); } this.properties.load(new FileInputStream(propertiesFile)); this.useNVMNetworkSetting = Boolean.parseBoolean(this.properties.getProperty("usenvm", "false")); } catch (Exception e) { try { this.setUseNVM(false); } catch (Exception e1) { log.fatal("unable to write back the property file: " + e1.getMessage()); } } } private boolean saveProperties() { if (properties == null) properties = new Properties(); try { if (this.properties != null) { File propertiesFile; if (useDataFileDir) { propertiesFile = this.ctxt.getBundleContext().getDataFile(propertyFilename); } else { URL url = new URL(propertiesFilename); propertiesFile = new File(url.getFile()); } this.properties.setProperty("usenvm", Boolean.toString(getUseNVM())); properties.store(new FileOutputStream(propertiesFile), null); } } catch (IOException e) { log.error("unable to save " + propertiesFilename, e); return false; } return true; } protected void addToBinding(String nodeIeeeAdddress, short nodeEp, short clusterId) throws IOException, GatewayException, Exception { if (this.gateway != null) { Vector devices = (Vector) this.getDevices(nodeIeeeAdddress); if (devices == null) { throw new Exception("node not found"); } BindingList bl = new BindingList(); Binding b = new Binding(); bl.getBinding().add(b); b.setClusterID(clusterId & 0xffff); b.setSourceEndpoint(nodeEp); b.setSourceIEEEAddress(new BigInteger(nodeIeeeAdddress, 16)); Device dev = new Device(); dev.setAddress(new BigInteger(this.galIeeeAddress, 16)); dev.setEndpoint(this.localEndpoint); b.getDeviceDestination().add(dev); this.gateway.addBinding(0, b); } } protected void removeToBinding(String nodeIeeeAdddress, short nodeEp, short clusterId) throws IOException, GatewayException, Exception { if (this.gateway != null) { Vector devices = (Vector) this.getDevices(nodeIeeeAdddress); if (devices == null) { throw new Exception("node not found"); } BindingList bl = new BindingList(); Binding b = new Binding(); bl.getBinding().add(b); b.setClusterID(clusterId & 0xffff); b.setSourceEndpoint(nodeEp); b.setSourceIEEEAddress(new BigInteger(nodeIeeeAdddress, 16)); Device dev = new Device(); dev.setAddress(new BigInteger(this.galIeeeAddress, 16)); dev.setEndpoint(this.localEndpoint); b.getDeviceDestination().add(dev); this.gateway.removeBinding(0, b); } } private void postEvent(String topic, Map props) { if (this.eventAdmin != null) { try { this.eventAdmin.postEvent(new Event(topic, props)); } catch (Exception e) { log.error(e); } } } private long lastOpenRequestTimestamp = 0; private static final short DEFAULT_OPEN_REQUEST_DURATION = 180; public boolean isNetworkOpen() { return System.currentTimeMillis() - lastOpenRequestTimestamp < DEFAULT_OPEN_REQUEST_DURATION * 1000; } public void openNetwork() throws Exception { openNetwork(DEFAULT_OPEN_REQUEST_DURATION); lastOpenRequestTimestamp = System.currentTimeMillis(); } public void openNetwork(int duration) throws Exception { permitJoin((short) duration); lastOpenRequestTimestamp = System.currentTimeMillis(); } public void closeNetwork() throws Exception { permitJoin((short) 0); lastOpenRequestTimestamp = 0; } public void bindingResult(Status status) { // TODO Auto-generated method stub } public void unbindingResult(Status status) { // TODO Auto-generated method stub log.debug("Received unbindingResult()"); } public void nodeBindingsRetrieved(Status status, BindingList bindings) { // TODO Auto-generated method stub log.debug("Received nodeBindingsRetrieved()"); } public boolean isRxTxLogEnabled() { return this.enableRxTxLogs; } public boolean isNotifyFrameLogEnabled() { return this.enableNotifyFrameLogs; } }