Skip to content

Commit

Permalink
Traction throttle fixes (#198)
Browse files Browse the repository at this point in the history
- Adds implementation of heartbeat request message.
- Registers the TractionThrottle as a listener to the train.
- Adds support for the forwarded (listener) messages coming in as TractionRequest messages.
- Implements getter methods for enquiring speed and function arguments on the TRactionControlRequest message.
- Adds tests for the new message features.

===

* Traction throttle fixes

- Adds implementation of heartbeat request message.
- Registers the TractionThrottle as a listener to the train.
- Adds support for the forwarded (listener) messages coming in as TractionRequest messages.
- Implements getter methods for enquiring speed and function arguments on the TRactionControlRequest message.
- Adds tests for the new message features.

* Remove warning logs that will spam.
  • Loading branch information
balazsracz authored Mar 7, 2022
1 parent 3b976a1 commit e37e1dd
Show file tree
Hide file tree
Showing 3 changed files with 134 additions and 6 deletions.
67 changes: 62 additions & 5 deletions src/org/openlcb/implementations/throttle/TractionThrottle.java
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@
import org.openlcb.messages.TractionControlReplyMessage;
import org.openlcb.messages.TractionControlRequestMessage;

import static org.openlcb.messages.TractionControlRequestMessage.CONSIST_FLAG_LISTENERS;

/**
* Traction protocol based implementation of the throttle. This differs from
* {@link org.openlcb.implementations.throttle.ThrottleImplementation} in that
Expand Down Expand Up @@ -106,7 +108,11 @@ public void refresh() {
*/
public void release() {
if (!assigned) return;
Message m = TractionControlRequestMessage.createReleaseController(iface.getNodeId(),
Message m = TractionControlRequestMessage.createConsistDetach(iface.getNodeId(),
trainNode.getNodeId(),iface.getNodeId());
iface.getOutputConnection().put(m, this);

m = TractionControlRequestMessage.createReleaseController(iface.getNodeId(),
trainNode.getNodeId());
iface.getOutputConnection().put(m, this);
assigned = false;
Expand All @@ -130,12 +136,29 @@ private void assign() {
iface.getOutputConnection().put(m, this);
}

/**
* Invoked when the controller assign reply message arrives from the train node with a
* successful result.
*/
private void assignComplete() {
assigned = true;
setStatus("Enabled.");
setEnabled(true);
// Refreshes functions and other settings after getting the definite promise from the node.
refresh();
Message m = TractionControlRequestMessage.createConsistAttach(iface.getNodeId(),
trainNode.getNodeId(),iface.getNodeId(),CONSIST_FLAG_LISTENERS);
iface.getOutputConnection().put(m, this);
}

/** Invoked when a heartbeat request message comes from the train node.
* Sends back a noop traction request.
* @param msg heartbeat request message
*/
private void handleHeartbeatMessage(TractionControlReplyMessage msg) {
Message m = TractionControlRequestMessage.createNoop(iface.getNodeId(),
trainNode.getNodeId());
iface.getOutputConnection().put(m, this);
}

/**
Expand Down Expand Up @@ -221,7 +244,7 @@ public VersionedValue<Boolean> getFunction(int fn) {
private synchronized FunctionInfo getFunctionInfo(int fn) {
FunctionInfo v = functions.get(fn);
if (v == null) {
logger.warning("Creating function " + fn);
logger.fine("Creating function " + fn);
v = new FunctionInfo(fn);
functions.put(fn, v);
if (!pendingAssign) {
Expand Down Expand Up @@ -261,7 +284,7 @@ public void handleTractionControlReply(TractionControlReplyMessage msg, Connecti
if (msg.getCmd() == TractionControlReplyMessage.CMD_GET_FN) {
int fn = msg.getFnNumber();
int val = msg.getFnVal();
logger.warning("Function response: train function " + fn + " value " + val);
logger.fine("Function response: train function " + fn + " value " + val);
getFunctionInfo(fn).fnUpdater.setFromOwner(val != 0);
return;
}
Expand Down Expand Up @@ -300,20 +323,54 @@ public void handleTractionControlReply(TractionControlReplyMessage msg, Connecti
queryConsist();
return;
}
if (msg.getCmd() == TractionControlReplyMessage.CMD_MGMT &&
msg.getSubCmd() == TractionControlReplyMessage.SUBCMD_MGMT_HEARTBEAT) {
handleHeartbeatMessage(msg);
return;
}
} catch (ArrayIndexOutOfBoundsException e) {
// Invalid message.
logger.warning("Invalid traction message " +msg.toString());
return;
}
logger.info("Unhandled traction response message " +msg.toString());
}

@Override
public void handleTractionControlRequest(TractionControlRequestMessage msg, Connection sender) {
if (trainNode == null) return;
if (!msg.getSourceNodeID().equals(trainNode.getNodeId())) return;
if (!msg.getDestNodeID().equals(iface.getNodeId())) return;
try {
if (msg.getCmd() == TractionControlRequestMessage.CMD_SET_SPEED) {
speedUpdater.setFromOwner(msg.getSpeed().getFloat());
return;
}
if (msg.getCmd() == TractionControlRequestMessage.CMD_ESTOP) {
speedUpdater.setFromOwner(Float.NaN);
return;
}
if (msg.getCmd() == TractionControlRequestMessage.CMD_SET_FN) {
int fn = msg.getFnNumber();
int val = msg.getFnVal();
logger.fine("Function request: train function " + fn + " value " + val);
getFunctionInfo(fn).fnUpdater.setFromOwner(val != 0);
return;
}
} catch (ArrayIndexOutOfBoundsException e) {
// Invalid message.
logger.warning("Invalid traction message " +msg.toString());
return;
}
logger.info("Unhandled traction message " +msg.toString());
logger.info("Unhandled traction request message (to listener) " +msg.toString());
}

public String getStatus() {
return status;
}

private void setStatus(String status) {
logger.warning("Throttle status: " + status);
logger.fine("Throttle status: " + status);
String oldStatus = this.status;
this.status = status;
firePropertyChange(UPDATE_PROP_STATUS, oldStatus, this.status);
Expand Down
41 changes: 40 additions & 1 deletion src/org/openlcb/messages/TractionControlRequestMessage.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,16 @@ public class TractionControlRequestMessage extends AddressedPayloadMessage {
public final static int CONSIST_FLAG_FNN = 0x08;
public final static int CONSIST_FLAG_HIDE = 0x80;

public final static int CONSIST_FLAG_LISTENERS =
CONSIST_FLAG_HIDE | CONSIST_FLAG_FN0 | CONSIST_FLAG_FNN;

public final static byte CMD_MGMT = 0x40;
public final static byte SUBCMD_MGMT_RESERVE = 1;
public final static byte SUBCMD_MGMT_RELEASE = 2;
public final static byte SUBCMD_MGMT_NOOP = 3;

public final static byte CMD_LISTENER_FORWARD = (byte)0x80;

/// 1 scale mph in meters per second for the getspeed/setspeed commands
public final static double MPH = 0.44704;
/// 1 scale km/h in meters per second for the getspeed/setspeed commands
Expand Down Expand Up @@ -168,7 +173,18 @@ public static TractionControlRequestMessage createNoop(NodeID source, NodeID des
}

public byte getCmd() throws ArrayIndexOutOfBoundsException {
return payload[0];
int p = payload[0] & 0xff;
// Removes the consist listener forward flag.
return (byte)(p & 0x7F);
}

/**
* Checks if this message is forwarded as part of the consist listener protocol.
* @return true if this message is a forwarded traciton request, false if this is an original
* (operator-initiated) command.
*/
public boolean isListenerMessage() {
return (payload[0] & CMD_LISTENER_FORWARD) != 0;
}

// Valid for messages that contain a subcommand.
Expand All @@ -181,6 +197,26 @@ public Float16 getSpeed() throws ArrayIndexOutOfBoundsException {
return new Float16((((int)payload[1]) << 8) | (payload[2] & 0xff));
}

// Valid only for get function request
public int getFnNumber() {
int retval = 0;
retval = payload[1] & 0xff;
retval <<= 8;
retval |= (payload[2] & 0xff);
retval <<= 8;
retval |= (payload[3] & 0xff);
return retval;
}

// Valid only for get function request
public int getFnVal() {
int retval = 0;
retval = payload[4] & 0xff;
retval <<= 8;
retval |= (payload[5] & 0xff);
return retval;
}

@Override
public void applyTo(MessageDecoder decoder, Connection sender) {
decoder.handleTractionControlRequest(this, sender);
Expand Down Expand Up @@ -234,6 +270,9 @@ public String toString() {
p.append(" ");
p.append(getEMTI().toString());
p.append(" ");
if (isListenerMessage()) {
p.append("[listener] ");
}
try {
switch (getCmd()) {
case CMD_SET_SPEED: {
Expand Down
32 changes: 32 additions & 0 deletions test/org/openlcb/messages/TractionControlRequestMessageTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,21 +32,49 @@ public void testCreateSetSpeed() throws Exception {
Assert.assertEquals("00 45 D0", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set speed F 13 mph", msg.toString());
Assert.assertEquals(13 * MPH, msg.getSpeed().getFloat(), 1e-2);

msg = TractionControlRequestMessage.createSetSpeed(src, dst, false, 13 * MPH);
Assert.assertEquals("00 C5 D0", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set speed R 13 mph", msg.toString());
Assert.assertEquals(-13 * MPH, msg.getSpeed().getFloat(), 1e-2);

msg = TractionControlRequestMessage.createSetSpeed(src, dst, true, 126 * MPH);
Assert.assertEquals("00 53 0A", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set speed F 126 mph", msg.toString());
Assert.assertEquals(126 * MPH, msg.getSpeed().getFloat(), 1e-1);

msg = TractionControlRequestMessage.createSetSpeed(src, dst, false, 126 * MPH);
Assert.assertEquals("00 D3 0A", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set speed R 126 mph", msg.toString());
Assert.assertEquals(-126 * MPH, msg.getSpeed().getFloat(), 1e-1);
}

@Test public void testListenerReply() throws Exception {
TractionControlRequestMessage msg = new TractionControlRequestMessage(src, dst,
Utilities.bytesFromHexString("80C5D0"));
Assert.assertEquals(TractionControlRequestMessage.CMD_SET_SPEED, msg.getCmd());
Assert.assertEquals(-13 * MPH, msg.getSpeed().getFloat(), 1e-2);
Assert.assertEquals(true, msg.isListenerMessage());

msg = new TractionControlRequestMessage(src, dst,
Utilities.bytesFromHexString("81 BB AA 99 DD BA"));
Assert.assertEquals(TractionControlRequestMessage.CMD_SET_FN, msg.getCmd());
Assert.assertEquals(12298905, msg.getFnNumber());
Assert.assertEquals(56762, msg.getFnVal());
Assert.assertEquals(true, msg.isListenerMessage());
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"[listener] set fn 12298905 to 56762", msg.toString());

msg = new TractionControlRequestMessage(src, dst,
Utilities.bytesFromHexString("01 BB AA 99 DD BA"));
Assert.assertEquals(TractionControlRequestMessage.CMD_SET_FN, msg.getCmd());
Assert.assertEquals(12298905, msg.getFnNumber());
Assert.assertEquals(56762, msg.getFnVal());
Assert.assertEquals(false, msg.isListenerMessage());
}

@Test
Expand All @@ -72,12 +100,16 @@ public void testCreateSetFn() throws Exception {
Assert.assertEquals("01 00 00 0B 00 01", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set fn 11 to 1", msg.toString());
Assert.assertEquals(11, msg.getFnNumber());
Assert.assertEquals(1, msg.getFnVal());

msg = TractionControlRequestMessage.createSetFn(src, dst,
12298905, 56762);
Assert.assertEquals("01 BB AA 99 DD BA", Utilities.toHexSpaceString(msg.getPayload()));
Assert.assertEquals("06.05.05.04.04.03 - 02.02.02.04.04.04 TractionControlRequest " +
"set fn 12298905 to 56762", msg.toString());
Assert.assertEquals(12298905, msg.getFnNumber());
Assert.assertEquals(56762, msg.getFnVal());
}

@Test
Expand Down

0 comments on commit e37e1dd

Please sign in to comment.