jmri.jmrix.combi.trafficCtrl
Class CombiTrafficController

java.lang.Object
  extended by jmri.jmrix.combi.trafficCtrl.CombiTrafficController
All Implemented Interfaces:
java.lang.Runnable, CommandStation, ResponseMaker, SetStreams, AsynchTrafficController, DataAvailableListener, Ymodem, Timeout.HandleTimeout, PowerManager

public class CombiTrafficController
extends java.lang.Object
implements java.lang.Runnable, DataAvailableListener, CommandStation, AsynchTrafficController, Ymodem, PowerManager, SetStreams

This class is reponsible for communication with the command station via potentially different adapters such as a serial, parallel etc. For this reason, it is supposed to be adapter-independent and has to be able to handle i/o streams and a callback (perhaps on a different thread), reflecting the appearance of new data.

The only deviation from the above is that the sendCommand method assumes that data is buffered, in that it is possible to send a request and wait for a response rather than receive all that character-by-character. This assumption only applies to short commands and responses, not to the DCC data.

The main reason to use asynchronous notification when data is available (via DataAvailable) is to avoid polling whenever possible. For a PIC, this is not an issue since there there is nothing for it to do anyway; a PC always has a lot to do. Usage of a notification makes it possible to block the receiving worker thread on a wait() until anything is received or a timeout expires.

The communication protocol between a PC and the station is binary; every sequence of bytes is protected with a checksum. When a sequence is submitted, the station waits for its DCC transmitter to finish, makes the transmitter send the newly-received data and then sends the old one (containing the responses received by the DCC transmitter) back to the PC. This means that after submitting a request, one can only get a response to it a cycle after the one following the transmission of this request. The good thing about this protocol is that it makes it possible to use DCC to the maximum: at 38.4kbs the DCC transmitter can be kept busy all the time. With loco timeouts at 1sec, this make it possible to operate 250 locos simultaneously, as long a 80Amp can be delivered to them.

Objects wishing to send commands to the station append requests to the queue of requests to be completed and subsequently the worker thread will pick requests from this queue and send them to the station. It is not known in advance how long it will take between a submission and completion, but such a delay is assumed to be unimportant, with all time-critical tasks completed on the station itself. Requests are processed on the first-come-first-serve basis. The controller does not know what it is sending - only a sender and a station have a clue as to how to interpret the data being transmitted. When a request is accomplished, the controller notifies whoever submitted it about it. Requests are never lost: either a success or an error is always reported. Typically, no object has more than one request in the queue at the same time. This is easy to accomplish by only submitting a new request if an old one was either cancelled or appendRequest has thrown an exception.

The fact that there is an overhead related to transmission of a few bytes preceding a stream of data, checksums etc, certain time-critical processing had to be offloaded to the station. This particularly affects acknowledgement processing. Below a sketch is provided as to why oldack has to be done on the station: CV-writing instruction will consume 5 bytes (3.5 ms). From the end of a previous transmission till the following CV-write packet, I'll have to transmit the result of the previous stream (an empty one since for CV-writing empty ones alternate with CV-writing ones) and hence I've got

Total: 25 bytes, i.e. 6.5ms.

In general, Java appears to be approximately 100 times faster that the UART transmission and hence one does not have to consider delays with Java to affect the above calculations. It is possible to run benchmarks with the help of the streamMirror object. This object can be run like an application; it implements an input and an output stream and mirrors data received on the output, in the input stream. As long as the controller is instrumented, logs produced can make it possible to determine the performance.

Every request (implementing the CombiCommand) interface is supposed to be able to provide the controller with data to be actually sent to the station and be able to process the data returned from the station. At present, the length of data submitted and that received have to be identical; this implies that even commands like idle have to be transmitted to the station and a response to them - back; moreover toti-receipt commands have to transmit a buffer for a station to fill. Nevertheless, this seems a reasonable decision because without it, the controller will have to interpret commands in order to determine how much data to expect back and the station will have to do something in the situation where a response to a command it is supposed to process does not fit in the space it has left in the buffer.

Requests have to implement three methods,

There are two types of append method, appendRequest and appendRequestForMode. The former is designed for appending requests which should always be appended, such as those for accessories. The latter command is designed for appending requests which should run as long as the specific mode (specified in the command) is active. If a mode is not active, the request gets immediately cancelled (this means that parseResponse serves as a callback of appendRequestForMode in this situation). appendRequestForMode is used by loco controllers which periodically append requests and do not care whether these requests gets completed or not but simply append them once again after a period of time.

It may not be possible to submit a request or a request may get cancelled; in the former situation, corresponding to a fatal error, a JmriException is thrown in response to an attempt to submit a request. This will always happen if a length of a request is bigger than the length of the station's buffer. In the latter case, which corresponds to a temporary error, the request receives the parseResponse call with the buffer argument set to null.

Initialisation of the controller is performed by calling its Init method. This starts the worker thread and requests the buffer length from the station. Afterwards, the PowerOff mode is entered. Before the controller is initialised, it is necessary to call its setStreams method with the input and output streams which are to be used by it for communications. The controller implements DataAvailableListener interface which is used to notify the controller that new data is available for receipt (this is done by calling the DataAvailable method). This method can be called at any time and an arbitrary number of times; in order not to interfere with normal processing, it is made synchronized hence a caller must be prepared to wait until it can get a lock on the controller. This is likely to be a short while, but since there are many synchronized methods in the controller, this time cannot be guaranteed.

The controller is responsible for forwarding timeout notifications to its mode objects. The aim is to avoid the following potential deadlock: (1) the controller (holding a lock on itself) calls the initialise method of a mode object which asks its timeout object to terminate (in the process, aquiring a lock on it); (2) the timeout object times out and calls the timeout method of the mode object which attempts to append a request; during a timeout call by the timeout object, such an object holds a lock on itself and since appending a request requires a lock on the controller, deadlock ensues. In the current implementation, mode timeouts call the controller which (in due course) calls mode's timeout method from the controller's worker thread (holding a lock on the controller). This way, the thread of the timeout object does not call any synchronized methods of the controller (the method it calls is not synchronized). The controller has to skip all such notifications when changing a mode because they may come from an old mode and should not be propagated to the new one.

The controller can operate in a few modes (implementing the CombiMode interface). In principle, any number of them is allowed to exist but only one can be active at a time. While in a particular mode, the controller notifies the mode whether a request for it was received via the appendRequestForMode call. This is used to determine whether any loco-related requests are being submitted. If nothing was submitted after a period of time, a mode may choose to submit a packet to avoid locos leaving the mode. A mode is not notified when the appendRequest method of the controller is called because such a method is not intended for loco commands but for those which should be completed regardless of the current mode.

A mode change occurs either when requested via the sendMessage method of the controller or when an overload or a loss-of-comms is detected. When a mode is changed,

Before a mode is initialised, the controller switches to the temporary ModeChange mode. This makes it possible for it to stop all loco requests from being submitted (these will get cancelled immediately upon a submission). If a new mode change is requested before the existing one finishes its initalisation, the same procedure as described above applies, but currently submitted requests are not cancelled (since no loco ones would be in a queue anyway). It is worth noting that if a loss of comms is detected, all requests previously submitted are re-appended to the queue so as to attempt them again. At present, four modes are defined and one pseudo-mode,

There are three main tasks an instance of this class has to fulfill,

Property changes broadcast contain a property name, the old and the new value. Some property change notifications are used purely for "messages", in which case the old value is always null (to ensure notification) and the new one contains the message; others are notifications that a property has changed. The only genuine property change notifications are those with propery names of msg_capChargeTimeout and msg_CommsLost.


Nested Class Summary
 
Nested classes/interfaces inherited from interface jmri.jmrix.combi.trafficCtrl.AsynchTrafficController
AsynchTrafficController.CombiMode
 
Field Summary
protected  java.lang.Object anObject
          An object to receive a notification (if non-null) that a message has been processed or will be used to enter a mode.
protected  boolean capChargeTimeout
          Stores true if the CDU capacitor is currently discharged, false if it is ok.
protected  jmri.jmrix.combi.trafficCtrl.classModeChangeMode combiModeChange
          A mode the controller is in while initialising a new mode.
protected  AsynchTrafficController.CombiMode combiOpsMode
          A possible mode
protected  AsynchTrafficController.CombiMode combiPowerOff
          A possible mode
protected  AsynchTrafficController.CombiMode combiServiceMode
          A possible mode
protected  int commBufferMaxLength
          The maximal length of the RX/TX buffer.
protected static int commBufferMiscLength
          The length of miscellaneouts elements in an RX/TX buffer (i.e. 5 bytes for a reset packet, including a preamble (service mode only) + 3 bytes for a current sense 1 byte for the end of a stream, 2 bytes for length and 2 for crc).
protected  java.io.InputStream commInputStream
          The input stream to be used for communication with a port
protected  java.io.OutputStream commOutputStream
          The output stream to be used for communication with a port
protected static byte[][] errorMessages
          Error messages transmitted upon an abort.
protected  java.lang.Thread flashThread
          The thread which is responsible to writing the firmware.
protected  boolean layoutOverload
          Stores true if the layout is experiencing an overload.
protected  int maxRequestListLength
          The maximal length of request queue.
protected  int MessageCommand
          Holds commands to the comm processing thread.
protected  int minCommBufferLength
          The minimal length of the comm buffer to be able to send/receive anything useful.
protected  AsynchTrafficController.CombiMode mode
          The current mode.
static java.lang.String msg_firmwareFileName
          Holds the name of the firmwareFileName property.
protected static byte[][] possibleResponses
          Contains possible responses by the command station.
protected  AsynchTrafficController.CombiMode previousMode
          The current mode.
protected  int request_Current
          Stores the position of the request which is transferred to the command station on a particular iteration through the main loop of the run() method.
protected  int request_First
          Stores the position in the request list where the first element is stored.
protected  int request_New
          Stores the position in the request list where the new request will be stored.
protected  int request_Submitted
          Stores the position of the request which was previously send to the command station (i.e. on a previous iteration through the main loop of the run() method.
protected  CombiCommand[] requestList
          This is a list of requests to be processed.
protected  java.lang.Thread workerThread
          The thread which performs all the work communicating with the station.
 
Fields inherited from interface jmri.jmrix.combi.trafficCtrl.AsynchTrafficController
chsumAsciiLen, cmdMODECHANGE, cmdOK, cmdQUIT, msg_AssertionFailed, msg_capChargeTimeout, msg_CombiMode, msg_CommsLost, msg_Download, msg_DriverMessage, msg_firmwareData, msg_InvalidFirmwareCRC, msg_InvalidResponseFromStatus, msg_layoutOverload, msg_Synchronising, msg_Termination, msg_TooSmallCommsBuffer, msg_ym_failure_cmdstation_abort, msg_ym_failure_file_unreadable, msg_ym_failure_Interrupted, msg_ym_failure_malformed_colon, msg_ym_failure_malformed_hex, msg_ym_failure_malformed_namelong, msg_ym_failure_nochallenge, msg_ym_failure_out_of_retransmissions, msg_ym_failure_recvY, msg_ym_failure_status, msg_ym_failure_unrecognised, msg_ym_failure_user_abort, msg_ym_pref_readonly, msg_ym_pref_readonly_err, msg_ym_pref_retransmitting, msg_ym_pref_transmitted, msg_ym_pref_TXfailed, msg_ym_transferDataOk, msg_ym_transferOkFailure, msg_ym_transferOkModified, msg_ym_transferOkUnchanged, msg_ym_transferOkUnrecognised, sCRCERROR, sERROR, sFINISHED, sINCONSISTENT, sLONGREQUEST, sNEGATIVE, sOVERFLOW, sOVERLOAD, sTIMEOUT, sWRONGLENGTH
 
Fields inherited from interface jmri.PowerManager
OFF, ON, UNKNOWN
 
Constructor Summary
CombiTrafficController()
          The constructor - does nothing since initialisation is deferred to the Init method and the worker thread.
 
Method Summary
protected  void abortRequests()
          Aborts all the requests from the request list, between request_Submitted and request_New.
 void addPropertyChangeListener(java.beans.PropertyChangeListener l)
          Adds a supplied listener to the list of listeners.
 void appendRequest(CombiCommand what)
          Appends a supplied command to the request list.
 void appendRequestForMode(CombiCommand what, AsynchTrafficController.CombiMode newMode)
          Appends a supplied command to the request list expecting a particular mode to be in place (null means `no mode').
protected  int buildTransaction(byte[] buffer, boolean newmode)
          This function populates the list of requests, lists of lengths and the TX buffer.
static java.lang.StringBuffer byteToString(byte[] data)
          Converts a sequence of bytes into a string which can be shown to a user, for instance.
 void DataAvailable()
          This method is called if any data is available for reception from a communications port.
 void dispose()
          Disposes with this controller.
 void firePropertyChange(java.lang.String message, java.lang.Object oldValue, java.lang.Object newValue)
          Fires a property change notification.
 boolean getCapChargeTimeout()
          Makes it possible to determine whether cap charge has timed out or not.
 boolean getCommsLost()
          Provides information as to whether communication with the command station has been lost or not.
protected  int getCurrentListLength()
          Returns the length of the list of those requests which should be submitted to the command station in the current iteration through the main loop of the run() method.
 java.lang.String getFirmwareFileName()
          Retrieves the value of the firmwareFileName variable
 AsynchTrafficController.CombiMode getMode()
          Returns the current mode
 AsynchTrafficController.CombiMode getOpsMode()
          Returns the mode name of the opreational mode change class
 boolean getOverload()
          Determines whether the command station has got an overload on its DCC output.
 int getPower()
          Returns whether power is on or off.
 AsynchTrafficController.CombiMode getPowerOffMode()
          Returns the mode name of the Power-off mode change class
protected  int getRequestListLength()
          Returns the length of the list of requests, discounting requests which are null (i.e. have been removed).
 int getRxTimeout()
          Retrieves the value of the rxTimeout variable.
 AsynchTrafficController.CombiMode getServiceMode()
          Returns the mode name of the service mode change class
 CombiTrafficStatistics getStatistics()
          Returns the traffic statistics for this controller.
 int getYmRetransmitNumber()
          Retrieves the value of the ymRetransmitNumber variable
 void Init()
          Initialises the traffic controller and retrieves the stream length from the station.
 void makeResponse(ProgListener whomToRespond, int value, int status)
          This is a helper method to respond to anyone who implements the ProgListener interface, via the gui thread.
 int match(byte[] whatToMatch)
          Matches a single string with data received in a stream.
protected  int match(byte[][] exBuffers)
          Matches a number of strings with data received in a stream.
protected  int moveToHoldingBuffer()
          Goes through the list of requests with the aim of picking as many of them as possible as long as the total length of requests picked does not exceed the length of comm buffers.
protected  int populateTXBuffer(byte[] buffer)
          Fills the transmission buffer with data to be sent out, starting from request_Current and up to (but not including) request_First.
protected  int recv(int expectedResponseLength)
          This function is responsible for receiving data into the rx buffer and checking that the checksum matches.
protected  int recvAscii(int commLength, java.lang.StringBuffer message)
          This function is similar to recv but works on ASCII strings and expects the length of the response to be supplied as a parameter.
 void removePropertyChangeListener(java.beans.PropertyChangeListener l)
          Removes a supplied listener from the list of listeners.
 boolean removeRequest(CombiCommand what)
          Removes a supplied command from the request list.
protected  void resetCommandStation()
          This method resets the station.
protected  void resynchronize(int status)
          Re-establishes the synch between the command station and jmri, which gets lost each time there is a comms error.
 void run()
          Sends data in the request buffer out.
protected  int sendBuffer(int bufferLength, int commLength)
          Sends the pending data and waits for a response.
protected  int sendBYTES(byte[] data, int length)
          Sends the buffer given to it, out.
protected  int sendCommand(byte[] command, byte[] response)
          This method sends a command to the station.
 void sendData(java.io.BufferedReader is, java.lang.String name)
          Sends the provided stream of data to the command station.
 void sendFile(java.io.File file)
          Sends a file provided via YModem to the command station.
 void sendMessage(int Message, java.lang.Object newObject)
          In order to send any message, I need to ensure both the sending and the comms thread are not going to modify the same chunk of data at the same time.
 void sendPacket(byte[] packet, int repeats)
          Sends DCC packets out as per the CommandStation interface.
protected  boolean sendStream(java.io.BufferedReader is, java.lang.String name)
          Sends a stream of data to the command station via YModem.
protected  void setCapChargeTimeout(boolean newCapChargeTimeout)
          This is the internal setter for the capChargeTimeout variable.
 void setFirmwareFileName(java.lang.String newFirmwareFileName)
          Assigns a new value to the firmwareFileName variable.
 void setMode(AsynchTrafficController.CombiMode newMode)
          Assigns a new mode without initialisation and fires property changes.
protected  void setOverload(boolean overload)
          Sets or clears the overload value.
 void setPower(int powerMode)
          Sets the power mode.
 void setRxTimeout(int newTimeout)
          Assigns a new value to the rxTimeout variable.
 void setStreams(java.io.InputStream is, java.io.OutputStream os, testInterface whomToNotify)
          Sets the two comm streams CombiTrafficController is expected to use.
 void setYmRetransmitNumber(int retransmitCnt)
          Assigns a new value to the ymRetransmitNumber variable.
protected  byte[] statusData(int from, int to)
          The following function retrieves the data of the station's response to the STATUS command, resynchronizing on comms errors.
protected  int statusResponse(java.lang.StringBuffer response)
          This function retrieves the response produced by the STATUS command.
protected  int submitData(int reqCurrentLength, int reqSubmittedLength)
          Sends the data in the txBuffer to the command station, resynchronizes if needed and otherwise does parseResponse on all elements of the list submitted.
 void timeout()
          Called by timout objects to indicate that a timeout should be forwarded to the current mode object.
protected  void txPacket(byte[] buffer, byte blockNo)
          Transmits a packet of data.
protected  boolean waitForData()
          Waits for data for the specific duration.
 
Methods inherited from class java.lang.Object
clone, equals, finalize, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Field Detail

maxRequestListLength

protected int maxRequestListLength
The maximal length of request queue. The number of elements which can be added to it is one less than this number, because it is a wrap-around array of elements and since there is no way to distinguish an empty array from a full one, one element goes to waste and it is assumed that the queue is full when it contains maxRequestListLength-1 elements.


requestList

protected final CombiCommand[] requestList
This is a list of requests to be processed. Any object can ask the controller to be added to this list and when time comes, it will be asked to fill in an array with data to be sent. The length of the station's input buffer is limited, hence it is necesary to chop the potential sequence of commands into manageable chunks, which is done at the point of building it for transmission. Ordinary command stations retransmit speed-related data internally, with data to be sent modified by messages. Combi-station does not do this - we generate all DCC data from Java. For this reason, the only solution is to store a buffer of data to be sent periodically; this is currently implemented in the form of CombiCommand-kind of objects which are asked to fill in a buffer to retransmit. In case loconet-type slots are implemented, CombiCommand-kind of objects could be stored in a list and their contents modified via messages. This two-layer interface is strange- we do not need to pretend to be loconet when we are not. For this reason, CombiCommand is directly implemented by the CombiThrottle, CombiTurnout etc high-level objects. Objects requesting data to be sent out are added to the end of the list and is removed from the front after station's replies are passed to those objects. In order to support removal of objects, nulls are allowed to be included in this list; they may only appear there if requests have been removed by removeRequest.


commBufferMiscLength

protected static final int commBufferMiscLength
The length of miscellaneouts elements in an RX/TX buffer (i.e. 5 bytes for a reset packet, including a preamble (service mode only) + 3 bytes for a current sense 1 byte for the end of a stream, 2 bytes for length and 2 for crc). This is necessary because the effective available length of the buffer is commBufferMaxLength-commBufferMiscLength

See Also:
Constant Field Values

minCommBufferLength

protected int minCommBufferLength
The minimal length of the comm buffer to be able to send/receive anything useful. It is checked that the length reported by the command station is at least that long.


commBufferMaxLength

protected int commBufferMaxLength
The maximal length of the RX/TX buffer.


commInputStream

protected java.io.InputStream commInputStream
The input stream to be used for communication with a port


commOutputStream

protected java.io.OutputStream commOutputStream
The output stream to be used for communication with a port


mode

protected AsynchTrafficController.CombiMode mode
The current mode.


previousMode

protected AsynchTrafficController.CombiMode previousMode
The current mode.


combiOpsMode

protected AsynchTrafficController.CombiMode combiOpsMode
A possible mode


combiServiceMode

protected AsynchTrafficController.CombiMode combiServiceMode
A possible mode


combiPowerOff

protected AsynchTrafficController.CombiMode combiPowerOff
A possible mode


combiModeChange

protected jmri.jmrix.combi.trafficCtrl.classModeChangeMode combiModeChange
A mode the controller is in while initialising a new mode.


request_New

protected int request_New
Stores the position in the request list where the new request will be stored. If request_New == request_First, there are no new requests, but there could be old ones between request_Submitted and request_First. Those between request_Submitted and request_Current have already been sent to the command station, those between request_Current and request_First should be sent on the current iteration through the main loop of the run() method. When I'm talking of "from ... to", "from" is inclusive but "to" is not.


request_First

protected int request_First
Stores the position in the request list where the first element is stored.


request_Current

protected int request_Current
Stores the position of the request which is transferred to the command station on a particular iteration through the main loop of the run() method.


request_Submitted

protected int request_Submitted
Stores the position of the request which was previously send to the command station (i.e. on a previous iteration through the main loop of the run() method.


workerThread

protected java.lang.Thread workerThread
The thread which performs all the work communicating with the station.


flashThread

protected java.lang.Thread flashThread
The thread which is responsible to writing the firmware. The two are different because a power manager needs to the traffic controller to be active (no power requests should be allowed while the firmware is being flashed).


MessageCommand

protected int MessageCommand
Holds commands to the comm processing thread.


anObject

protected java.lang.Object anObject
An object to receive a notification (if non-null) that a message has been processed or will be used to enter a mode.


capChargeTimeout

protected boolean capChargeTimeout
Stores true if the CDU capacitor is currently discharged, false if it is ok.


layoutOverload

protected boolean layoutOverload
Stores true if the layout is experiencing an overload.


possibleResponses

protected static final byte[][] possibleResponses
Contains possible responses by the command station.


errorMessages

protected static final byte[][] errorMessages
Error messages transmitted upon an abort.


msg_firmwareFileName

public static final java.lang.String msg_firmwareFileName
Holds the name of the firmwareFileName property.

See Also:
Constant Field Values
Constructor Detail

CombiTrafficController

public CombiTrafficController()
The constructor - does nothing since initialisation is deferred to the Init method and the worker thread.

Method Detail

getStatistics

public final CombiTrafficStatistics getStatistics()
Returns the traffic statistics for this controller.

Returns:
the instance of the class containing transmission statistics for this controller.

getMode

public final AsynchTrafficController.CombiMode getMode()
Returns the current mode

Returns:
the current mode.

setMode

public void setMode(AsynchTrafficController.CombiMode newMode)
Assigns a new mode without initialisation and fires property changes. For a proper mode change, use sendMessage(cmdCHANGEMODE, newMode). This method has to be public in order for it to be included in the AsynchTrafficController interface. Such an inclusion is needed in order for mode-related classes to use the interface and hence be testable through a mock implementation of it.

Specified by:
setMode in interface AsynchTrafficController
Parameters:
newMode - the mode to switch to.

getCommsLost

public boolean getCommsLost()
Provides information as to whether communication with the command station has been lost or not.

Specified by:
getCommsLost in interface AsynchTrafficController
Returns:
true if communication has been lost.

getServiceMode

public final AsynchTrafficController.CombiMode getServiceMode()
Returns the mode name of the service mode change class

Specified by:
getServiceMode in interface AsynchTrafficController
Returns:
an instance of the service-mode mode class.

getOpsMode

public final AsynchTrafficController.CombiMode getOpsMode()
Returns the mode name of the opreational mode change class

Specified by:
getOpsMode in interface AsynchTrafficController
Returns:
an instance of the ops-mode mode class.

getPowerOffMode

public final AsynchTrafficController.CombiMode getPowerOffMode()
Returns the mode name of the Power-off mode change class

Specified by:
getPowerOffMode in interface AsynchTrafficController
Returns:
an instance of the poweroff-mode mode class.

setPower

public void setPower(int powerMode)
              throws JmriException
Sets the power mode.

Specified by:
setPower in interface PowerManager
Parameters:
powerMode - either PowerManager.ON or PowerManager.OFF.
Throws:
JmriException - if either an invalide power mode is specified or the controller is not active.

getPower

public int getPower()
Returns whether power is on or off. If the controller is not active, returns OFF.

Specified by:
getPower in interface PowerManager
Returns:
PowerManager.ON if power is on, otherwise PowerManager.OFF.

appendRequest

public void appendRequest(CombiCommand what)
                   throws JmriException
Appends a supplied command to the request list.

Specified by:
appendRequest in interface AsynchTrafficController
Parameters:
what - the command to send in the due course.
Throws:
JmriException - if the supplied request can never be completed, such as when it is longer the the station's buffer. The request is cancelled if it cannot be completed at the moment (such as when there are already too many requests outstanding, but it could be completed if attempted later).

appendRequestForMode

public void appendRequestForMode(CombiCommand what,
                                 AsynchTrafficController.CombiMode newMode)
                          throws JmriException
Appends a supplied command to the request list expecting a particular mode to be in place (null means `no mode'). If the current mode is different, the request is cancelled.

Specified by:
appendRequestForMode in interface AsynchTrafficController
Parameters:
what - the command to send in the due course.
newMode - the mode expected by request what.
Throws:
JmriException - if the supplied request can never be completed, such as when it is longer the the station's buffer. The request is cancelled if it cannot be completed at the moment (such as when there are already too many requests outstanding or the mode is wrong, but it could be completed if attempted later).

removeRequest

public boolean removeRequest(CombiCommand what)
Removes a supplied command from the request list.

Specified by:
removeRequest in interface AsynchTrafficController
Parameters:
what - the command to remove from the request list
Returns:
whether the element was successfully removed.

setStreams

public void setStreams(java.io.InputStream is,
                       java.io.OutputStream os,
                       testInterface whomToNotify)
Sets the two comm streams CombiTrafficController is expected to use.

Specified by:
setStreams in interface SetStreams
Specified by:
setStreams in interface DataAvailableListener
Parameters:
is - the input stream to use
os - the output stream to use
whomToNotify - whom to notify instead of doing a wait(rxTimeout).

Init

public void Init()
          throws JmriException
Initialises the traffic controller and retrieves the stream length from the station. This method is synchronized to prevent multiple calls to it from being active at the same time.

If Init does not throw an exception, this means that the processing has successfully started on a separate thread. When completed, the msg_Termination property change will be thrown.

Specified by:
Init in interface AsynchTrafficController
Throws:
JmriException - if an error occurs.

byteToString

public static final java.lang.StringBuffer byteToString(byte[] data)
Converts a sequence of bytes into a string which can be shown to a user, for instance. Every printable ascii character is represented as is and others have their hex codes surrounded with brackets, for instance [0x12].

Parameters:
data - what to convert.
Returns:
the result of the conversion.

resynchronize

protected void resynchronize(int status)
                      throws java.lang.InterruptedException
Re-establishes the synch between the command station and jmri, which gets lost each time there is a comms error.

Parameters:
status - what to re-synchronize from (either sERROR or sTIMEOUT).
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.

resetCommandStation

protected void resetCommandStation()
                            throws java.lang.InterruptedException
This method resets the station. The routine itself is similar to resynchronize.

Throws:
java.lang.InterruptedException - if resetting was interrupted.

match

public int match(byte[] whatToMatch)
          throws java.io.IOException,
                 java.lang.InterruptedException
Matches a single string with data received in a stream.

Parameters:
whatToMatch - the string to match
Returns:
either sFINISHED (matched ok), sERROR (unexpected char), sTIMEOUT (timeout received)
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
java.io.IOException - if access to a serial port causes IOException.

match

protected int match(byte[][] exBuffers)
             throws java.io.IOException,
                    java.lang.InterruptedException
Matches a number of strings with data received in a stream. Neither of the strings can be a prefix of another one - the longer will otherwise never be matched. Returns the index of a matched string.

The method calls commInputStream.available() (as per EasyDCC) and then processes that number of bytes. The received number of bytes is held in a buffer, hence we can consider it being received atomically and thus a timeout needs reset not after each byte but after we've gone through the available bytes. The method then does commInputStream.available() and repeats the same.

Parameters:
exBuffers - the array of strings to match
Returns:
either sFINISHED+index_of_matched_string (matched ok), sERROR (unexpected char), sTIMEOUT (timeout received)
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
java.io.IOException - if access to a serial port causes IOException.

DataAvailable

public void DataAvailable()
This method is called if any data is available for reception from a communications port. We do not care about the call to notify() here because we are not doing a wait() only when sending bytes out (there we do a poll in the beginning of the transmission). An extra notify() will not cause resynchronise to skip a wait for a timeout because no wait will be in progress when notify() runs. Finally, asynchronous notifications are considered to be no more than advisory when not doing a wait: we use the .available on an input stream when needed.

Specified by:
DataAvailable in interface DataAvailableListener

waitForData

protected boolean waitForData()
                       throws java.lang.InterruptedException,
                              java.io.IOException
Waits for data for the specific duration. The fact that both this method and DataAvailable are synchronized implies that a notification cannot go unnoticed, i.e. between controller's call to available() and its call to wait().

Returns:
true if data was received, false on a timeout.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
java.io.IOException - if access to a serial port causes IOException.

recv

protected int recv(int expectedResponseLength)
            throws java.io.IOException,
                   java.lang.InterruptedException
This function is responsible for receiving data into the rx buffer and checking that the checksum matches.

Parameters:
expectedResponseLength - the expected length of the response to receive. If not 0, this is verified against what the command station sends, if 0 the number of bytes returned by a station is trusted (as long as it is not above commBufferMaxLength)
Returns:
whether the outcome was successful or not.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
java.io.IOException - if access to a serial port causes IOException.

sendBYTES

protected int sendBYTES(byte[] data,
                        int length)
                 throws java.io.IOException
Sends the buffer given to it, out. Any character received before the transmission commences is interpreted as a failure.

Parameters:
data - what to send out.
length - the length of data in the buffer (we'd like not to allocate a new buffer for each request).
Returns:
either successs (sFINISHED) or a failure (sERROR).
Throws:
java.io.IOException - if an I/O error occurrs.

moveToHoldingBuffer

protected int moveToHoldingBuffer()
Goes through the list of requests with the aim of picking as many of them as possible as long as the total length of requests picked does not exceed the length of comm buffers. At entry it is assumed that request_Current == request_First.

Returns:
the number of bytes to place in a tx buffer.

buildTransaction

protected int buildTransaction(byte[] buffer,
                               boolean newmode)
                        throws JmriException
This function populates the list of requests, lists of lengths and the TX buffer.

Parameters:
buffer - buffer to be filled with data.
newmode - true if we're processing a mode change.
Returns:
the length of the transaction
Throws:
JmriException - if the length of the data to be transmitted has unexpectedly changed.

populateTXBuffer

protected int populateTXBuffer(byte[] buffer)
Fills the transmission buffer with data to be sent out, starting from request_Current and up to (but not including) request_First.

Parameters:
buffer - what to populate from reqList.
Returns:
the number of bytes stored in buffer.

sendCommand

protected int sendCommand(byte[] command,
                          byte[] response)
                   throws java.lang.InterruptedException
This method sends a command to the station. It differs from sendBYTES in that it expects every byte to be echoed by the station.

Parameters:
command - the text of the command to send, ending with \r
response - the expected response to the command. This is typically command with \n appended.
Returns:
sFINISHED upon a success, otherwise either sERROR or sTIMEOUT.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.

sendBuffer

protected int sendBuffer(int bufferLength,
                         int commLength)
                  throws java.lang.InterruptedException
Sends the pending data and waits for a response. The current implementation assumes that we've got buffering, i.e. data transmitted by the station is buffered and delivered. This makes it possible first to transmit a request and then wait for a response.

Parameters:
bufferLength - the number of bytes from the txBuffer to transmit. This is expected to fit in the command station's buffer.
commLength - the expected length of the response to receive.
Returns:
sFINISHED upon a success, otherwise either sERROR or sTIMEOUT.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.

sendMessage

public void sendMessage(int Message,
                        java.lang.Object newObject)
In order to send any message, I need to ensure both the sending and the comms thread are not going to modify the same chunk of data at the same time. Locking on the instance of CombiTrafficController thread will do the trick. Moreover, the quit request has a priority over other ones, such that once it is set, no mode change request can override it. Sending cmdOK is not allowed. If a message overrides an existing one, nothing happens, but whoever requested a mode change will be expected to receive a notification of a mode change to a different mode than it requested, which is synonymous with an abort.

Parameters:
Message - the message to send to the controller thread.
newObject - the object which will be used to enter a mode.

abortRequests

protected void abortRequests()
Aborts all the requests from the request list, between request_Submitted and request_New.


getCapChargeTimeout

public boolean getCapChargeTimeout()
Makes it possible to determine whether cap charge has timed out or not.

Specified by:
getCapChargeTimeout in interface AsynchTrafficController
Returns:
whether the CDU charging has timed out.

setCapChargeTimeout

protected void setCapChargeTimeout(boolean newCapChargeTimeout)
This is the internal setter for the capChargeTimeout variable.

Parameters:
newCapChargeTimeout - the new value of the capChargeTimeout variable.

getOverload

public boolean getOverload()
Determines whether the command station has got an overload on its DCC output.

Specified by:
getOverload in interface AsynchTrafficController
Returns:
true if there is an overload.

setOverload

protected void setOverload(boolean overload)
Sets or clears the overload value.

Parameters:
overload - whether there is an overload or not.

submitData

protected int submitData(int reqCurrentLength,
                         int reqSubmittedLength)
                  throws JmriException,
                         java.lang.InterruptedException
Sends the data in the txBuffer to the command station, resynchronizes if needed and otherwise does parseResponse on all elements of the list submitted.

Parameters:
reqCurrentLength - the number of data bytes in the txBuffer to submit (+2 bytes will be submitted).
reqSubmittedLength - the number of bytes previously submitted.
Returns:
the outcome (sOVERFLOW upon an overflow).
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
JmriException - if parseResponse throws it.

timeout

public void timeout()
Called by timout objects to indicate that a timeout should be forwarded to the current mode object.

Specified by:
timeout in interface Timeout.HandleTimeout

statusResponse

protected int statusResponse(java.lang.StringBuffer response)
                      throws java.lang.InterruptedException
This function retrieves the response produced by the STATUS command.

Parameters:
response - the response from STATUS.
Returns:
the status (sFINISHED if everything went ok, otherwise an error code).
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.

statusData

protected byte[] statusData(int from,
                            int to)
                     throws java.lang.InterruptedException
The following function retrieves the data of the station's response to the STATUS command, resynchronizing on comms errors. Response data between from and to-1 is converted to hex and returned as an array of bytes. This function does not check the validity of its two arguments. An IllegalArgumentException is thrown if the number of bytes to extract is negative, the from argument is negative or if the number of bytes between to and from is odd.

Parameters:
from - the index of the first byte of response to convert to hex.
to - the index of the last+1 byte of response to convert to hex.
Returns:
an array of bytes comprising a response. null if the station is terminating or something went wrong.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.

getRequestListLength

protected int getRequestListLength()
Returns the length of the list of requests, discounting requests which are null (i.e. have been removed).

Returns:
request list length.

getCurrentListLength

protected int getCurrentListLength()
Returns the length of the list of those requests which should be submitted to the command station in the current iteration through the main loop of the run() method. This method does not count null requests, i.e. those which have been removed.

Returns:
request list length.

run

public void run()
Sends data in the request buffer out. In the meantime, the list of requests can get modified, however new requests are appended at the end and we pick requests from the head. There are a few situations worth distinguishing,
mode == modeChange modechange == true description.
Y Y a new mode requested while servicing a transition to the new mode.
Y N initialisation of a new mode in progress, past the TXold.
N Y about to do TXold.
N N normal operation
It is worth noting that upon startup, we may have requests in our request list which were appended between the previous shutdown and our startup (or right after the object creation before the thread was run). We first send data out from the first buffer, then from the second one. Upon an error, requests in both of them get cancelled.

Specified by:
run in interface java.lang.Runnable

sendPacket

public void sendPacket(byte[] packet,
                       int repeats)
Sends DCC packets out as per the CommandStation interface. Refer to PacketSender class for more details as to how this is done.

Specified by:
sendPacket in interface CommandStation
Parameters:
packet - data to send
repeats - how many times to send the packet

makeResponse

public void makeResponse(ProgListener whomToRespond,
                         int value,
                         int status)
This is a helper method to respond to anyone who implements the ProgListener interface, via the gui thread. It is not made static because we need to stub it for testing via the testIO method.

Specified by:
makeResponse in interface ResponseMaker
Parameters:
whomToRespond - whoever to respond to
value - the value to return, -1 has a special meaning in that while ordinary numbers will be displayed in a dialogue (if at all), -1 will be displayed as a blank.
status - whether whatever we are reporting on was successful or not.

addPropertyChangeListener

public void addPropertyChangeListener(java.beans.PropertyChangeListener l)
Adds a supplied listener to the list of listeners.

Specified by:
addPropertyChangeListener in interface PowerManager
See Also:
PowerManager.addPropertyChangeListener(java.beans.PropertyChangeListener)

removePropertyChangeListener

public void removePropertyChangeListener(java.beans.PropertyChangeListener l)
Removes a supplied listener from the list of listeners.

Specified by:
removePropertyChangeListener in interface PowerManager
See Also:
PowerManager.removePropertyChangeListener(java.beans.PropertyChangeListener)

firePropertyChange

public void firePropertyChange(java.lang.String message,
                               java.lang.Object oldValue,
                               java.lang.Object newValue)
Fires a property change notification.

Parameters:
message - the message to send, such as a property name.
oldValue - the old value of the property.
newValue - the new value of the property.

dispose

public void dispose()
Disposes with this controller. In real terms, all this method does is ensures that the worker thread is terminated. This is guaranteed to happen after a period of time, rxTimeout*5/4 seconds at most.

The traffic controller does not eliminate any listeners, since we'd like to be able to initialise and terminate it at will. Nothing seems wrong even with two threads terminating it at the same time. Nothing seems obviously wrong even with one thread initialising and another one - terminating the controller at the same time.

Specified by:
dispose in interface PowerManager

recvAscii

protected int recvAscii(int commLength,
                        java.lang.StringBuffer message)
                 throws java.io.IOException,
                        java.lang.InterruptedException
This function is similar to recv but works on ASCII strings and expects the length of the response to be supplied as a parameter.

Parameters:
commLength - the number of bytes (including the 4 bytes of CRC) to receive.
message - the buffer which is to be filled with the mesage.
Returns:
sFINISHED upon success or an error code otherwise.
Throws:
java.lang.InterruptedException - if the CombiTrafficController thread was asked to terminate.
java.io.IOException - if access to a serial port causes IOException.

sendFile

public void sendFile(java.io.File file)
              throws JmriException
Sends a file provided via YModem to the command station.

Specified by:
sendFile in interface Ymodem
Parameters:
file - the file to send.
Throws:
JmriException - if an error occurrs anywhere. In that case, the transmission will already be aborted.

sendData

public void sendData(java.io.BufferedReader is,
                     java.lang.String name)
              throws JmriException
Sends the provided stream of data to the command station.

Firmware download is similar to string-sending but does not synch for sending of every line; it synches per packet. The downloader has to load a file, convert it into binary .hex and send it out.

If it does not throw an exception, this means that the processing has successfully started on a separate thread. When completed, the msg_Termination property change will be thrown. The "new value" of this property will be set to a Boolean if transmission went successfully or the JmriException if it failed. AssertionFailedError is thrown if an assertion is thrown from within the thread (useful for testing). The boolean will be set to true if the station's program memory was modified, false if it was not.

Specified by:
sendData in interface Ymodem
Parameters:
is - the stream of data to send.
name - the name of the file to give to this stream. This is important since the station will ignore any name other than the one which was wired into its firmware.
Throws:
JmriException - if something went wrong.

sendStream

protected boolean sendStream(java.io.BufferedReader is,
                             java.lang.String name)
                      throws JmriException
Sends a stream of data to the command station via YModem. If I get some data from the station when I do not expect it or if I get an IOError, abort a transmission and send the abort to the station.

Parameters:
is - the stream of data to send.
name - the name of the file to give to this stream. This is important since the station will ignore any name other than the one which was wired into its firmware.
Returns:
true if the program memory was modified, false if it was not.
Throws:
JmriException - if a transmission was aborted by the station.

txPacket

protected void txPacket(byte[] buffer,
                        byte blockNo)
                 throws java.io.IOException,
                        JmriException
Transmits a packet of data.

Parameters:
buffer - what to transmit, either null (in order to transmit EOT) or of length 128 or 1024.
blockNo - the number of this block.
Throws:
java.io.IOException - if an error occurs which requires us to abort a transmission.
JmriException - if a transmission was aborted by the command station.

getRxTimeout

public int getRxTimeout()
Retrieves the value of the rxTimeout variable.

Returns:
the value of rxTimeout.

setRxTimeout

public void setRxTimeout(int newTimeout)
Assigns a new value to the rxTimeout variable.

Parameters:
newTimeout - the new value.

getYmRetransmitNumber

public int getYmRetransmitNumber()
Retrieves the value of the ymRetransmitNumber variable

Returns:
the value of ymRetransmitNumber.

setYmRetransmitNumber

public void setYmRetransmitNumber(int retransmitCnt)
Assigns a new value to the ymRetransmitNumber variable.

Parameters:
retransmitCnt - the new value.

getFirmwareFileName

public java.lang.String getFirmwareFileName()
Retrieves the value of the firmwareFileName variable

Returns:
the value of firmwareFileName.

setFirmwareFileName

public void setFirmwareFileName(java.lang.String newFirmwareFileName)
Assigns a new value to the firmwareFileName variable.

Parameters:
newFirmwareFileName - the new value.


Copyright © 1997 - 2004 JMRI Community.
JMRI, DecoderPro, PanelPro, DispatcherPro and associated logos are our trademarks.

Site hosted by: SourceForge Logo