org.bidib.wizard.simulation.ReadyBoostSimulator 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.LinkedHashMap;
import java.util.LinkedList;
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 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.BoosterControl;
import org.bidib.jbidibc.messages.enums.BoosterState;
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.SpeedStepsEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.AccessoryGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaResponse;
import org.bidib.jbidibc.messages.message.AccessoryParaSetMessage;
import org.bidib.jbidibc.messages.message.AccessorySetMessage;
import org.bidib.jbidibc.messages.message.AccessoryStateResponse;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.BoostDiagnosticResponse;
import org.bidib.jbidibc.messages.message.BoostOffMessage;
import org.bidib.jbidibc.messages.message.BoostOnMessage;
import org.bidib.jbidibc.messages.message.BoostStatResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveStateResponse;
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.FeedbackSpeedResponse;
import org.bidib.jbidibc.messages.utils.ByteUtils;
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.bushe.swing.event.annotation.AnnotationProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@BidibNodeSimulators({ @BidibNodeSimulator(vid = "251", pid = "117"), @BidibNodeSimulator(vid = "251", pid = "118"),
@BidibNodeSimulator(vid = "251", pid = "119") })
public class ReadyBoostSimulator extends DefaultNodeSimulator {
private static final Logger LOGGER = LoggerFactory.getLogger(ReadyBoostSimulator.class);
private static final String SIMULATION_PANEL_CLASS =
"org.bidib.wizard.simulation.client.view.panel.LightControlPanel";
private BoosterState boosterState = BoosterState.OFF;
private CommandStationState commandStationState = CommandStationState.OFF;
private Map mapLocoCV = new LinkedHashMap();
protected final ScheduledExecutorService boosterDiagWorker = Executors.newScheduledThreadPool(1);
protected static final int MAX_NUM_OF_LOCO_DECODERS = 16;
protected final ScheduledExecutorService feedbackLocoDecoderWorker = Executors.newScheduledThreadPool(1);
public ReadyBoostSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {
super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);
}
@Override
protected void prepareFeatures() {
LOGGER.info("Prepare the features.");
super.prepareFeatures();
features.add(new Feature(BidibLibrary.FEATURE_ACCESSORY_COUNT, 1));
features.add(new Feature(BidibLibrary.FEATURE_ACCESSORY_SURVEILLED, 0));
features.add(new Feature(BidibLibrary.FEATURE_BST_TURNOFF_TIME, 8));
features.add(new Feature(BidibLibrary.FEATURE_BST_CURMEAS_INTERVAL, 200));
features.add(new Feature(BidibLibrary.FEATURE_BST_AMPERE, 186));
features.add(new Feature(BidibLibrary.FEATURE_BST_AMPERE_ADJUSTABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BST_INHIBIT_AUTOSTART, 0));
features.add(new Feature(BidibLibrary.FEATURE_BST_CUTOUT_ON, 1));
features.add(new Feature(BidibLibrary.FEATURE_BST_CUTOUT_AVAIALABLE, 1));
features.add(new Feature(BidibLibrary.FEATURE_BST_INHIBIT_LOCAL_ONOFF, 0));
features.add(new Feature(BidibLibrary.FEATURE_BST_TURNOFF_TIME, 30));
features.add(new Feature(BidibLibrary.FEATURE_GEN_WATCHDOG, 20));
// simulate the global detector
features.add(new Feature(BidibLibrary.FEATURE_BM_CV_AVAILABLE, 2));
features.add(new Feature(BidibLibrary.FEATURE_BM_CV_ON, 2));
features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_AVAILABLE, 2));
features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_ON, 2));
}
@Override
public void postConstruct() {
super.postConstruct();
}
@Override
protected void prepareCVs() {
super.prepareCVs();
configurationVariables.put("81", "253");
configurationVariables.put("82", "2");
configurationVariables.put("83", "0");
configurationVariables.put("84", "3");
configurationVariables.put("86", "0");
}
@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
super.start();
}
@Override
public void stop() {
AnnotationProcessor.unprocess(this);
super.stop();
}
@Override
protected byte[] prepareResponse(BidibMessageInterface bidibMessage) {
byte[] response = null;
switch (ByteUtils.getInt(bidibMessage.getType())) {
case BidibLibrary.MSG_ACCESSORY_SET:
response = processAccessorySetRequest(bidibMessage);
break;
case BidibLibrary.MSG_ACCESSORY_GET:
response = processAccessoryGetRequest(bidibMessage);
break;
case BidibLibrary.MSG_ACCESSORY_PARA_SET:
response = processAccessoryParaSetRequest(bidibMessage);
break;
case BidibLibrary.MSG_ACCESSORY_PARA_GET:
response = processAccessoryParaGetRequest(bidibMessage);
break;
case BidibLibrary.MSG_BOOST_QUERY:
response = processBoostQueryRequest(bidibMessage);
break;
case BidibLibrary.MSG_BOOST_ON:
response = processBoostOnRequest(bidibMessage);
break;
case BidibLibrary.MSG_BOOST_OFF:
response = processBoostOffRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_SET_STATE:
response = processCsSetStateRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_PROG:
response = processCsProgRequest(bidibMessage);
break;
case BidibLibrary.MSG_CS_QUERY:
response = processCsQueryRequest(bidibMessage);
break;
default:
response = super.prepareResponse(bidibMessage);
break;
}
return response;
}
protected byte[] processAccessorySetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessorySet request: {}", bidibMessage);
byte[] response = null;
try {
AccessorySetMessage accessorySetMessage = (AccessorySetMessage) bidibMessage;
int accessoryNumber = accessorySetMessage.getAccessoryNumber();
int aspect = accessorySetMessage.getAspect();
if (accessoryNumber == 0) {
if (aspect == 1) {
// local source control
boosterControl = BoosterControl.LOCAL;
}
else {
// connect source control
boosterControl = BoosterControl.CONNECT;
}
LOGGER.info("Current boosterControl: {}", boosterControl);
// publish booster state response
byte boosterStateValue =
ByteUtils
.getLowByte(boosterState.getType() | (boosterControl == BoosterControl.LOCAL ? 0x40 : 0x00));
BoostStatResponse boostStatResponse =
new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterStateValue);
LOGGER.info("Publish the booster state response: {}", boostStatResponse);
sendSpontanousResponse(boostStatResponse.getContent());
}
byte[] value = new byte[] { 0, 0, 0 };
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
(byte) aspect, value);
response = accessoryStateResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
return response;
}
protected byte[] processAccessoryGetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryGet request: {}", bidibMessage);
byte[] response = null;
try {
AccessoryGetMessage accessoryGetMessage = (AccessoryGetMessage) bidibMessage;
int accessoryNumber = accessoryGetMessage.getAccessoryNumber();
int aspect = 0;
byte[] value = new byte[] { 2, 0, 0 };
AccessoryStateResponse accessoryStateResponse =
new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
(byte) aspect, value);
response = accessoryStateResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryState response failed.", ex);
}
return response;
}
protected byte[] processAccessoryParaSetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryParaSet request: {}", bidibMessage);
byte[] response = null;
try {
AccessoryParaSetMessage accessoryParaSetMessage = (AccessoryParaSetMessage) bidibMessage;
int accessoryNumber = accessoryParaSetMessage.getAccessoryNumber();
int paraNumber = accessoryParaSetMessage.getParaNumber();
byte[] value = accessoryParaSetMessage.getValue();
AccessoryParaResponse accessoryParaResponse =
new AccessoryParaResponse(bidibMessage.getAddr(), getNextSendNum(),
ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(paraNumber), value);
response = accessoryParaResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryPara response failed.", ex);
}
return response;
}
protected byte[] processAccessoryParaGetRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the AccessoryParaGet request: {}", bidibMessage);
byte[] response = null;
try {
AccessoryParaGetMessage accessoryParaGetMessage = (AccessoryParaGetMessage) bidibMessage;
int accessoryNumber = accessoryParaGetMessage.getAccessoryNumber();
int paraNumber = accessoryParaGetMessage.getParaNumber();
// TODO provide the correct data here ...
byte[] value = new byte[] { 0, 0, 0, 0 };
if (paraNumber == BidibLibrary.BIDIB_ACCESSORY_SWITCH_TIME
|| paraNumber == BidibLibrary.BIDIB_ACCESSORY_PARA_STARTUP) {
LOGGER.info("The param is not known currently!");
value = new byte[] { ByteUtils.getLowByte(paraNumber) };
paraNumber = BidibLibrary.BIDIB_ACCESSORY_PARA_NOTEXIST;
}
AccessoryParaResponse accessoryParaResponse =
new AccessoryParaResponse(bidibMessage.getAddr(), getNextSendNum(),
ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(paraNumber), value);
response = accessoryParaResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create AccessoryPara response failed.", ex);
}
return response;
}
/**
* booster messages
*/
protected byte[] processBoostQueryRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the BoostQuery request: {}", bidibMessage);
byte[] response = null;
try {
BoostStatResponse boostStatResponse =
new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterState);
response = boostStatResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create BoostStatResponse response failed.", ex);
}
return response;
}
private BoosterControl boosterControl = BoosterControl.CONNECT;
private ScheduledFuture> futureTriggerBoostDiagnostic;
private ScheduledFuture> futureTriggerGlobalDetector;
private int interval = 3000;
private static final int LOCO_DECODER_ADDRESS_BASE = 3;
// private int currentLocoDecoder = LOCO_DECODER_ADDRESS_BASE;
private static final class LocoListItem {
protected int address;
protected int speed;
public LocoListItem(int address, int speed) {
this.address = address;
this.speed = speed;
}
}
private final List locoList = new LinkedList<>();
protected byte[] processBoostOnRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the BoostOn request: {}", bidibMessage);
byte[] response = null;
try {
BoostOnMessage boostOnMessage = (BoostOnMessage) bidibMessage;
byte broadcast = boostOnMessage.getBroadcast();
LOGGER.info("BoostOn with broadcast: {}", broadcast);
if (broadcast == BoostOnMessage.BROADCAST_MESSAGE) {
// TODO handle the requested broadcast
}
// activate the booster
boosterState = BoosterState.ON;
byte boosterStateValue =
ByteUtils.getLowByte(boosterState.getType() | (boosterControl == BoosterControl.LOCAL ? 0x40 : 0x00));
BoostStatResponse boostStatResponse =
new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterStateValue);
response = boostStatResponse.getContent();
sendSpontanousResponse(response);
}
catch (ProtocolException ex) {
LOGGER.warn("Create BoostStatResponse response failed.", ex);
}
// use executor to send response
if (futureTriggerBoostDiagnostic == null) {
Feature curMeasInterval =
features
.stream().filter(f -> f.getFeatureEnum().equals(FeatureEnum.FEATURE_BST_CURMEAS_INTERVAL))
.findFirst().orElse(null);
LOGGER.info("Schedule the boost diagnostic trigger, curMeasInterval: {}", curMeasInterval);
int interval = (curMeasInterval != null ? curMeasInterval.getValue() : 200) * 10 /* ms */;
futureTriggerBoostDiagnostic = boosterDiagWorker.scheduleAtFixedRate(new Runnable() {
@Override
public void run() {
LOGGER.info("Trigger boost diag");
try {
triggerBoostDiagnosticResponse();
}
catch (Exception ex) {
LOGGER.warn("Trigger the boost diagnostic failed.", ex);
}
LOGGER.info("Trigger boost has finished.");
}
}, 500, interval, TimeUnit.MILLISECONDS);
}
// use executor to send response
if (futureTriggerGlobalDetector == null) {
LOGGER.info("Start the global detector feedback worker.");
locoList.clear();
for (int address = LOCO_DECODER_ADDRESS_BASE; address < LOCO_DECODER_ADDRESS_BASE
+ MAX_NUM_OF_LOCO_DECODERS; address++) {
locoList.add(new LocoListItem(address, 0));
}
futureTriggerGlobalDetector = feedbackLocoDecoderWorker.scheduleWithFixedDelay(new Runnable() {
@Override
public void run() {
try {
// currentLocoDecoder++;
// if (currentLocoDecoder >= (LOCO_DECODER_ADDRESS_BASE + MAX_NUM_OF_LOCO_DECODERS)) {
// currentLocoDecoder = LOCO_DECODER_ADDRESS_BASE;
// }
LOGGER.info("Trigger addresses for loco decoder: {}", locoList);
// triggerFeedbackAddressResponse(FeedbackPort.GLOBAL_FEEDBACK_PORT_NUMBER, currentLocoDecoder);
triggerFeedbackAddressResponse(FeedbackPort.GLOBAL_FEEDBACK_PORT_NUMBER,
locoList.stream().mapToInt(i -> i.address).toArray());
Thread.sleep(500);
int delayValue = ThreadLocalRandom.current().nextInt(300, 4000);
LOGGER.info("Wait for next execution, delayValue: {}", delayValue);
Thread.sleep(delayValue);
}
catch (Exception ex) {
LOGGER.warn("Trigger the feedback address failed.", ex);
}
LOGGER.info("Trigger feedback address has finished.");
}
}, 5000, interval, TimeUnit.MILLISECONDS);
}
return null;
}
protected byte[] processBoostOffRequest(BidibMessageInterface bidibMessage) {
LOGGER.info("Process the BoostOff request: {}", bidibMessage);
byte[] response = null;
try {
BoostOffMessage boostOffMessage = (BoostOffMessage) bidibMessage;
byte broadcast = boostOffMessage.getBroadcast();
LOGGER.info("BoostOn with broadcast: {}", broadcast);
if (broadcast == BoostOnMessage.BROADCAST_MESSAGE) {
// TODO handle the requested broadcast
}
// deactivate the booster
boosterState = BoosterState.OFF;
BoostStatResponse boostStatResponse =
new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterState);
response = boostStatResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create BoostStatResponse response failed.", ex);
}
if (futureTriggerGlobalDetector != null) {
LOGGER.info("Stop the global detector trigger.");
futureTriggerGlobalDetector.cancel(false);
futureTriggerGlobalDetector = null;
}
if (futureTriggerBoostDiagnostic != null) {
LOGGER.info("Stop the boost diagnostic trigger.");
futureTriggerBoostDiagnostic.cancel(false);
futureTriggerBoostDiagnostic = null;
}
return response;
}
protected void triggerBoostDiagnosticResponse() {
LOGGER.info("Trigger the boostDiagnostic repsonse.");
byte[] response = null;
try {
int currentValue = ThreadLocalRandom.current().nextInt(10, 180 + 1);
int tempValue = ThreadLocalRandom.current().nextInt(25, 70);
BoostDiagnosticResponse boostDiagnosticResponse =
new BoostDiagnosticResponse(nodeAddress, getNextSendNum(), currentValue, 0xA9, tempValue);
response = boostDiagnosticResponse.getContent();
}
catch (ProtocolException ex) {
LOGGER.warn("Create boostDiagnostic response failed.", ex);
}
sendSpontanousResponse(response);
}
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();
}
catch (ProtocolException ex) {
LOGGER.warn("Create CommandStationState 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:
// 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[] 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;
}
private void triggerFeedbackAddressResponse(int portNum, int... locoAddresses) {
LOGGER.info("Trigger the feedback address repsonse, portNum: {}", portNum);
byte[] response = null;
List addresses = new LinkedList();
for (int addressData : locoAddresses) {
byte lowByte = ByteUtils.getLowByte(addressData);
byte highByte = ByteUtils.getLowByte(0x80);
int address = ByteUtils.getWord(lowByte, (byte) (highByte & 0x3F));
AddressData addressDataIn =
new AddressData(address, AddressTypeEnum.valueOf((byte) ((highByte & 0xC0) >> 6)));
addresses
.add(new FeedbackAddressData(addressDataIn.getAddress(), EnrailmentDirectionEnum.LOCOMOTIVE_RIGHT));
}
try {
int detectorNumber = portNum;
List bidibAddresses = new ArrayList<>();
// port is occupied -> add the addresses
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);
}
}
if (CollectionUtils.isNotEmpty(bidibAddresses)) {
FeedbackAddressResponse feedbackAddressResponse =
new FeedbackAddressResponse(nodeAddress, getNextSendNum(), detectorNumber, bidibAddresses);
LOGGER.info("Prepared feedbackAddressResponse: {}", feedbackAddressResponse);
response = feedbackAddressResponse.getContent();
sendSpontanousResponse(response);
}
if (CollectionUtils.isNotEmpty(bidibAddresses)) {
for (AddressData addressData : bidibAddresses) {
int address = addressData.getAddress();
int speedValue = ThreadLocalRandom.current().nextInt(2, 100);
FeedbackSpeedResponse feedbackSpeedResponse =
new FeedbackSpeedResponse(nodeAddress, getNextSendNum(), address, speedValue);
sendSpontanousResponse(feedbackSpeedResponse.getContent());
}
}
}
catch (ProtocolException ex) {
LOGGER.warn("Create FeedbackAddressResponse failed.", ex);
}
}
}