/* * 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.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.util.TagHelper; import org.fosstrak.reader.rp.proxy.DataSelector; import org.fosstrak.reader.rp.proxy.NotificationChannel; import org.fosstrak.reader.rp.proxy.NotificationChannelEndPoint; import org.fosstrak.reader.rp.proxy.NotificationChannelListener; import org.fosstrak.reader.rp.proxy.RPProxyException; import org.fosstrak.reader.rp.proxy.ReadReport; import org.fosstrak.reader.rp.proxy.ReaderDevice; import org.fosstrak.reader.rp.proxy.Source; import org.fosstrak.reader.rp.proxy.Trigger; import org.fosstrak.reader.rp.proxy.factories.DataSelectorFactory; import org.fosstrak.reader.rp.proxy.factories.NotificationChannelFactory; import org.fosstrak.reader.rp.proxy.factories.ReaderDeviceFactory; import org.fosstrak.reader.rp.proxy.factories.TriggerFactory; import org.fosstrak.reader.rp.proxy.msg.Handshake; import org.fosstrak.reader.rprm.core.EventType; import org.fosstrak.reader.rprm.core.FieldName; import org.fosstrak.reader.rprm.core.msg.notification.Notification; import org.fosstrak.reader.rprm.core.msg.reply.ReadReportType; import org.fosstrak.reader.rprm.core.msg.reply.ReadReportType.SourceReport; import org.fosstrak.reader.rprm.core.msg.reply.TagEventType; import org.fosstrak.reader.rprm.core.msg.reply.TagType; /** * This class is the connector between the reader protocol and the ALE. * It creates the necessary objects on the reader device to get all required data. * * @author swieland * @author regli */ public class InputGenerator implements NotificationChannelListener { /** logger. */ private static final Logger LOG = Logger.getLogger(InputGenerator.class); /** prefix for each object name in the reader device created by the input generator. */ private static final String PREFIX = "InputGenerator_"; /** * the default read trigger name. * if this name is already in use, a number will be added to the name automatically. */ private static final String DEFAULT_READ_TRIGGER_NAME = PREFIX + "ReadTrigger"; /** * the default notification channel name. * if this name is already in use, a number will be added to the name automatically. */ private static final String DEFAULT_NOTIFICATION_CHANNEL_NAME = PREFIX + "NotificationChannel"; /** * the default data selector name. * if this name is already in use, a number will be added to the name automatically. */ private static final String DEFAULT_DATA_SELECTOR_NAME = PREFIX + "DataSelector"; /** * the default notification trigger name. * if this name is already in use, a number will be added to the name automatically. */ private static final String DEFAULT_NOTIFICATION_TRIGGER_NAME = PREFIX + "NotificationTrigger"; /** notification channel mode. */ private static final String NOTIFICATION_CHANNEL_MODE = "connect"; /** reader device proxy. */ private ReaderDevice readerDevice; /** notification channel proxy. */ private NotificationChannel notificationChannel; /** data selector proxy. */ private DataSelector dataSelector; /** read trigger proxy .*/ private Trigger readTrigger; /** notification trigger proxy. */ private Trigger notificationTrigger; /** notification channel end point. */ private NotificationChannelEndPoint notificationChannelEndPoint; /** notification channel name. */ private String notificationChannelName = DEFAULT_NOTIFICATION_CHANNEL_NAME; /** data selector name. */ private String dataSelectorName = DEFAULT_DATA_SELECTOR_NAME; /** read trigger name. */ private String readTriggerName = DEFAULT_READ_TRIGGER_NAME; /** notification trigger name. */ private String notificationTriggerName = DEFAULT_NOTIFICATION_TRIGGER_NAME; /** indicates if this input generator is ready or not. */ private boolean isReady = false; /** indicates if the initialization of this input generator is failed or not. */ private boolean isFailed = false; /** adaptor to which the input generator belongs. */ private RPAdaptor adaptor = null; /** * Constructor sets parameter and starts initializer. * * @param adaptor the adaptor holding this inputGenerator * @throws ImplementationException if an implementation exception occurs */ public InputGenerator(RPAdaptor adaptor) throws ImplementationException { this.adaptor = adaptor; // start Initializer Thread try { new Initializer(this).start(); } catch (Exception e) { throw new ImplementationException("could not initialize the input generator", e); } } /** * This method is invoked if a notification is arrived at the notification channel end-point. * * @param notification from the reader device */ public void dataReceived(Notification notification) { LOG.debug("Notification received but not processed - polling mode is used"); } /** * This method indicates if the input generator is ready or not. * * @return true if the input generator is ready and false otherwise */ public boolean isReady() { return isReady; } /** * This method indicates if the initialization of the input generator is failed or not. * * @return true if the initialization is failed and false otherwise */ public boolean isFailed() { return isFailed; } /** * This method removes all objects on the reader device which are created by this input generator. */ public void remove() { LOG.debug("Try to remove InputGenerator."); // remove Triggers, NotificationChannel and DataSelector if (readerDevice != null) { try { readerDevice.removeTriggers(new Trigger[] {readTrigger}); LOG.debug("ReadTrigger '" + readTriggerName + "' successfully removed."); } catch (RPProxyException e) { LOG.debug("caught exception", e); } try { readerDevice.removeTriggers(new Trigger[] {notificationTrigger}); LOG.debug("NotificationTrigger '" + notificationTriggerName + "' successfully removed."); } catch (RPProxyException e) { LOG.debug("caught exception", e); } try { readerDevice.removeNotificationChannels(new NotificationChannel[] {notificationChannel}); LOG.debug("NotificationChannel '" + notificationChannelName + "' successfully removed."); } catch (RPProxyException e) { LOG.debug("caught exception", e); } try { readerDevice.removeDataSelectors(new DataSelector[] {dataSelector}); LOG.debug("DataSelector '" + dataSelectorName + "' successfully removed."); } catch (RPProxyException e) { LOG.debug("caught exception", e); } } // stop NotificationChannelEndPoint if (notificationChannelEndPoint != null) { notificationChannelEndPoint.stop(); LOG.debug("NotificationChannelEndPoint stopped."); } LOG.debug("InputGenerator removed"); } /** * polls the rp-proxy for all tags available. * @throws RPProxyException when the polling failed */ public void poll() throws RPProxyException { //LOG.debug("Polling the rp-proxy"); // get all the sources from the device Source[] sources = readerDevice.getAllSources(); if (sources != null) { for (Source source : sources) { // remove all data-selectors on the source // this is important !!!!!!!!!!!!! // otherwise you will not get any tags source.removeAllTagSelectors(); // read all ids on the source // just for safety pass an empty dataSelector ReadReport readReport = source.rawReadIDs(null); if (readReport != null) { // get the readReport holding the sourceReports ReadReportType report = readReport.getReport(); // get the sourceReports List<SourceReport> sourceReports = report.getSourceReport(); List<Tag> tagsToNotify = new LinkedList<Tag>(); // for each sourceReport process the tags for (SourceReport sourceReport : sourceReports) { List<TagType> tags = sourceReport.getTag(); if (tags != null) { // for each tag create a new reader api tag for (TagType tag : tags) { List<TagEventType> tagEvents = tag.getTagEvent(); if (tagEvents != null) { for (TagEventType tagEvent : tagEvents) { String eventType = tagEvent.getEventType(); //LOG.debug("Tag '" + tag.getTagIDAsPureURI() + "' fired Event of type '" + eventType + "'"); if (EventType.EV_OBSERVED.equals(eventType)) { // somehow rp delivers binary instead // pure uri String binary = tag.getTagIDAsPureURI(); Tag nt = new Tag( adaptor.getName(), tag.getTagID(), TagHelper.convert_to_PURE_IDENTITY( "64", "1", "7", binary), System.currentTimeMillis()); nt.setTagAsBinary(binary); tagsToNotify.add(nt); } } } } } } if (tagsToNotify.size() > 0) { adaptor.addTags(tagsToNotify); } } else { LOG.debug("readReport null"); } } } else { LOG.debug("sources null"); } } /** * This class initializes the input generator by creating objects on the reader device using the proxies. * * @author regli */ private class Initializer extends Thread { /** this input generator. */ private final InputGenerator generator; /** * Constructor sets the input generator. * * @param generator the inputGenerator for this reader. */ public Initializer(InputGenerator generator) { this.generator = generator; } /** * This method contains the main loop. */ public void run() { try { initialize(); } catch (RPProxyException e) { isFailed(e); } } /** * This method creates objects on the reader device using the proxies. * * @throws RPProxyException if a proxy operation fails */ private void initialize() throws RPProxyException { LOG.debug("-----------------------------------------------------------"); LOG.debug("Start initializing InputGenerator..."); LOG.info("Try connecting to reader devices..."); // create ReaderDevice LOG.debug("Create ReaderDevice Proxy"); Handshake handshake = new Handshake(); handshake.setTransportProtocol(Handshake.HTTP); handshake.setMessageFormat(Handshake.FORMAT_XML); readerDevice = ReaderDeviceFactory.getReaderDevice(adaptor.getCommandChannelHost(), adaptor.getCommandChannelPort(), handshake); // create notification channel endpoint and add listener LOG.debug("Try to create NotificationChannelEndpoint at port '" + adaptor.getNotificationChannelPort() + "'..."); notificationChannelEndPoint = new NotificationChannelEndPoint(adaptor.getNotificationChannelPort()); notificationChannelEndPoint.addListener(generator); LOG.debug("NotificationChannelEndpoint at port '" + adaptor.getNotificationChannelPort() + "' created."); // create read trigger readTrigger = TriggerFactory.createTrigger(readTriggerName, Trigger.TIMER, String.format("ms=%d", adaptor.getReadTimeInterval()), readerDevice); LOG.debug("created read trigger: " + readTriggerName); // create notification trigger notificationTrigger = TriggerFactory.createTrigger(notificationTriggerName, Trigger.CONTINUOUS, "", readerDevice); LOG.debug("created notification trigger: " + notificationTriggerName); // create the data selector LOG.debug("create DataSelector"); dataSelector = DataSelectorFactory.createDataSelector(dataSelectorName, readerDevice); LOG.debug("adding Fieldnames to dataSelector"); dataSelector.addFieldNames(new String[] {FieldName.EVENT_TYPE, FieldName.READER_NAME, FieldName.TAG_ID, FieldName.SOURCE_NAME}); // create notification channel String notificationAddress = "tcp://" + adaptor.getNotificationChannelHost() + ":" + adaptor.getNotificationChannelPort() + "?mode=" + NOTIFICATION_CHANNEL_MODE; LOG.debug("create NotificationChannel" + notificationChannelName); notificationChannel = NotificationChannelFactory.createNotificationChannel( notificationChannelName, notificationAddress, readerDevice); LOG.debug("NotificationChannel " + notificationChannelName + " created"); LOG.debug("adding dataSelector to notificationChannel"); notificationChannel.setDataSelector(dataSelector); LOG.debug("adding notificationTrigger to notificationChannel"); notificationChannel.addNotificationTriggers(new Trigger[] {notificationTrigger}); // add sources to notification channel LOG.debug("Add Sources to NotificationChannel..."); Source[] sources = readerDevice.getAllSources(); notificationChannel.addSources(sources); LOG.debug("Sources were added to NotificationChannel."); LOG.debug("add readTriggers to the sources"); for (Source source : sources) { if (adaptor.getSources().contains(source.getName())) { LOG.debug("adding physical source " + source.getName()); source.addReadTriggers(new Trigger[] {readTrigger}); } } // set isReady isReady = true; // notify all when InputGenerator is ready synchronized (generator) { generator.notifyAll(); } LOG.debug("InputGenerator initialized."); LOG.info("Connection to reader devices established."); LOG.debug("-----------------------------------------------------------"); } /** * This method is invoked if the initialization is failed. * An error message will be displayed in log. * * @param exception exception thrown */ private void isFailed(Exception exception) { isFailed = true; LOG.error("InputGenerator '" + adaptor.getCommandChannelHost() + ":" + adaptor.getCommandChannelPort() + "' initialization failed. (" + exception.getMessage() + ")"); // notify all when InputGenerator is failed synchronized (generator) { generator.notifyAll(); } } } }