org.bidib.wizard.simulation.S88BidibBridgeSimulator 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
The newest version!
package org.bidib.wizard.simulation;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
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.EnrailmentDirectionEnum;
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.FeedbackAddressResponse;
import org.bidib.jbidibc.messages.message.FeedbackConfidenceResponse;
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.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.jbidibc.simulation.SimulationBidibMessageProcessor;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulator;
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;
@BidibNodeSimulator(vid = "13", pid = "105")
public class S88BidibBridgeSimulator extends DefaultNodeSimulator {
private static final Logger LOGGER = LoggerFactory.getLogger(S88BidibBridgeSimulator.class);
private static final String SIMULATION_PANEL_CLASS =
"org.bidib.wizard.simulation.client.view.panel.GBMboostMasterPanel";
protected static final int MAX_NUM_OF_FEEDBACK_PORTS = 128;
protected int feedbackPortCount = 8;
private final Map feedbackPorts = new HashMap();
private final AtomicBoolean statusFreeze = new AtomicBoolean();
private final AtomicBoolean statusVoid = new AtomicBoolean();
private final AtomicBoolean statusNoSignal = new AtomicBoolean();
private int interval = 3000;
private int currentLocoPosition = -1;
private boolean startFeedbackWorker = true;
protected final ScheduledExecutorService feedbackAddrWorker;
public S88BidibBridgeSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {
super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);
final ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("feedbackAddrWorkers-thread-%d").build();
feedbackAddrWorker = Executors.newScheduledThreadPool(1, namedThreadFactory);
}
@Override
protected void prepareFeatures() {
LOGGER.info("Prepare the features.");
super.prepareFeatures();
// features.add(new Feature(BidibLibrary.FEATURE_BM_SIZE, feedbackPortCount));
features.add(new Feature(BidibLibrary.FEATURE_BM_ON, 1));
}
@Override
public void postConstruct() {
super.postConstruct();
features.add(new Feature(BidibLibrary.FEATURE_BM_SIZE, feedbackPortCount));
}
private void setupFeedbackPorts() {
LOGGER.info("Setup the feedback ports, feedbackPortCount: {}", feedbackPortCount);
Feature featureBmSize = getFeature(BidibLibrary.FEATURE_BM_SIZE);
feedbackPortCount = featureBmSize.getValue();
for (int id = 0; id < feedbackPortCount; 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
public void start() {
LOGGER.info("Start the simulator for address: {}", getAddress());
AnnotationProcessor.process(this);
// prepare the feedback ports
setupFeedbackPorts();
super.start();
if (isFeedbackChangesEnabled() && startFeedbackWorker) {
feedbackAddrWorker.scheduleWithFixedDelay(() -> {
try {
int portNum = currentLocoPosition;
LOGGER.info("Trigger feedback address, portNum: {}", portNum);
FeedbackPortSetStatusEvent evtFree = null;
if (portNum > -1) {
portNum = currentLocoPosition;
if (portNum < 0) {
portNum = MAX_NUM_OF_FEEDBACK_PORTS - 1;
}
// make the active position occupied
evtFree = new FeedbackPortSetStatusEvent(getAddress(), portNum, FeedbackPortStatus.FREE);
}
currentLocoPosition++;
if (currentLocoPosition >= MAX_NUM_OF_FEEDBACK_PORTS) {
currentLocoPosition = 0;
}
FeedbackPortSetStatusEvent evtOcc =
new FeedbackPortSetStatusEvent(getAddress(), currentLocoPosition, FeedbackPortStatus.OCCUPIED);
// send the occupied event
setFeedbackPortStatus(evtOcc);
Thread.sleep(500);
if (evtFree != null) {
// send the free event
setFeedbackPortStatus(evtFree);
}
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);
}
else {
LOGGER.info("The feedback address publisher is not started.");
}
}
@Override
public void stop() {
AnnotationProcessor.unprocess(this);
if (feedbackAddrWorker != null) {
LOGGER.info("Stop the booster diag worker.");
feedbackAddrWorker.shutdownNow();
}
super.stop();
}
@Override
public String getSimulationPanelClass() {
return SIMULATION_PANEL_CLASS;
}
@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_ADDR_GET_RANGE:
processBmAddrGetRangeRequest(bidibMessage);
break;
case BidibLibrary.MSG_BM_GET_CONFIDENCE:
response = processBmGetConfidenceRequest(bidibMessage);
break;
default:
response = super.prepareResponse(bidibMessage);
break;
}
return response;
}
@Override
protected Feature updateFeatureValue(Feature feature, int featureValue) {
Integer oldBmSize = null;
if (feature.getType() == BidibLibrary.FEATURE_BM_SIZE) {
oldBmSize = feature.getValue();
}
feature.setValue(featureValue);
if (oldBmSize != null && oldBmSize.intValue() != featureValue) {
LOGGER.info("BM_SIZE has changed, update the feedback ports. New size: {}", featureValue);
setupFeedbackPorts();
}
return feature;
}
@Override
protected Feature autoAddFeature(int featureNum, int featureValue) {
Feature foundFeature = super.autoAddFeature(featureNum, featureValue);
return foundFeature;
}
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;
byte[] feedbackMultiple = new byte[feedbackSize / 8];
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;
index++;
if (index > 7) {
feedbackMultiple[position - 1] = value;
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 processBmAddrGetRangeRequest(BidibMessageInterface bidibMessage) {
try {
for (FeedbackPort port : feedbackPorts.values()) {
int detectorNumber = port.getId();
List bidibAddresses = new ArrayList<>();
List addresses = port.getAddresses();
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) (statusVoid.get() ? 1 : 0);
byte freeze = (byte) (statusFreeze.get() ? 1 : 0);
byte signal = (byte) (statusNoSignal.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;
}
protected void triggerConfidenceResponse() {
LOGGER.info("Trigger the confidence repsonse");
byte valid = (byte) (statusVoid.get() ? 1 : 0);
byte freeze = (byte) (statusFreeze.get() ? 1 : 0);
byte signal = (byte) (statusNoSignal.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);
}
}
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), port.getId(), status));
}
@Override
public void queryStatus(Class> portClass) {
if (FeedbackPort.class.equals(portClass)) {
for (FeedbackPort feedbackPort : feedbackPorts.values()) {
publishFeedbackPortChange(feedbackPort);
}
// publish the confidence
publishFeedbackConfidenceStatusEvent(statusVoid.get(), statusFreeze.get(), statusNoSignal.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;
}
statusVoid.set(feedbackConfidenceEvent.getValid());
statusFreeze.set(feedbackConfidenceEvent.getFreeze());
statusNoSignal.set(feedbackConfidenceEvent.getSignal());
triggerConfidenceResponse();
publishFeedbackConfidenceStatusEvent(statusVoid.get(), statusFreeze.get(), statusNoSignal.get());
}
private void publishFeedbackConfidenceStatusEvent(boolean statusVoid, boolean freeze, boolean noSignal) {
LOGGER
.info("The feedbackport confidence status has changed, notify the listeners, nodeAddress: {}", nodeAddress);
EventBus
.publish(
new FeedbackConfidenceStatusEvent(NodeUtils.formatAddress(nodeAddress), statusVoid, freeze, noSignal));
}
@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);
}
}
protected void changeFeedbackPortStatus(int portNum) throws ProtocolException {
FeedbackPort port = feedbackPorts.get(portNum);
if (port != null) {
BidibMessage response = null;
switch (port.getStatus()) {
case FREE:
port.setStatus(FeedbackPortStatus.OCCUPIED);
response = new FeedbackOccupiedResponse(getNodeAddress(), getNextSendNum(), portNum);
break;
default:
port.setStatus(FeedbackPortStatus.FREE);
response = new FeedbackFreeResponse(getNodeAddress(), getNextSendNum(), portNum);
break;
}
sendSpontanousResponse(response.getContent());
publishFeedbackPortChange(port);
}
else {
LOGGER.warn("The requested feedback port is not available: {}", portNum);
}
}
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);
}
protected void setFeedbackPortStatus(FeedbackPortSetStatusEvent statusEvent) throws ProtocolException {
int portNum = statusEvent.getPortNum();
FeedbackPort port = feedbackPorts.get(portNum);
if (port != null) {
BidibMessage response = null;
switch (statusEvent.getStatus()) {
case OCCUPIED:
port.setStatus(FeedbackPortStatus.OCCUPIED);
if (hasTimestampFeature()) {
response =
new FeedbackOccupiedResponse(getNodeAddress(), getNextSendNum(), portNum, getTimestamp());
}
else {
response = new FeedbackOccupiedResponse(getNodeAddress(), getNextSendNum(), portNum);
}
break;
default:
port.setStatus(FeedbackPortStatus.FREE);
response = new FeedbackFreeResponse(getNodeAddress(), getNextSendNum(), portNum);
break;
}
LOGGER.info("Prepared the FeedbackFreeResponse: {}", response);
sendSpontanousResponse(response.getContent());
publishFeedbackPortChange(port);
}
else {
LOGGER.warn("The requested feedback port is not available: {}", portNum);
}
}
}