
org.bidib.wizard.simulation.RFBasisNodeSimulator Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of bidibwizard-simulation Show documentation
Show all versions of bidibwizard-simulation Show documentation
jBiDiB BiDiB Wizard Simulation POM
package org.bidib.wizard.simulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.collections4.CollectionUtils;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.FeedbackAddressData;
import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
import org.bidib.jbidibc.messages.enums.CommandStationPom;
import org.bidib.jbidibc.messages.enums.CommandStationProgState;
import org.bidib.jbidibc.messages.enums.CommandStationPt;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.CsQueryTypeEnum;
import org.bidib.jbidibc.messages.enums.EnrailmentDirectionEnum;
import org.bidib.jbidibc.messages.enums.FeatureEnum;
import org.bidib.jbidibc.messages.enums.PositionLocationEnum;
import org.bidib.jbidibc.messages.enums.SpeedStepsEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.BidibMessage;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.CommandStationAccessoryAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationAccessoryMessage;
import org.bidib.jbidibc.messages.message.CommandStationBinaryStateMessage;
import org.bidib.jbidibc.messages.message.CommandStationDriveAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveMessage;
import org.bidib.jbidibc.messages.message.CommandStationDriveStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomMessage;
import org.bidib.jbidibc.messages.message.CommandStationProgMessage;
import org.bidib.jbidibc.messages.message.CommandStationProgStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationQueryMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.CommandStationStateResponse;
import org.bidib.jbidibc.messages.message.FeedbackAddressResponse;
import org.bidib.jbidibc.messages.message.FeedbackConfidenceResponse;
import org.bidib.jbidibc.messages.message.FeedbackCvResponse;
import org.bidib.jbidibc.messages.message.FeedbackFreeResponse;
import org.bidib.jbidibc.messages.message.FeedbackGetRangeMessage;
import org.bidib.jbidibc.messages.message.FeedbackMultipleResponse;
import org.bidib.jbidibc.messages.message.FeedbackOccupiedResponse;
import org.bidib.jbidibc.messages.message.FeedbackPositionResponse;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.simulation.SimulationBidibMessageProcessor;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulator;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulators;
import org.bidib.jbidibc.simulation.nodes.DefaultNodeSimulator;
import org.bidib.wizard.model.ports.FeedbackPort;
import org.bidib.wizard.model.ports.Port;
import org.bidib.wizard.model.status.FeedbackPortStatus;
import org.bidib.wizard.simulation.events.FeedbackConfidenceSetEvent;
import org.bidib.wizard.simulation.events.FeedbackConfidenceStatusEvent;
import org.bidib.wizard.simulation.events.FeedbackPortSetStatusEvent;
import org.bidib.wizard.simulation.events.FeedbackPortStatusEvent;
import org.bushe.swing.event.EventBus;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.bushe.swing.event.annotation.EventSubscriber;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@BidibNodeSimulators({ @BidibNodeSimulator(vid = "13", pid = "32770"), @BidibNodeSimulator(vid = "251", pid = "302") })
public class RFBasisNodeSimulator extends DefaultNodeSimulator {
private static final Logger LOGGER = LoggerFactory.getLogger(RFBasisNodeSimulator.class);
private static final String SIMULATION_PANEL_CLASS =
"org.bidib.wizard.simulation.client.view.panel.GBMboostMasterPanel";
private final Map feedbackPorts = new HashMap();
private final AtomicBoolean statusFreeze = new AtomicBoolean();
private final AtomicBoolean statusValid = new AtomicBoolean();
private final AtomicBoolean statusSignal = new AtomicBoolean();
private CommandStationState commandStationState = CommandStationState.OFF;
private Map mapLocoCV = new LinkedHashMap();
protected static final int NUM_OF_FEEDBACK_PORTS = 16;
protected final ScheduledExecutorService positionWorker = Executors.newScheduledThreadPool(1);
public RFBasisNodeSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {
super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);
}
@Override
protected void prepareFeatures() {
super.prepareFeatures();
features.add(new Feature(BidibLibrary.FEATURE_BM_SIZE, NUM_OF_FEEDBACK_PORTS));
features.add(new Feature(BidibLibrary.FEATURE_BM_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_SECACK_AVAILABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_SECACK_ON, 100));
features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_AVAILABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_AND_DIR, 1));
// features.add(new Feature(BidibLibrary.FEATURE_BM_TIMESTAMP_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_ISTSPEED_AVAILABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_ISTSPEED_INTERVAL, 5));
features.add(new Feature(BidibLibrary.FEATURE_BM_CV_AVAILABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_CV_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_DYN_STATE_INTERVAL, 100));
features.add(new Feature(BidibLibrary.FEATURE_BM_POSITION_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BM_POSITION_SECACK, 30));
features.add(new Feature(BidibLibrary.FEATURE_GEN_WATCHDOG, 20));
features.add(new Feature(BidibLibrary.FEATURE_GEN_POM_REPEAT, 3));
features.add(new Feature(BidibLibrary.FEATURE_GEN_DRIVE_BUS, 1));
features.add(new Feature(BidibLibrary.FEATURE_GEN_LOK_LOST_DETECT, 1));
features.add(new Feature(BidibLibrary.FEATURE_GEN_NOTIFY_DRIVE_MANUAL, 1));
features.add(new Feature(BidibLibrary.FEATURE_GEN_START_STATE, 1));
}
@Override
public String getSimulationPanelClass() {
return SIMULATION_PANEL_CLASS;
}
@Override
public void start() {
LOGGER.info("Start the simulator for address: {}", getAddress());
AnnotationProcessor.process(this);
// setup loco decoder CV
mapLocoCV.put(1, Integer.valueOf(3));
mapLocoCV.put(29, Integer.valueOf(0));
mapLocoCV.put(28, Integer.valueOf(3)); // set the railcom config
// prepare the feedback ports
setupFeedbackPorts();
super.start();
}
@Override
public void stop() {
AnnotationProcessor.unprocess(this);
if (positionWorker != null) {
LOGGER.info("Stop the position worker.");
positionWorker.shutdownNow();
}
super.stop();
}
private void setupFeedbackPorts() {
for (int id = 0; id < NUM_OF_FEEDBACK_PORTS; id++) {
FeedbackPort port = new FeedbackPort();
port.setId(id);
// port.setStatus(id % 3 == 0 ? FeedbackPortStatus.FREE : FeedbackPortStatus.OCCUPIED);
port.setStatus(FeedbackPortStatus.FREE);
feedbackPorts.put(id, port);
}
}
@Override
protected byte[] prepareResponse(BidibMessageInterface bidibMessage) {
byte[] response = null;
switch (ByteUtils.getInt(bidibMessage.getType())) {
case BidibLibrary.MSG_BM_GET_RANGE:
response = processBmGetRangeRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_MIRROR_MULTIPLE:
processBmMirrorMultipleRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_MIRROR_OCC:
processBmMirrorOccupiedRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_MIRROR_FREE:
processBmMirrorFreeRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_MIRROR_POSITION:
processBmMirrorPositionRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_ADDR_GET_RANGE:
processBmAddrGetRangeRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_GET_CONFIDENCE:
response = processBmGetConfidenceRequest(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_ACCESSORY:
response = processCsAccessoryRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_QUERY:
response = processCsQueryRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_BIN_STATE:
response = processCsBinStateRequest(bidibMessage);
break;
default:
response = super.prepareResponse(bidibMessage);
break;
}
return response;
}
private ScheduledFuture futureTriggerPositionWorker;
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);
switch (state) {
case OFF:
commandStationState = CommandStationState.OFF;
break;
case STOP:
case SOFTSTOP:
commandStationState = CommandStationState.STOP;
break;
case GO:
case GO_IGN_WD:
commandStationState = CommandStationState.GO;
break;
case PROG:
commandStationState = CommandStationState.PROG;
break;
case QUERY:
LOGGER.warn("Query command station state requested");
break;
default:
LOGGER.warn("Unprocessed command station state: {}", state);
break;
}
LOGGER.info("Return current command station state: {}", commandStationState);
CommandStationStateResponse commandStationStateResponse =
new CommandStationStateResponse(bidibMessage.getAddr(), getNextSendNum(),
commandStationState.getType());
response = commandStationStateResponse.getContent();
// publish response now
publishResponse(response);
response = null;
// handle state
handleCsState(commandStationState);
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationState response failed.", ex);
}
return response;
}
private void handleCsState(final CommandStationState commandStationState) {
// use executor to send response
switch (commandStationState) {
case GO:
case GO_IGN_WD:
if (futureTriggerPositionWorker == null) {
LOGGER.info("Schedule the position trigger");
int interval = 4000;
futureTriggerPositionWorker = positionWorker.scheduleAtFixedRate(() -> {
LOGGER.info("Trigger position");
try {
triggerPositionResponse();
}
catch (Exception ex) {
LOGGER.warn("Trigger the position failed.", ex);
}
LOGGER.info("Trigger position has finished.");
}, 500, interval, TimeUnit.MILLISECONDS);
}
break;
case STOP:
case SOFTSTOP:
case OFF:
if (futureTriggerPositionWorker != null) {
LOGGER.info("Stop the position trigger.");
futureTriggerPositionWorker.cancel(false);
futureTriggerPositionWorker = null;
}
break;
default:
break;
}
}
protected static final int MAX_NUM_OF_FEEDBACK_PORTS = 16;
private int currentCarPosition = -1;
private int carAddress = 3;
protected void triggerPositionResponse() {
try {
int portNum = currentCarPosition;
LOGGER.info("Trigger position address, portNum: {}", portNum);
if (portNum > -1) {
portNum = currentCarPosition;
if (portNum < 0) {
portNum = MAX_NUM_OF_FEEDBACK_PORTS - 1;
}
// make the previous active position free
}
currentCarPosition++;
if (currentCarPosition >= MAX_NUM_OF_FEEDBACK_PORTS) {
currentCarPosition = 0;
}
int offset = getLocalAddr();
// TODO set the minimum offset number correct
if (offset < 12) {
offset = 12;
}
int locationType = ByteUtils.getInt(PositionLocationEnum.LOCATOR_BADGE.getType());
triggerPositionAddressResponse(currentCarPosition + ((offset - 12) * 100), carAddress + (offset * 2),
locationType, currentCarPosition);
int delayValue = ThreadLocalRandom.current().nextInt(300, 800);
LOGGER.info("Wait for next execution, delayValue: {}", delayValue);
Thread.sleep(delayValue);
triggerPositionAddressResponse(currentCarPosition + ((offset - 12) * 100), carAddress + 1 + (offset * 2),
locationType, currentCarPosition);
}
catch (Exception ex) {
LOGGER.warn("Trigger the feedback address failed.", ex);
}
LOGGER.info("Trigger feedback address has finished.");
}
private void triggerPositionAddressResponse(int portNum, int carAddress, int locationType, int carPosition) {
LOGGER.info("Trigger the feedback address repsonse, portNum: {}", portNum);
byte[] response = null;
try {
// int locationType = 0;
FeedbackPositionResponse feedbackPositionEvent =
new FeedbackPositionResponse(nodeAddress, getNextSendNum(), carAddress, locationType, carPosition);
response = feedbackPositionEvent.getContent();
sendSpontanousResponse(response);
}
catch (ProtocolException ex) {
LOGGER.warn("Create FeedbackPositionResponse failed.", ex);
}
}
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);
CommandStationPomAcknowledgeResponse commandStationPomAckResponse =
new CommandStationPomAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
(byte) 1);
response = commandStationPomAckResponse.getContent();
LOGGER.info("Publish the running response: {}", commandStationPomAckResponse);
publishResponse(response);
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationPomAck response failed.", ex);
}
// prepare the MSG_BM_CV that contains the real data
try {
LOGGER.info("prepare the MSG_BM_CV that contains the real data.");
CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
org.bidib.jbidibc.messages.PomAddressData addressData = commandStationPomMessage.getDecoderAddress();
int cvNumber = commandStationPomMessage.getCvNumber();
LOGGER.info("Current CV number: {}", cvNumber);
byte cvValue = 12;
if (cvNumber == 29) {
cvValue = 0;
}
CommandStationPom opCode =
CommandStationPom.valueOf(ByteUtils.getLowByte(commandStationPomMessage.getOpCode()));
switch (opCode) {
case WR_BYTE:
cvValue = ByteUtils.getLowByte(commandStationPomMessage.getCvValue());
break;
default:
break;
}
FeedbackCvResponse feedbackCvResponse =
new FeedbackCvResponse(bidibMessage.getAddr(), getNextSendNum(), addressData.getAddress(), cvNumber - 1,
cvValue);
response = feedbackCvResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationPomAck response failed.", ex);
}
return response;
}
protected byte[] processCsProgRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsProg request: {}", bidibMessage);
byte[] response = null;
try {
CommandStationProgMessage commandStationProgMessage = (CommandStationProgMessage) bidibMessage;
CommandStationPt opCode = commandStationProgMessage.getOpCode();
int cvNumber = commandStationProgMessage.getCvNumber();
int cvData = commandStationProgMessage.getCvData();
LOGGER.info("Received opCode: {}, cvNumber: {}, cvData: {}", opCode, cvNumber, cvData);
boolean sendTimeoutResponse = false;
switch (opCode) {
case BIDIB_CS_PROG_BREAK:
break;
case BIDIB_CS_PROG_QUERY:
break;
case BIDIB_CS_PROG_RDWR_BIT:
if ((cvData & 0x10) == 0x10) {
Integer storedValue = mapLocoCV.get(cvNumber);
// write operation
if (storedValue != null) {
byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
byteValue =
ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
mapLocoCV.put(cvNumber, Integer.valueOf(byteValue));
}
else {
byte byteValue = 0;
byteValue =
ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
mapLocoCV.put(cvNumber, Integer.valueOf(byteValue));
}
}
else {
// read operation
Integer storedValue = mapLocoCV.get(cvNumber);
if (storedValue != null) {
byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
boolean bitIsSetEqual =
ByteUtils.isBitSetEqual(byteValue, ByteUtils.getBit(cvData, 3), cvData & 0x07);
LOGGER.info("Verify bitIsSetEqual: {}, byteValue: {}", bitIsSetEqual, byteValue);
if (!bitIsSetEqual) {
LOGGER.warn("Send timeout response!");
sendTimeoutResponse = true;
}
}
else {
LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
sendTimeoutResponse = true;
}
}
break;
case BIDIB_CS_PROG_WR_BYTE:
mapLocoCV.put(cvNumber, cvData);
break;
default:
Integer storedValue = mapLocoCV.get(cvNumber);
if (storedValue != null) {
cvData = ByteUtils.getLowByte(storedValue.intValue());
}
else {
LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
sendTimeoutResponse = true;
}
break;
}
CommandStationProgStateResponse commandStationProgStateResponse =
new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
CommandStationProgState.PROG_RUNNING, 1, cvNumber, cvData);
response = commandStationProgStateResponse.getContent();
LOGGER.info("Publish the running response: {}", commandStationProgStateResponse);
publishResponse(response);
LOGGER.info("Sleep a second.");
// sleep some time
try {
Thread.sleep(1000);
}
catch (InterruptedException e) {
LOGGER.warn("Sleep thread was interrupted.", e);
}
if (!sendTimeoutResponse) {
LOGGER.info("Prepare the OKAY state response.");
commandStationProgStateResponse =
new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
CommandStationProgState.PROG_OKAY, 1, cvNumber, cvData);
}
else {
LOGGER.info("Prepare the NO_ANSWER state response.");
commandStationProgStateResponse =
new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
CommandStationProgState.PROG_NO_ANSWER, 1, cvNumber, 0xFF);
}
response = commandStationProgStateResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationProgState response failed.", ex);
}
return response;
}
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 messageNumber = commandStationDriveMessage.getNum();
LOGGER.info("Received addressData: {}", addressData);
CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
(byte) 1, Integer.valueOf(messageNumber));
response = commandStationDriveAckResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
}
return response;
}
private byte[] processCsBinStateRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsBinState request: {}", bidibMessage);
Integer requestedCellNumber = null;
Integer carAddress = null;
byte[] response = null;
try {
CommandStationBinaryStateMessage commandStationBinStateMessage =
(CommandStationBinaryStateMessage) bidibMessage;
org.bidib.jbidibc.messages.AddressData addressData = commandStationBinStateMessage.getDecoderAddress();
int binStateNumber = commandStationBinStateMessage.getBinaryStateNumber();
int binStateValue = commandStationBinStateMessage.getBinaryStateValue();
int messageNumber = commandStationBinStateMessage.getNum();
LOGGER
.info("Received addressData: {}, binStateNumber: {}, binStateValue: {}", addressData, binStateNumber,
binStateValue);
CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
(byte) 1, Integer.valueOf(messageNumber));
response = commandStationDriveAckResponse.getContent();
sendSpontanousResponse(response);
if (binStateValue > 0 && binStateNumber >= 512 && binStateNumber <= 519) {
requestedCellNumber = Integer.valueOf(binStateNumber - 511);
carAddress = addressData.getAddress();
LOGGER.info("The requested cell number: {}, carAddress: {}", requestedCellNumber, carAddress);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
}
response = null;
// signal the MSG_BM_POSITION for the decoder with the new cell identifier
final Feature featureCellNumber = getFeature(FeatureEnum.FEATURE_CELL_NUMBER.getNumber());
if (featureCellNumber != null && requestedCellNumber != null
&& featureCellNumber.getValue() == requestedCellNumber.intValue()) {
int offset = getLocalAddr();
// TODO set the minimum offset number correct
if (offset < 12) {
offset = 12;
}
LOGGER.info("The cell number has changed, requested cellnumber: {}", requestedCellNumber);
triggerPositionAddressResponse(currentCarPosition + ((offset - 12) * 100), carAddress, 1,
requestedCellNumber.intValue());
}
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:
// prepare some locos
// TODO check the real csQuery value to check if a single loco is requested
org.bidib.jbidibc.messages.AddressData addressData =
new AddressData(13, AddressTypeEnum.LOCOMOTIVE_FORWARD);
byte[] functions = new byte[] { (byte) 0x80, 0x00, 0x72, (byte) 0x85 };
CommandStationDriveStateResponse driveStateResponse =
new CommandStationDriveStateResponse(bidibMessage.getAddr(), getNextSendNum(), 0x41,
addressData, SpeedStepsEnum.DCC128, 39, functions);
response = driveStateResponse.getContent();
break;
default:
LOGGER.warn("The CsQueryRequest is not implemented for type: {}", csQueryType);
break;
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
}
return response;
}
protected byte[] processCsAccessoryRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the CsAccessory request: {}", bidibMessage);
byte[] response = null;
try {
CommandStationAccessoryMessage commandStationAccessoryMessage =
(CommandStationAccessoryMessage) bidibMessage;
org.bidib.jbidibc.messages.AddressData addressData = commandStationAccessoryMessage.getDecoderAddress();
LOGGER.info("Received addressData: {}", addressData);
CommandStationAccessoryAcknowledgeResponse commandStationAccessoryAckResponse =
new CommandStationAccessoryAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
(byte) 1);
response = commandStationAccessoryAckResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationAccessoryAck response failed.", ex);
}
return response;
}
protected byte[] processBmGetRangeRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the FeedbackGetRangeMessage: {}", bidibMessage);
byte[] response = null;
try {
FeedbackGetRangeMessage feedbackGetRangeMessage = (FeedbackGetRangeMessage) bidibMessage;
int baseAddress = feedbackGetRangeMessage.getBeginRange();
int end = feedbackGetRangeMessage.getEndRange();
int feedbackSize = feedbackGetRangeMessage.getEndRange() - feedbackGetRangeMessage.getBeginRange();
byte value = 0x00;
int index = 0;
int feedbackByteSize = feedbackSize / 8 + (feedbackSize % 8 > 0 ? 1 : 0);
byte[] feedbackMultiple = new byte[feedbackByteSize];
int position = feedbackMultiple.length;
for (int portNum = end; portNum > baseAddress; portNum--) {
value = (byte) ((value & 0xFF) << 1);
FeedbackPort fbp = feedbackPorts.get(portNum - 1);
byte status = 0;
if (fbp != null) {
status = ByteUtils.getLowByte(fbp.getStatus().getType().getType(), 0x01);
}
value |= status;
feedbackMultiple[position - 1] = value;
index++;
if (index > 7) {
value = 0;
index = 0;
position--;
}
}
LOGGER.info("Prepared feedback multiple: {}", ByteUtils.bytesToHex(feedbackMultiple));
FeedbackMultipleResponse feedbackMultipleResponse =
new FeedbackMultipleResponse(bidibMessage.getAddr(), getNextSendNum(),
ByteUtils.getLowByte(baseAddress), ByteUtils.getLowByte(feedbackSize), feedbackMultiple);
response = feedbackMultipleResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create feedbackMultiple response failed.", ex);
}
return response;
}
protected void processBmMirrorMultipleRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the FeedbackMirrorMultipleMessage: {}, do nothing ...", bidibMessage);
}
protected void processBmMirrorOccupiedRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the processBmMirrorOccupiedRequest: {}, do nothing ...", bidibMessage);
}
protected void processBmMirrorFreeRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the processBmMirrorFreeRequest: {}, do nothing ...", bidibMessage);
}
protected void processBmMirrorPositionRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the processBmMirrorPositionRequest: {}, do nothing ...", bidibMessage);
}
protected void processBmAddrGetRangeRequest(BidibMessageInterface bidibMessage) {
try {
for (FeedbackPort port : feedbackPorts.values()) {
int detectorNumber = port.getId();
List bidibAddresses = new ArrayList<>();
List addresses = port.getAddresses();
if (CollectionUtils.isNotEmpty(addresses)) {
for (FeedbackAddressData addressData : addresses) {
final EnrailmentDirectionEnum enrailmentDirection = addressData.getType();
AddressTypeEnum addressType = null;
switch (enrailmentDirection) {
case LOCOMOTIVE_LEFT:
case LOCOMOTIVE_RIGHT:
addressType = AddressTypeEnum.LOCOMOTIVE_FORWARD;
break;
case BASIC_ACCESSORY:
addressType = AddressTypeEnum.ACCESSORY;
break;
case EXTENDED_ACCESSORY:
addressType = AddressTypeEnum.EXTENDED_ACCESSORY;
break;
default:
break;
}
AddressData bidibAddress = new AddressData(addressData.getAddress(), addressType);
bidibAddresses.add(bidibAddress);
}
}
FeedbackAddressResponse feedbackAddressResponse =
new FeedbackAddressResponse(bidibMessage.getAddr(), getNextSendNum(), detectorNumber,
bidibAddresses);
byte[] response = feedbackAddressResponse.getContent();
LOGGER.info("Prepare feedbackAddressResponse: {}", ByteUtils.bytesToHex(response));
sendSpontanousResponse(response);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create feedbackAddress response failed.", ex);
}
}
protected byte[] processBmGetConfidenceRequest(BidibMessageInterface bidibMessage) {
byte[] response = null;
try {
byte valid = (byte) (statusValid.get() ? 1 : 0);
byte freeze = (byte) (statusFreeze.get() ? 1 : 0);
byte signal = (byte) (statusSignal.get() ? 1 : 0);
// TODO if more than a single GBM16T is attached we must set more bits? See 4.7.4. Uplink: Nachrichten für
// Belegtmelder --> MSG_BM_CONFIDENCE
// Test with real system: See MainMessageListener.confidence()
FeedbackConfidenceResponse feedbackConfidenceResponse =
new FeedbackConfidenceResponse(bidibMessage.getAddr(), getNextSendNum(), valid, freeze, signal);
response = feedbackConfidenceResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create feedbackConfidence response failed.", ex);
}
return response;
}
private void publishFeedbackPortChange(Port port) {
FeedbackPort feedbackPort = (FeedbackPort) port;
FeedbackPortStatus status = feedbackPort.getStatus();
LOGGER.info("The feedbackport status has changed, notify the listeners, nodeAddress: {}", nodeAddress);
EventBus
.publish(new FeedbackPortStatusEvent(NodeUtils.formatAddress(nodeAddress), feedbackPort.getId(), status));
}
@Override
public void queryStatus(Class portClass) {
if (FeedbackPort.class.equals(portClass)) {
for (FeedbackPort feedbackPort : feedbackPorts.values()) {
publishFeedbackPortChange(feedbackPort);
}
// publish the confidence
publishFeedbackConfidenceStatusEvent(statusValid.get(), statusFreeze.get(), statusSignal.get());
}
}
@EventSubscriber(eventClass = FeedbackConfidenceSetEvent.class)
public void feedbackConfidenceSetEvent(FeedbackConfidenceSetEvent feedbackConfidenceEvent) {
String nodeAddress = feedbackConfidenceEvent.getNodeAddr();
LOGGER.info("The change of the feedback confidence was requested, nodeAddress: {}", nodeAddress);
// check if the node is addressed
if (!isAddressEqual(nodeAddress)) {
LOGGER.trace("Another node is addressed.");
return;
}
statusValid.set(feedbackConfidenceEvent.getValid());
statusFreeze.set(feedbackConfidenceEvent.getFreeze());
statusSignal.set(feedbackConfidenceEvent.getSignal());
byte valid = (byte) (statusValid.get() ? 1 : 0);
byte freeze = (byte) (statusFreeze.get() ? 1 : 0);
byte signal = (byte) (statusSignal.get() ? 1 : 0);
try {
FeedbackConfidenceResponse feedbackConfidenceResponse =
new FeedbackConfidenceResponse(this.nodeAddress, getNextSendNum(), valid, freeze, signal);
LOGGER.info("Prepared feedbackConfidenceResponse: {}", feedbackConfidenceResponse);
sendSpontanousResponse(feedbackConfidenceResponse.getContent());
}
catch (ProtocolException ex) {
LOGGER.warn("Send feedbackConfidenceResponse failed.", ex);
}
publishFeedbackConfidenceStatusEvent(statusValid.get(), statusFreeze.get(), statusSignal.get());
}
private void publishFeedbackConfidenceStatusEvent(boolean valid, boolean freeze, boolean signal) {
LOGGER
.info("The feedbackport confidence status has changed, notify the listeners, nodeAddress: {}", nodeAddress);
EventBus
.publish(new FeedbackConfidenceStatusEvent(NodeUtils.formatAddress(nodeAddress), valid, freeze, signal));
}
@EventSubscriber(eventClass = FeedbackPortSetStatusEvent.class)
public void feedbackPortSetStatus(FeedbackPortSetStatusEvent setStatusEvent) {
LOGGER.info("The change of the feedback port was requested.");
String nodeAddress = setStatusEvent.getNodeAddr();
// check if the node is addressed
if (!isAddressEqual(nodeAddress)) {
LOGGER.trace("Another node is addressed.");
return;
}
int portNum = setStatusEvent.getPortNum();
try {
changeFeedbackPortStatus(portNum);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish feedback status failed.", ex);
}
}
private int timestamp;
private int getTimestamp() {
timestamp += 10;
if (timestamp > 65000) {
timestamp = 0;
}
return timestamp;
}
private boolean hasTimestampFeature() {
Feature feature = Feature.findFeature(features, BidibLibrary.FEATURE_BM_TIMESTAMP_ON);
return (feature != null && feature.getValue() > 0);
}
private int decoderAddress = 10;
private int getDecoderAddress() {
decoderAddress++;
if (decoderAddress > 15) {
decoderAddress = 10;
}
return decoderAddress;
}
private int locationAddress = 120;
private int getLocationAddress() {
locationAddress++;
if (locationAddress > 150) {
locationAddress = 120;
}
return locationAddress;
}
protected void changeFeedbackPortStatus(int portNum) throws ProtocolException {
FeedbackPort port = feedbackPorts.get(portNum);
if (port != null) {
BidibMessage response = null;
BidibMessage responsePosition = null;
switch (port.getStatus()) {
case FREE:
port.setStatus(FeedbackPortStatus.OCCUPIED);
if (hasTimestampFeature()) {
response =
new FeedbackOccupiedResponse(getNodeAddress(), getNextSendNum(), portNum, getTimestamp());
}
else {
response = new FeedbackOccupiedResponse(getNodeAddress(), getNextSendNum(), portNum);
}
int locationType = ByteUtils.getInt(PositionLocationEnum.LOCATOR_BADGE.getType());
responsePosition =
new FeedbackPositionResponse(getNodeAddress(), getNextSendNum(), getDecoderAddress(),
locationType, getLocationAddress());
break;
default:
port.setStatus(FeedbackPortStatus.FREE);
response = new FeedbackFreeResponse(getNodeAddress(), getNextSendNum(), portNum);
break;
}
sendSpontanousResponse(response.getContent());
if (responsePosition != null) {
sendSpontanousResponse(responsePosition.getContent());
}
publishFeedbackPortChange(port);
}
else {
LOGGER.warn("The requested feedback port is not available: {}", portNum);
}
}
}