/* * Copyright 1990-2009 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License version * 2 only, as published by the Free Software Foundation. * * This program 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 * General Public License version 2 for more details (a copy is * included at /legal/license.txt). * * You should have received a copy of the GNU General Public License * version 2 along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA * 02110-1301 USA * * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa * Clara, CA 95054 or visit www.sun.com if you need additional * information or have any questions. */ package com.sun.javame.sensor; import java.util.*; import javax.microedition.sensor.*; /** * Pair contains condition and its listener. * * The vector of pairs contaons conditions which awaiting the met * and listener for execute when condition is met. */ class ConditionListenerPair { private ConditionListener listener; private Condition condition; private Data data; public ConditionListenerPair(ConditionListener listener, Condition condition) { this.listener = listener; this.condition = condition; } public Condition getCondition() { return condition; } public ConditionListener getListener() { return listener; } public void setData(Data data) { this.data = data; } public Data getData() { return data; } public boolean equals(ConditionListenerPair pair) { return ((this.listener == pair.listener) && (this.condition == pair.condition)); } public boolean matches(ConditionListener listener) { return (this.listener == listener); } public boolean matches(ConditionListener listener, Condition condition) { return ((this.listener == listener) && (this.condition == condition)); } } public class ChannelImpl implements Channel, ChannelInfo{ private class ChannelEventQueue implements Runnable { /** Channel message queue. */ private Vector messages = new Vector(); /** Stop flag. */ private boolean isStop = true; /** Flag of notification. */ private boolean isNotify; /** Thread of event queue. */ private Thread eventQueueThread; /** Channel state for data collecting */ private int stateData = StatesEvents.CHANNEL_IDLE; /** * Put message to queue. * * @param msg message code */ private synchronized void putMessage(int msg) { messages.addElement(new Integer(msg)); isNotify = true; notify(); } /** * Gets the current data state. */ private int getStateData() { return stateData; } /** * Sets the current data state. */ private synchronized void setStateData(int state) { stateData = state; } /** * Start process the queue. */ private synchronized void start() { isStop = false; eventQueueThread = new Thread(this); eventQueueThread.start(); } /** * Stop process the queue. */ private void stop() { if (!isStop) { synchronized (this) { isStop = true; isNotify = true; notify(); } try { eventQueueThread.join(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } /** * Process the queue. */ public void run() { while (!isStop) { synchronized (this) { if (isStop) { break; } if (messages.size() == 0) { isNotify = false; while (!isNotify) { try { wait(); } catch (InterruptedException ex) { Thread.currentThread().interrupt(); } } } } // Process messages while (!isStop && messages.size() > 0) { int msg = ((Integer)messages.firstElement()).intValue(); messages.removeElementAt(0); switch (msg) { case StatesEvents.START_GET_DATA: // start data collecting from sensor if (stateData == StatesEvents.CHANNEL_IDLE) { stateData = StatesEvents.CHANNEL_WAIT_DATA; startDataCollection(); } break; case StatesEvents.RESPONSE_DATA: // data arrived from device switch (stateData) { case StatesEvents.CHANNEL_WAIT_DATA: // add data if (addData()) { // need more data channelDevice.startGetData(channelControl); } else { // return data to sensor stateData = StatesEvents.CHANNEL_IDLE; retDataToSensor(); if (isRepeat()) { stateData = StatesEvents.CHANNEL_WAIT_DATA; startDataCollection(); } } break; case StatesEvents.WAIT_STOP_DATA: // send confirmation confirmStopData(); stateData = StatesEvents.CHANNEL_IDLE; break; } break; case StatesEvents.RESPONSE_ERROR: // error arrived from device switch (stateData) { case StatesEvents.CHANNEL_WAIT_DATA: // send error to sensor reportErrorData(); break; } break; case StatesEvents.STOP_GET_DATA: // stop collecting data switch (stateData) { case StatesEvents.CHANNEL_IDLE: // send confirmation confirmStopData(); break; case StatesEvents.CHANNEL_WAIT_DATA: stateData = StatesEvents.WAIT_STOP_DATA; break; } break; } } } } } //end of private class declaration /** Measurement ranges array. */ private MeasurementRange[] ranges; /** Channel's name. */ private String name; /** Sensor's number. */ private int sensorsNumber; /** Channel's number. */ private int number; /** Channel's data type. */ private int dataType; /** Channel's scale. */ private int scale; /** Channel's unit. */ private Unit unit; /** Channel's accuracy. */ private float accuracy; /** Channel's condition array. */ private Vector conditions = new Vector(); /** Channel's met condition array. */ private Vector conditionsMet = new Vector(); /** Channel device instance. */ private ChannelDevice channelDevice; /** Sensor instance. */ private Sensor sensor; /** Return data. */ private DataImpl retData; /** End time of data collecting. */ private long endTime; /** Current value of read data items. */ private int readItems; /** Channel control. */ private ChannelControl channelControl; /** Listener dor data notification. */ private ChannelDataListener listener; /** Buffer size for data items. */ private int buffersize; /** Maximal time for data measuring. */ private long bufferingPeriod; /** Flag of timestamping. */ private boolean isTimestampIncluded; /** Flag of uncertainty including. */ private boolean isUncertaintyIncluded; /** Flag of validity including. */ private boolean isValidityIncluded; /** Repeating flag. */ private boolean isRepeat; /** Sensor event queue. */ private ChannelEventQueue eventQueue; /** * Creates a new instance of ChannelImpl. * * @param num number of sensor based 0 * @param number number of channel based 0 */ public ChannelImpl(int num, int number) { this.sensorsNumber = num; this.number = number; initFields(); eventQueue = new ChannelEventQueue(); } /** * Initializes the processing of message queue. */ boolean initChannel() { eventQueue.start(); return true; } /** * Stops the processing of message queue. */ void stopChannel() { eventQueue.stop(); } /** * Gets the channel device instance. * * @return the channel device instance */ ChannelDevice getChannelDevice() { return channelDevice; } /** * Gets the array of channel's conditions. * * @return the array of channel's conditions */ synchronized Condition[] getAllConditions() { Condition[] conds = null; if (conditions.size() > 0) { conds = new Condition[conditions.size()]; for (int i = 0; i < conds.length; i++) { conds[i] = ((ConditionListenerPair)conditions.elementAt(i)).getCondition(); } } return conds; } /* * ChannelInfo methods */ /** * Returns the accuracy of this channel. * * @return the accuracy of the channel of the sensor */ public float getAccuracy() { return accuracy; } /** * Returns the data type of the channel. * * @return the data type of the channel */ public int getDataType() { return dataType; } /** * This method returns all the measurement ranges * of this channel of the sensor. * * @return all measurement ranges of the channel */ public MeasurementRange[] getMeasurementRanges() { MeasurementRange[] retValue = new MeasurementRange[0]; if (dataType != TYPE_OBJECT && ranges != null && ranges.length > 0) { MeasurementRange curRange; retValue = new MeasurementRange[ranges.length]; for (int i = 0; i < ranges.length; i++) { curRange = ranges[i]; retValue[i] = new MeasurementRange(curRange.getSmallestValue(), curRange.getLargestValue(), curRange.getResolution()); } } return retValue; } /** * Returns the name of the channel. * * @return the name of the channel */ public String getName() { return name; } /** * Returns the scale used for the measurement values of this channel. * * @return scale */ public int getScale() { return scale; } /** * Returns the unit, in which data values are presented. * * @return the unit, in which data values of the channel are presented */ public Unit getUnit() { return unit; } /* * Channel methods */ /** * Sets a Condition object to be monitored. * * @param listener - the ConditionListener to which the * ConditionListener.conditionMet() notifications are sent * @param condition - the Condition object defining the * condition to be monitored. * @throws java.lang.NullPointerException - if the listener is null * or in the case of the application is not automatically launched * by the push mechanism and the condition is null * @throws java.lang.IllegalArgumentException - if the data type * of the channel is TYPE_INT or TYPE_DOUBLE and an ObjectCondition * is passed in; or if the data type is TYPE_OBJECT and a LimitCondition * or a RangeCondition is passed in * @throws java.lang.IllegalStateException - if the SensorConnection * is in the STATE_CLOSED state */ public synchronized void addCondition(ConditionListener listener, Condition condition) { if (listener == null) { throw new NullPointerException(); } if (sensor.getState() == SensorConnection.STATE_CLOSED) { throw new IllegalStateException(); } if (condition == null) { //IMPL_NOTE: determine here is application is //automatically launched by push mechanism throw new NullPointerException(); } /* MUST BE IMPLEMENTED: handle push */ boolean isObjCond = condition instanceof ObjectCondition; if ((isObjCond && (dataType != TYPE_OBJECT)) || (!isObjCond && (dataType == TYPE_OBJECT))) { throw new IllegalArgumentException(); } int count = conditions.size(); ConditionListenerPair pair = new ConditionListenerPair(listener, condition); for (int i = 0; i < count; i++) { if (((ConditionListenerPair)conditions.elementAt(i)).equals(pair)) { return; } } conditions.addElement(pair); channelDevice.startGetData(channelControl); } /* * ValueListener methods */ /** * Object value from channel has been received. * * @param number the channel number * @param value the object value * @param uncertainty the uncertainty of data * @param validity the validity of data */ public void condValueReceived(int number, Object[] value, float[] uncertainty, boolean[] validity) { if (conditions.size() == 0) { return; } boolean isNumeric = value instanceof Double[] || value instanceof Integer[]; double[] doubleValue = null; int dataIndex = 0; if (isNumeric) { doubleValue = new double[value.length]; for ( dataIndex = 0; dataIndex < value.length; dataIndex ++ ) { if (value instanceof Double[]) { doubleValue[dataIndex] = ((Double)value[dataIndex]).doubleValue(); } else if (value instanceof Integer[]) { doubleValue[dataIndex] = ((Integer)value[dataIndex]).doubleValue(); } } } ConditionListenerPair currPair; Condition currCond; int dataType = ChannelInfo.TYPE_OBJECT; if (value instanceof Double[]) { dataType = ChannelInfo.TYPE_DOUBLE; } else if (value instanceof Integer[]) { dataType = ChannelInfo.TYPE_INT; } boolean wasMet = false; Enumeration en = conditions.elements(); while (en.hasMoreElements()) { boolean conMet = false; currPair = (ConditionListenerPair)en.nextElement(); currCond = currPair.getCondition(); DataImpl data = new DataImpl(this, 1, dataType, true, true, true); for ( dataIndex = 0; dataIndex < value.length; dataIndex ++ ){ if ( isNumeric && currCond.isMet(doubleValue[dataIndex])){ conMet = true; break; } } if ( conMet ) { wasMet = true; data.setData(0, value[dataIndex]); data.setTimestamp(0, System.currentTimeMillis()); data.setUncertainty(0, uncertainty[dataIndex]); data.setValidity(0, validity[dataIndex]); currPair.setData(data); conditionsMet.addElement(currPair); conditions.removeElement(currPair); } } if (wasMet) { new Thread(new RunConditionMet(sensor, this)).start(); } if (conditions.size() > 0) { // some conditions were not met channelDevice.startGetData(channelControl); } } /** * Gets the condition listener pair. * * @return condition listener pair instance */ synchronized ConditionListenerPair getCondPair() { ConditionListenerPair returnValue = null; if (conditionsMet.size() > 0) { returnValue = (ConditionListenerPair)conditionsMet.firstElement(); conditionsMet.removeElementAt(0); } return returnValue; } /** * Wrong data reading. * * @param number the channel number * @param errorCode the code error of data reading */ public void dataReadError(int number, int errorCode) { // ignore } /** * Returns the ChannelInfo object associated with the Channel. * * The ChannelInfo contains the properties of the channel data. * * @return a ChannelInfo object */ public ChannelInfo getChannelInfo() { return this; } /** * Returns the Condition objects set for the given listener. * * @param listener - the ConditionListener whose Condition * objects are requested * @return the Condition objects set for the listener object. * A zero-length Condition array is returned if the given * listener has no Condition objects. * @throws java.lang.NullPointerException - if the listener is null */ public synchronized Condition[] getConditions(ConditionListener listener) { if (listener == null) { throw new NullPointerException(); } Vector mv = new Vector(); ConditionListenerPair currPair; Enumeration en = conditions.elements(); while (en.hasMoreElements()) { currPair = (ConditionListenerPair)en.nextElement(); if (currPair.matches(listener)) { mv.addElement(currPair.getCondition()); } } Condition[] conds = new Condition[mv.size()]; for (int i = 0; i < conds.length; i++) { conds[i] = (Condition)mv.elementAt(i); } return conds; } /** * This method returns a string identifying the channel * and listing all its unique conditions. * * @return an URL of channel */ public java.lang.String getChannelUrl() { return ChannelUrl.createUrl(this); } /** * Removes all Condition and ConditionListener objects * registered in this Channel. * * @throws java.lang.IllegalStateException - if the SensorConnection * is in the STATE_CLOSED state */ public synchronized void removeAllConditions() { if (sensor.getState() == SensorConnection.STATE_CLOSED) { throw new IllegalStateException(); } conditions.removeAllElements(); conditionsMet.removeAllElements(); } /** * Removes a given Condition and ConditionListener object pair * from this Channel. * * @param listener - the ConditionListener whose Condition * will be removed * @param condition - the Condition to be removed * @throws java.lang.NullPointerException - if either of the * parameters is null * @throws java.lang.IllegalStateException - if the SensorConnection * is in the STATE_CLOSED state */ public synchronized void removeCondition(ConditionListener listener, Condition condition) { if ((listener == null) || (condition == null)) { throw new NullPointerException(); } if (sensor.getState() == SensorConnection.STATE_CLOSED) { throw new IllegalStateException(); } boolean isMatch = false; ConditionListenerPair currPair; Enumeration en = conditions.elements(); while (en.hasMoreElements()) { currPair = (ConditionListenerPair)en.nextElement(); if (currPair.matches(listener, condition)) { conditions.removeElement(currPair); isMatch = true; break; // Only one pair can exist in the Vector according to the spec } } if (!isMatch) { en = conditionsMet.elements(); while (en.hasMoreElements()) { currPair = (ConditionListenerPair)en.nextElement(); if (currPair.matches(listener, condition)) { conditionsMet.removeElement(currPair); break; // Only one pair can exist in the Vector according to the spec } } } } /** * Removes a given ConditionListener and all Condition objects * associated with it. * * @param listener - the ConditionListener to be removed * @throws java.lang.NullPointerException - if the listener is null * @throws java.lang.IllegalStateException - if the SensorConnection * is in the STATE_CLOSED state */ public synchronized void removeConditionListener(ConditionListener listener) { if (listener == null) { throw new NullPointerException(); } if (sensor.getState() == SensorConnection.STATE_CLOSED) { throw new IllegalStateException(); } for (int i = 0; i < conditions.size(); i++) { if (((ConditionListenerPair)conditions.elementAt(i)).matches(listener)) { conditions.removeElementAt(i); i--; // Elements are shifted so we have to check the same index again } } } /* * Internal methods */ /** * Sets the parent sensor instance. * * @param sensor - the parent sensor instance */ void setSensor(Sensor sensor) { this.sensor = sensor; } /** * Starts the collecting data from channel. * * @param listener - the listener for data modification * @param buffersize - the maximal buffer size for data * @param bufferingPeriod - the maximal time for data * collecting or unlimited in case of < 1 * @param isTimestampIncluded - add timestamp on true * @param isUncertaintyIncluded - add uncertaintyIncluded on true * @param isValidityIncluded - add validity on true * @param isRepeat - repeat reading data on true * @param startTime - time of starting measurement */ void startGetData(ChannelDataListener listener, int buffersize, long bufferingPeriod, boolean isTimestampIncluded, boolean isUncertaintyIncluded, boolean isValidityIncluded, boolean isRepeat, long startTime) { this.listener = listener; this.buffersize = buffersize; this.bufferingPeriod = bufferingPeriod; this.isTimestampIncluded = isTimestampIncluded; this.isUncertaintyIncluded = isUncertaintyIncluded; this.isValidityIncluded = isValidityIncluded; this.isRepeat = isRepeat; if (bufferingPeriod > 0) { endTime = startTime + bufferingPeriod; } eventQueue.putMessage(StatesEvents.START_GET_DATA); } private void initFields() { ChannelModel channelModel = new ChannelModel(); doGetChannelModel(sensorsNumber, number, channelModel); name = channelModel.name; dataType = channelModel.dataType; accuracy = channelModel.accuracy; scale = channelModel.scale; unit = Unit.getUnit(channelModel.unit); ranges = channelModel.getMeasurementRanges(); channelDevice = DeviceFactory.generateChannel(sensorsNumber, number); } /** * Prepare to collecting data from channel. * */ void startDataCollection() { retData = new DataImpl(this, buffersize, getDataType(), isTimestampIncluded, isUncertaintyIncluded, isValidityIncluded); readItems = 0; channelControl = new ChannelControl(this, channelDevice, isTimestampIncluded, isUncertaintyIncluded, isValidityIncluded); channelDevice.startGetData(channelControl); } /** * Add data from device to buffer. * * @return true when more data is need, * else false */ boolean addData() { long currTimeStamp = 0L; if (isTimestampIncluded) { currTimeStamp = channelControl.getTimestamp(); } if (bufferingPeriod > 0) { // check timeout if (isTimestampIncluded) { if (endTime < currTimeStamp) { return false; } } else { // checking timeout when !isTimestampIncluded if (endTime < System.currentTimeMillis()) { return false; } } } Object[] data = channelControl.getReadData(); if (data == null) { return false; } int i = readItems; int copyLen = data.length; if (copyLen < 1) { return false; } if (i + copyLen > buffersize) { readItems = buffersize; copyLen = buffersize - i; } else { readItems += copyLen; } for ( int dataNum = 0; dataNum < copyLen; dataNum ++ ){ // checking timeout retData.setData( i + dataNum, data[dataNum] ); if (isTimestampIncluded) { retData.setTimestamp(i + dataNum, currTimeStamp); } if (isUncertaintyIncluded) { retData.setUncertainty(i + dataNum, channelControl.getReadUncertainty()[dataNum]); } if (isValidityIncluded) { retData.setValidity(i + dataNum, channelControl.getReadValidity()[dataNum]); } } if (readItems >= buffersize) { // buffer is full return false; } return true; } /** * Stops the collecting data from channel. * */ void stopGetData() { eventQueue.putMessage(StatesEvents.STOP_GET_DATA); } private native void doGetChannelModel(int sensorNumber, int channelNumber, ChannelModel m); /** * Puts the message into event queue. * */ void putMessage(int msg) { eventQueue.putMessage(msg); } /** * Returns data to sensor. * */ void retDataToSensor() { listener.channelDataReceived(number, retData); } /** * Confirms the stopping of data processing. * */ void confirmStopData() { sensor.confirmStopData(number); } /** * Reports about error data. * */ void reportErrorData() { listener.channelErrorReceived(number, channelControl.getErrorCode(), channelControl.getTimestamp()); } /** * Checks is need to repeat reading data. * */ boolean isRepeat() { return isRepeat; } } class ChannelControl implements ValueListener { /** Channel instance. */ private ChannelImpl channel; /** Channel device instance. */ private ChannelDevice channelDevice; /** Dava value. */ private Object[] dataValue; /** Timerstamp. */ private long timeStamp; /** Uncertainty. */ private float[] uncertainty; /** Validity. */ private boolean[] validity; /** Timestamp including flag. */ private boolean isTimestampIncluded; /** Uncertainty including flag. */ private boolean isUncertaintyIncluded; /** Validity including flag. */ private boolean isValidityIncluded; /** Error code. */ private int errorCode; /** * Initialization. * * @param channel - ChannelImpl instance * @param channelDevice - ChannelDevice instance * @param isTimestampIncluded - if true timestamps should be * included in returned Data objects * @param isUncertaintyIncluded - if true uncertainties should be * included in returned Data objects * @param isValidityIncluded - if true validities should be * included in returned Data objects */ ChannelControl(ChannelImpl channel, ChannelDevice channelDevice, boolean isTimestampIncluded, boolean isUncertaintyIncluded, boolean isValidityIncluded) { this.channel = channel; this.channelDevice = channelDevice; this.isTimestampIncluded = isTimestampIncluded; this.isUncertaintyIncluded = isUncertaintyIncluded; this.isValidityIncluded = isValidityIncluded; errorCode = ValueListener.DATA_READ_OK; } /** * Gets data that has been read. * * @return data object from channel */ synchronized Object[] getReadData() { return dataValue; } /** * Gets last error code. * * @return data object from channel */ synchronized int getErrorCode() { return errorCode; } /** * Gets time stamp that has been read. * * @return time stamp from channel */ synchronized long getTimestamp() { return timeStamp; } /** * Gets uncertainty that has been read. * * @return uncertainty from channel */ synchronized float[] getReadUncertainty() { return uncertainty; } /** * Gets validity that has been read. * * @return validity from channel */ synchronized boolean[] getReadValidity() { return validity; } /** * Object value from channel has been received. * * @param number the channel number * @param value the object value * @param uncertainty the uncertainty of data * @param validity the validity of data */ public synchronized void valueReceived(int number, Object[] value, float[] uncertainty, boolean[] validity) { // Check for conditions channel.condValueReceived(number, value, uncertainty, validity); dataValue = value; if (isTimestampIncluded) { timeStamp = System.currentTimeMillis(); } if (isUncertaintyIncluded) { this.uncertainty = uncertainty; } if (isValidityIncluded) { this.validity = validity; } channel.putMessage(StatesEvents.RESPONSE_DATA); } /** * Wrong data reading. * * @param number the channel number * @param errorCode the code error of data reading */ public synchronized void dataReadError(int number, int errorCode) { this.errorCode = errorCode; this.validity = new boolean[dataValue.length]; // all is false channel.putMessage(StatesEvents.RESPONSE_ERROR); } } //end of main class declaration class RunConditionMet implements Runnable { private Sensor sensor; private ChannelImpl channel; RunConditionMet(Sensor sensor, ChannelImpl channel) { this.sensor = sensor; this.channel = channel; } public void run() { ConditionListenerPair pair; while ((pair = channel.getCondPair()) != null) { try { pair.getListener().conditionMet(sensor, pair.getData(), pair.getCondition()); } catch (Exception exc) { // user exception - ignore } } } }