/* * Copyright (C) 2007 ETH Zurich * * This file is part of Fosstrak (www.fosstrak.org). * * Fosstrak is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License version 2.1, as published by the Free Software Foundation. * * Fosstrak 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 for more details. * * You should have received a copy of the GNU Lesser General Public * License along with Fosstrak; if not, write to the Free * Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA */ package org.fosstrak.ale.server.readers.rp; import java.net.MalformedURLException; import java.net.URL; import java.util.HashSet; import java.util.Hashtable; import java.util.List; import java.util.Set; 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.xsd.ale.epcglobal.CCOpSpec; import org.fosstrak.ale.xsd.ale.epcglobal.CCSpec; import org.fosstrak.ale.xsd.ale.epcglobal.LRSpec; import org.fosstrak.hal.HardwareException; import org.fosstrak.hal.Observation; import org.fosstrak.reader.rp.proxy.RPProxyException; /** * represents an adaptor to the RP reader. * @author swieland * */ public class RPAdaptor extends BaseReader { /** logger. */ private static final Logger LOG = Logger.getLogger(RPAdaptor.class); /** default readTimeInterval. */ public static final int DEFAULT_READTIME_INTERVALL = 2000; /** input generator for the RP that establishes connection and receives tags. */ private InputGenerator inputGenerator = null; /** the host where commands shall be sent. */ private String commandChannelHost = null; /** the port where to connect to. */ private int commandChannelPort = -1; /** the host where notifications shall be sent. */ private String notificationChannelHost = null; /** the corresponding port. */ private int notificationChannelPort = -1; /** the interval in which shall be read from the reader. */ private int readTimeInterval = -1; /** the physical sources where tags are read in a String (eg shelf1, shelf2). */ private String sourcesString = null; /** the physical sources. */ private Set<String> sources = new HashSet<String>(); /** to get all the tags we need a polling thread. */ private IdentifyThread identifyThread = null; /** * constructor for the RP adaptor. */ public RPAdaptor() { super(); } /** * initializes a RPAdaptor. 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 { try { super.initialize(name, spec); } catch (ImplementationException ie) { LOG.error("error in initialize of superclass"); throw ie; } try { // extract from LRSpec how to connect to the reader extractConnectionSettings(); } catch (ImplementationException ie) { LOG.error("could not extract connection settings from LRSpec", ie); throw ie; } // connect to the reader connectReader(); } /** * this method calls the URL constructor with a string. * @param urlString the URL to be converted into a java.net.URL * @param comment "NotificationPoint" or " ConnectionPoint" * @return a URL created from urlString * @throws ImplementationException when a MalformedURLException is thrown */ private URL toURL(String urlString, String comment) throws ImplementationException { URL url = null; try { url = new URL(urlString); } catch (MalformedURLException e) { LOG.debug("Malformed url " + urlString, e); throw new ImplementationException("Could not extract " + comment + " from LRProperty", e); } return url; } /** * this method extracts the connection settings from the LRProperty. when there * is the need to disconnect the reader (when something in the connection to the * reader has been changed in the LRSpec), true will be returned false otherwise. * if necessary the reader will be restarted * @throws ImplementationException whenever an error occurs * @return returns true if the connection to the reader needs to be reconnected */ private boolean extractConnectionSettings() throws ImplementationException { URL connectionPoint = toURL(logicalReaderProperties.get("ConnectionPoint"), "ConnectionPoint"); URL notificationPoint = toURL(logicalReaderProperties.get("NotificationPoint"), "NotificationPoint"); String interval = logicalReaderProperties.get("ReadTimeInterval"); setReadTimeInterval(-1); try { setReadTimeInterval(Integer.parseInt(interval)); } catch (Exception ne) { LOG.error("could not extract readTimeIntervall from LRPropery"); throw new ImplementationException("could not extract notificationPoint from LRPropery"); } // assert that the readTimeInterval is not -1 if (readTimeInterval == -1) { LOG.error("ReadTimeInterval not set - assuming 2000ms"); setReadTimeInterval(DEFAULT_READTIME_INTERVALL); } boolean disconnect = false; // compare the new sources string to the old sources string String nsources = logicalReaderProperties.get("PhysicalReaderSource"); if (!nsources.equalsIgnoreCase(sourcesString)) { disconnect = true; } // disconnect if command channel host has changed if (!connectionPoint.getHost().equalsIgnoreCase(commandChannelHost)) { disconnect = true; } // disconnect if notificationChannelHost has changed if (!notificationPoint.getHost().equalsIgnoreCase(notificationChannelHost)) { disconnect = true; } // disconnect when the port has changed if (connectionPoint.getPort() != commandChannelPort) { disconnect = true; } // disconnect when the port has changed if (notificationPoint.getPort() != notificationChannelPort) { disconnect = true; } // disconnect when the reader was not connected before if (inputGenerator != null && !inputGenerator.isReady()) { disconnect = true; } LOG.debug("readTimeInterval " + readTimeInterval); LOG.debug(String.format("commandChannelHost %s on port %d", connectionPoint.getHost(), connectionPoint.getPort())); LOG.debug(String.format("notificationChannelHost %s on port %d", notificationPoint.getHost(), notificationPoint.getPort())); // set the new commandChannelSettings setCommandChannelHost(connectionPoint.getHost()); setCommandChannelPort(connectionPoint.getPort()); // set the new notificationChannelSettings setNotificationChannelHost(notificationPoint.getHost()); setNotificationChannelPort(notificationPoint.getPort()); // set the new sources sourcesString = nsources; setSourcesFromArray(sourcesString.split("[,]")); return disconnect; } /** * sets up a reader. if the adaptor can be connected to the rp-proxy * the adaptor will be set to connected. * @throws ImplementationException whenever an internal error occured */ @Override public void connectReader() throws ImplementationException { if (isConnected()) { return; } try { inputGenerator = new InputGenerator(this); } catch (ImplementationException e) { setDisconnected(); throw e; } LOG.debug("setup identifyThread on RPAdaptor " + getName()); // setup the polling thread identifyThread = new IdentifyThread(this); identifyThread.setPollingFrequency(readTimeInterval); identifyThread.start(); // suspend the polling thread to the beginning identifyThread.suspendIdentify(); setConnected(); } /** * destroys a reader. * @throws ImplementationException whenever an internal error occured */ @Override public void disconnectReader() throws ImplementationException { setCommandChannelHost(null); setNotificationChannelHost(null); setCommandChannelPort(-1); setNotificationChannelPort(-1); setReadTimeInterval(-1); if (inputGenerator != null) { inputGenerator.remove(); inputGenerator = null; } if (identifyThread != null) { identifyThread.stopIdentify(); identifyThread = null; } setDisconnected(); setStopped(); } /** * 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) { setChanged(); tag.addTrace(getName()); //LOG.debug("calling observers"); 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()); } notifyObservers(tags); } /** * starts a base reader to read tags. if the reader * is not yet connected this command will connect the reader * immediately and then start it. * If the reader cannot be connected then the reader is * not started as well. * */ @Override public synchronized void start() { if (!isConnected()) { try { connectReader(); } catch (ImplementationException e) { LOG.debug("caught exception - setting to disconnected.", e); setDisconnected(); } } // reader is not connected so it is not started as well if (!isConnected()) { setStopped(); } else { LOG.debug("identifyThread starting to identify"); identifyThread.resumeIdentify(); setStarted(); } } /** * stops a reader from reading tags. * */ @Override public synchronized void stop() { LOG.debug("identifyThread suspend to identify"); 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 { stop(); setLRSpec(spec); if (extractConnectionSettings()) { LOG.debug("restarting reader " + getName()); disconnectReader(); // set the connection settings again and then start the reader extractConnectionSettings(); connectReader(); } start(); } /** * 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("identify called an RPAdaptor " + getName()); if ((inputGenerator != null) && (countObservers() > 0)) { if (inputGenerator.isReady()) { try { inputGenerator.poll(); } catch (RPProxyException e) { LOG.debug("caught exception", e); throw new HardwareException("Could not poll the adaptor " + getName(), e); } } else { LOG.debug("rp-proxy not ready (yet)"); } } return null; } /** * returns the commandChannelHost of the rp-proxy. * @return the commandChannelHost. */ public String getCommandChannelHost() { return commandChannelHost; } /** * sets the commandChannelHost of the rp-proxy. * @param commandChannelHost new commandChannelHost. */ private void setCommandChannelHost(String commandChannelHost) { this.commandChannelHost = commandChannelHost; } /** * returns the commandChannelPort of the rp-proxy. * @return the commandChannelPort. */ public int getCommandChannelPort() { return commandChannelPort; } /** * sets the commandChannelPort of the rp-proxy. * @param commandChannelPort the new commandChannelPort */ private void setCommandChannelPort(int commandChannelPort) { this.commandChannelPort = commandChannelPort; } /** * returns the notificationChannelHost of the rp-proxy. * @return the notificationChannelHost */ public String getNotificationChannelHost() { return notificationChannelHost; } /** * sets the notificationChannelHost of the rp-proxy. * @param notificationChannelHost the new notificationChannelHost */ private void setNotificationChannelHost(String notificationChannelHost) { this.notificationChannelHost = notificationChannelHost; } /** * returns the notificationChannelPort of the rp-proxy. * @return the notificationChannelPort. */ public int getNotificationChannelPort() { return notificationChannelPort; } /** * sets the notificationChannelPort of the rp-proxy. * @param notificationChannelPort the new notificationChannelPort */ private void setNotificationChannelPort(int notificationChannelPort) { this.notificationChannelPort = notificationChannelPort; } /** * returns the readTimeInterval. * @return the readTimeInterval */ public int getReadTimeInterval() { return readTimeInterval; } /** * sets the readTimeInterval. * @param readTimeInterval the readTimeInterval */ private void setReadTimeInterval(int readTimeInterval) { this.readTimeInterval = readTimeInterval; } /** * returns the sources from where the reader reads tags. * @return the sources from where the reader reads tags. */ public Set<String> getSources() { return sources; } /** * sets the sources from where the reader reads tags. the * sources are copied into the HashSet sources * @param sources an array of strings containing the sources. */ private void setSourcesFromArray(String[] sources) { for (String source : sources) { this.sources.add(source); } } @Override public void ADDACCESSSPECfromCCSpec(CCSpec ccspec, Hashtable<Integer, CCOpSpec> OpSpecTable) { try { throw new ImplementationException("RPAdaptor 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("RPAdaptor 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 } }