de.xirp.io.comm.lowlevel.AbstractStreamCommunicationInterface.java Source code

Java tutorial

Introduction

Here is the source code for de.xirp.io.comm.lowlevel.AbstractStreamCommunicationInterface.java

Source

/** 
 * ============================================================================
 * Xirp 2: eXtendable interface for robotic purposes.
 * ============================================================================
 * 
 * Copyright (C) 2005-2007, by Authors and Contributors listed in CREDITS.txt
 * 
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at:
 *
 *             http://www.opensource.org/licenses/cpl1.0.php
 *
 * ----------------------------
 * AbstractStreamCommunicationInterface.java
 * ----------------------------
 *
 * Original Author:  Matthias Gernand [matthias.gernand AT gmx.de]
 *                 Rabea Gransberger [rgransberger AT web.de]
 * Contributor(s):   
 *
 * Changes
 * -------
 * 05.02.2007:      Created by Matthias Gernand.
 */
package de.xirp.io.comm.lowlevel;

import java.io.IOException;
import java.io.InputStream;
import java.util.Timer;
import java.util.TimerTask;

import javax.naming.InvalidNameException;

import javolution.util.FastList;
import javolution.util.FastMap;

import org.apache.commons.io.input.CountingInputStream;
import org.apache.commons.io.output.CountingOutputStream;

import de.xirp.io.comm.CommunicationManager;
import de.xirp.io.comm.DataAmount;
import de.xirp.io.event.ConnectionEvent;
import de.xirp.io.logging.RobotLogger;
import de.xirp.plugin.PluginInfo;
import de.xirp.util.Constants;
import de.xirp.util.I18n;

/**
 * An abstract default implementation for communication interface
 * which send <code>byte []</code> and receives over a
 * <code>InputStream</code>
 * 
 * @author Matthias Gernand
 * @author Rabea Gransberger
 */
public abstract class AbstractStreamCommunicationInterface
        extends AbstractCommunicationInterface<byte[], InputStream> {

    /**
     * Log4j Logger for this class
     */
    private static RobotLogger logClass = RobotLogger.getLogger(AbstractStreamCommunicationInterface.class);

    /**
     * <code>true</code> if connection to device established
     */
    protected boolean connected = false;

    /**
     * Time in milliseconds between two datums send
     */
    protected int SEND_SLEEP_TIME = 15;

    /**
     * Map which saves the sending status for each registered
     * periodically sending thread. If value is false, the thread does
     * not do anything. This is needed because there is no Thread.stop
     * method available.
     */
    protected FastMap<String, Timer> periodicSenders = new FastMap<String, Timer>().setShared(true);
    /**
     * Stream of data sent from the robot to the GUI
     */
    protected CountingInputStream in = null;
    /**
     * Stream of data sent by GUI to the robot
     */
    protected CountingOutputStream out = null;
    /**
     * Flag which holds the sending status of this communication
     * device.<br>
     * <code>true</code> if sending thread is active,
     * <code>false</code> otherwise
     */
    protected boolean sending = false;
    /**
     * Data is not send directly over the communication device. It's
     * stored in this structure and send by a Thread to be sure that
     * each data-fragment is sent even if the program is busy at the
     * moment the data originally should be sent
     */
    private FastList<byte[]> toSend = new FastList<byte[]>();

    /**
     * Constructs a new communication interface plugin for the given
     * robot and information about the plugin itself.<br/><br/>Don't
     * call it on your own, it is called when a connection to the
     * robot is established.
     * 
     * @param robotName
     *            the name of the robot this handler is for
     * @param ownInfo
     *            information about this plugin
     */
    public AbstractStreamCommunicationInterface(String robotName, PluginInfo ownInfo) {
        super(robotName, ownInfo);
    }

    /**
     * @see de.xirp.io.comm.lowlevel.ICommunicationInterface#isConnected()
     */
    public boolean isConnected() {
        return connected;
    }

    /**
     * Sends data periodically.
     * 
     * @param name
     *            the name to use for the send timer
     * @param time
     *            the time interval in milliseconds between two sends
     * @param data
     *            the data to send
     * @throws InvalidNameException
     *             if the name is already in use
     */
    public void sendPeriodically(String name, int time, byte[] data) throws InvalidNameException {

        if (periodicSenders.get(name) != null) {
            throw new InvalidNameException(
                    I18n.getString("AbstractCommunication.log.alreadyExcecutingThread") + name); //$NON-NLS-1$
        }

        Timer timer = new Timer(name);
        timer.schedule(getSendPeriodicTask(data), 0, time);
        periodicSenders.put(name, timer);
    }

    /**
     * Constructs a timer task which will send the given data array.
     * 
     * @param data
     *            byte array which will be send
     * @return the task to use within a timer
     */
    private TimerTask getSendPeriodicTask(final byte[] data) {
        TimerTask task = new TimerTask() {

            @Override
            public void run() {
                if (connected) {
                    send(data);
                }
            }
        };
        return task;
    }

    /**
     * Stops the periodic sending for the given tasks name.
     * 
     * @param name
     *            the name of the task to stop
     * @return <code>true</code> if the task was found and stopped,
     *         <code>false</code> otherwise
     */
    public boolean stopSendPeriodically(String name) {
        Timer timer = periodicSenders.get(name);
        if (timer != null) {
            timer.cancel();
            periodicSenders.remove(name);
            return true;
        }
        return false;
    }

    /**
     * Stops to send data.
     * 
     * @return <code>true</code> if sending was stopped
     */
    private boolean stopSending() {
        // Disable periodical sending
        for (FastMap.Entry<String, Timer> e = periodicSenders.head(), end = periodicSenders
                .tail(); (e = e.getNext()) != end;) {
            e.getValue().cancel();
        }
        periodicSenders.clear();

        // do not send old data after a reconnect
        toSend.clear();
        sending = false;
        return true;
    }

    /**
     * Starts the thread which sends data from the GUI to the robot.<br/>
     * The data which should be send is read from an internal
     * structure. The GUI writes the data to this structure by calling
     * {@link #send(byte[])}. Furthermore a thread fires bytes
     * send/receive events from time to time.
     */
    protected void startSend() {
        if (sending) {
            return;
        }
        sending = true;

        Thread sendReceive = new Thread(robotName + "_SendRecvStats") { //$NON-NLS-1$

            @Override
            public void run() {
                while (sending && isConnected()) {
                    int inCnt = in.getCount();
                    int outCnt = out.getCount();
                    // check if the stream is still there
                    try {
                        in.available();
                    } catch (IOException e) {
                        CommunicationManager.disconnect(getRobotName());
                        logClass.debug(getRobotName(), e.getMessage() + Constants.LINE_SEPARATOR, e);
                    }

                    in.resetCount();
                    out.resetCount();

                    fireByteReceivedEvent(
                            new ByteEvent(AbstractStreamCommunicationInterface.this, new DataAmount(inCnt)));
                    fireByteSendEvent(
                            new ByteEvent(AbstractStreamCommunicationInterface.this, new DataAmount(outCnt)));
                    try {
                        Thread.sleep(250);
                    } catch (InterruptedException e) {
                        // do nothing
                    }

                }
            }
        };
        sendReceive.start();

        Thread thread = new Thread(robotName + "_SendThread") { //$NON-NLS-1$

            @Override
            public void run() {
                // Run the Thread until receiving
                // was disabled or Comm disconnected
                while (sending && isConnected()) {
                    try {
                        if (!toSend.isEmpty()) {
                            byte[] value = toSend.removeFirst();

                            if (value != null) {
                                out.write(value);
                            }
                        }
                    } catch (IOException e) {
                        logClass.error(robotName, I18n.getString("AbstractCommunication.log.errorSendingOver", //$NON-NLS-1$
                                getClass().getSimpleName(), e.getMessage()) + Constants.LINE_SEPARATOR, e);
                    }
                    // Need to sleep so that
                    // other threads can run
                    try {
                        sleep(SEND_SLEEP_TIME);
                    } catch (Exception e) {
                        // do nothing
                    }
                }
            }
        };
        // Start Receiving Thread
        thread.start();
        logClass.debug(robotName,
                I18n.getString("AbstractCommunication.log.runningSending") + Constants.LINE_SEPARATOR); //$NON-NLS-1$
    }

    /**
     * @param value
     *            the byte array to send
     * @see de.xirp.io.comm.lowlevel.ICommunicationInterface#send(java.lang.Object)
     */
    public void send(byte[] value) {
        if (connected) {
            toSend.add(value);
        }
    }

    /**
     * Disconnects from the interface, stops sending, closes the
     * streams and fires a disconnect event.
     * 
     * @see de.xirp.io.comm.lowlevel.ICommunicationInterface#disconnect()
     */
    @SuppressWarnings("unchecked")
    public void disconnect() {
        try {
            if (connected) {
                stopSending();
                in.close();
                out.close();
                connected = false;
                this.fireDisconnectEvent(new ConnectionEvent(this, this.robotName));
                logClass.info(robotName,
                        I18n.getString("AbstractCommunication.log.successfullyDisconnected", getClass() //$NON-NLS-1$
                                .getSimpleName()) + Constants.LINE_SEPARATOR);
            }
            running = false;
        } catch (IOException e) {
            logClass.error(robotName, I18n.getString("AbstractCommunication.log.errorDisconnecting", getClass() //$NON-NLS-1$
                    .getSimpleName(), e.getMessage()) + Constants.LINE_SEPARATOR, e);
        }
    }

    /**
     * Gets the input stream which is used for receiving data.
     * 
     * @return the input stream used for receiving data
     * @see de.xirp.io.comm.lowlevel.ICommunicationInterface#receive()
     */
    public InputStream receive() {
        return in;
    }

}