package org.fosstrak.ale.server.readers.hal;
import java.net.URL;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.List;
import org.apache.log4j.Logger;
import org.fosstrak.ale.exception.ImplementationException;
import org.fosstrak.ale.server.Tag;
import org.fosstrak.ale.server.readers.BaseReader;
import org.fosstrak.ale.server.readers.IdentifyThread;
import org.fosstrak.ale.server.util.TagHelper;
import org.fosstrak.ale.xsd.ale.epcglobal.CCOpSpec;
import org.fosstrak.ale.xsd.ale.epcglobal.CCSpec;
import org.fosstrak.ale.xsd.ale.epcglobal.LRSpec;
import org.fosstrak.hal.HardwareAbstraction;
import org.fosstrak.hal.HardwareException;
import org.fosstrak.hal.Observation;
import org.fosstrak.hal.Trigger;
import org.fosstrak.tdt.TDTEngine;
/**
* adaptor for all HAL devices.
* this adaptor allows to attach HAL devices directly to the ale
* @author swieland
*
*/
public class HALAdaptor extends BaseReader {
/** logger. */
private static final Logger LOG = Logger.getLogger(HALAdaptor.class);
/** whenever the hal device does not support auto-polling we need to install a polling thread. */
private IdentifyThread identifyThread = null;
/** interface to the HAL device. */
private HardwareAbstraction hal = null;
/** the time intervall in which the reader will look for new tags. */
private long pollingFrequency = -1;
/** indicates whether the hal needs a pollingThread or not . */
private boolean autoPolling = false;
/** the properties file for this adaptor. */
private String propertiesFile = null;
/** the name of the hal device. */
private String halName = null;
/** the readpoints where shall be read from. */
private String [] readPoints = null;
/** the name of the default implementing class to be chosen. */
public static final String DEFAULT_IMPLCLASS = "org.fosstrak.hal.impl.sim.SimulatorController";
// cfg param read time interval / polling frequency.
private static final String PARAM_READTIME_INTERVAL = "ReadTimeInterval";
// cfg param read points
private static final String PARAM_READ_POINTS = "ReadPoints";
// cfg param properties file
private static final String PARAM_PROPERTIES_FILE = "PropertiesFile";
// cfg param implementing class
private static final String PARAM_IMPLEMENTING_CLASS = "ImplementingClass";
private static final String PARAM_PYSICAL_READER_NAME = "PhysicalReaderName";
/**
* constructor for the HAL adaptor.
*/
public HALAdaptor() {
super();
}
/**
* initializes a HALAdaptor. this method must be called befor the Adaptor can
* be used.
* @param name the name for the reader encapsulated by this adaptor.
* @param spec the specification that describes the current reader.
* @throws ImplementationException whenever an internal error occurs.
*/
public void initialize(String name, LRSpec spec) throws ImplementationException {
super.initialize(name, spec);
pollingFrequency = Long.parseLong(logicalReaderProperties.get(PARAM_READTIME_INTERVAL));
halName = logicalReaderProperties.get(PARAM_PYSICAL_READER_NAME);
String rpS = logicalReaderProperties.get(PARAM_READ_POINTS);
if (rpS != null) {
readPoints = rpS.split(",");
}
// there is a problem with the HAL simulators when they try to load
// a relative path from within a jar.
// the easiest solution is to provide the absolute path
propertiesFile = logicalReaderProperties.get(PARAM_PROPERTIES_FILE);
URL url = this.getClass().getResource(propertiesFile);
propertiesFile = url.getFile();
// get the implementing class
String implClass = logicalReaderProperties.get(PARAM_IMPLEMENTING_CLASS);
if (implClass == null) {
// the implementing class is missing,
// therefore set to the default implementor
implClass = DEFAULT_IMPLCLASS;
LOG.error(String.format("The implementing class is missing, therefore using the default class '%s'",
DEFAULT_IMPLCLASS));
}
hal = HALManager.getInstance().define(halName, propertiesFile, implClass);
setDisconnected();
setStopped();
// now need to determine whether the HAL device supports auto-polling or
// whether we need to install a polling thread
if (hal.supportsAsynchronousIdentify()) {
setAutoPolling(true);
} else {
setAutoPolling(false);
}
}
/**
* sets up a reader.
* @throws ImplementationException whenever an internal error occured
*/
@Override
public void connectReader() throws ImplementationException {
if (!isConnected()) {
LOG.debug("Connecting reader " + getName());
if (!isAutoPolling()) {
LOG.debug("reader " + getName() + " needs a polling thread - setting it up.");
// create the polling thread
identifyThread = new IdentifyThread(this);
identifyThread.setPollingFrequency(pollingFrequency);
identifyThread.start();
identifyThread.suspendIdentify();
}
setConnected();
LOG.debug("reader " + getName() + " is connected");
}
}
/**
* destroys a reader.
* @throws ImplementationException whenever an internal error occured
*/
@Override
public void disconnectReader() throws ImplementationException {
if (isConnected()) {
if (isAutoPolling()) {
try {
hal.stopAsynchronousIdentify();
} catch (Exception e) {
LOG.error("Could not stop Asynchronous identify:", e);
}
} else {
// use the identifyThread
identifyThread.stopIdentify();
identifyThread = null;
}
setDisconnected();
setStopped();
}
}
/**
* starts a base reader to read tags.
*
*/
@Override
public synchronized void start() {
if (!isConnected()) {
try {
connectReader();
} catch (ImplementationException e) {
LOG.info("could not start the reader " + readerName, e);
return;
}
}
if (!isStarted()) {
if (isAutoPolling()) {
try {
Trigger trigger = null;
if (pollingFrequency == 0) {
trigger = Trigger.createContinuousTrigger();
} else {
trigger = Trigger.createTimerTrigger(pollingFrequency);
}
hal.startAsynchronousIdentify(hal.getReadPointNames(), trigger);
} catch (Exception e) {
LOG.error("Could not start asynchronous identify: ", e);
}
} else {
// use the identify thread
identifyThread.resumeIdentify();
}
setStarted();
}
}
/**
* stops a reader from reading tags.
*
*/
@Override
public synchronized void stop() {
if (isStarted()) {
if (isAutoPolling()) {
try {
hal.stopAsynchronousIdentify();
} catch (Exception e) {
LOG.info("could not stop the reader " + readerName);
}
} else {
// use the identify Thread
identifyThread.suspendIdentify();
}
setStopped();
}
}
/**
* updates a reader according the specified LRSpec.
* @param spec LRSpec for the reader
* @throws ImplementationException whenever an internal error occurs
*/
@Override
public synchronized void update(LRSpec spec) throws ImplementationException {
try {
// we update the properties, so stop the reader from retrieving tags
stop();
// set the specification
setLRSpec(spec);
// extract the pollingFrequency
String pf = logicalReaderProperties.get(PARAM_READTIME_INTERVAL);
if (null != pf) {
pollingFrequency = Long.parseLong(pf);
}
readPoints = null;
String rpS = logicalReaderProperties.get(PARAM_READ_POINTS);
if (rpS != null) {
readPoints = rpS.split(",");
}
// restart the reader
start();
} catch (Exception e) {
throw new ImplementationException(e.getMessage(), e);
}
}
/**
* whenever a new Tag is read a notification is sent to the observers.
* @param tag a tag read on the reader
*/
@Override
public void addTag(Tag tag) {
//LOG.debug("HALAdaptor: notifying observers");
tag.addTrace(getName());
setChanged();
notifyObservers(tag);
}
/**
* whenever new Tags are read a notification is sent to the observers.
* @param tags a list of tags to be added to the reader
*/
@Override
public void addTags(List<Tag> tags) {
setChanged();
for (Tag tag : tags) {
tag.addTrace(getName());
}
LOG.debug("notifying observers about " + tags.size() + " tags");
notifyObservers(tags);
}
/**
* Triggers the identification of all tags that are currently available
* on the reader. this method is used when the IdentifyThread is used to poll the adaptor.
* @param readPointNames the readers/sources that have to be polled
* @return a set of Observations
* @throws HardwareException whenever an internal hardware error occurs (as reader not available...)
*/
@Override
public Observation[] identify(String[] readPointNames)
throws HardwareException {
LOG.debug("got observation trigger");
Observation[] observations = null;
if (countObservers() > 0) {
try {
synchronized (hal) {
// if there are no readPoints specified through the
// lrspec, just use all available readPoints
if (readPoints == null) {
observations = hal.identify(hal.getReadPointNames());
} else {
observations = hal.identify(readPoints);
}
}
} catch (Exception e) {
LOG.error(String.format(
"caught exception from hal. %s",
e.getMessage()));
}
// don't process if observations are null.
if (null == observations) {
return null;
}
// only process if there are tags
if (observations.length > 0) {
List<Tag> tags = new LinkedList<Tag>();
for (Observation observation : observations) {
try {
// For each tag create a new Tag
for (String tagobserved : observation.getIds()) {
Tag tag = new Tag(getName());
TDTEngine tdt = TagHelper.getTDTEngine();
String bin = tdt.hex2bin(tagobserved);
tag.setTagAsBinary(bin);
// 96 bit length
if ((bin.length() > 64) && (bin.length() <= 96)) {
// leading 00 gets truncated away by the big int.
if (bin.startsWith("1") &&
(bin.length() < 96)) {
bin = "00" + bin;
tag.setTagAsBinary(bin);
}
}
try {
tag.setTagIDAsPureURI(
TagHelper.convert_to_PURE_IDENTITY(
null,
null,
null,
bin)
);
} catch (Exception myE) {
LOG.debug("exception when converting tag: ", myE);
}
tag.setTimestamp(observation.getTimestamp());
tags.add(tag);
} // \\END FOR
} catch (Exception e) {
LOG.debug("exception when processing tags: ", e);
}
}
// send the tags as a list
addTags(tags);
}
}
return observations;
}
/**
* indicates whether this HAL device needs a polling mechanism or not.
* @return boolean indicating the polling - capabilities
*/
private boolean isAutoPolling() {
return autoPolling;
}
/**
* sets the polling capabilities.
* @param autoPolling boolean indicating if polling is supported by the HAL device
*/
private void setAutoPolling(boolean autoPolling) {
this.autoPolling = autoPolling;
}
@Override
public void cleanup() {
HALManager.getInstance().undefine(halName);
}
@Override
public void ADDACCESSSPECfromCCSpec(CCSpec ccspec, Hashtable<Integer, CCOpSpec> OpSpecTable) {
try {
throw new ImplementationException("HALAdaptor ADD_ACCESSSPEC from CCSpec is not supported");
} catch (ImplementationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void DELETEACCESSSPEC()
{
try {
throw new ImplementationException("HALAdaptor DELETE_ACCESSSPEC from CCSpec is not supported");
} catch (ImplementationException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
@Override
public void recoveryACCESSSPEC3() {
// TODO Auto-generated method stub
}
}