package com.serotonin.bacnet4j.npdu.mstp;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.free.bacnet4j.util.SerialParameters;
public class SlaveNode extends MstpNode {
private static final Logger LOG = Logger.getLogger(SlaveNode.class.toString());
private enum SlaveNodeState {
idle, answerDataRequest
}
private SlaveNodeState state;
private long replyDeadline;
private Frame replyFrame;
public SlaveNode(SerialParameters serialParams, byte thisStation) throws IllegalArgumentException {
super(serialParams, thisStation);
validate();
}
public SlaveNode(InputStream in, OutputStream out, byte thisStation) throws IllegalArgumentException {
super(in, out, thisStation);
validate();
}
private void validate() {
int is = thisStation & 0xff;
if (is > 254)
throw new IllegalArgumentException("thisStation cannot be greater than 254");
state = SlaveNodeState.idle;
}
@Override
public void setReplyFrame(FrameType type, byte destination, byte[] data) {
synchronized (this) {
if (state == SlaveNodeState.answerDataRequest)
// If there is still time to reply immediately...
replyFrame = new Frame(type, frame.getSourceAddress(), thisStation, data);
}
}
@Override
protected void doCycle() {
readFrame();
if (state == SlaveNodeState.idle)
idle();
if (state == SlaveNodeState.answerDataRequest)
answerDataRequest();
}
/**
* In the IDLE state, the node waits for a frame.
*/
private void idle() {
if (receivedInvalidFrame != null) {
// ReceivedInvalidFrame
// debug("idle:ReceivedInvalidFrame");
if (LOG.isLoggable(Level.FINE))
LOG.fine("Received invalid frame: " + receivedInvalidFrame);
receivedInvalidFrame = null;
activity = true;
}
else if (receivedValidFrame) {
FrameType type = frame.getFrameType();
if (type == null) {
// ReceivedUnwantedFrame
if (LOG.isLoggable(Level.FINE))
LOG.fine("Unknown frame type");
}
else if (frame.broadcast()
&& type.oneOf(FrameType.token, FrameType.bacnetDataExpectingReply, FrameType.testRequest)) {
// ReceivedUnwantedFrame
if (LOG.isLoggable(Level.FINE))
LOG.fine("Frame type should not be broadcast: " + type);
}
else if (type.oneOf(FrameType.pollForMaster))
// ReceivedUnwantedFrame
; // It happens
else if (type.oneOf(FrameType.token, FrameType.pollForMaster, FrameType.replyToPollForMaster,
FrameType.replyPostponed)) {
// ReceivedUnwantedFrame
if (LOG.isLoggable(Level.FINE))
LOG.fine("Received unwanted frame type: " + type);
}
else if (frame.forStationOrBroadcast(thisStation)
&& type.oneOf(FrameType.bacnetDataNotExpectingReply, FrameType.testResponse)) {
// ReceivedDataNoReply
// debug("idle:ReceivedDataNoReply");
receivedDataNoReply(frame);
}
else if (frame.forStation(thisStation)
&& type.oneOf(FrameType.bacnetDataExpectingReply, FrameType.testRequest)) {
// ReceivedDataNeedingReply
// debug("idle:ReceivedDataNeedingReply");
state = SlaveNodeState.answerDataRequest;
replyDeadline = lastNonSilence + Constants.REPLY_DELAY;
replyFrame = null;
receivedDataNeedingReply(frame);
}
receivedValidFrame = false;
activity = true;
}
}
/**
* The ANSWER_DATA_REQUEST state is entered when a BACnet Data Expecting Reply, a Test_Request, or a proprietary
* frame that expects a reply is received.
*/
private void answerDataRequest() {
synchronized (this) {
if (replyFrame != null) {
// Reply
// debug("answerDataRequest:Reply");
sendFrame(replyFrame);
replyFrame = null;
state = SlaveNodeState.idle;
activity = true;
}
else if (replyDeadline >= timeSource.currentTimeMillis()) {
// CannotReply
// debug("answerDataRequest:CannotReply");
if (LOG.isLoggable(Level.FINE))
LOG.fine("Failed to respond to request: " + frame);
state = SlaveNodeState.idle;
activity = true;
}
}
}
}