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

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

There is a newer version: 2.0.26
Show newest version
package org.bidib.wizard.simulation;

import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

import org.apache.commons.collections4.CollectionUtils;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.FeedbackAddressData;
import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
import org.bidib.jbidibc.messages.enums.BoosterControl;
import org.bidib.jbidibc.messages.enums.BoosterState;
import org.bidib.jbidibc.messages.enums.CommandStationProgState;
import org.bidib.jbidibc.messages.enums.CommandStationPt;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.CsQueryTypeEnum;
import org.bidib.jbidibc.messages.enums.EnrailmentDirectionEnum;
import org.bidib.jbidibc.messages.enums.FeatureEnum;
import org.bidib.jbidibc.messages.enums.SpeedStepsEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.AccessoryGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaGetMessage;
import org.bidib.jbidibc.messages.message.AccessoryParaResponse;
import org.bidib.jbidibc.messages.message.AccessoryParaSetMessage;
import org.bidib.jbidibc.messages.message.AccessorySetMessage;
import org.bidib.jbidibc.messages.message.AccessoryStateResponse;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibRequestFactory;
import org.bidib.jbidibc.messages.message.BoostDiagnosticResponse;
import org.bidib.jbidibc.messages.message.BoostOffMessage;
import org.bidib.jbidibc.messages.message.BoostOnMessage;
import org.bidib.jbidibc.messages.message.BoostStatResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationProgMessage;
import org.bidib.jbidibc.messages.message.CommandStationProgStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationQueryMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.CommandStationStateResponse;
import org.bidib.jbidibc.messages.message.FeedbackAddressResponse;
import org.bidib.jbidibc.messages.message.FeedbackSpeedResponse;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.simulation.SimulationBidibMessageProcessor;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulator;
import org.bidib.jbidibc.simulation.annotation.BidibNodeSimulators;
import org.bidib.jbidibc.simulation.nodes.DefaultNodeSimulator;
import org.bidib.wizard.model.ports.FeedbackPort;
import org.bushe.swing.event.annotation.AnnotationProcessor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@BidibNodeSimulators({ @BidibNodeSimulator(vid = "251", pid = "117"), @BidibNodeSimulator(vid = "251", pid = "118"),
    @BidibNodeSimulator(vid = "251", pid = "119") })
public class ReadyBoostSimulator extends DefaultNodeSimulator {
    private static final Logger LOGGER = LoggerFactory.getLogger(ReadyBoostSimulator.class);

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

    private BoosterState boosterState = BoosterState.OFF;

    private CommandStationState commandStationState = CommandStationState.OFF;

    private Map mapLocoCV = new LinkedHashMap();

    protected final ScheduledExecutorService boosterDiagWorker = Executors.newScheduledThreadPool(1);

    protected static final int MAX_NUM_OF_LOCO_DECODERS = 16;

    protected final ScheduledExecutorService feedbackLocoDecoderWorker = Executors.newScheduledThreadPool(1);

    public ReadyBoostSimulator(byte[] nodeAddress, long uniqueId, boolean autoAddFeature,
        SimulationBidibMessageProcessor messageReceiver, final BidibRequestFactory bidibRequestFactory) {
        super(nodeAddress, uniqueId, autoAddFeature, messageReceiver, bidibRequestFactory);
    }

    @Override
    protected void prepareFeatures() {
        LOGGER.info("Prepare the features.");
        super.prepareFeatures();

        features.add(new Feature(BidibLibrary.FEATURE_ACCESSORY_COUNT, 1));
        features.add(new Feature(BidibLibrary.FEATURE_ACCESSORY_SURVEILLED, 0));

        features.add(new Feature(BidibLibrary.FEATURE_BST_TURNOFF_TIME, 8));
        features.add(new Feature(BidibLibrary.FEATURE_BST_CURMEAS_INTERVAL, 200));

        features.add(new Feature(BidibLibrary.FEATURE_BST_AMPERE, 186));
        features.add(new Feature(BidibLibrary.FEATURE_BST_AMPERE_ADJUSTABLE, 1));
        features.add(new Feature(BidibLibrary.FEATURE_BST_INHIBIT_AUTOSTART, 0));
        features.add(new Feature(BidibLibrary.FEATURE_BST_CUTOUT_ON, 1));
        features.add(new Feature(BidibLibrary.FEATURE_BST_CUTOUT_AVAIALABLE, 1));

        features.add(new Feature(BidibLibrary.FEATURE_BST_INHIBIT_LOCAL_ONOFF, 0));
        features.add(new Feature(BidibLibrary.FEATURE_BST_TURNOFF_TIME, 30));

        features.add(new Feature(BidibLibrary.FEATURE_GEN_WATCHDOG, 20));

        // simulate the global detector
        features.add(new Feature(BidibLibrary.FEATURE_BM_CV_AVAILABLE, 2));
        features.add(new Feature(BidibLibrary.FEATURE_BM_CV_ON, 2));

        features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_AVAILABLE, 2));
        features.add(new Feature(BidibLibrary.FEATURE_BM_ADDR_DETECT_ON, 2));
    }

    @Override
    public void postConstruct() {
        super.postConstruct();
    }

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

        configurationVariables.put("81", "253");
        configurationVariables.put("82", "2");

        configurationVariables.put("83", "0");
        configurationVariables.put("84", "3");
        configurationVariables.put("86", "0");
    }

    @Override
    public String getSimulationPanelClass() {
        return SIMULATION_PANEL_CLASS;
    }

    @Override
    public void start() {
        LOGGER.info("Start the simulator for address: {}", getAddress());

        AnnotationProcessor.process(this);

        // setup loco decoder CV
        mapLocoCV.put(1, Integer.valueOf(3));
        mapLocoCV.put(29, Integer.valueOf(0));
        mapLocoCV.put(28, Integer.valueOf(3)); // set the railcom config

        super.start();
    }

    @Override
    public void stop() {
        AnnotationProcessor.unprocess(this);
        super.stop();
    }

    @Override
    protected byte[] prepareResponse(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        switch (ByteUtils.getInt(bidibMessage.getType())) {
            case BidibLibrary.MSG_ACCESSORY_SET:
                response = processAccessorySetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_ACCESSORY_GET:
                response = processAccessoryGetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_ACCESSORY_PARA_SET:
                response = processAccessoryParaSetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_ACCESSORY_PARA_GET:
                response = processAccessoryParaGetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_BOOST_QUERY:
                response = processBoostQueryRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_BOOST_ON:
                response = processBoostOnRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_BOOST_OFF:
                response = processBoostOffRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_SET_STATE:
                response = processCsSetStateRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_PROG:
                response = processCsProgRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_CS_QUERY:
                response = processCsQueryRequest(bidibMessage);
                break;
            default:
                response = super.prepareResponse(bidibMessage);
                break;
        }
        return response;
    }

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

        try {
            AccessorySetMessage accessorySetMessage = (AccessorySetMessage) bidibMessage;
            int accessoryNumber = accessorySetMessage.getAccessoryNumber();
            int aspect = accessorySetMessage.getAspect();

            if (accessoryNumber == 0) {
                if (aspect == 1) {
                    // local source control
                    boosterControl = BoosterControl.LOCAL;
                }
                else {
                    // connect source control
                    boosterControl = BoosterControl.CONNECT;
                }
                LOGGER.info("Current boosterControl: {}", boosterControl);

                // publish booster state response
                byte boosterStateValue =
                    ByteUtils
                        .getLowByte(boosterState.getType() | (boosterControl == BoosterControl.LOCAL ? 0x40 : 0x00));

                BoostStatResponse boostStatResponse =
                    new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterStateValue);

                LOGGER.info("Publish the booster state response: {}", boostStatResponse);
                sendSpontanousResponse(boostStatResponse.getContent());
            }

            byte[] value = new byte[] { 0, 0, 0 };

            AccessoryStateResponse accessoryStateResponse =
                new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
                    (byte) aspect, value);
            response = accessoryStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create AccessoryState response failed.", ex);
        }
        return response;
    }

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

        try {
            AccessoryGetMessage accessoryGetMessage = (AccessoryGetMessage) bidibMessage;
            int accessoryNumber = accessoryGetMessage.getAccessoryNumber();
            int aspect = 0;
            byte[] value = new byte[] { 2, 0, 0 };

            AccessoryStateResponse accessoryStateResponse =
                new AccessoryStateResponse(bidibMessage.getAddr(), getNextSendNum(), (byte) accessoryNumber,
                    (byte) aspect, value);
            response = accessoryStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create AccessoryState response failed.", ex);
        }
        return response;
    }

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

        try {
            AccessoryParaSetMessage accessoryParaSetMessage = (AccessoryParaSetMessage) bidibMessage;
            int accessoryNumber = accessoryParaSetMessage.getAccessoryNumber();
            int paraNumber = accessoryParaSetMessage.getParaNumber();

            byte[] value = accessoryParaSetMessage.getValue();

            AccessoryParaResponse accessoryParaResponse =
                new AccessoryParaResponse(bidibMessage.getAddr(), getNextSendNum(),
                    ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(paraNumber), value);
            response = accessoryParaResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create AccessoryPara response failed.", ex);
        }
        return response;
    }

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

        try {
            AccessoryParaGetMessage accessoryParaGetMessage = (AccessoryParaGetMessage) bidibMessage;
            int accessoryNumber = accessoryParaGetMessage.getAccessoryNumber();
            int paraNumber = accessoryParaGetMessage.getParaNumber();

            // TODO provide the correct data here ...
            byte[] value = new byte[] { 0, 0, 0, 0 };

            if (paraNumber == BidibLibrary.BIDIB_ACCESSORY_SWITCH_TIME
                || paraNumber == BidibLibrary.BIDIB_ACCESSORY_PARA_STARTUP) {

                LOGGER.info("The param is not known currently!");

                value = new byte[] { ByteUtils.getLowByte(paraNumber) };
                paraNumber = BidibLibrary.BIDIB_ACCESSORY_PARA_NOTEXIST;
            }

            AccessoryParaResponse accessoryParaResponse =
                new AccessoryParaResponse(bidibMessage.getAddr(), getNextSendNum(),
                    ByteUtils.getLowByte(accessoryNumber), ByteUtils.getLowByte(paraNumber), value);
            response = accessoryParaResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create AccessoryPara response failed.", ex);
        }
        return response;
    }

    /**
     * booster messages
     */

    protected byte[] processBoostQueryRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the BoostQuery request: {}", bidibMessage);
        byte[] response = null;
        try {
            BoostStatResponse boostStatResponse =
                new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterState);
            response = boostStatResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create BoostStatResponse response failed.", ex);
        }
        return response;
    }

    private BoosterControl boosterControl = BoosterControl.CONNECT;

    private ScheduledFuture futureTriggerBoostDiagnostic;

    private ScheduledFuture futureTriggerGlobalDetector;

    private int interval = 3000;

    private static final int LOCO_DECODER_ADDRESS_BASE = 3;

    // private int currentLocoDecoder = LOCO_DECODER_ADDRESS_BASE;

    private static final class LocoListItem {
        protected int address;

        protected int speed;

        public LocoListItem(int address, int speed) {
            this.address = address;
            this.speed = speed;
        }
    }

    private final List locoList = new LinkedList<>();

    protected byte[] processBoostOnRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the BoostOn request: {}", bidibMessage);
        byte[] response = null;
        try {
            BoostOnMessage boostOnMessage = (BoostOnMessage) bidibMessage;
            byte broadcast = boostOnMessage.getBroadcast();
            LOGGER.info("BoostOn with broadcast: {}", broadcast);

            if (broadcast == BoostOnMessage.BROADCAST_MESSAGE) {
                // TODO handle the requested broadcast
            }

            // activate the booster
            boosterState = BoosterState.ON;

            byte boosterStateValue =
                ByteUtils.getLowByte(boosterState.getType() | (boosterControl == BoosterControl.LOCAL ? 0x40 : 0x00));

            BoostStatResponse boostStatResponse =
                new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterStateValue);
            response = boostStatResponse.getContent();

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

        // use executor to send response
        if (futureTriggerBoostDiagnostic == null) {

            Feature curMeasInterval =
                features
                    .stream().filter(f -> f.getFeatureEnum().equals(FeatureEnum.FEATURE_BST_CURMEAS_INTERVAL))
                    .findFirst().orElse(null);

            LOGGER.info("Schedule the boost diagnostic trigger, curMeasInterval: {}", curMeasInterval);

            int interval = (curMeasInterval != null ? curMeasInterval.getValue() : 200) * 10 /* ms */;

            futureTriggerBoostDiagnostic = boosterDiagWorker.scheduleAtFixedRate(new Runnable() {
                @Override
                public void run() {
                    LOGGER.info("Trigger boost diag");
                    try {
                        triggerBoostDiagnosticResponse();
                    }
                    catch (Exception ex) {
                        LOGGER.warn("Trigger the boost diagnostic failed.", ex);
                    }
                    LOGGER.info("Trigger boost has finished.");
                }
            }, 500, interval, TimeUnit.MILLISECONDS);
        }

        // use executor to send response
        if (futureTriggerGlobalDetector == null) {
            LOGGER.info("Start the global detector feedback worker.");

            locoList.clear();
            for (int address = LOCO_DECODER_ADDRESS_BASE; address < LOCO_DECODER_ADDRESS_BASE
                + MAX_NUM_OF_LOCO_DECODERS; address++) {
                locoList.add(new LocoListItem(address, 0));
            }

            futureTriggerGlobalDetector = feedbackLocoDecoderWorker.scheduleWithFixedDelay(new Runnable() {
                @Override
                public void run() {
                    try {

                        // currentLocoDecoder++;
                        // if (currentLocoDecoder >= (LOCO_DECODER_ADDRESS_BASE + MAX_NUM_OF_LOCO_DECODERS)) {
                        // currentLocoDecoder = LOCO_DECODER_ADDRESS_BASE;
                        // }

                        LOGGER.info("Trigger addresses for loco decoder: {}", locoList);

                        // triggerFeedbackAddressResponse(FeedbackPort.GLOBAL_FEEDBACK_PORT_NUMBER, currentLocoDecoder);
                        triggerFeedbackAddressResponse(FeedbackPort.GLOBAL_FEEDBACK_PORT_NUMBER,
                            locoList.stream().mapToInt(i -> i.address).toArray());

                        Thread.sleep(500);

                        int delayValue = ThreadLocalRandom.current().nextInt(300, 4000);
                        LOGGER.info("Wait for next execution, delayValue: {}", delayValue);
                        Thread.sleep(delayValue);

                    }
                    catch (Exception ex) {
                        LOGGER.warn("Trigger the feedback address failed.", ex);
                    }
                    LOGGER.info("Trigger feedback address has finished.");
                }
            }, 5000, interval, TimeUnit.MILLISECONDS);
        }

        return null;
    }

    protected byte[] processBoostOffRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the BoostOff request: {}", bidibMessage);
        byte[] response = null;
        try {
            BoostOffMessage boostOffMessage = (BoostOffMessage) bidibMessage;
            byte broadcast = boostOffMessage.getBroadcast();
            LOGGER.info("BoostOn with broadcast: {}", broadcast);

            if (broadcast == BoostOnMessage.BROADCAST_MESSAGE) {
                // TODO handle the requested broadcast
            }

            // deactivate the booster
            boosterState = BoosterState.OFF;

            BoostStatResponse boostStatResponse =
                new BoostStatResponse(bidibMessage.getAddr(), getNextSendNum(), boosterState);
            response = boostStatResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create BoostStatResponse response failed.", ex);
        }

        if (futureTriggerGlobalDetector != null) {
            LOGGER.info("Stop the global detector trigger.");
            futureTriggerGlobalDetector.cancel(false);

            futureTriggerGlobalDetector = null;
        }

        if (futureTriggerBoostDiagnostic != null) {
            LOGGER.info("Stop the boost diagnostic trigger.");
            futureTriggerBoostDiagnostic.cancel(false);

            futureTriggerBoostDiagnostic = null;
        }
        return response;
    }

    protected void triggerBoostDiagnosticResponse() {
        LOGGER.info("Trigger the boostDiagnostic repsonse.");
        byte[] response = null;
        try {
            int currentValue = ThreadLocalRandom.current().nextInt(10, 180 + 1);
            int tempValue = ThreadLocalRandom.current().nextInt(25, 70);

            BoostDiagnosticResponse boostDiagnosticResponse =
                new BoostDiagnosticResponse(nodeAddress, getNextSendNum(), currentValue, 0xA9, tempValue);
            response = boostDiagnosticResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create boostDiagnostic response failed.", ex);
        }
        sendSpontanousResponse(response);
    }

    protected byte[] processCsSetStateRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsSetState request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationSetStateMessage commandStationSetStateMessage = (CommandStationSetStateMessage) bidibMessage;
            CommandStationState state = commandStationSetStateMessage.getState();
            LOGGER.info("The requested command station state is: {}", state);

            switch (state) {
                case OFF:
                    commandStationState = CommandStationState.OFF;
                    break;
                case STOP:
                case SOFTSTOP:
                    commandStationState = CommandStationState.STOP;
                    break;
                case GO:
                case GO_IGN_WD:
                    commandStationState = CommandStationState.GO;
                    break;
                case PROG:
                    commandStationState = CommandStationState.PROG;
                    break;
                case QUERY:
                    LOGGER.warn("Query command station state requested");
                    break;
                default:
                    LOGGER.warn("Unprocessed command station state: {}", state);
                    break;
            }

            LOGGER.info("Return current command station state: {}", commandStationState);
            CommandStationStateResponse commandStationStateResponse =
                new CommandStationStateResponse(bidibMessage.getAddr(), getNextSendNum(),
                    commandStationState.getType());
            response = commandStationStateResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create CommandStationState response failed.", ex);
        }
        return response;
    }

    protected byte[] processCsQueryRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the CsQuery request: {}", bidibMessage);
        byte[] response = null;
        try {
            CommandStationQueryMessage commandStationQueryMessage = (CommandStationQueryMessage) bidibMessage;
            CsQueryTypeEnum csQueryType = commandStationQueryMessage.getCsQueryType();

            switch (csQueryType) {
                case LOCO_LIST:
                    // prepare some locos

                    // TODO check the real csQuery value to check if a single loco is requested
                    org.bidib.jbidibc.messages.AddressData addressData =
                        new AddressData(13, AddressTypeEnum.LOCOMOTIVE_FORWARD);
                    byte[] functions = new byte[] { (byte) 0x80, 0x00, 0x72, (byte) 0x85 };
                    CommandStationDriveStateResponse driveStateResponse =
                        new CommandStationDriveStateResponse(bidibMessage.getAddr(), getNextSendNum(), 0x41,
                            addressData, SpeedStepsEnum.DCC128, 39, functions);

                    response = driveStateResponse.getContent();
                    break;
                default:
                    LOGGER.warn("The CsQueryRequest is not implemented for type: {}", csQueryType);
                    break;
            }

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

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

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

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

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

    private void triggerFeedbackAddressResponse(int portNum, int... locoAddresses) {
        LOGGER.info("Trigger the feedback address repsonse, portNum: {}", portNum);
        byte[] response = null;

        List addresses = new LinkedList();

        for (int addressData : locoAddresses) {
            byte lowByte = ByteUtils.getLowByte(addressData);
            byte highByte = ByteUtils.getLowByte(0x80);
            int address = ByteUtils.getWord(lowByte, (byte) (highByte & 0x3F));

            AddressData addressDataIn =
                new AddressData(address, AddressTypeEnum.valueOf((byte) ((highByte & 0xC0) >> 6)));
            addresses
                .add(new FeedbackAddressData(addressDataIn.getAddress(), EnrailmentDirectionEnum.LOCOMOTIVE_RIGHT));
        }

        try {
            int detectorNumber = portNum;
            List bidibAddresses = new ArrayList<>();

            // port is occupied -> add the addresses
            if (CollectionUtils.isNotEmpty(addresses)) {
                for (FeedbackAddressData addressData : addresses) {
                    final EnrailmentDirectionEnum enrailmentDirection = addressData.getType();
                    AddressTypeEnum addressType = null;
                    switch (enrailmentDirection) {
                        case LOCOMOTIVE_LEFT:
                        case LOCOMOTIVE_RIGHT:
                            addressType = AddressTypeEnum.LOCOMOTIVE_FORWARD;
                            break;
                        case BASIC_ACCESSORY:
                            addressType = AddressTypeEnum.ACCESSORY;
                            break;
                        case EXTENDED_ACCESSORY:
                            addressType = AddressTypeEnum.EXTENDED_ACCESSORY;
                            break;
                        default:
                            break;
                    }
                    AddressData bidibAddress = new AddressData(addressData.getAddress(), addressType);
                    bidibAddresses.add(bidibAddress);
                }
            }

            if (CollectionUtils.isNotEmpty(bidibAddresses)) {
                FeedbackAddressResponse feedbackAddressResponse =
                    new FeedbackAddressResponse(nodeAddress, getNextSendNum(), detectorNumber, bidibAddresses);

                LOGGER.info("Prepared feedbackAddressResponse: {}", feedbackAddressResponse);

                response = feedbackAddressResponse.getContent();
                sendSpontanousResponse(response);
            }

            if (CollectionUtils.isNotEmpty(bidibAddresses)) {
                for (AddressData addressData : bidibAddresses) {

                    int address = addressData.getAddress();
                    int speedValue = ThreadLocalRandom.current().nextInt(2, 100);

                    FeedbackSpeedResponse feedbackSpeedResponse =
                        new FeedbackSpeedResponse(nodeAddress, getNextSendNum(), address, speedValue);
                    sendSpontanousResponse(feedbackSpeedResponse.getContent());
                }
            }
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create FeedbackAddressResponse failed.", ex);
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy