All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.bidib.wizard.simulation.S88BidibBridgeSimulator Maven / Gradle / Ivy

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);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy