org.bidib.jbidibc.usbstickbasis.UsbStickBasisBidib Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jbidibc-usbstickbasis Show documentation
Show all versions of jbidibc-usbstickbasis Show documentation
jBiDiB jbidibc USB Stick Basis POM
package org.bidib.jbidibc.usbstickbasis;
import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.core.AbstractBidib;
import org.bidib.jbidibc.core.AbstractMessageReceiver;
import org.bidib.jbidibc.core.BidibInterface;
import org.bidib.jbidibc.core.MessageListener;
import org.bidib.jbidibc.core.NodeListener;
import org.bidib.jbidibc.core.message.BidibRequestFactory;
import org.bidib.jbidibc.core.message.BidibRequestFactoryMessageMapInitializer;
import org.bidib.jbidibc.core.node.NodeRegistry;
import org.bidib.jbidibc.core.node.listener.TransferListener;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.ConnectionListener;
import org.bidib.jbidibc.messages.DriveState;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.MessageReceiver;
import org.bidib.jbidibc.messages.ProtocolVersion;
import org.bidib.jbidibc.messages.SoftwareVersion;
import org.bidib.jbidibc.messages.VendorData;
import org.bidib.jbidibc.messages.base.AbstractBaseBidib;
import org.bidib.jbidibc.messages.base.RawMessageListener;
import org.bidib.jbidibc.messages.enums.CommandStationPom;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.CsQueryTypeEnum;
import org.bidib.jbidibc.messages.exception.InvalidConfigurationException;
import org.bidib.jbidibc.messages.exception.PortNotFoundException;
import org.bidib.jbidibc.messages.exception.PortNotOpenedException;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.message.BidibMessage;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibResponseFactory;
import org.bidib.jbidibc.messages.message.CommandStationDriveAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveMessage;
import org.bidib.jbidibc.messages.message.CommandStationPomAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomMessage;
import org.bidib.jbidibc.messages.message.CommandStationQueryMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.FeatureCountResponse;
import org.bidib.jbidibc.messages.message.FeatureGetMessage;
import org.bidib.jbidibc.messages.message.FeatureNotAvailableResponse;
import org.bidib.jbidibc.messages.message.FeatureResponse;
import org.bidib.jbidibc.messages.message.FeatureSetMessage;
import org.bidib.jbidibc.messages.message.ResponseFactory;
import org.bidib.jbidibc.messages.message.StringGetMessage;
import org.bidib.jbidibc.messages.message.StringResponse;
import org.bidib.jbidibc.messages.message.StringSetMessage;
import org.bidib.jbidibc.messages.message.SysMagicResponse;
import org.bidib.jbidibc.messages.message.SysPVersionResponse;
import org.bidib.jbidibc.messages.message.SysSwVersionResponse;
import org.bidib.jbidibc.messages.message.SysUniqueIdResponse;
import org.bidib.jbidibc.messages.message.VendorAckResponse;
import org.bidib.jbidibc.messages.message.VendorEnableMessage;
import org.bidib.jbidibc.messages.message.VendorGetMessage;
import org.bidib.jbidibc.messages.message.VendorResponse;
import org.bidib.jbidibc.messages.message.VendorSetMessage;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.serial.SerialMessageEncoder;
import org.bidib.jbidibc.serial.SerialMessageReceiver;
import org.bidib.jbidibc.usbstickbasis.adapter.SelectedCar;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisAdapter;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisModel;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisResponseInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class UsbStickBasisBidib extends AbstractBidib {
private static final Logger LOGGER = LoggerFactory.getLogger(UsbStickBasisBidib.class);
private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("RAW");
private UsbStickBasisAdapter usbStickBasisAdapter;
private UsbStickBasisModel usbStickBasisModel;
private UsbStickBasisResponseInterface responseInterface;
private boolean firstPacketSent = false;
protected String requestedPortName;
private final SelectedCar selectedCar = new SelectedCar();
// flag to track the initial reset command is skipped
private boolean initialResetSkipped;
private long uniqueId = 0x58000d0c801234L;
private ProtocolVersion protocolVersion = new ProtocolVersion(0, 8);
private SoftwareVersion softwareVersion = new SoftwareVersion(1, 0, 0);
private String[] stringValue = new String[] { "", "" };
private int sendNum;
private int currentFeature;
private SortedSet features = new TreeSet<>();
private BidibRequestFactory requestFactory;
private Object responseMessageLock = new Object();
private UsbStickConnector connector;
private ByteArrayOutputStream sendBuffer = new ByteArrayOutputStream(100);
private class UsbStickConnector extends AbstractBaseBidib {
@Override
protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {
LOGGER.info("Send data stream: {}", ByteUtils.bytesToHex(data));
try {
sendBuffer.reset();
// encode the message
SerialMessageEncoder.encodeMessage(data, sendBuffer);
byte[] bytes = sendBuffer.toByteArray();
LOGGER.info("Send is called with bytes: {}", ByteUtils.bytesToHex(bytes));
if (rawMessageListener != null) {
rawMessageListener.notifySend(bytes);
}
List bidibMessages = requestFactory.create(bytes);
for (BidibMessageInterface bidibMessage : bidibMessages) {
synchronized (responseMessageLock) {
byte[] response = processRequest(bidibMessage);
if (response != null) {
publishResponse(response);
}
}
}
}
catch (Exception ex) {
LOGGER.warn("Process request failed.", ex);
}
finally {
sendBuffer.reset();
}
}
@Override
protected void internalOpen(String portName, Context context)
throws PortNotFoundException, PortNotOpenedException {
super.internalOpen(portName, context);
LOGGER.info("Internal open the port.");
}
public void doReceive(byte[] content, int length) {
receive(content, length);
}
@Override
public boolean close() {
super.close();
if (usbStickBasisAdapter != null) {
LOGGER.info("Close the connection of the usbStickBasisAdapter: {}", usbStickBasisAdapter);
try {
usbStickBasisAdapter.closeConnection();
}
catch (Exception ex) {
LOGGER.warn("Close connection to USB stick basis failed.", ex);
}
usbStickBasisAdapter = null;
return true;
}
return false;
}
}
/**
* Get a new initialized instance of UsbStickBasisBidib.
*
* @return the instance of UsbStickBasisBidib
*/
public static BidibInterface createInstance(final Context context) {
LOGGER.info("Create new instance of UsbStickBasisBidib.");
UsbStickBasisBidib instance = new UsbStickBasisBidib();
instance.initialize(context);
return instance;
}
@Override
public void initialize(final Context context) {
super.initialize(context);
// prepare features
features.add(new Feature(BidibLibrary.FEATURE_STRING_SIZE, 24));
features.add(new Feature(BidibLibrary.FEATURE_RELEVANT_PID_BITS, 16));
connector = new UsbStickConnector();
final MessageReceiver messageReceiver = getMessageReceiver();
connector.setMessageReceiver(messageReceiver);
initializeConnector(connector);
}
public long getUniqueId() {
return uniqueId;
}
@Override
public void setConnectionListener(final ConnectionListener connectionListener) {
super.setConnectionListener(connectionListener);
}
@Override
public void open(
String portName, ConnectionListener connectionListener, Set nodeListeners,
Set messageListeners, Set transferListeners, Context context)
throws PortNotFoundException, PortNotOpenedException {
LOGGER.info("Open the port, portName: {}, connectionListener: {}", portName, connectionListener);
setConnectionListener(connectionListener);
// register the listeners
registerListeners(nodeListeners, messageListeners, transferListeners);
requestedPortName = portName;
requestFactory = new BidibRequestFactory();
requestFactory.initialize();
// initialize the message type map
BidibRequestFactoryMessageMapInitializer.loadMessageTypeMap(requestFactory);
usbStickBasisModel = new UsbStickBasisModel();
usbStickBasisModel.setBaudRate(19200);
usbStickBasisModel.setSelectedPort(portName);
final Object startupLock = new Object();
responseInterface = new UsbStickBasisResponseInterface() {
@Override
public void addLog(String message) {
LOGGER.info("Received log message: {}", message.trim());
}
@Override
public void publishReponse(BidibMessageInterface bidibMessage) {
LOGGER.info("Publish response: {}", bidibMessage);
if (bidibMessage instanceof SysSwVersionResponse) {
SysSwVersionResponse sysSwVersionResponse = (SysSwVersionResponse) bidibMessage;
setSoftwareVersion(sysSwVersionResponse.getVersion().toString());
}
prepareMessage(bidibMessage);
}
@Override
public void publishProductName(String productName) {
LOGGER.info("Publish productName: {}", productName);
if (StringUtils.isNotBlank(productName)) {
setProductName(productName);
}
synchronized (startupLock) {
startupLock.notifyAll();
}
}
@Override
public SelectedCar getSelectedCarModel() {
return selectedCar;
}
@Override
public void publishPomRepeat(int pomRepeat) {
LOGGER.info("Publish the POM repeat: {}", pomRepeat);
setPomRepeat(pomRepeat);
synchronized (startupLock) {
startupLock.notifyAll();
}
}
};
try {
usbStickBasisAdapter = new UsbStickBasisAdapter(responseInterface, usbStickBasisModel);
usbStickBasisAdapter.openConnection(portName, "rxtx");
}
catch (PortNotFoundException | PortNotOpenedException ex) {
LOGGER.warn("Open connection to USB stick basis failed.", ex);
throw new PortNotOpenedException("Open connection to USB stick basis failed.", ex);
}
LOGGER.info("Opened the connection to the USB Stick Basis.");
connector.startReceiverAndQueues(getMessageReceiver(), context);
try {
usbStickBasisAdapter.clearReceiveBuffer();
LOGGER.info("Query the info from the usbStickBasis.");
usbStickBasisAdapter.transmit("info");
Thread.sleep(1000);
// get the FEATURE_GEN_POM_REPEAT from the basis with "CP"
usbStickBasisAdapter.transmit("CP");
}
catch (Exception ex) {
LOGGER.warn("Send 'help' to USB stick basis failed.", ex);
}
try {
LOGGER.info("Wait for the startupLock.");
synchronized (startupLock) {
startupLock.wait(2000);
}
}
catch (Exception ex) {
LOGGER.warn("Wait for notify startup lock was interrupted.", ex);
}
if (getConnectionListener() != null) {
LOGGER.info("Notify that the port was opened: {}", requestedPortName);
getConnectionListener().opened(requestedPortName);
}
}
protected void setProductName(String productName) {
LOGGER.info("Set the product name: {}", productName);
this.stringValue[0] = productName;
}
protected void setSoftwareVersion(String softwareVersion) {
LOGGER.info("Set the software version: {}", softwareVersion);
this.softwareVersion = SoftwareVersion.parse(softwareVersion);
}
protected void setPomRepeat(int pomRepeat) {
LOGGER.info("Set the pomRepeat: {}", pomRepeat);
try {
LOGGER.info("Set the FEATURE_GEN_POM_REPEAT: {}", pomRepeat);
features.add(new Feature(BidibLibrary.FEATURE_GEN_POM_REPEAT, pomRepeat));
}
catch (Exception ex) {
LOGGER.warn("Set the feature POM repeat failed.", ex);
}
}
@Override
public boolean isOpened() {
return usbStickBasisAdapter != null;
}
@Override
public void close() {
LOGGER.info("Close is called, usbStickBasisAdapter: {}", usbStickBasisAdapter);
if (connector.close()) {
super.close();
// clear the requestedPortName
requestedPortName = null;
cleanupAfterClose(null);
}
LOGGER.info("Close the port finished.");
}
@Override
public List getPortIdentifiers() {
return Collections.emptyList();
}
@Override
protected MessageReceiver createMessageReceiver(
final NodeRegistry nodeRegistry, final RawMessageListener rawMessageListener, final Context context) {
final ResponseFactory responseFactory = new BidibResponseFactory();
responseFactory.initialize();
AbstractMessageReceiver messageReceiver = new UsbStickBasisMessageReceiver(nodeRegistry, responseFactory, true);
messageReceiver.setRawMessageListener(rawMessageListener);
messageReceiver.init(context);
LOGGER.info("Get the root node into the registry.");
nodeRegistry.getRootNode();
return messageReceiver;
}
@Override
protected void cleanupAfterClose(final MessageReceiver messageReceiver) {
releaseRootNode();
firstPacketSent = false;
InvalidConfigurationException ice = null;
if (messageReceiver instanceof SerialMessageReceiver) {
SerialMessageReceiver serialMessageReceiver = (SerialMessageReceiver) messageReceiver;
serialMessageReceiver.clearMessageListeners();
serialMessageReceiver.clearNodeListeners();
LOGGER.info("Purge the received data in the message buffer.");
try {
serialMessageReceiver.purgeReceivedDataInBuffer();
}
catch (InvalidConfigurationException ex) {
LOGGER.warn("Purge output stream has signaled an error.", ex);
if ("debug-interface-active".equals(ex.getReason())) {
ice = ex;
}
}
}
else {
LOGGER.warn("No message receiver to purge received data buffer available.");
}
if (getConnectionListener() != null) {
LOGGER.info("Notify that the port was closed: {}", requestedPortName);
getConnectionListener().closed(requestedPortName);
}
else {
LOGGER
.info("No connection listener available to publish the closed report for port: {}", requestedPortName);
}
requestedPortName = null;
super.cleanupAfterClose(messageReceiver);
if (ice != null) {
LOGGER.warn("Signal the invalid configuration exception to the caller.");
throw ice;
}
}
// @Override
protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {
if (!firstPacketSent) {
LOGGER.info("Send initial sequence.");
try {
byte[] initialSequence = new byte[] { ByteUtils.MAGIC };
final ByteArrayOutputStream firstPacketSendBuffer = new ByteArrayOutputStream(20);
firstPacketSendBuffer.write(initialSequence);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(firstPacketSendBuffer));
}
// output.write(initialSequence);
connector.sendData(firstPacketSendBuffer, rawMessageListener);
Thread.sleep(10);
if (MSG_RAW_LOGGER.isInfoEnabled()) {
MSG_RAW_LOGGER
.info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(firstPacketSendBuffer));
}
// output.write(initialSequence);
connector.sendData(firstPacketSendBuffer, rawMessageListener);
firstPacketSent = true;
LOGGER.info("Send initial sequence passed.");
}
catch (Exception ex) {
LOGGER.warn("Send initial sequence failed.", ex);
}
}
connector.sendData(data, rawMessageListener);
}
private void prepareMessage(BidibMessageInterface message) {
synchronized (responseMessageLock) {
message.setSendMsgNum(getNextResponseSendNum());
publishResponse(message.getContent());
}
}
protected void publishResponse(byte[] content) {
LOGGER.info("Publish response: {}", ByteUtils.bytesToHex(content));
connector.doReceive(content, content.length);
}
/////////////////////////////////////////////
protected byte[] processRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
switch (ByteUtils.getInt(bidibMessage.getType())) {
case BidibLibrary.MSG_SYS_GET_MAGIC:
response = processSysGetMagicRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_CLOCK:
processSysClockRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_GET_P_VERSION:
response = processSysGetPVersionRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_GET_SW_VERSION:
response = processSysGetSwVersionRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_ENABLE:
processSysEnableRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_DISABLE:
processSysDisableRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_GET_UNIQUE_ID:
response = processSysGetUniqueIdRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_SET_STATE:
response = processCsSetStateRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_POM:
response = processCsPomRequest(bidibMessage);
break;
// case BidibLibrary.MSG_CS_PROG:
// response = processCsProgRequest(bidibMessage);
// break;
case BidibLibrary.MSG_CS_DRIVE:
response = processCsDriveRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_QUERY:
response = processCsQueryRequest(bidibMessage);
break;
// case BidibLibrary.MSG_CS_BIN_STATE:
// response = processCsBinStateRequest(bidibMessage);
// break;
case BidibLibrary.MSG_FEATURE_GET:
response = processFeatureGetRequest(bidibMessage);
break;
case BidibLibrary.MSG_FEATURE_SET:
response = processFeatureSetRequest(bidibMessage);
break;
case BidibLibrary.MSG_FEATURE_GETALL:
response = processFeatureGetAllRequest(bidibMessage);
break;
case BidibLibrary.MSG_FEATURE_GETNEXT:
response = processFeatureGetNextRequest(bidibMessage);
break;
case BidibLibrary.MSG_VENDOR_ENABLE:
response = processVendorEnableRequest(bidibMessage);
break;
case BidibLibrary.MSG_VENDOR_DISABLE:
response = processVendorDisableRequest(bidibMessage);
break;
case BidibLibrary.MSG_VENDOR_SET:
response = processVendorSetRequest(bidibMessage);
break;
case BidibLibrary.MSG_VENDOR_GET:
response = processVendorGetRequest(bidibMessage);
break;
case BidibLibrary.MSG_STRING_SET:
response = processStringSetRequest(bidibMessage);
break;
case BidibLibrary.MSG_STRING_GET:
response = processStringGetRequest(bidibMessage);
break;
case BidibLibrary.MSG_SYS_RESET:
processResetRequest(bidibMessage);
break;
default:
LOGGER.warn("Unprocessed bidibMessage: {}", bidibMessage);
// response = super.prepareResponse(bidibMessage);
break;
}
return response;
}
protected byte[] processSysGetUniqueIdRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysGetUniqueId request: {}", bidibMessage);
byte[] response = null;
try {
SysUniqueIdResponse sysUniqueIdResponse =
new SysUniqueIdResponse(bidibMessage.getAddr(), getNextResponseSendNum(), getUniqueId());
response = sysUniqueIdResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create sysUniqueId response failed.", ex);
}
return response;
}
protected byte[] processSysGetPVersionRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysGetPVersion request: {}, do nothing ...", bidibMessage);
byte[] response = null;
try {
LOGGER.info("Current protocolVersion: {}", protocolVersion);
SysPVersionResponse sysPVersionResponse =
new SysPVersionResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
protocolVersion.getMajorVersion(), protocolVersion.getMinorVersion());
response = sysPVersionResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create sysPVersion response failed.", ex);
}
return response;
}
protected byte[] processSysGetSwVersionRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysGetSwVersion request: {}", bidibMessage);
byte[] response = null;
try {
LOGGER.info("Current softwareVersion: {}", softwareVersion);
SysSwVersionResponse sysSwVersionResponse =
new SysSwVersionResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
softwareVersion.asByteArray());
response = sysSwVersionResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create sysSwVersion response failed.", ex);
}
return response;
}
protected void processSysEnableRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysEnable request: {}, do nothing ...", bidibMessage);
}
protected void processSysDisableRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysDisable request: {}, do nothing ...", bidibMessage);
}
protected void processSysIdentifyRequest(BidibMessageInterface bidibMessage) {
// TODO Process the SysIdentify request
LOGGER.info("Process the SysIdentify request: {}", bidibMessage);
}
protected byte[] processSysGetMagicRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
LOGGER.info("Reset the sendNum because the SYS_MAGIC is tranferred with 0.");
resetSendNum();
SysMagicResponse magicResponse =
new SysMagicResponse(bidibMessage.getAddr(), getNextResponseSendNum(), (byte) 0xFE, (byte) 0xAF);
response = magicResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create magic response failed.", ex);
}
return response;
}
protected void processSysClockRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the SysClockMessage: {}, do nothing ...", bidibMessage);
}
protected byte[] processCsSetStateRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsSetState request: {}", bidibMessage);
byte[] response = null;
try {
CommandStationSetStateMessage commandStationSetStateMessage = (CommandStationSetStateMessage) bidibMessage;
CommandStationState state = commandStationSetStateMessage.getState();
LOGGER.info("The requested command station state is: {}", state);
String commandStationStateCommand = null;
switch (state) {
case OFF:
commandStationStateCommand = "S 0";
break;
case STOP:
case SOFTSTOP:
commandStationStateCommand = "S 0";
break;
case GO:
case GO_IGN_WD:
commandStationStateCommand = "S 1";
break;
case PROG:
break;
case QUERY:
LOGGER.info("Query command station state requested");
commandStationStateCommand = "S";
break;
default:
LOGGER.warn("Unprocessed command station state: {}", state);
break;
}
if (commandStationStateCommand != null) {
usbStickBasisAdapter.transmit(commandStationStateCommand);
}
}
catch (Exception ex) {
LOGGER.warn("Create CommandStationState response failed.", ex);
}
return response;
}
private static final int SLEEP_BETWEEN_FUNCTIONS = 10;
protected byte[] processCsDriveRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsDrive request: {}", bidibMessage);
byte[] response = null;
try {
CommandStationDriveMessage commandStationDriveMessage = (CommandStationDriveMessage) bidibMessage;
org.bidib.jbidibc.messages.AddressData addressData = commandStationDriveMessage.getDecoderAddress();
int speed = commandStationDriveMessage.getSpeed();
int activeOutputBits = commandStationDriveMessage.getOutputActiveBits();
// speed
LOGGER
.info("Received addressData: {}, speed: {}, activeOutputBits: {}", addressData, speed,
activeOutputBits);
// make sure the car was addressed once
Integer currentAddress = selectedCar.getDecoderAddress();
if (currentAddress == null || addressData.getAddress() != currentAddress) {
currentAddress = addressData.getAddress();
LOGGER.info("Updated the decoder address: {}", currentAddress);
selectedCar.setDecoderAddress(currentAddress);
}
else {
// reset to prevent transmit
currentAddress = null;
}
if ((activeOutputBits & 0x01) == 0x01) {
String speedCommand = "A " + addressData.getAddress() + "," + speed;
LOGGER.info("Prepared speed command: {}", speedCommand);
usbStickBasisAdapter.transmit(speedCommand);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
else if (currentAddress != null) {
String addressCommand = "A " + currentAddress;
usbStickBasisAdapter.transmit(addressCommand);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
final Map functionMap = selectedCar.getFunctionMap();
LOGGER.info("Current functionMap: {}", functionMap);
if ((activeOutputBits & 0x02) == 0x02) {
// lights and F1-F4
int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F0_F4];
String functionCommand = null;
int newVal = ((functions & 0x10) >> 4);
Integer fKey = Integer.valueOf(0);
Integer fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
functionCommand = "F 0," + newVal;
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
for (int bit = 3; bit > -1; bit--) {
newVal = ((functions & (1 << bit)) >> bit);
fKey = Integer.valueOf(bit + 1);
fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
functionCommand = "F " + fKey + "," + newVal;
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
}
}
// @formatter:off
// 17:52:14.044 [INFO] org.bidib.jbidibc.usbstickbasis.UsbStickBasisBidib [sendQueueWorker] - Send data
// stream: 0C 00 12 64 03 00 03 04 80 00 04 00 00
//
// 03 00 Address
// 03 DATA 0
// 04 DATA 1 --> Active output bits
// 80
// 00 DATA 3
// 04 DATA 4 --> F12 - F5 0000 0100 <<-- F7 is 1
// 00 DATA 5
// 00 DATA 6
// @formatter:on
// send the F5 .. F28
if ((activeOutputBits & 0x04) == 0x04) {
// F5-F8
int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F5_F12];
for (int bit = 0; bit < 4; bit++) {
int newVal = ((functions & (1 << bit)) >> bit);
Integer fKey = Integer.valueOf(bit + 5);
Integer fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
String functionCommand = "F " + (bit + 5) + "," + ((functions & (1 << bit)) >> bit);
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
}
}
if ((activeOutputBits & 0x08) == 0x08) {
// F9-F12
int functions = commandStationDriveMessage.getFunctionBits()[0];
for (int bit = 4; bit < 8; bit++) {
int newVal = ((functions & (1 << bit)) >> bit);
Integer fKey = Integer.valueOf(bit + 9);
Integer fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
String functionCommand = "F " + (bit + 9) + "," + ((functions & (1 << bit)) >> bit);
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
}
}
if ((activeOutputBits & 0x10) == 0x10) {
// F13-F20
int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F13_F20];
for (int bit = 0; bit < 8; bit++) {
int newVal = ((functions & (1 << bit)) >> bit);
Integer fKey = Integer.valueOf(bit + 13);
Integer fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
String functionCommand = "F " + (bit + 13) + "," + ((functions & (1 << bit)) >> bit);
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
}
}
if ((activeOutputBits & 0x20) == 0x20) {
// F21-F28
int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F21_F28];
for (int bit = 0; bit < 8; bit++) {
int newVal = ((functions & (1 << bit)) >> bit);
Integer fKey = Integer.valueOf(bit + 21);
Integer fVal = functionMap.get(fKey);
LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);
if (fVal == null || fVal != newVal) {
String functionCommand = "F " + (bit + 21) + "," + ((functions & (1 << bit)) >> bit);
usbStickBasisAdapter.transmit(functionCommand);
functionMap.put(fKey, newVal);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
}
}
CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
addressData, (byte) 1, null);
response = commandStationDriveAckResponse.getContent();
}
catch (ProtocolException | InterruptedException ex) {
LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
}
return response;
}
protected byte[] processCsQueryRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsQuery request: {}", bidibMessage);
byte[] response = null;
try {
CommandStationQueryMessage commandStationQueryMessage = (CommandStationQueryMessage) bidibMessage;
CsQueryTypeEnum csQueryType = commandStationQueryMessage.getCsQueryType();
switch (csQueryType) {
case LOCO_LIST:
String carQueryCommand = "A";
usbStickBasisAdapter.transmit(carQueryCommand);
break;
default:
LOGGER.warn("The CsQueryRequest is not implemented for type: {}", csQueryType);
break;
}
}
catch (Exception ex) {
LOGGER.warn("Process the CsQuery request failed.", ex);
}
return response;
}
protected byte[] processFeatureGetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
FeatureGetMessage featureGetMessage = (FeatureGetMessage) bidibMessage;
int featureNum = featureGetMessage.getNumber();
LOGGER.info("Get feature with number: {}", featureNum);
Feature foundFeature = null;
for (Feature feature : features) {
if (feature.getType() == featureNum) {
foundFeature = feature;
LOGGER.info("Found feature: {}", foundFeature);
break;
}
}
if (foundFeature != null) {
FeatureResponse featureResponse =
new FeatureResponse(featureGetMessage.getAddr(), getNextResponseSendNum(), featureNum,
foundFeature.getValue());
response = featureResponse.getContent();
LOGGER.info("Prepared response: {}", ByteUtils.bytesToHex(response));
}
else {
FeatureNotAvailableResponse featureResponse =
new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), featureNum);
response = featureResponse.getContent();
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create feature response failed.", ex);
}
return response;
}
protected byte[] processFeatureSetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
FeatureSetMessage featureSetMessage = (FeatureSetMessage) bidibMessage;
int featureNum = featureSetMessage.getNumber();
int featureValue = featureSetMessage.getValue();
LOGGER.info("Set feature with number: {}, value: {}", featureNum, featureValue);
Feature foundFeature = null;
for (Feature feature : features) {
if (feature.getType() == featureNum) {
foundFeature = updateFeatureValue(feature, featureValue);
LOGGER.info("Found feature: {}", foundFeature);
break;
}
}
if (foundFeature != null) {
FeatureResponse featureResponse =
new FeatureResponse(featureSetMessage.getAddr(), getNextResponseSendNum(), featureNum,
foundFeature.getValue());
response = featureResponse.getContent();
LOGGER.info("Prepared response: {}", ByteUtils.bytesToHex(response));
}
else {
FeatureNotAvailableResponse featureResponse =
new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), featureNum);
response = featureResponse.getContent();
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create feature response failed.", ex);
}
return response;
}
protected Feature updateFeatureValue(Feature feature, int featureValue) {
// TODO set the feature on the speedometer
if (feature.getFeatureEnum() != null) {
switch (feature.getFeatureEnum()) {
case FEATURE_GEN_POM_REPEAT:
if (featureValue > 4) {
// limit the max value
featureValue = 4;
}
else if (featureValue < 1) {
// limit the min value
featureValue = 1;
}
LOGGER.info("Set the POM repeat value on the speedometer: {}", featureValue);
usbStickBasisAdapter.transmit("CP " + featureValue);
break;
default:
break;
}
}
feature.setValue(featureValue);
return feature;
}
protected Feature autoAddFeature(int featureNum, int featureValue) {
Feature foundFeature = new Feature(featureNum, featureValue);
features.add(foundFeature);
return foundFeature;
}
protected byte[] processFeatureGetAllRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
FeatureCountResponse featureResponse =
new FeatureCountResponse(bidibMessage.getAddr(), getNextResponseSendNum(), features.size());
response = featureResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create feature count response failed.", ex);
}
currentFeature = 0;
return response;
}
protected byte[] processFeatureGetNextRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
if (currentFeature >= features.size()) {
try {
FeatureNotAvailableResponse featureResponse =
new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), 255);
response = featureResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create feature N/A response failed.", ex);
}
}
else {
try {
Feature feature = IterableUtils.get(features, currentFeature);
// Feature feature = features.toArray(new Feature[0])[currentFeature];
FeatureResponse featureResponse =
new FeatureResponse(bidibMessage.getAddr(), getNextResponseSendNum(), feature.getType(),
feature.getValue());
response = featureResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create feature response failed.", ex);
}
catch (Exception ex) {
LOGGER.warn("Create feature response failed.", ex);
}
}
currentFeature++;
return response;
}
protected byte[] processVendorEnableRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
VendorEnableMessage vendorEnableMessage = (VendorEnableMessage) bidibMessage;
long uniqueId = vendorEnableMessage.getUniqueId();
LOGGER.info("Enable the user config mode for uniqueId: {}", uniqueId);
byte userConfigModeActive = 1;
VendorAckResponse vendorAckResponse =
new VendorAckResponse(bidibMessage.getAddr(), getNextResponseSendNum(), userConfigModeActive);
response = vendorAckResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create vendor ack response failed.", ex);
}
return response;
}
protected byte[] processVendorDisableRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
// VendorDisableMessage vendorDisableMessage = (VendorDisableMessage) bidibMessage;
LOGGER.info("Disable the user config mode");
byte userConfigModeActive = 0;
VendorAckResponse vendorAckResponse =
new VendorAckResponse(bidibMessage.getAddr(), getNextResponseSendNum(), userConfigModeActive);
response = vendorAckResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create vendor ack response failed.", ex);
}
return response;
}
protected byte[] processVendorSetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
VendorSetMessage vendorSetMessage = (VendorSetMessage) bidibMessage;
VendorData vendorData = vendorSetMessage.getVendorData();
LOGGER.info("Set the vendor data: {}", vendorData);
if ("Scale".equals(vendorData.getName())) {
String scale = vendorData.getValue();
LOGGER.info("Set the scale value of the selected car: {}", scale);
Integer scaleValue = Integer.valueOf(scale);
selectedCar.setScale(scaleValue != null && scaleValue.intValue() > 0 ? scaleValue : null);
byte[] nodeAddress = vendorSetMessage.getAddr();
response = prepareCvResponse(nodeAddress, vendorData.getName(), scale);
}
else if ("SpeedMeasurement".equals(vendorData.getName())) {
String speedMeasurementCommand = vendorData.getValue();
LOGGER.info("Prepared speed measurement command: {}", speedMeasurementCommand);
usbStickBasisAdapter.transmit(speedMeasurementCommand);
// TODO remove if the response from the speedo is changed
byte[] nodeAddress = vendorSetMessage.getAddr();
response = prepareCvResponse(nodeAddress, vendorData.getName(), speedMeasurementCommand);
}
else {
String bcvReadCommand = "BCV " + vendorData.getName() + "," + vendorData.getValue();
LOGGER.info("Prepared BCV set command: {}", bcvReadCommand);
usbStickBasisAdapter.transmit(bcvReadCommand);
}
}
catch (Exception ex) {
LOGGER.warn("Process vendorSet request failed.", ex);
}
return response;
}
private byte[] prepareCvResponse(byte[] nodeAddress, String cvName, String cvValue) throws ProtocolException {
try {
BidibMessage bidibMessage = new VendorResponse(nodeAddress, getNextResponseSendNum(), cvName, cvValue);
return bidibMessage.getContent();
}
catch (Exception ex) {
LOGGER.warn("Create VendorResponse failed, cvName: {}, cvValue", cvName, cvValue, ex);
throw new ProtocolException("Create VendorResponse failed, cvName: " + cvName + ", cvValue: " + cvValue);
}
}
protected byte[] processVendorGetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
VendorGetMessage vendorGetMessage = (VendorGetMessage) bidibMessage;
String vendorDataName = vendorGetMessage.getVendorDataName();
LOGGER.info("Get the vendor data with name: {}", vendorDataName);
String bcvReadCommand = "BCV " + vendorDataName;
LOGGER.info("Prepared BCV read command: {}", bcvReadCommand);
usbStickBasisAdapter.transmit(bcvReadCommand);
}
catch (Exception ex) {
LOGGER.warn("Process vendorGet request failed.", ex);
}
return response;
}
protected byte[] processStringSetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
StringSetMessage stringSetMessage = (StringSetMessage) bidibMessage;
int stringId = stringSetMessage.getStringId();
stringValue[stringId] = stringSetMessage.getString();
StringResponse stringResponse =
new StringResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
ByteUtils.getLowByte(stringSetMessage.getNamespace()),
ByteUtils.getLowByte(stringSetMessage.getStringId()), stringValue[stringId]);
response = stringResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create string response failed.", ex);
}
return response;
}
protected byte[] processStringGetRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
StringGetMessage stringGetMessage = (StringGetMessage) bidibMessage;
int stringId = stringGetMessage.getStringId();
LOGGER.info("Get STRING[{}]: {}", stringId, stringValue[stringId]);
StringResponse stringResponse =
new StringResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
ByteUtils.getLowByte(stringGetMessage.getNamespace()),
ByteUtils.getLowByte(stringGetMessage.getStringId()), stringValue[stringId]);
response = stringResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create string response failed.", ex);
}
return response;
}
protected byte[] processCsPomRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsPom request: {}", bidibMessage);
byte[] response = null;
// prepare the POM acknowledge
try {
CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
org.bidib.jbidibc.messages.PomAddressData addressData = commandStationPomMessage.getDecoderAddress();
LOGGER.info("Received addressData: {}", addressData);
// make sure the car was addressed once
Integer currentAddress = selectedCar.getDecoderAddress();
if (currentAddress == null || addressData.getAddress() != currentAddress) {
currentAddress = addressData.getAddress();
LOGGER.info("Updated the decoder address: {}", currentAddress);
selectedCar.setDecoderAddress(currentAddress);
String addressCommand = "A " + currentAddress;
usbStickBasisAdapter.transmit(addressCommand);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
}
String cvCommand = null;
int cvNumber = commandStationPomMessage.getCvNumber();
int cvValue = 0;
CommandStationPom opCode =
CommandStationPom.valueOf(ByteUtils.getLowByte(commandStationPomMessage.getOpCode()));
switch (opCode) {
case WR_BYTE:
cvValue = commandStationPomMessage.getCvValue();
cvCommand = "CV " + cvNumber + "," + cvValue;
break;
default:
cvCommand = "CV " + cvNumber;
break;
}
LOGGER.info("Prepared CV command: {}", cvCommand);
usbStickBasisAdapter.transmit(cvCommand);
Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
CommandStationPomAcknowledgeResponse commandStationPomAckResponse =
new CommandStationPomAcknowledgeResponse(bidibMessage.getAddr(), /* getNextResponseSendNum() */0,
addressData, (byte) 1);
LOGGER.info("Publish the running response: {}", commandStationPomAckResponse);
prepareMessage(commandStationPomAckResponse);
}
catch (ProtocolException | InterruptedException ex) {
LOGGER.warn("Create CommandStationPomAck response failed.", ex);
}
return response;
}
protected void processResetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the reset request, bidibMessage: {}", bidibMessage);
if (!initialResetSkipped) {
LOGGER.warn("Skip the initial reset command as the Speedometer boots on connect.");
initialResetSkipped = true;
return;
}
resetSendNum();
String restartCommand = "REBOOT";
LOGGER.info("Prepared restart command: {}", restartCommand);
usbStickBasisAdapter.transmit(restartCommand);
}
protected void resetSendNum() {
LOGGER.info("Reset the sendNum to 0.");
sendNum = 0;
}
protected int getNextSendNum() {
return 0;
}
protected int getCurrentSendNum() {
return sendNum;
}
protected int getNextResponseSendNum() {
int nextSendNum = sendNum;
sendNum++;
if (sendNum > 255) {
sendNum = 0;
}
return nextSendNum;
}
@Override
public void send(byte[] data) {
connector.send(data);
}
@Override
protected int contactInterface() {
// TODO Auto-generated method stub
return 0;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy