/***************************************************************** SPINE - Signal Processing In-Node Environment is a framework that allows dynamic on node configuration for feature extraction and a OtA protocol for the management for WSN Copyright (C) 2007 Telecom Italia S.p.A. GNU Lesser General Public License This library is free software; you can redistribute modify it under the terms of the sub-license (below). *****************************************************************/ /***************************************************************** BSPAN - BlueTooth Sensor Processing for Android is a framework that extends the SPINE framework to work on Android and the Android Bluetooth communication services. Copyright (C) 2011 The National Center for Telehealth and Technology Eclipse Public License 1.0 (EPL-1.0) This library is free software; you can redistribute it and/or modify it under the terms of the Eclipse Public License as published by the Free Software Foundation, version 1.0 of the License. The Eclipse Public License is a reciprocal license, under Section 3. REQUIREMENTS iv) states that source code for the Program is available from such Contributor, and informs licensees how to obtain it in a reasonable manner on or through a medium customarily used for software exchange. Post your updates and modifications to our GitHub or email to t2@tee2.org. This library is distributed WITHOUT ANY WARRANTY; without the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the Eclipse Public License 1.0 (EPL-1.0) for more details. You should have received a copy of the Eclipse Public License along with this library; if not, visit http://www.opensource.org/licenses/EPL-1.0 *****************************************************************/ package spine; import jade.util.Logger; import java.util.Vector; import spine.datamodel.Address; import spine.datamodel.Data; import spine.datamodel.Node; import spine.datamodel.ServiceMessage; import spine.datamodel.functions.CodecInfo; import spine.datamodel.functions.SpineCodec; import spine.datamodel.functions.SpineObject; import spine.datamodel.serviceMessages.ServiceWarningMessage; import spine.exceptions.MethodNotSupportedException; import spine.exceptions.PacketDecodingException; import com.tilab.gal.WSNConnection; /** * This class is responsible for dispatching events on behalf of the SPINEManager * @author Fabio Bellifemine, Telecom Italia * @since 1.3 */ class EventDispatcher { private static final String TAG = "EventDispatcher"; SPINEManager spineManager; /** package-scoped constructor **/ EventDispatcher(SPINEManager spineManager) { this.spineManager = spineManager; this.spineManager.connection.setListener(new WSNConnectionListenerImpl()); } private Vector listeners = new Vector(1); // initialized to 1 element as we expect usually to have just 1 listener /** * Registers a SPINEListener with the manager instance * * @param listener the listener to register */ void addListener(SPINEListener listener) { this.listeners.addElement(listener); } /** * Deregisters a SPINEListener with the manager instance * * @param listener the listener to deregister */ void removeListener(SPINEListener listener) { this.listeners.removeElement(listener); } /* * Regarding to the 'eventType', this method notify the SPINEListeners properly, by * casting in the right way the Object 'o'. * Notice that if the eventType is SERVICE_ADV the listener is notified only if !discoveryCompleted * @param eventType * @param o * @param spineManager a reference to the SPINEManager which is used to retrieve activeNodes, baseStation, and discoveryCompleted */ void notifyListeners(short eventType, Object o) { for (int i = 0; i<this.listeners.size(); i++) switch(eventType) { case SPINEPacketsConstants.SERVICE_ADV: if (!spineManager.isDiscoveryCompleted()) ((SPINEListener)this.listeners.elementAt(i)).newNodeDiscovered((Node)spineManager.getActiveNodes().lastElement()); break; case SPINEPacketsConstants.DATA: ((SPINEListener)this.listeners.elementAt(i)).received((Data)o); break; case SPINEPacketsConstants.SVC_MSG: if(((ServiceMessage)o).getNode() != null) ((SPINEListener)this.listeners.elementAt(i)).received((ServiceMessage)o); break; case SPINEManager.DISC_COMPL_EVT_COD: ((SPINEListener)this.listeners.elementAt(i)).discoveryCompleted((Vector)o); break; default: { ServiceMessage sm = new ServiceWarningMessage(); sm.setMessageDetail(SPINEServiceMessageConstants.UNKNOWN_PKT_RECEIVED); sm.setNode(spineManager.getBaseStation()); ((SPINEListener)this.listeners.elementAt(i)).received(sm); break; } } } private class WSNConnectionListenerImpl implements WSNConnection.Listener { /* * This method is called to notify the SPINEManager of a new SPINE message reception. */ public void messageReceived(com.tilab.gal.Message msg) { Address nodeID = new Address(msg.getSourceURL().substring(SPINEManager.URL_PREFIX.length())); SpineObject o = null; short pktType = msg.getClusterId(); short[] payloadShort = msg.getPayload(); byte[] payload = new byte[payloadShort.length]; for (int i = 0; i<payloadShort.length; i++) payload[i] = (byte)payloadShort[i]; // Util.logHexByteString(TAG, "payload Data Received:", payload); switch(pktType) { case SPINEPacketsConstants.SERVICE_ADV: { try { // dynamic class loading of the proper SpineCodec implementation EventDispatcher.this.spineManager.spineCodec = (SpineCodec)EventDispatcher.this.spineManager.htInstance.get("ServiceAdvertisement"); if (EventDispatcher.this.spineManager.spineCodec == null) { Class d = Class.forName(SPINEManager.SPINEDATACODEC_PACKAGE + "ServiceAdvertisement"); EventDispatcher.this.spineManager.spineCodec = (SpineCodec)d.newInstance(); EventDispatcher.this.spineManager.htInstance.put("ServiceAdvertisement", EventDispatcher.this.spineManager.spineCodec); } // Invoking decode and setting SpineObject data o = EventDispatcher.this.spineManager.spineCodec.decode(new Node(nodeID), payload); } catch (Exception e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } // TODO: Not sure why they don't allow disvovery messages after the official // discovery period. Anyway for testing right now I'm removing this requirement // if (!EventDispatcher.this.spineManager.discoveryCompleted) { boolean alreadyDiscovered = false; for(int i = 0; i<EventDispatcher.this.spineManager.activeNodes.size(); i++) { if(((Node)EventDispatcher.this.spineManager.activeNodes.elementAt(i)).getPhysicalID().equals(nodeID)) { alreadyDiscovered = true; break; } } if (!alreadyDiscovered) EventDispatcher.this.spineManager.activeNodes.addElement((Node)o); // } break; } case SPINEPacketsConstants.DATA: { if(EventDispatcher.this.spineManager.getNodeByPhysicalID(nodeID) == null) { // TODO: for now ignore this - it will be fixed when we get discovery working (I think) // if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) // SPINEManager.getLogger().log(Constants.ANDROIDLOGTEMP, "Unexpected DATA message received " + "[from node:" + nodeID + "]"); // return; //throw new UnexpectedMessageException("Unexpected DATA message received " + "[from node:" + nodeID + "]"); } byte functionCode; // Setting functionCode try { // dynamic class loading of the proper CodecInformation CodecInfo codecInformation = (CodecInfo)EventDispatcher.this.spineManager.htInstance.get("CodecInformation"); if (codecInformation == null) { Class g = Class.forName(SPINEManager.SPINEDATACODEC_PACKAGE + "CodecInformation"); codecInformation = (CodecInfo)g.newInstance(); EventDispatcher.this.spineManager.htInstance.put("CodecInformation", codecInformation); } functionCode = codecInformation.getFunctionCode(payload); } catch (Exception e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } try { // dynamic class loading of the proper SpineCodec implementation String className = SPINEFunctionConstants.functionCodeToString(functionCode) + SPINEManager.SPINEDATA_FUNCT_CLASSNAME_SUFFIX; EventDispatcher.this.spineManager.spineCodec = (SpineCodec)EventDispatcher.this.spineManager.htInstance.get (className); if (EventDispatcher.this.spineManager.spineCodec == null){ Class d = Class.forName(SPINEManager.SPINEDATACODEC_PACKAGE + className); EventDispatcher.this.spineManager.spineCodec = (SpineCodec)d.newInstance(); EventDispatcher.this.spineManager.htInstance.put(className, EventDispatcher.this.spineManager.spineCodec); } // Invoking decode and setting SpineObject data Node node = EventDispatcher.this.spineManager.getNodeByPhysicalID(nodeID); if (node != null) { // Don't try to decode unless we have a valid node o = EventDispatcher.this.spineManager.spineCodec.decode(node, payload); } } catch (PacketDecodingException e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } catch (MethodNotSupportedException e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } catch (InstantiationException e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } catch (IllegalAccessException e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } catch (ClassNotFoundException e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } break; } case SPINEPacketsConstants.SVC_MSG: { byte serviceMessageType; // Setting functionCode try { // dynamic class loading of the proper CodecInformation CodecInfo codecInformation = (CodecInfo)EventDispatcher.this.spineManager.htInstance.get("CodecInformation"); if (codecInformation == null) { Class g = Class.forName(SPINEManager.SPINEDATACODEC_PACKAGE + "CodecInformation"); codecInformation = (CodecInfo)g.newInstance(); EventDispatcher.this.spineManager.htInstance.put("CodecInformation", codecInformation); } serviceMessageType = codecInformation.getServiceMessageType(payload); } catch (Exception e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } try { // dynamic class loading of the proper SpineCodec implementation String className = SPINEServiceMessageConstants.serviceMessageTypeToString(serviceMessageType) + SPINEManager.SPINE_SERVICE_MESSAGE_CLASSNAME_SUFFIX; EventDispatcher.this.spineManager.spineCodec = (SpineCodec)EventDispatcher.this.spineManager.htInstance.get(className); if (EventDispatcher.this.spineManager.spineCodec == null){ Class d = Class.forName(SPINEManager.SPINE_SERVICE_MESSAGE_CODEC_PACKAGE + className); EventDispatcher.this.spineManager.spineCodec = (SpineCodec)d.newInstance(); EventDispatcher.this.spineManager.htInstance.put(className, EventDispatcher.this.spineManager.spineCodec); } // Invoking decode and setting SpineObject data o = EventDispatcher.this.spineManager.spineCodec.decode(EventDispatcher.this.spineManager.getNodeByPhysicalID(nodeID), payload); } catch (Exception e) { e.printStackTrace(); if (SPINEManager.getLogger().isLoggable(Logger.SEVERE)) SPINEManager.getLogger().log(Logger.SEVERE, e.getMessage()); return; } break; } default: break; } // SPINEListeners are notified of the reception from the node 'nodeID' of some data if (o != null) { notifyListeners(pktType, o); } //System.out.println("Memory available: " + Runtime.getRuntime().freeMemory() + " KB"); // call to the garbage collector to favour the recycling of unused memory // System.gc(); } } }