/*
* Comms.java
*/
/**
* Communication object performs basic handling of incoming and outgoing messages.
* It checks the incomming message for correct envelope (STX, VLI, ... ETX, LRC)
* and if checks out then it dispatches the message to the HostInterface object where
* the message is processed further.
* If the HostInterface object wishes to send a response back to the host communication
* object creates appropriate envelope around outgoing data. <p>
* Outgoing message is not transmitted if node address is global (ADDR_ALL)
* addressing all nodes. This is to prevent collisions on the network.
*/
class Comms extends Thread
{
/** Maximum size of the input/output message */
public static final int MAX_MSG_SIZE = 128;
/** Start of text character */
public static final byte CH_STX = 2;
/** End of text character */
public static final byte CH_ETX = 3;
/** No error detected */
public static final int NO_ERR = 0;
public static final int ERR_OVERFLOW = 1;
public static final int ERR_NODATA = 2;
public static final int ERR_DISABLED = 3;
public static final int ERR_NOETX = 4;
public static final int ERR_BADLRC = 5;
/** Message receive states */
private static final int ST_STX = 1;
private static final int ST_VLI = 2;
private static final int ST_DATA = 3;
private static final int ST_ETX = 4;
private static final int ST_LRC = 5;
private static final int ST_RECEIVED = 6;
/** Buffer for incomming messages */
private byte data_in[] = new byte[MAX_MSG_SIZE];
/** Buffer for outgoing messages */
private byte data_out[] = new byte[MAX_MSG_SIZE];
private int pos, inLen, error, state;
private byte lrc;
/** Reference to configuration object */
private Config Conf = null;
/**
* Set to true every time message is received. If not set within certain
* time limit (usually 60secs) then Jackpot board is re-started
*/
boolean haveMessage = false;
/**
* Constructor for communication object.
* @param Conf reference to configuration object
*/
public Comms(Config Conf)
{
state = ST_STX;
error = NO_ERR;
this.Conf = Conf;
}
/**
* Initialises the communication interface to specified baud rate,
* flushes the in/out buffers and enables the receiver.
* @param Baud baud rate to be used for communication
* @return True if initialisation was successful. False is returned if
* specified Baud rate was not 9600,19200 or 38400.
*/
public boolean Init(int Baud)
{
if (Baud != 38400 && Baud != 19200 && Baud != 9600)
return false;
SetBaudRate(Baud);
RxMode();
Flush();
return true;
}
/**
* Writes the message to the communication port. First it checks for the
* correctness of data, then it enables the transmitter and puts
* envelope around the message. Envelope has the following format:
* <STX><VLI><..data..><ETX><LRC>. LRC is calculated as EOR operation on
* all data sent including STX, VLI and ETX. At the end of transmission
* receiver is re-enabled.
* @param Len length of the outgoing data
* @param Msg byte array containing the outgoing data
* @return ERR_NODATA if input parameters do not contain message.
* NO_ERR is returned is transmission was OK.
*/
private int WriteMessage(int Len, byte[] Msg)
{
if (Msg == null || Msg.length == 0 || Len < 1)
return ERR_NODATA;
TxMode();
lrc = CH_STX;
lrc ^= (byte)Len;
PutByte(CH_STX);
PutByte((byte)(Len));
for (int i=0; i < Len; i++)
{
PutByte(Msg[i]);
lrc ^= Msg[i];
}
PutByte(CH_ETX);
lrc ^= CH_ETX;
PutByte(lrc);
RxMode();
return NO_ERR;
}
/**
* Main processing method for Comms object thread. Here are all
* incomming messages assembled and checked. If message checks Ok then
* handler, corresponding to the type of message, is invoked. Once
* the message has been processed an outgoing message can be formed by
* the HostInterface object and transmitted.
*/
public void run()
{
Timer MsgTimer = new Timer();
HostInterface HostInt = new HostInterface(Conf);
while (true)
{
if (state != ST_STX && MsgTimer.Expired())
{
state = ST_STX;
error = NO_ERR;
Flush();
}
if (ByteReceived())
{
byte ch = GetByte();
switch (state)
{
case ST_STX:
if (ch == CH_STX)
{
lrc = ch;
state = ST_VLI;
// start message timeout
MsgTimer.Start(1000);
}
break;
case ST_VLI:
inLen = ch;
lrc ^= ch;
pos = 0;
state = ST_DATA;
break;
case ST_DATA:
if (pos < inLen)
{
data_in[pos++] = ch;
lrc ^= ch;
}
if (pos >= inLen)
state = ST_ETX;
break;
case ST_ETX:
if (ch == CH_ETX)
{
lrc ^= ch;
state = ST_LRC;
}
else
error = ERR_NOETX;
break;
case ST_LRC:
if (lrc == ch)
{
state = ST_RECEIVED;
}
else
error = ERR_BADLRC;
break;
}
}
if (state == ST_RECEIVED)
{
haveMessage = true;
int outLen = HostInt.Process(inLen, data_in, data_out);
if (outLen > 0)
{
// do not send response if all nodes were addressed
if (data_out[HostInt.POS_ADDR] != HostInt.ADDR_ALL)
sWriteMessage(outLen, data_out);
// reset the system if required
if (data_out[HostInt.POS_CMD] == HostInt.CMD_RESET &&
data_out[HostInt.POS_RESP] == HostInt.NO_ERR)
{
JackSystem.SystemReset(2000);
}
}
state = ST_STX;
}
}
}
/**
* Writes single byte to the comm port.
* @param Val byte value to be transmitted.
* @return False if there is not enough room in the transmit buffer,
* otherwise true is returned.
*/
private native boolean PutByte(byte Val);
/**
* Checks is the receive buffer contain any unread characters.
* @return True if there are characters in the receive buffer,
* otherwise false is returned.
*/
private native boolean ByteReceived();
/**
* Reads the byte from the receive buffer.
* @return Received byte.
*/
private native byte GetByte();
/**
* Clears the input/output buffers.
*/
private native void Flush();
private native void Enable(boolean Tx, boolean Rx);
/**
* Install the interrupt handler for the comm port and sets the baud
* rate to the specified value.
* @param Baud baud rate to be used for communication.
*/
private native void SetBaudRate(int Baud);
/**
* Activates the transmitter and deactivates the receiver.
*/
private native void TxMode();
/**
* Activates receiver the and deactivates the transmitter.
*/
private native void RxMode();
}