![JAR search and dependency download from the Maven repository](/logo.png)
org.bidib.wizard.localhost.LocalHostBidibDistributedMessageHandler Maven / Gradle / Ivy
package org.bidib.wizard.localhost;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.BitSet;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.core.BidibDistributedMessageListener;
import org.bidib.jbidibc.core.node.BidibNode;
import org.bidib.jbidibc.messages.AccessoryState;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.DriveState;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.Node;
import org.bidib.jbidibc.messages.enums.BoosterState;
import org.bidib.jbidibc.messages.enums.ClassIdEnum;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.DriveAcknowledge;
import org.bidib.jbidibc.messages.enums.IdentifyState;
import org.bidib.jbidibc.messages.enums.MessageClassEnum;
import org.bidib.jbidibc.messages.enums.TargetModeEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.message.AccessorySetMessage;
import org.bidib.jbidibc.messages.message.BidibGuestMessage;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibResponseFactory;
import org.bidib.jbidibc.messages.message.CommandStationDriveMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.GuestRequestSendMessage;
import org.bidib.jbidibc.messages.message.GuestRequestSubscribeMessage;
import org.bidib.jbidibc.messages.message.GuestResponseNotifyMessage;
import org.bidib.jbidibc.messages.message.GuestResponseSentMessage;
import org.bidib.jbidibc.messages.message.GuestResponseSubscriptionMessage;
import org.bidib.jbidibc.messages.message.NodeTabGetAllMessage;
import org.bidib.jbidibc.messages.message.NodeTabGetNextMessage;
import org.bidib.jbidibc.messages.message.RequestFactory;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.messages.utils.ConversionUtils;
import org.bidib.jbidibc.messages.utils.NodeUtils;
import org.bidib.jbidibc.messages.utils.ThreadFactoryBuilder;
import org.bidib.wizard.api.model.Accessory;
import org.bidib.wizard.api.model.CommandStationNodeInterface;
import org.bidib.wizard.api.model.NodeInterface;
import org.bidib.wizard.api.model.NodeProvider;
import org.bidib.wizard.api.model.connection.AbstractMessageEvent;
import org.bidib.wizard.api.model.connection.BidibConnection;
import org.bidib.wizard.api.model.connection.GatewayConnection;
import org.bidib.wizard.api.model.connection.event.AccessoryStateMessageEvent;
import org.bidib.wizard.api.model.connection.event.BoosterDiagMessageEvent;
import org.bidib.wizard.api.model.connection.event.BoosterStateMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationDriveAcknowledgeMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationDriveManualMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationDriveStateMessageEvent;
import org.bidib.wizard.api.model.connection.event.CommandStationStateMessageEvent;
import org.bidib.wizard.api.model.connection.event.FeatureMessageEvent;
import org.bidib.wizard.api.model.connection.event.NodeLostMessageEvent;
import org.bidib.wizard.api.model.connection.event.SysIdentifyStateMessageEvent;
import org.bidib.wizard.api.service.node.CommandStationService;
import org.bidib.wizard.api.service.node.SwitchingNodeService;
import org.bidib.wizard.api.utils.AccessoryListUtils;
import org.bidib.wizard.localhost.event.SubscriberEvent;
import org.bidib.wizard.localhost.event.SubscriberEvent.SubscribeAction;
import org.bidib.wizard.localhost.exception.InvalidNodeClassException;
import org.bidib.wizard.localhost.exception.InvalidSubscriptionException;
import org.bidib.wizard.model.status.CommandStationStatus;
import org.bidib.wizard.model.status.DirectionStatus;
import org.bidib.wizard.model.status.SpeedSteps;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import io.reactivex.rxjava3.core.Scheduler;
import io.reactivex.rxjava3.disposables.CompositeDisposable;
import io.reactivex.rxjava3.disposables.Disposable;
import io.reactivex.rxjava3.functions.Consumer;
import io.reactivex.rxjava3.plugins.RxJavaPlugins;
import io.reactivex.rxjava3.schedulers.Schedulers;
import io.reactivex.rxjava3.subjects.PublishSubject;
public class LocalHostBidibDistributedMessageHandler implements BidibDistributedMessageListener {
private static final Logger LOGGER = LoggerFactory.getLogger(LocalHostBidibDistributedMessageHandler.class);
private final BidibConnection connection;
private final NodeProvider nodeProvider;
private final BidibResponseFactory responseFactory;
private final CommandStationService commandStationService;
private final SwitchingNodeService switchingNodeService;
private CompositeDisposable compDispMessages = new CompositeDisposable();
private Map> subscriptions = new HashMap<>();
private final BlockingQueue receiveQueue = new LinkedBlockingQueue<>();
private ScheduledExecutorService receiveQueueWorker;
private AtomicBoolean processReceiveQueue = new AtomicBoolean();
private final Scheduler computationScheduler;
private PublishSubject subjectSubscriberEvents;
private static final class ReceiveMessgeContainer {
private final BidibNode bidibNode;
private final BidibMessageInterface message;
/**
* Create a new container to transport the node and the message that is releated to the node.
*
* @param bidibNode
* the node that sent the message (origin of message)
* @param message
* the message
*/
public ReceiveMessgeContainer(final BidibNode bidibNode, final BidibMessageInterface message) {
this.bidibNode = bidibNode;
this.message = message;
}
/**
* @return the bidib node that sent the message
*/
public BidibNode getBidibNode() {
return bidibNode;
}
public BidibMessageInterface getMessage() {
return message;
}
}
/**
* Create a new instance of the LocalHostBidibDistributedMessageHandler.
*
* @param connection
* the connection
* @param nodeProvider
* the node provider
*/
public LocalHostBidibDistributedMessageHandler(final BidibConnection connection, final NodeProvider nodeProvider,
final BidibResponseFactory responseFactory, final CommandStationService commandStationService,
final SwitchingNodeService switchingNodeService) {
LOGGER.info("Create new instance of LocalHostBidibDistributedMessageHandler.");
this.connection = connection;
this.nodeProvider = nodeProvider;
this.responseFactory = responseFactory;
this.commandStationService = commandStationService;
this.switchingNodeService = switchingNodeService;
this.subjectSubscriberEvents = PublishSubject.create();
// subscribe to messages of the connection
this.computationScheduler =
RxJavaPlugins
.createComputationScheduler(
new ThreadFactoryBuilder().setNameFormat("localHostSubjectMessagesWorkers-thread-%d").build());
Disposable dispMessages =
this.connection.getSubjectMessages().subscribeOn(this.computationScheduler).subscribe(me -> {
LOGGER.debug("Received message event: {}", me);
processMessageEvent(me);
}, err -> {
LOGGER.warn("The message subject has signalled an error.", err);
});
this.compDispMessages.add(dispMessages);
final ThreadFactory namedThreadFactory =
new ThreadFactoryBuilder().setNameFormat("localHostRcvQueueWorkers-thread-%d").build();
receiveQueueWorker = Executors.newScheduledThreadPool(2, namedThreadFactory);
final int receiveTimeout = 1000;
processReceiveQueue.set(true);
receiveQueueWorker.submit(() -> {
LOGGER.info("Start the receiver queue.");
while (processReceiveQueue.get()) {
try {
final ReceiveMessgeContainer messageContainer =
this.receiveQueue.poll(receiveTimeout, TimeUnit.MILLISECONDS);
if (messageContainer != null) {
handleDistributedMessage(messageContainer);
}
}
catch (InterruptedException ex) {
LOGGER.warn("Process receive queue was interrupted.");
break;
}
catch (Exception ex) {
LOGGER.warn("Process receive queue failed. Ignore and continue.");
}
}
LOGGER.info("The receiver queue has finished.");
});
this.connection.setLocalHostEnabled(true);
}
@Override
public void shutdown() {
LOGGER.info("Shutdown the LocalHostBidibDistributedMessageHandler.");
this.compDispMessages.dispose();
LOGGER.info("Stop the receiver queue.");
processReceiveQueue.set(false);
receiveQueueWorker.shutdownNow();
this.computationScheduler.shutdown();
}
@Override
public void handleDistributedMessage(final BidibNode bidibNode, final BidibMessageInterface message) {
LOGGER.info("handleDistributedMessage, node: {}, message: {}", bidibNode, message);
// add the message to the receive queue
final ReceiveMessgeContainer messgeContainer = new ReceiveMessgeContainer(bidibNode, message);
this.receiveQueue.add(messgeContainer);
}
/**
* Process the distributed message.
*
* @param messgeContainer
* the container with the node and the message
*/
private void handleDistributedMessage(final ReceiveMessgeContainer messgeContainer) {
final BidibNode bidibNode = messgeContainer.getBidibNode();
final BidibMessageInterface message = messgeContainer.getMessage();
final RequestFactory requestFactory = bidibNode.getRequestFactory();
int type = ByteUtils.getInt(message.getType());
switch (type) {
case BidibLibrary.MSG_GATE_REQ_SUBSCRIBE:
case BidibLibrary.MSG_GATE_REQ_SEND:
case BidibLibrary.MSG_GATE_REQ_TAKE_RESPONSIBILITY:
case BidibLibrary.MSG_GATE_REQ_PASS_RESPONSIBILITY:
case BidibLibrary.MSG_GUEST_RESP_SUBSCRIPTION:
case BidibLibrary.MSG_GUEST_RESP_SENT:
case BidibLibrary.MSG_GUEST_RESP_NOTIFY:
case BidibLibrary.MSG_GUEST_RESP_RESPONSIBILITY:
break;
case BidibLibrary.MSG_GUEST_REQ_SUBSCRIBE:
handleGuestReqSubscribe(requestFactory, (GuestRequestSubscribeMessage) message, this.connection,
bidibNode);
break;
case BidibLibrary.MSG_GUEST_REQ_SEND:
handleGuestReqSend(requestFactory, (GuestRequestSendMessage) message, this.connection, bidibNode);
break;
case BidibLibrary.MSG_GUEST_REQ_TAKE_RESPONSIBILITY:
case BidibLibrary.MSG_GUEST_REQ_PASS_RESPONSIBILITY:
case BidibLibrary.MSG_GATE_RESP_SUBSCRIPTION:
case BidibLibrary.MSG_GATE_RESP_SENT:
case BidibLibrary.MSG_GATE_RESP_FYI:
case BidibLibrary.MSG_GATE_RESP_RESPONSIBILITY:
break;
default:
break;
}
}
/**
*
* @param requestFactory
* the request factory
* @param guestRequestSubscribeMessage
* the message
* @param connection
* the connection
* @param bidibNode
* the bidib node that sent the message
*/
protected void handleGuestReqSubscribe(
final RequestFactory requestFactory, final GuestRequestSubscribeMessage guestRequestSubscribeMessage,
final BidibConnection connection, final BidibNode bidibNode) {
LOGGER.info("Handle the GuestRequestSubscribeMessage: {}", guestRequestSubscribeMessage);
// the address of the node that sent the message
final byte[] originNodeAddress = guestRequestSubscribeMessage.getAddr();
// TODO should we better resolve the address to the uniqueId of the origin node?
final TargetModeEnum targetMode = guestRequestSubscribeMessage.getTargetMode();
int subscription = guestRequestSubscribeMessage.getSubscription();
final NodeSubscriptionData nodeSubscriptionData =
new NodeSubscriptionData(originNodeAddress, targetMode, bidibNode.getCachedUniqueId(), subscription);
final int subscriptionUpstream = guestRequestSubscribeMessage.getSubscriptionUpstream();
final int subscriptionDownstream = guestRequestSubscribeMessage.getSubscriptionDownstream();
LOGGER
.info(
"Received subscribe request from node with address: {} for targetMode: {}, subscriptionUpstream: {}, subscriptionUpstream: {}, subscription: {}",
NodeUtils.formatAddress(nodeSubscriptionData.getSubscriberAddress()), targetMode, subscriptionUpstream,
subscriptionDownstream, StringUtils.leftPad(Integer.toString(subscription, 2), 16, "0"));
int acknowledge = 0;
// keep track of the nodes and messageClass that are registered
prepareSubscription(subscription, nodeSubscriptionData);
byte[] targetModeUniqueId = null;
NodeInterface node = null;
try {
// TODO we must provide the response for all targetModes
switch (targetMode) {
case BIDIB_TARGET_MODE_UID:
byte[] uniqueId = guestRequestSubscribeMessage.getUniqueId();
LOGGER.info("Publish gateway response, uniqueId: {}", ByteUtils.convertUniqueIdToString(uniqueId));
node = nodeProvider.findNodeByUniqueIdWithoutClass(uniqueId);
if (node == null) {
LOGGER.warn("No node found with uniqueId: {}", ByteUtils.convertUniqueIdToString(uniqueId));
acknowledge = 0xFF;
}
break;
case BIDIB_TARGET_MODE_DCCGEN:
// check if DCC generator nodes are available
List dccGenNodes =
nodeProvider.findNodesByClass(ClassIdEnum.BIT_DCC_GEN_DRIVE_AND_SWITCH);
if (CollectionUtils.isEmpty(dccGenNodes)) {
LOGGER.warn("No DCCGEN node found.");
acknowledge = 0xFF;
}
else {
node = dccGenNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_BOOSTER:
// check if booster nodes are available
List boosterNodes = nodeProvider.findNodesByClass(ClassIdEnum.BIT_BOOSTER);
if (CollectionUtils.isEmpty(boosterNodes)) {
LOGGER.warn("No BOOSTER node found.");
acknowledge = 0xFF;
}
else {
node = boosterNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_ACCESSORY:
// check if accessory nodes are available
List accessoryNodes = nodeProvider.findNodesByClass(ClassIdEnum.BIT_ACCESSORY);
if (CollectionUtils.isEmpty(accessoryNodes)) {
LOGGER.warn("No ACCESSORY node found.");
acknowledge = 0xFF;
}
else {
node = accessoryNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_SWITCH:
// check if switching nodes are available
List switchNodes = nodeProvider.findNodesByClass(ClassIdEnum.BIT_SWITCHING);
if (CollectionUtils.isEmpty(switchNodes)) {
LOGGER.warn("No SWITCHING node found.");
acknowledge = 0xFF;
}
else {
node = switchNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_TOP:
node = nodeProvider.findNodeByAddress(Node.ROOTNODE_ADDR);
if (node == null) {
LOGGER.warn("No root node found!");
acknowledge = 0xFF;
}
break;
default:
LOGGER.warn("Unsupported targetMode detected: {}", targetMode);
throw new InvalidSubscriptionException("Unsupported targetMode detected: " + targetMode);
}
if (node != null) {
targetModeUniqueId = NodeUtils.getTargetModeUniqueId(targetMode, node.getUniqueId());
}
// send the MSG_GUEST_RESP_SUBSCRIPTION to the node
final GuestResponseSubscriptionMessage gateResponseSubscriptionMessage =
new GuestResponseSubscriptionMessage(nodeSubscriptionData.getSubscriberAddress(), 0,
TargetModeEnum.BIDIB_TARGET_MODE_UID, targetModeUniqueId, acknowledge, subscription);
publishMessage(nodeSubscriptionData.getSubscriberAddress(), gateResponseSubscriptionMessage);
}
catch (InvalidSubscriptionException ex) {
LOGGER.warn("The subscription is not supported.", ex);
acknowledge = 0x80;
// if (node != null) {
// targetModeUniqueId = NodeUtils.getTargetModeUniqueId(node.getUniqueId());
// }
// send the MSG_GUEST_RESP_SUBSCRIPTION to the node
try {
final GuestResponseSubscriptionMessage gateResponseSubscriptionMessage =
new GuestResponseSubscriptionMessage(nodeSubscriptionData.getSubscriberAddress(), 0,
TargetModeEnum.BIDIB_TARGET_MODE_UID, targetModeUniqueId, acknowledge, subscription);
publishMessage(nodeSubscriptionData.getSubscriberAddress(), gateResponseSubscriptionMessage);
}
catch (ProtocolException ex1) {
LOGGER.warn("Publish gateResponseSubscriptionMessage failed.", ex1);
}
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseSubscriptionMessage failed.", ex);
}
}
protected void prepareSubscription(int subscriptionClasses, final NodeSubscriptionData nodeSubscriptionData) {
for (MessageClassEnum messageClass : MessageClassEnum.values()) {
if (messageClass == MessageClassEnum.BIDIB_MESSAGE_CLASS_NOTHING) {
continue;
}
boolean isActive = MessageClassEnum.isActive(messageClass, subscriptionClasses);
LOGGER.info("Current subscription message class: {}, isActive: {}", messageClass, isActive);
try {
List subscriptionDataList = this.subscriptions.get(messageClass);
if (subscriptionDataList == null) {
LOGGER
.info("No existing subscriptions for message class found: {}, current nodeSubscriptionData: {}",
messageClass, nodeSubscriptionData);
if (!isActive) {
LOGGER.info("The subscription is not active.");
continue;
}
// no existing subscriptions for message class found
subscriptionDataList = new ArrayList<>();
this.subscriptions.put(messageClass, subscriptionDataList);
subscriptionDataList.add(nodeSubscriptionData);
subjectSubscriberEvents
.onNext(new SubscriberEvent(SubscribeAction.add, nodeSubscriptionData.getSubscriberAddress(),
subscriptionClasses).withUniqueId(nodeSubscriptionData.getSubscriberUniqueId()));
}
else {
LOGGER.info("Found existing subscriptions for message class: {}", messageClass);
// check if the subscription for the node is available already
NodeSubscriptionData existingSubscriptionData =
IterableUtils.find(subscriptionDataList, new Predicate() {
@Override
public boolean evaluate(NodeSubscriptionData data) {
return Objects
.equals(data.getSubscriberUniqueId(), nodeSubscriptionData.getSubscriberUniqueId());
}
});
if (existingSubscriptionData != null) {
LOGGER
.info("The original uniqueId is registered already: {}",
ByteUtils.formatHexUniqueId(nodeSubscriptionData.getSubscriberUniqueId()));
if (!isActive) {
LOGGER.info("Remove subscription for messageClass: {}", messageClass);
subscriptionDataList.remove(nodeSubscriptionData);
subjectSubscriberEvents
.onNext(new SubscriberEvent(SubscribeAction.add,
nodeSubscriptionData.getSubscriberAddress(), subscriptionClasses)
.withUniqueId(nodeSubscriptionData.getSubscriberUniqueId()));
}
else {
LOGGER.info("The subscription is still active: {}", messageClass);
}
}
else {
LOGGER
.info("Register uniqueId: {}, messageClass: {}",
ByteUtils.formatHexUniqueId(nodeSubscriptionData.getSubscriberUniqueId()),
messageClass);
subscriptionDataList.add(nodeSubscriptionData);
subjectSubscriberEvents
.onNext(
new SubscriberEvent(SubscribeAction.add, nodeSubscriptionData.getSubscriberAddress(),
subscriptionClasses).withUniqueId(nodeSubscriptionData.getSubscriberUniqueId()));
}
}
}
catch (Exception ex) {
LOGGER.warn("Prepare subscriptions failed.", ex);
// TODO: handle exception
}
}
}
private final Map nodeContainerMap =
Collections.synchronizedMap(new LinkedHashMap());
private BidibNodeContainer getBidibNodeContainer(Long uniqueId) {
BidibNodeContainer bidibNodeContainer = null;
synchronized (nodeContainerMap) {
bidibNodeContainer = nodeContainerMap.get(uniqueId);
if (bidibNodeContainer == null) {
bidibNodeContainer = new BidibNodeContainer(uniqueId);
nodeContainerMap.put(uniqueId, bidibNodeContainer);
}
}
;
return bidibNodeContainer;
}
protected void handleGuestReqSend(
final RequestFactory requestFactory, final GuestRequestSendMessage guestRequestSendMessage,
final BidibConnection connection, final BidibNode bidibNode) {
LOGGER.info("Handle the GuestRequestSendMessage: {}", guestRequestSendMessage);
final BidibNodeContainer bidibNodeContainer = getBidibNodeContainer(bidibNode.getCachedUniqueId());
final byte[] originNodeAddress = guestRequestSendMessage.getAddr();
final TargetModeEnum targetMode = guestRequestSendMessage.getTargetMode();
final NodeInterface node;
try {
node = findNodeByTargetMode(guestRequestSendMessage, targetMode);
}
catch (ProtocolException ex) {
LOGGER.warn("Find node by targetMode failed. Discard handle message: {}", guestRequestSendMessage, ex);
// send error
int result = 0xFF; // no existing node
int ackSequence = guestRequestSendMessage.getNum();
try {
// send the MSG_GUEST_RESP_SENT to the node
final GuestResponseSentMessage guestResponseSentMessage =
new GuestResponseSentMessage(originNodeAddress, 0, targetMode, new byte[0],
new byte[] { ByteUtils.getLowByte(ackSequence), ByteUtils.getLowByte(result) });
publishMessage(originNodeAddress, guestResponseSentMessage);
}
catch (ProtocolException ex1) {
LOGGER.info("Process command from wrapped message failed.", ex1);
}
return;
}
// TODO force subscription on addressed node
final NodeSubscriptionData nodeSubscriptionData =
new NodeSubscriptionData(originNodeAddress, targetMode, node.getUniqueId(), null);
// new NodeSubscriptionData(originNodeAddress, targetMode, bidibNode.getCachedUniqueId(), null);
int subscription = 0xFFFF;
prepareSubscription(subscription, nodeSubscriptionData);
byte[] targetNodeAddr = node.getAddr();
LOGGER
.info("Current originNodeAddress: {}, targetMode: {}, targetNodeAddr: {}", originNodeAddress, targetMode,
targetNodeAddr);
try {
byte[] wrappedContent = guestRequestSendMessage.getWrappedContent();
LOGGER.info("Extracted wrappedContent: {}", ByteUtils.bytesToHex(wrappedContent));
// prepare the message
int addrLen = targetNodeAddr.length;
if (!Arrays.equals(targetNodeAddr, Node.ROOTNODE_ADDR)) {
addrLen += 1; /* term 0 */
}
// len, addr, sequenceNum, wrappedContent
int len = addrLen + 1 /* seq */ + wrappedContent.length;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(len & 0xFF);
baos.write(targetNodeAddr);
if (!Arrays.equals(targetNodeAddr, Node.ROOTNODE_ADDR)) {
baos.write(0); // term 0
}
baos.write(0); // seq
baos.write(wrappedContent);
baos.write(ByteUtils.getLowByte(BidibLibrary.BIDIB_PKT_MAGIC));
wrappedContent = baos.toByteArray();
LOGGER.info("Process wrappedContent: {}", ByteUtils.bytesToHex(wrappedContent));
requestFactory.create(wrappedContent).forEach(command -> {
LOGGER.info("Current command: {}", command);
try {
// NodeInterface node = findNodeByTargetMode(guestRequestSendMessage, targetMode);
switch (ByteUtils.getInt(command.getType())) {
case BidibLibrary.MSG_SYS_GET_MAGIC:
handleSysGetMagicCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
});
break;
case BidibLibrary.MSG_CS_SET_STATE:
handleCsSetCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
});
break;
case BidibLibrary.MSG_CS_DRIVE:
handleCsDriveCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
});
break;
case BidibLibrary.MSG_ACCESSORY_SET:
handleAccessorySetCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
});
break;
case BidibLibrary.MSG_NODETAB_GETALL:
handleNodeTabGetAllCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
}, bidibNodeContainer);
break;
case BidibLibrary.MSG_NODETAB_GETNEXT:
handleNodeTabGetNextCommand(command, nodeSubscriptionData, node, result -> {
sendGuestResponse(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage.getNum(), result, null);
}, bidibNodeContainer);
break;
default:
break;
}
}
catch (InvalidNodeClassException ex) {
LOGGER.info("Process command failed.", ex);
// handle exception
try {
handleInvalidNodeClassError(nodeSubscriptionData, getTargetModeUniqueId(targetMode, node),
guestRequestSendMessage);
}
catch (ProtocolException ex1) {
LOGGER.info("Publish the error message failed.", ex1);
}
}
catch (ProtocolException ex) {
LOGGER.info("Process command failed.", ex);
}
catch (Exception ex) {
LOGGER.info("Process command failed.", ex);
}
});
}
catch (ProtocolException ex) {
LOGGER.info("Process command from wrapped message failed.", ex);
}
catch (Exception ex) {
LOGGER.info("Process command from wrapped message failed.", ex);
}
}
private NodeInterface findNodeByTargetMode(final BidibGuestMessage guestMessage, final TargetModeEnum targetMode)
throws ProtocolException {
NodeInterface node = null;
LOGGER.info("Find node by targetMode: {}", targetMode);
switch (targetMode) {
case BIDIB_TARGET_MODE_UID:
node = nodeProvider.findNodeByUniqueIdWithoutClass(guestMessage.getUniqueId());
break;
case BIDIB_TARGET_MODE_DCCGEN:
// get the first DCC generator
List dccGenNodes =
nodeProvider.findNodesByClass(ClassIdEnum.BIT_DCC_GEN_DRIVE_AND_SWITCH);
if (CollectionUtils.isNotEmpty(dccGenNodes)) {
node = dccGenNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_BOOSTER:
// get the first booster
List boosterNodes = nodeProvider.findNodesByClass(ClassIdEnum.BIT_BOOSTER);
if (CollectionUtils.isNotEmpty(boosterNodes)) {
node = boosterNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_ACCESSORY:
// get the first accessory node
List accessoryNodes = nodeProvider.findNodesByClass(ClassIdEnum.BIT_ACCESSORY);
if (CollectionUtils.isNotEmpty(accessoryNodes)) {
node = accessoryNodes.get(0);
}
break;
case BIDIB_TARGET_MODE_TOP:
node = nodeProvider.findNodeByAddress(Node.ROOTNODE_ADDR);
break;
default:
LOGGER.warn("Unsupported target mode detected: {}", targetMode);
break;
}
LOGGER.info("The found node: {}", node);
if (node == null) {
throw new ProtocolException("Requested node not found.");
}
return node;
}
private byte[] getTargetModeUniqueId(final TargetModeEnum targetMode, final NodeInterface node) {
byte[] targetModeUniqueId = NodeUtils.getTargetModeUniqueId(targetMode, node.getUniqueId());
return targetModeUniqueId;
}
private void handleInvalidNodeClassError(
final NodeSubscriptionData nodeSubscriptionData, final byte[] targetModeUniqueId,
final BidibGuestMessage guestMessage) throws ProtocolException {
LOGGER
.error("Handle the invalid node class error. Provided addressNodeData: {}, guestMessage: {}",
nodeSubscriptionData, guestMessage);
// byte[] targetModeUniqueId = getTargetModeUniqueId(targetMode, guestMessage);
int result = 0xFF; // no existing node
sendGuestResponse(nodeSubscriptionData, targetModeUniqueId, guestMessage.getNum(), result, null);
}
private void sendGuestResponse(
final NodeSubscriptionData nodeSubscriptionData, byte[] targetModeUniqueId, int ackSequence, int result,
byte[] details) throws ProtocolException {
// final TargetModeEnum targetMode = nodeSubscriptionData.getTargetMode();
final TargetModeEnum targetMode = TargetModeEnum.BIDIB_TARGET_MODE_UID;
LOGGER
.info("Send the GuestResponse, ackSequence: {}, targetMode: {}, targetModeUniqueId: {}", ackSequence,
targetMode, ByteUtils.bytesToHex(targetModeUniqueId));
// send the MSG_GUEST_RESP_SENT to the node
final GuestResponseSentMessage guestResponseSentMessage =
new GuestResponseSentMessage(nodeSubscriptionData.getSubscriberAddress(), 0, targetMode, targetModeUniqueId,
ByteUtils
.concat(new byte[] { ByteUtils.getLowByte(ackSequence), ByteUtils.getLowByte(result) }, details));
publishMessage(nodeSubscriptionData.getSubscriberAddress(), guestResponseSentMessage);
}
private static void sendGuestResponse(final Consumer sendGuestResponse, Integer result) {
// send the RESP_SENT
try {
sendGuestResponse.accept(result);
}
catch (Throwable ex) {
LOGGER.warn("Send guest response failed", ex);
}
}
private void handleSysGetMagicCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse) throws ProtocolException {
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, node != null ? Integer.valueOf(0x00) : Integer.valueOf(0xFF));
if (node != null) {
// final NodeSubscriptionData originNodeAddress = nodeSubscriptionData;
final BidibMessageInterface sysMagicResponse =
responseFactory.createSysMagicResponse(node.getAddr(), 0, node.getNode().getMagic());
LOGGER.info("Prepared sysMagicResponse: {}", sysMagicResponse);
publishAsGuestResponseNotifyMessage(sysMagicResponse, Arrays.asList(nodeSubscriptionData));
}
}
private void handleNodeTabGetAllCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse, final BidibNodeContainer bidibNodeContainer)
throws ProtocolException {
final NodeTabGetAllMessage nodeTabGetAllMessage = (NodeTabGetAllMessage) command;
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, node != null ? Integer.valueOf(0x00) : Integer.valueOf(0xFF));
if (node != null) {
// reset the current nodeTab index
bidibNodeContainer.setCurrentNodeTabIndex(0);
int tabCount = 1;
if (NodeUtils.hasSubNodesFunctions(node.getUniqueId())) {
// get the subnodes
final List subNodes = this.nodeProvider.findSubNodes(node);
if (CollectionUtils.isNotEmpty(subNodes)) {
subNodes.add(0, node);
// keep the node info
bidibNodeContainer.setSubNodes(subNodes);
}
else {
subNodes.add(node);
}
tabCount = subNodes.size();
}
LOGGER.info("Current tabCount: {}", tabCount);
final BidibMessageInterface sysMagicResponse =
responseFactory.createNodeTabCountResponse(node.getAddr(), 0, tabCount);
LOGGER.info("Prepared sysMagicResponse: {}", sysMagicResponse);
publishAsGuestResponseNotifyMessage(sysMagicResponse, Arrays.asList(nodeSubscriptionData));
}
}
private void handleNodeTabGetNextCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse, final BidibNodeContainer bidibNodeContainer)
throws ProtocolException {
final NodeTabGetNextMessage nodeTabGetNextMessage = (NodeTabGetNextMessage) command;
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, node != null ? Integer.valueOf(0x00) : Integer.valueOf(0xFF));
if (node != null) {
final NodeInterface subNode = bidibNodeContainer.getNextSubNode();
BidibMessageInterface nodeTabResponse = null;
if (subNode != null) {
LOGGER.info("Current subNode: {}", subNode);
// get the correct nodeTabVersion
int nodeTabVersion = subNode.getNode().getVersion();
int localAddress = NodeUtils.getLocalAddress(subNode.getAddr());
long uniqueId = subNode.getUniqueId();
nodeTabResponse =
responseFactory.createNodeTabResponse(node.getAddr(), 0, nodeTabVersion, localAddress, uniqueId);
}
else {
LOGGER.info("Next node not found.");
// we are done
bidibNodeContainer.clearSubNodes();
int localAddress = 255;
nodeTabResponse = responseFactory.createNodeNotAvailableResponse(node.getAddr(), 0, localAddress);
}
LOGGER.info("Prepared nodeTabResponse: {}", nodeTabResponse);
publishAsGuestResponseNotifyMessage(nodeTabResponse, Arrays.asList(nodeSubscriptionData));
}
}
private void handleCsSetCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse) throws ProtocolException {
final CommandStationSetStateMessage csSetStateMessage = (CommandStationSetStateMessage) command;
final CommandStationState commandStationState = csSetStateMessage.getState();
LOGGER.info("The requested CS state: {}", commandStationState);
switch (commandStationState) {
case QUERY:
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0x00));
publishCommandStationStateDirect(nodeSubscriptionData, node);
break;
case GO:
case GO_IGN_WD:
case STOP:
case SOFTSTOP:
case OFF:
// send the command station state to the node
if (node != null && node.getCommandStationNode() != null) {
final CommandStationNodeInterface commandStationNode = node.getCommandStationNode();
LOGGER
.info("The requested CS state: {}, current CS state: {}", commandStationState,
commandStationNode.getCommandStationState());
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0x00));
if (commandStationNode.getCommandStationState() == commandStationState
|| (CommandStationState.isOffState(commandStationNode.getCommandStationState())
&& commandStationState == CommandStationState.STOP)) {
LOGGER
.info(
"The command station is in the requested state already. Do not send the requested status.");
publishCommandStationStateDirect(nodeSubscriptionData, node);
}
else {
commandStationService
.setCommandStationState(connection.getConnectionId(), node.getCommandStationNode(),
CommandStationStatus.valueOf(commandStationState));
}
}
else {
LOGGER.warn("No command station node available.");
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0xFF));
}
break;
default:
break;
}
}
private void handleCsDriveCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse) throws ProtocolException {
final CommandStationDriveMessage csDriveMessage = (CommandStationDriveMessage) command;
LOGGER.info("The requested CS drive message: {}", csDriveMessage);
// send the command station state to the node
if (node != null && node.getCommandStationNode() != null) {
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0x00));
int locoAddress = csDriveMessage.getDecoderAddress().getAddress();
SpeedSteps speedSteps = SpeedSteps.valueOf(csDriveMessage.getSpeedSteps());
Integer speed = csDriveMessage.getSpeed();
int outputActive = csDriveMessage.getOutputActiveBits();
if (ByteUtils.isBitSetEqual(outputActive, 0, 0)) {
speed = null;
}
final DirectionStatus direction = DirectionStatus.valueOf(csDriveMessage.getDriveState().getDirection());
final Context context = null;
LOGGER
.info("The loco address: {}, speedSteps: {}, speed: {}, direction: {}, outputActive: {}", locoAddress,
speedSteps, speed, direction, outputActive);
// prepare the active functions
BitSet activeFunctions = new BitSet(8);
// bit 0 is the speed output
outputActive = outputActive >> 1;
for (int bit = 0; bit < 8; bit++) {
int bitVal = ByteUtils.getBit(outputActive, bit);
LOGGER.debug("Current bit: {}, bitVal: {}", bit, bitVal);
activeFunctions.set(bit, ByteUtils.getBit(outputActive, bit) == 1);
}
BitSet functions = ConversionUtils.convertFunctions(csDriveMessage.getDriveState().getFunctions());
commandStationService
.setSpeed(connection.getConnectionId(), node.getCommandStationNode(), locoAddress, speedSteps, speed,
direction, activeFunctions, functions, context);
}
else {
LOGGER.warn("No command station node available.");
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0xFF));
}
}
private void handleAccessorySetCommand(
final BidibMessageInterface command, final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node,
final Consumer sendGuestResponse) throws ProtocolException, InvalidNodeClassException {
final AccessorySetMessage accessorySetMessage = (AccessorySetMessage) command;
int accessoryNumber = accessorySetMessage.getAccessoryNumber();
int aspectNumber = accessorySetMessage.getAspect();
LOGGER.info("Set the accessory: {}, aspect: {}, node: {}", accessoryNumber, aspectNumber, node);
if (node != null && node.getSwitchingNode() != null) {
final Accessory accessory =
AccessoryListUtils.findAccessoryByAccessoryNumber(node.getAccessories(), accessoryNumber);
if (accessory != null) {
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0x00));
this.switchingNodeService
.setAccessoryAspect(connection.getConnectionId(), node.getSwitchingNode(), accessory, aspectNumber);
}
else {
// send the RESP_SENT
sendGuestResponse(sendGuestResponse, Integer.valueOf(0x83));
throw new ProtocolException("Accessory not found, accessoryNumber: " + accessoryNumber);
}
}
else {
throw new InvalidNodeClassException("Node not found or not a switching node, node: " + node);
}
}
/**
* Process the message event that is received from the interface.
*
* @param me
* the message event
*/
private void processMessageEvent(final AbstractMessageEvent me) {
switch (me.getMessageType()) {
case BidibLibrary.MSG_CS_STATE:
notifyCommandStationSubscribers((CommandStationStateMessageEvent) me);
break;
case BidibLibrary.MSG_CS_DRIVE_ACK:
notifyCommandStationSubscribers((CommandStationDriveAcknowledgeMessageEvent) me);
break;
case BidibLibrary.MSG_CS_DRIVE_MANUAL:
notifyCommandStationSubscribers((CommandStationDriveManualMessageEvent) me);
break;
case BidibLibrary.MSG_CS_DRIVE_STATE:
notifyCommandStationSubscribers((CommandStationDriveStateMessageEvent) me);
break;
case BidibLibrary.MSG_ACCESSORY_STATE:
notifyAccessorySubscribers((AccessoryStateMessageEvent) me);
break;
case BidibLibrary.MSG_BOOST_STAT:
notifyBoosterSubscribers((BoosterStateMessageEvent) me);
break;
case BidibLibrary.MSG_BOOST_DIAGNOSTIC:
notifyBoosterSubscribers((BoosterDiagMessageEvent) me);
break;
case BidibLibrary.MSG_NODE_LOST:
notifyNodeLost((NodeLostMessageEvent) me);
break;
case BidibLibrary.MSG_SYS_IDENTIFY_STATE:
notifySystemSubscribers((SysIdentifyStateMessageEvent) me);
break;
case BidibLibrary.MSG_FEATURE:
notifyFeatureAndUserSubscribers((FeatureMessageEvent) me);
break;
default:
break;
}
}
private void notifyNodeLost(NodeLostMessageEvent me) {
LOGGER.info("Node lost: {}", me.getNode());
// the node address of the lost node
byte[] nodeAddress = me.getNode().getAddr();
for (Entry> entry : this.subscriptions.entrySet()) {
// create a copy of the addresses
final List subscriptionDataList = new ArrayList<>(entry.getValue());
for (NodeSubscriptionData subscriptionData : subscriptionDataList) {
// check if the node or a subnode of the node is registered
if (ByteUtils.arrayEquals(subscriptionData.getSubscriberAddress(), nodeAddress)
|| NodeUtils.isSubNode(nodeAddress, subscriptionData.getSubscriberAddress())) {
LOGGER.info("Remove address: {}", NodeUtils.formatAddress(subscriptionData.getSubscriberAddress()));
entry.getValue().remove(subscriptionData);
}
}
}
}
private List getSubscriptionDataList(final MessageClassEnum messageClass) {
final List subscriptionDataList = subscriptions.get(messageClass);
return subscriptionDataList != null ? subscriptionDataList : Collections.emptyList();
}
private void notifyFeatureAndUserSubscribers(final FeatureMessageEvent me) {
try {
final Feature feature = me.getFeature();
byte[] nodeAddress = me.getAddress(); // the address of the node
final BidibMessageInterface csStateResponse =
responseFactory.createFeatureResponse(nodeAddress, 0, feature);
// fetch all address node data that are registered for the message class
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_FEATURE_AND_USER);
LOGGER.info("Fetched subscriptions for class FEATURE_AND_USER: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifySystemSubscribers(final SysIdentifyStateMessageEvent me) {
try {
final IdentifyState identifyState = me.getIdentifyState();
byte[] nodeAddress = me.getAddress(); // the address of the node
final BidibMessageInterface csStateResponse =
responseFactory.createSysIdentifyStateResponse(nodeAddress, 0, identifyState);
// fetch all address node data that are registered for the message class
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_SYSTEM);
LOGGER.info("Fetched subscriptions for class SYSTEM: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyCommandStationSubscribers(final CommandStationStateMessageEvent me) {
try {
final CommandStationState csState = me.getCommandStationState();
byte[] nodeAddress = me.getAddress(); // the address of the command station node
final BidibMessageInterface csStateResponse =
responseFactory.createCommandStationStateResponse(nodeAddress, 0, csState);
// fetch all address node data that are registered for the message class
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_COMMANDSTATION);
LOGGER.info("Fetched subscriptions for class COMMANDSTATION: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyCommandStationSubscribers(final CommandStationDriveAcknowledgeMessageEvent me) {
try {
byte[] nodeAddress = me.getAddress();
final DriveAcknowledge driveAckn = me.getDriveAcknowledge();
final int dccAddress = me.getDccAddress();
final Integer acknowledgedMessageNumber = me.getAcknowledgedMessageNumber();
final BidibMessageInterface csStateResponse =
responseFactory
.createCommandStationDriveAckResponse(nodeAddress, 0, new AddressData(dccAddress, null), driveAckn,
acknowledgedMessageNumber);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_COMMANDSTATION);
LOGGER.info("Fetch subscriptions for class COMMANDSTATION: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyCommandStationSubscribers(final CommandStationDriveManualMessageEvent me) {
try {
byte[] nodeAddress = me.getAddress();
final DriveState driveState = me.getDriveState();
final BidibMessageInterface csStateResponse =
responseFactory.createCommandStationDriveManualResponse(nodeAddress, 0, driveState);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_COMMANDSTATION);
LOGGER.info("Fetch subscriptions for class COMMANDSTATION: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyCommandStationSubscribers(final CommandStationDriveStateMessageEvent me) {
try {
byte[] nodeAddress = me.getAddress();
final int opCode = me.getOpCode();
final DriveState driveState = me.getDriveState();
final BidibMessageInterface csStateResponse =
responseFactory.createCommandStationDriveStateResponse(nodeAddress, 0, opCode, driveState);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_COMMANDSTATION);
LOGGER.info("Fetch subscriptions for class COMMANDSTATION: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(csStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyAccessorySubscribers(final AccessoryStateMessageEvent me) {
try {
final AccessoryState accessoryState = me.getAccessoryState();
byte[] nodeAddress = me.getAddress();
int accessoryNumber = me.getAccessoryNumber();
Integer aspect = accessoryState.getAspect();
byte[] value =
new byte[] { accessoryState.getTotal(), accessoryState.getExecute(),
ByteUtils.getLowByte(accessoryState.getWait()) };
final BidibMessageInterface accessoryStateResponse =
responseFactory.createAccessoryStateResponse(nodeAddress, 0, accessoryNumber, aspect, value);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_ACCESSORY);
LOGGER.info("Fetch subscriptions for class ACCESSORY: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(accessoryStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyBoosterSubscribers(final BoosterStateMessageEvent me) {
try {
final BoosterState boosterState = me.getBoosterState();
byte[] nodeAddress = me.getAddress();
final BidibMessageInterface boostStateResponse =
responseFactory.createBoosterStateResponse(nodeAddress, 0, boosterState);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_BOOSTER);
LOGGER.info("Fetch subscriptions for class BOOSTER: {}", subscriptionDataList);
publishAsGuestResponseNotifyMessage(boostStateResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseFyiMessage failed.", ex);
}
}
private void notifyBoosterSubscribers(final BoosterDiagMessageEvent me) {
try {
byte[] nodeAddress = me.getAddress();
int current = me.getCurrent();
int voltage = me.getVoltage();
int temperature = me.getTemperature();
final BidibMessageInterface boostDiagResponse =
responseFactory.createBoosterDiagnosticResponse(nodeAddress, 0, current, voltage, temperature);
final List subscriptionDataList =
getSubscriptionDataList(MessageClassEnum.BIDIB_MESSAGE_CLASS_BOOSTER);
publishAsGuestResponseNotifyMessage(boostDiagResponse, subscriptionDataList);
}
catch (ProtocolException ex) {
LOGGER.warn("Publish gateResponseNotifyMessage failed.", ex);
}
}
/**
* Publish the messahe as guest response notify message.
*
* @param bidibMessage
* the original bidib message
* @param nodeAddress
* the node address of the sender node
* @param subscriptionDataList
* the subscription data list
* @throws ProtocolException
* thrown if create {@code GuestResponseNotifyMessage} failed
*/
private void publishAsGuestResponseNotifyMessage(
final BidibMessageInterface bidibMessage, final List subscriptionDataList)
throws ProtocolException {
if (CollectionUtils.isNotEmpty(subscriptionDataList)) {
for (NodeSubscriptionData nodeSubscriptionData : subscriptionDataList) {
byte[] nodeAddress = nodeSubscriptionData.getSubscriberAddress();
// NodeInterface node = this.nodeProvider.findNodeByAddress(nodeAddress);
// send the MSG_GUEST_RESP_NOTIFY to the node
final GuestResponseNotifyMessage gateResponseNotifyMessage =
new GuestResponseNotifyMessage(nodeAddress, 0, TargetModeEnum.BIDIB_TARGET_MODE_UID,
NodeUtils.getTargetModeUniqueId(TargetModeEnum.BIDIB_TARGET_MODE_UID, nodeSubscriptionData.getSubscriberUniqueId()),
bidibMessage.getNum(), bidibMessage.getMessageContent());
publishMessage(nodeSubscriptionData, gateResponseNotifyMessage);
}
}
}
private void publishMessage(
final NodeSubscriptionData nodeSubscriptionData, final BidibMessageInterface bidibMessage) {
LOGGER
.info("Publish message, current subscriber address: {}, subscriber uniqueId: {}",
NodeUtils.formatAddress(nodeSubscriptionData.getSubscriberAddress()),
ByteUtils.getUniqueIdAsString(nodeSubscriptionData.getSubscriberUniqueId()));
// TODO must evaluate if the addresses are subscribed to the current node address and filter out the not
// matching addresses
// send the message to the connection
try {
final NodeInterface node = nodeProvider.findNodeByAddress(nodeSubscriptionData.getSubscriberAddress());
final GatewayConnection gatewayConnection = (GatewayConnection) this.connection;
if (node != null) {
bidibMessage.setAddr(nodeSubscriptionData.getSubscriberAddress());
gatewayConnection.publishBidibMessage(node, bidibMessage);
}
else {
LOGGER.warn("The node is not available, node address: {}", nodeSubscriptionData);
}
}
catch (Exception ex) {
LOGGER.warn("Publish bidib message to connection failed: {}", bidibMessage, ex);
}
}
/**
* Publish the message to the node with the provided nodeAddress.
*
* @param nodeAddress
* the node address
* @param bidibMessage
* the message
*/
private void publishMessage(byte[] nodeAddress, final BidibMessageInterface bidibMessage) {
LOGGER
.info("Publish message, current nodeAddress: {}, bidibMessage: {}", NodeUtils.formatAddress(nodeAddress),
bidibMessage);
// send the message to the connection
try {
final NodeInterface node = nodeProvider.findNodeByAddress(nodeAddress);
final GatewayConnection gatewayConnection = (GatewayConnection) this.connection;
gatewayConnection.publishBidibMessage(node, bidibMessage);
}
catch (Exception ex) {
LOGGER.warn("Publish bidib message to connection failed: {}", bidibMessage, ex);
}
}
/**
* Publish the command station state directly to the provided originNodeAddress
*
* @param originNodeAddress
* the origin node address
* @param node
* the addressed node
* @throws ProtocolException
*/
private void publishCommandStationStateDirect(
final NodeSubscriptionData nodeSubscriptionData, final NodeInterface node) throws ProtocolException {
CommandStationState csState = node.getCommandStationNode().getCommandStationState();
LOGGER.info("Publish the initial command station status: {}, node: {}", csState, node);
final BidibMessageInterface csStateResponse =
responseFactory.createCommandStationStateResponse(node.getAddr(), 0, csState);
publishAsGuestResponseNotifyMessage(csStateResponse, Arrays.asList(nodeSubscriptionData));
}
public Disposable subscribeSubscriberEvents(Consumer onNext, Consumer onError) {
return subjectSubscriberEvents
.subscribeOn(Schedulers.single()).observeOn(Schedulers.computation()).subscribe(onNext, onError);
}
public Map> getSubscriptions() {
return MapUtils.unmodifiableMap(subscriptions);
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy