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

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

The newest version!
package org.bidib.wizard.simulation;

import java.nio.charset.StandardCharsets;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.collections4.Predicate;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.DccATidData;
import org.bidib.jbidibc.messages.DecoderIdAddressData;
import org.bidib.jbidibc.messages.DecoderUniqueIdData;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.StringData;
import org.bidib.jbidibc.messages.TidData;
import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
import org.bidib.jbidibc.messages.enums.CommandStationPom;
import org.bidib.jbidibc.messages.enums.CommandStationProgState;
import org.bidib.jbidibc.messages.enums.CommandStationPt;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.CsQueryTypeEnum;
import org.bidib.jbidibc.messages.enums.DccAOpCodeBm;
import org.bidib.jbidibc.messages.enums.DccAdvSelectInfoOpCode;
import org.bidib.jbidibc.messages.enums.FeatureEnum;
import org.bidib.jbidibc.messages.enums.RcPlusPhase;
import org.bidib.jbidibc.messages.enums.SpeedStepsEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.CommandStationAccessoryAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationAccessoryMessage;
import org.bidib.jbidibc.messages.message.CommandStationBinaryStateMessage;
import org.bidib.jbidibc.messages.message.CommandStationDccAdvAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationDccAdvMessage;
import org.bidib.jbidibc.messages.message.CommandStationDriveAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveMessage;
import org.bidib.jbidibc.messages.message.CommandStationDriveStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomMessage;
import org.bidib.jbidibc.messages.message.CommandStationProgMessage;
import org.bidib.jbidibc.messages.message.CommandStationProgStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationQueryMessage;
import org.bidib.jbidibc.messages.message.CommandStationRcPlusAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationRcPlusMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.CommandStationStateResponse;
import org.bidib.jbidibc.messages.message.FeedbackCvResponse;
import org.bidib.jbidibc.messages.message.FeedbackDccAResponse;
import org.bidib.jbidibc.messages.message.StringResponse;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.simulation.InterfaceNode;
import org.bidib.jbidibc.simulation.SimulationBidibMessageProcessor;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulator;
import org.bidib.jbidibc.simulation.nodes.DefaultNodeSimulator;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@BidibNodeSimulator(vid = "13", pid = "132")
public class IF2Simulator extends DefaultNodeSimulator implements InterfaceNode {
    private static final Logger LOGGER = LoggerFactory.getLogger(IF2Simulator.class);

    private static final String SIMULATION_PANEL_CLASS =
        "org.bidib.wizard.simulation.client.view.panel.GBMboostMasterPanel";

    private CommandStationState commandStationState = CommandStationState.OFF;

    private Map mapLocoCV = new LinkedHashMap();

    private TidData tid = new TidData(new DecoderUniqueIdData(0x0D0C0B0A, 13), 1);

    private DccATidData dccAdvTid = new DccATidData(0x0B0A, 1);

    public IF2Simulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
        SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {

        super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);

        // da000d840064ea --> 90000d840064ea
        this.uniqueId = (uniqueId & 0x00FFFFFFFFFFFFL) | (0x90L << (12 * 4));
    }

    @Override
    protected void prepareFeatures() {
        super.prepareFeatures();

        features.add(new Feature(BidibLibrary.FEATURE_GEN_SPYMODE, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_WATCHDOG, 20));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_DRIVE_ACK, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_SWITCH_ACK, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_POM_REPEAT, 3));

        features.add(new Feature(BidibLibrary.FEATURE_GEN_DRIVE_BUS, 0));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_LOK_LOST_DETECT, 1));
        features.add(new Feature(BidibLibrary.FEATURE_GEN_NOTIFY_DRIVE_MANUAL, 1));

        features.add(new Feature(BidibLibrary.FEATURE_GEN_EXT_AVAILABLE, 13));

        // TODO enable this feature to send the debug string
        // features.add(new Feature(BidibLibrary.FEATURE_STRING_DEBUG, 1));
    }

    @Override
    protected void prepareCVs() {
        super.prepareCVs();
    }

    @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_CS_SET_STATE:
                response = processCsSetStateRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_POM:
                response = processCsPomRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_PROG:
                response = processCsProgRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_DRIVE:
                response = processCsDriveRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_ACCESSORY:
                response = processCsAccessoryRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_RCPLUS:
                response = processCsRcPlusRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_QUERY:
                response = processCsQueryRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_BIN_STATE:
                response = processCsBinStateRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_DCCA:
                response = processCsDccAdvRequest(bidibMessage);
                break;
            default:
                response = super.prepareResponse(bidibMessage);
                break;
        }
        return response;
    }

    protected byte[] processCsSetStateRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsSetState request: {}", bidibMessage);

        if (Feature.getIntFeatureValue(features, FeatureEnum.FEATURE_STRING_DEBUG.getNumber()) > 0) {

            // 25.11.2019 21:05:40.051: [receiveQueueWorker] - << MSG_STRING[[0],num=17,type=149,data=[1, 3, 16, 66,
            // 105, 68, 105, 66, 32, 68, 101, 99, 111, 100, 101, 114, 32, 45, 32]] : 16 00 11 95 01 03 10 42 69 44 69 42
            // 20 44 65 63 6F 64 65 72 20 2D 20
            // 25.11.2019 21:05:40.053: [receiveQueueWorker] - << MSG_STRING[[0],num=18,type=149,data=[1, 3, 6, 48, 46,
            // 50, 46, 48, 10]] : 0C 00 12 95 01 03 06 30 2E 32 2E 30 0A

            try {
                StringResponse stringResponse =
                    new StringResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) StringData.NAMESPACE_DEBUG,
                        (byte) 3, new byte[] { 0x10, 0x42, 0x69, 0x44, 0x69, 0x42, 0x20, 0x44, 0x65, 0x63, 0x6F, 0x64,
                            0x65, 0x72, 0x20, 0x2D, 0x20 });
                sendSpontanousResponse(stringResponse.getContent());

                StringResponse stringResponse2 =
                    new StringResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) StringData.NAMESPACE_DEBUG,
                        (byte) 3, new byte[] { 0x06, 0x30, 0x2E, 0x32, 0x2E, 0x30, 0x0A });
                sendSpontanousResponse(stringResponse2.getContent());

                // 01 04 11 53 30 36 30 37 30 30 33 46 34 43 31 32 30 33 0A 0D
                StringResponse stringResponse3 =
                    new StringResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) StringData.NAMESPACE_DEBUG,
                        (byte) 4, new byte[] { 0x11, 0x53, 0x30, 0x36, 0x30, 0x37, 0x30, 0x30, 0x33, 0x46, 0x34, 0x43,
                            0x31, 0x32, 0x30, 0x33, 0x0A, 0x0D });
                sendSpontanousResponse(stringResponse3.getContent());
            }
            catch (ProtocolException ex) {
                LOGGER.warn("Create CommandStationState response failed.", ex);
            }
        }

        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.info("Query command station state requested, return the current command station state!");
                    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[] processCsPomRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsPom request: {}", bidibMessage);
        byte[] response = null;

        // prepare the POM acknowledge
        try {
            CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
            org.bidib.jbidibc.messages.PomAddressData addressData = commandStationPomMessage.getDecoderAddress();
            LOGGER.info("Received addressData: {}", addressData);
            CommandStationPomAcknowledgeResponse commandStationPomAckResponse =
                new CommandStationPomAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1);
            response = commandStationPomAckResponse.getContent();

            LOGGER.info("Publish the running response: {}", commandStationPomAckResponse);
            publishResponse(response);
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationPomAck response failed.", ex);
        }

        // prepare the MSG_BM_CV that contains the real data
        try {
            LOGGER.info("prepare the MSG_BM_CV that contains the real data.");
            CommandStationPomMessage commandStationPomMessage = (CommandStationPomMessage) bidibMessage;
            org.bidib.jbidibc.messages.PomAddressData addressData = commandStationPomMessage.getDecoderAddress();
            int cvNumber = commandStationPomMessage.getCvNumber();

            LOGGER.info("Current CV number: {}", cvNumber);
            byte cvValue = 12;

            if (cvNumber == 29) {
                cvValue = 0;
            }

            CommandStationPom opCode =
                CommandStationPom.valueOf(ByteUtils.getLowByte(commandStationPomMessage.getOpCode()));
            switch (opCode) {
                case WR_BYTE:
                    cvValue = ByteUtils.getLowByte(commandStationPomMessage.getCvValue());
                    break;
                default:
                    break;
            }

            FeedbackCvResponse feedbackCvResponse =
                new FeedbackCvResponse(bidibMessage.getAddr(), getNextSendNum(), addressData.getAddress(), cvNumber - 1,
                    cvValue);
            response = feedbackCvResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationPomAck response failed.", ex);
        }

        return response;
    }

    protected byte[] processCsProgRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsProg request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationProgMessage commandStationProgMessage = (CommandStationProgMessage) bidibMessage;
            CommandStationPt opCode = commandStationProgMessage.getOpCode();
            int cvNumber = commandStationProgMessage.getCvNumber();
            int cvData = commandStationProgMessage.getCvData();
            LOGGER.info("Received opCode: {}, cvNumber: {}, cvData: {}", opCode, cvNumber, cvData);
            boolean sendTimeoutResponse = false;
            switch (opCode) {
                case BIDIB_CS_PROG_BREAK:
                    break;
                case BIDIB_CS_PROG_QUERY:
                    break;
                case BIDIB_CS_PROG_RDWR_BIT:
                    if ((cvData & 0x10) == 0x10) {
                        Integer storedValue = mapLocoCV.get(cvNumber);
                        // write operation
                        if (storedValue != null) {
                            byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
                            byteValue =
                                ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
                            LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
                            mapLocoCV.put(cvNumber, Integer.valueOf(byteValue));
                        }
                        else {
                            byte byteValue = 0;
                            byteValue =
                                ByteUtils.setBit(byteValue, (cvData & 0x08) == 0x08 ? true : false, cvData & 0x07);
                            LOGGER.info("Changed CV value: {}", ByteUtils.byteToHex(byteValue));
                            mapLocoCV.put(cvNumber, Integer.valueOf(byteValue));
                        }
                    }
                    else {
                        // read operation
                        Integer storedValue = mapLocoCV.get(cvNumber);
                        if (storedValue != null) {
                            byte byteValue = ByteUtils.getLowByte(storedValue.intValue());
                            boolean bitIsSetEqual =
                                ByteUtils.isBitSetEqual(byteValue, ByteUtils.getBit(cvData, 3), cvData & 0x07);
                            LOGGER.info("Verify bitIsSetEqual: {}, byteValue: {}", bitIsSetEqual, byteValue);
                            if (!bitIsSetEqual) {
                                LOGGER.warn("Send timeout response!");
                                sendTimeoutResponse = true;
                            }
                        }
                        else {
                            LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
                            sendTimeoutResponse = true;
                        }
                    }
                    break;
                case BIDIB_CS_PROG_WR_BYTE:
                    mapLocoCV.put(cvNumber, cvData);
                    break;
                default:
                    Integer storedValue = mapLocoCV.get(cvNumber);
                    if (storedValue != null) {
                        cvData = ByteUtils.getLowByte(storedValue.intValue());
                    }
                    else {
                        LOGGER.warn("The requested CV value is not stored, cvNumber: {}", cvNumber);
                        sendTimeoutResponse = true;
                    }
                    break;
            }

            CommandStationProgStateResponse commandStationProgStateResponse =
                new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                    CommandStationProgState.PROG_RUNNING, 1, cvNumber, cvData);
            response = commandStationProgStateResponse.getContent();
            LOGGER.info("Publish the running response: {}", commandStationProgStateResponse);
            publishResponse(response);

            LOGGER.info("Sleep a second.");
            // sleep some time
            try {
                Thread.sleep(1000);
            }
            catch (InterruptedException e) {
                LOGGER.warn("Sleep thread was interrupted.", e);
            }

            if (!sendTimeoutResponse) {
                LOGGER.info("Prepare the OKAY state response.");
                commandStationProgStateResponse =
                    new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                        CommandStationProgState.PROG_OKAY, 1, cvNumber, cvData);
            }
            else {
                LOGGER.info("Prepare the NO_ANSWER state response.");
                commandStationProgStateResponse =
                    new CommandStationProgStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                        CommandStationProgState.PROG_NO_ANSWER, 1, cvNumber, 0xFF);
            }
            response = commandStationProgStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationProgState response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsDriveRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsDrive request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationDriveMessage commandStationDriveMessage = (CommandStationDriveMessage) bidibMessage;
            org.bidib.jbidibc.messages.AddressData addressData = commandStationDriveMessage.getDecoderAddress();
            int messageNumber = commandStationDriveMessage.getNum();

            LOGGER.info("Received addressData: {}", addressData);
            CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
                new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1, Integer.valueOf(messageNumber));
            response = commandStationDriveAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationDriveAck response failed.", ex);
        }
        return response;
    }

    private byte[] processCsBinStateRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsBinState request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationBinaryStateMessage commandStationBinStateMessage =
                (CommandStationBinaryStateMessage) bidibMessage;
            org.bidib.jbidibc.messages.AddressData addressData = commandStationBinStateMessage.getDecoderAddress();
            int binStateNumber = commandStationBinStateMessage.getBinaryStateNumber();
            int binStateValue = commandStationBinStateMessage.getBinaryStateValue();
            int messageNumber = commandStationBinStateMessage.getNum();

            LOGGER
                .info("Received addressData: {}, binStateNumber: {}, binStateValue: {}", addressData, binStateNumber,
                    binStateValue);
            CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
                new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1, Integer.valueOf(messageNumber));
            response = commandStationDriveAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationDriveAck 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[] processCsAccessoryRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsAccessory request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationAccessoryMessage commandStationAccessoryMessage =
                (CommandStationAccessoryMessage) bidibMessage;
            org.bidib.jbidibc.messages.AddressData addressData = commandStationAccessoryMessage.getDecoderAddress();
            LOGGER.info("Received addressData: {}", addressData);
            CommandStationAccessoryAcknowledgeResponse commandStationAccessoryAckResponse =
                new CommandStationAccessoryAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), addressData,
                    (byte) 1);
            response = commandStationAccessoryAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationAccessoryAck response failed.", ex);
        }
        return response;
    }

    private static class DccAdvDecoderInfo {

        private DecoderUniqueIdData did;

        private String fullName;

        private String shortName;

        private byte[] firmwareId;

        private String productName;

        private int lastFullNameIndex;

        private int selectedNamespace;

        private int retryCounter;

        private int logonAssignCounter;

        public DccAdvDecoderInfo(long mun, int mid, String fullName, String shortName, byte[] firmwareId,
            String productName) {
            did = new DecoderUniqueIdData(mun, mid);
            this.fullName = fullName;
            this.shortName = shortName;
            this.firmwareId = firmwareId;
            this.productName = productName;
        }

        public int getLastFullNameIndex() {
            return lastFullNameIndex;
        }

        public void setLastFullNameIndex(int lastFullNameIndex) {
            this.lastFullNameIndex = lastFullNameIndex;
        }

        public void setSelectedNamespace(int selectedNamespace) {
            LOGGER.info("Set the selected namespace of the decoder: {}", selectedNamespace);
            this.selectedNamespace = selectedNamespace;
        }

        public int getSelectedNamespace() {
            return selectedNamespace;
        }

        public int getRetryCounter() {
            return retryCounter;
        }

        public void setRetryCounter(int retryCounter) {
            this.retryCounter = retryCounter;
        }

        public void incRetryCounter() {
            this.retryCounter++;
        }
    }

    private List dccAdvDecoders = new LinkedList<>();

    private DccAdvDecoderInfo activeDccAdvDecoder;

    protected byte[] processCsDccAdvRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the DccAdv request: {}", bidibMessage);
        byte[] response = null;

        // prepare the DCCAdv acknowledge
        try {
            CommandStationDccAdvMessage commandStationDccAdvMessage = (CommandStationDccAdvMessage) bidibMessage;

            CommandStationDccAdvAcknowledgeResponse acknowledgeResponse = null;

            switch (commandStationDccAdvMessage.getOpCode()) {
                case BIDIB_DCCA_GET_TID:
                    acknowledgeResponse =
                        new CommandStationDccAdvAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(),
                            dccAdvTid);
                    break;
                case BIDIB_DCCA_LOGON_ENABLE_ALL:
                    LOGGER.info("Enable all was received.");

                    if (CollectionUtils.isEmpty(dccAdvDecoders)) {
                        LOGGER.info("Prepare the dccAdvDecoders.");
                        int mid = 0x0D;
                        for (long index = 1; index < 5; index++) {
                            long mun = 0x04030200L + index;

                            DccAdvDecoderInfo decoder =
                                new DccAdvDecoderInfo(mun, mid, "BiDiB Loco Decoder ..: " + index, "Shrtna_" + index,
                                    new byte[] { 0x00, 0x00, (byte) 0xFB, 0x02, 0x00, 0x02, 0x00, 0x01 }, "TamsFDR");
                            dccAdvDecoders.add(decoder);
                        }
                    }

                    for (DccAdvDecoderInfo decoder : dccAdvDecoders) {
                        DecoderIdAddressData addressData =
                            new DecoderIdAddressData(decoder.did.getMun(), decoder.did.getMid());
                        LOGGER.info("Return demo decoder with addressData: {}", addressData);
                        int detectorAddress = 0;
                        FeedbackDccAResponse feedbackResponse =
                            new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(), detectorAddress,
                                DccAOpCodeBm.BIDIB_DCCA_LOGON_NEW_DID, addressData);

                        publishResponse(feedbackResponse.getContent());
                    }
                    break;
                case BIDIB_DCCA_LOGON_ASSIGN:
                    LOGGER.info("Address is assigned: {}", commandStationDccAdvMessage.getAssignedAddress());

                    int detectorAddress = 0;
                    int hashValue = commandStationDccAdvMessage.getAssignedAddress();
                    DecoderUniqueIdData did = commandStationDccAdvMessage.getDid();

                    // search the decoder
                    DccAdvDecoderInfo decoder = findDecoder(did);

                    decoder.logonAssignCounter++;
                    if (decoder != null && decoder.logonAssignCounter < 3 && (decoder.did.getMun() % 2 == 0)) {
                        LOGGER.warn("Ignore LogonAssign request, decoder: {}", decoder);
                        break;
                    }

                    LOGGER.info("Provide the hashValue: {}, did: {}", hashValue, did);

                    // send the assign ack
                    FeedbackDccAResponse feedbackResponse =
                        new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(), detectorAddress,
                            DccAOpCodeBm.BIDIB_DCCA_LOGON_ASSIGN_ACK, did, hashValue);

                    publishResponse(feedbackResponse.getContent());
                    break;
                case BIDIB_DCCA_SELECT_INFO:
                    LOGGER.info("GetInfo is requested.");
                    DecoderUniqueIdData didGetInfo = commandStationDccAdvMessage.getDid();
                    DccAdvSelectInfoOpCode getInfoOpCode = commandStationDccAdvMessage.getInfoOpCode();
                    LOGGER.info("Provided did: {}, getInfoOpCode: {}", didGetInfo, getInfoOpCode);

                    decoder = findDecoder(didGetInfo);
                    LOGGER.info("Found registered decoder: {}", decoder);

                    detectorAddress = 0;

                    // keep the active decoder
                    activeDccAdvDecoder = decoder;
                    activeDccAdvDecoder.setSelectedNamespace(ByteUtils.getInt(getInfoOpCode.getType()));

                    switch (getInfoOpCode) {
                        case BIDIB_DCCA_SPACE_SHORTINFO:
                            break;
                        case BIDIB_DCCA_SPACE_SHORTGUI:
                            break;
                        case BIDIB_DCCA_SPACE_FIRMWARE_ID:
                            activeDccAdvDecoder.setLastFullNameIndex(0);
                            break;
                        case BIDIB_DCCA_SPACE_PRODUCTNAME:
                            activeDccAdvDecoder.setLastFullNameIndex(0);
                            break;
                        case BIDIB_DCCA_SPACE_SHORTNAME:
                            activeDccAdvDecoder.setLastFullNameIndex(0);
                            break;
                        case BIDIB_DCCA_SPACE_FULLNAME:
                            activeDccAdvDecoder.setLastFullNameIndex(0);
                            break;
                        default:
                            LOGGER.warn("Unknown getInfoOpCode: {}", getInfoOpCode);
                            break;
                    }
                    break;
                case BIDIB_DCCA_GET_DATA:
                    LOGGER.info("GetInfo_Cont is requested.");

                    int repetitions = commandStationDccAdvMessage.getRepetitions();

                    detectorAddress = 0;
                    if (activeDccAdvDecoder != null) {
                        FeedbackDccAResponse feedbackResponseInfo = null;

                        LOGGER
                            .info("Get the data from the selected namespace: {}, repetitions: {}",
                                activeDccAdvDecoder.getSelectedNamespace(), repetitions);

                        switch (activeDccAdvDecoder.getSelectedNamespace()) {
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_SHORTINFO:
                                feedbackResponseInfo =
                                    new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(), detectorAddress,
                                        DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTINFO, activeDccAdvDecoder.did, 0 /* index */,
                                        new byte[] { 0x46, (byte) 0xA5, 0x18, 0x00, 0x00 });
                                publishResponse(feedbackResponseInfo.getContent());
                                break;
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_SHORTGUI:
                                feedbackResponseInfo =
                                    new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(), detectorAddress,
                                        DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTGUI, activeDccAdvDecoder.did, 0 /* index */,
                                        new byte[] { (byte) 0x01 /* 0x91 */, 0x00, 0x00, 0x00, 0x00 });
                                publishResponse(feedbackResponseInfo.getContent());
                                break;
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_FIRMWARE_ID:
                                int lastFirmwareIdIndex = activeDccAdvDecoder.getLastFullNameIndex();

                                // firmware id, size = 8 bytes
                                for (int idx = 0; idx < repetitions; idx++) {
                                    activeDccAdvDecoder.setLastFullNameIndex(lastFirmwareIdIndex);
                                    int startIndex = lastFirmwareIdIndex * 4;

                                    LOGGER
                                        .info("startIndex: {}", startIndex,
                                            ByteUtils.bytesToHex(activeDccAdvDecoder.firmwareId));

                                    byte[] firmwareIdPart =
                                        ByteUtils.subArray(activeDccAdvDecoder.firmwareId, startIndex, 4);

                                    LOGGER
                                        .info("Provide the firmwareIdPart: {}, lastFullNameIndex: {}, startIndex: {}",
                                            ByteUtils.bytesToHex(firmwareIdPart), lastFirmwareIdIndex, startIndex);

                                    if (idx == 0 && activeDccAdvDecoder.getRetryCounter() == 0) {
                                        LOGGER.warn("Skip send the part of the firmware id: {}", firmwareIdPart);
                                    }
                                    else {
                                        feedbackResponseInfo =
                                            new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(),
                                                detectorAddress, DccAOpCodeBm.BIDIB_DCCA_INFO_FIRMWAREID,
                                                activeDccAdvDecoder.did, lastFirmwareIdIndex, firmwareIdPart);
                                    }

                                    if (feedbackResponseInfo != null) {
                                        publishResponse(feedbackResponseInfo.getContent());
                                    }

                                    lastFirmwareIdIndex++;

                                    feedbackResponseInfo = null;
                                }

                                activeDccAdvDecoder.incRetryCounter();
                                break;
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_PRODUCTNAME:
                                int lastProductNameIndex = activeDccAdvDecoder.getLastFullNameIndex();

                                // productName, size = 8 bytes
                                for (int idx = 0; idx < repetitions; idx++) {
                                    activeDccAdvDecoder.setLastFullNameIndex(lastProductNameIndex);
                                    int startIndex = lastProductNameIndex * 4;
                                    String productNamePart =
                                        StringUtils
                                            .substring(activeDccAdvDecoder.productName, startIndex, startIndex + 4);
                                    LOGGER
                                        .info("Provide the productNamePart: {}, lastFullNameIndex: {}, startIndex: {}",
                                            productNamePart, lastProductNameIndex, startIndex);

                                    if (idx == 0 && activeDccAdvDecoder.getRetryCounter() == 1) {
                                        LOGGER.warn("Skip send the part of the productName: {}", productNamePart);
                                    }
                                    else {

                                        feedbackResponseInfo =
                                            new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(),
                                                detectorAddress, DccAOpCodeBm.BIDIB_DCCA_INFO_PRODUCTNAME,
                                                activeDccAdvDecoder.did, lastProductNameIndex,
                                                productNamePart.getBytes(StandardCharsets.ISO_8859_1));
                                    }

                                    if (feedbackResponseInfo != null) {
                                        publishResponse(feedbackResponseInfo.getContent());
                                    }
                                    lastProductNameIndex++;

                                    feedbackResponseInfo = null;

                                    activeDccAdvDecoder.incRetryCounter();
                                }
                                break;
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_SHORTNAME:
                                int lastShortNameIndex = activeDccAdvDecoder.getLastFullNameIndex();

                                // shortName, size = 8 bytes
                                for (int idx = 0; idx < repetitions; idx++) {
                                    activeDccAdvDecoder.setLastFullNameIndex(lastShortNameIndex);
                                    int startIndex = lastShortNameIndex * 4;
                                    String shortNamePart =
                                        StringUtils
                                            .substring(activeDccAdvDecoder.shortName, startIndex, startIndex + 4);
                                    LOGGER
                                        .info("Provide the shortNamePart: {}, lastFullNameIndex: {}, startIndex: {}",
                                            shortNamePart, lastShortNameIndex, startIndex);
                                    feedbackResponseInfo =
                                        new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(),
                                            detectorAddress, DccAOpCodeBm.BIDIB_DCCA_INFO_SHORTNAME,
                                            activeDccAdvDecoder.did, lastShortNameIndex,
                                            shortNamePart.getBytes(StandardCharsets.ISO_8859_1));

                                    if (feedbackResponseInfo != null) {
                                        publishResponse(feedbackResponseInfo.getContent());
                                    }
                                    lastShortNameIndex++;
                                }
                                break;
                            case BidibLibrary.BIDIB_DCCA_SELECT_INFO_FULLNAME:
                                int lastFullNameIndex = activeDccAdvDecoder.getLastFullNameIndex();

                                // fullName, size = 24 bytes
                                for (int idx = 0; idx < repetitions; idx++) {

                                    activeDccAdvDecoder.setLastFullNameIndex(lastFullNameIndex);
                                    int startIndex = lastFullNameIndex * 4;
                                    String fullNamePart =
                                        StringUtils.substring(activeDccAdvDecoder.fullName, startIndex, startIndex + 4);
                                    LOGGER
                                        .info("Provide the fullNamePart: {}, lastFullNameIndex: {}, startIndex: {}",
                                            fullNamePart, lastFullNameIndex, startIndex);
                                    feedbackResponseInfo =
                                        new FeedbackDccAResponse(bidibMessage.getAddr(), getNextSendNum(),
                                            detectorAddress, DccAOpCodeBm.BIDIB_DCCA_INFO_FULLNAME,
                                            activeDccAdvDecoder.did, lastFullNameIndex,
                                            fullNamePart.getBytes(StandardCharsets.ISO_8859_1));

                                    if (feedbackResponseInfo != null) {
                                        publishResponse(feedbackResponseInfo.getContent());
                                    }
                                    lastFullNameIndex++;
                                }
                                break;
                        }

                        activeDccAdvDecoder = null;
                    }
                    break;
                default:
                    LOGGER
                        .error("The DccAdv acknowledge is not yet implemented for OpCode: {}",
                            commandStationDccAdvMessage.getOpCode());
                    break;
            }

            if (acknowledgeResponse != null) {
                response = acknowledgeResponse.getContent();
            }
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationDccAdvAck response failed.", ex);
        }
        return response;
    }

    private DccAdvDecoderInfo findDecoder(final DecoderUniqueIdData did) {

        DccAdvDecoderInfo decoder = IterableUtils.find(dccAdvDecoders, new Predicate() {

            @Override
            public boolean evaluate(DccAdvDecoderInfo decoder) {
                if (Objects.equals(decoder.did, did)) {
                    return true;
                }
                return false;
            }
        });

        return decoder;
    }

    protected byte[] processCsRcPlusRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsRcPlus request: {}", bidibMessage);
        byte[] response = null;

        // prepare the RCPLUS acknowledge
        try {
            CommandStationRcPlusMessage commandStationRcPlusMessage = (CommandStationRcPlusMessage) bidibMessage;

            CommandStationRcPlusAcknowledgeResponse acknowledgeResponse = null;
            switch (commandStationRcPlusMessage.getOpCode()) {
                case RC_GET_TID:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), tid);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_SET_TID:
                    TidData newTid = commandStationRcPlusMessage.getTid();
                    LOGGER.info("Set the new TID: {}", newTid);
                    tid = newTid;

                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(), tid);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_PING_ONCE_P0:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(),
                            RcPlusPhase.P0, 0x00);

                    response = acknowledgeResponse.getContent();
                    break;
                case RC_PING_ONCE_P1:
                    acknowledgeResponse =
                        new CommandStationRcPlusAcknowledgeResponse(bidibMessage.getAddr(), getNextSendNum(),
                            RcPlusPhase.P1, 0x00);

                    response = acknowledgeResponse.getContent();
                    break;
                default:
                    LOGGER
                        .error("The RailCom+ acknowledge is not yet implemented for OpCode: {}",
                            commandStationRcPlusMessage.getOpCode());
                    break;
            }

        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationRcPlusAck response failed.", ex);
        }
        return response;
    }

    @Override
    protected void processResetRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the reset request, bidibMessage: {}", bidibMessage);

        resetSendNum();

        // notify the master that we're gone
        // EventBus.publish(new NodeLostEvent(nodeAddress[nodeAddress.length - 1]));
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy