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

org.bidib.jbidibc.usbstickbasis.UsbStickBasisBidib Maven / Gradle / Ivy

There is a newer version: 2.0.28
Show newest version
package org.bidib.jbidibc.usbstickbasis;

import java.io.ByteArrayOutputStream;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import org.apache.commons.collections4.IterableUtils;
import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.core.AbstractBidib;
import org.bidib.jbidibc.core.AbstractMessageReceiver;
import org.bidib.jbidibc.core.BidibInterface;
import org.bidib.jbidibc.core.MessageListener;
import org.bidib.jbidibc.core.NodeListener;
import org.bidib.jbidibc.core.message.BidibRequestFactory;
import org.bidib.jbidibc.core.message.BidibRequestFactoryMessageMapInitializer;
import org.bidib.jbidibc.core.node.NodeRegistry;
import org.bidib.jbidibc.core.node.listener.TransferListener;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.ConnectionListener;
import org.bidib.jbidibc.messages.DriveState;
import org.bidib.jbidibc.messages.Feature;
import org.bidib.jbidibc.messages.MessageReceiver;
import org.bidib.jbidibc.messages.ProtocolVersion;
import org.bidib.jbidibc.messages.SoftwareVersion;
import org.bidib.jbidibc.messages.VendorData;
import org.bidib.jbidibc.messages.base.AbstractBaseBidib;
import org.bidib.jbidibc.messages.base.RawMessageListener;
import org.bidib.jbidibc.messages.enums.CommandStationPom;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.CsQueryTypeEnum;
import org.bidib.jbidibc.messages.exception.InvalidConfigurationException;
import org.bidib.jbidibc.messages.exception.PortNotFoundException;
import org.bidib.jbidibc.messages.exception.PortNotOpenedException;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.helpers.Context;
import org.bidib.jbidibc.messages.message.BidibMessage;
import org.bidib.jbidibc.messages.message.BidibMessageInterface;
import org.bidib.jbidibc.messages.message.BidibResponseFactory;
import org.bidib.jbidibc.messages.message.CommandStationDriveAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationDriveMessage;
import org.bidib.jbidibc.messages.message.CommandStationPomAcknowledgeResponse;
import org.bidib.jbidibc.messages.message.CommandStationPomMessage;
import org.bidib.jbidibc.messages.message.CommandStationQueryMessage;
import org.bidib.jbidibc.messages.message.CommandStationSetStateMessage;
import org.bidib.jbidibc.messages.message.FeatureCountResponse;
import org.bidib.jbidibc.messages.message.FeatureGetMessage;
import org.bidib.jbidibc.messages.message.FeatureNotAvailableResponse;
import org.bidib.jbidibc.messages.message.FeatureResponse;
import org.bidib.jbidibc.messages.message.FeatureSetMessage;
import org.bidib.jbidibc.messages.message.ResponseFactory;
import org.bidib.jbidibc.messages.message.StringGetMessage;
import org.bidib.jbidibc.messages.message.StringResponse;
import org.bidib.jbidibc.messages.message.StringSetMessage;
import org.bidib.jbidibc.messages.message.SysMagicResponse;
import org.bidib.jbidibc.messages.message.SysPVersionResponse;
import org.bidib.jbidibc.messages.message.SysSwVersionResponse;
import org.bidib.jbidibc.messages.message.SysUniqueIdResponse;
import org.bidib.jbidibc.messages.message.VendorAckResponse;
import org.bidib.jbidibc.messages.message.VendorEnableMessage;
import org.bidib.jbidibc.messages.message.VendorGetMessage;
import org.bidib.jbidibc.messages.message.VendorResponse;
import org.bidib.jbidibc.messages.message.VendorSetMessage;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.bidib.jbidibc.serial.SerialMessageEncoder;
import org.bidib.jbidibc.serial.SerialMessageReceiver;
import org.bidib.jbidibc.usbstickbasis.adapter.SelectedCar;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisAdapter;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisModel;
import org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisResponseInterface;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UsbStickBasisBidib extends AbstractBidib {

    private static final Logger LOGGER = LoggerFactory.getLogger(UsbStickBasisBidib.class);

    private static final Logger MSG_RAW_LOGGER = LoggerFactory.getLogger("RAW");

    private UsbStickBasisAdapter usbStickBasisAdapter;

    private UsbStickBasisModel usbStickBasisModel;

    private UsbStickBasisResponseInterface responseInterface;

    private boolean firstPacketSent = false;

    protected String requestedPortName;

    private final SelectedCar selectedCar = new SelectedCar();

    // flag to track the initial reset command is skipped
    private boolean initialResetSkipped;

    private long uniqueId = 0x58000d0c801234L;

    private ProtocolVersion protocolVersion = new ProtocolVersion(0, 8);

    private SoftwareVersion softwareVersion = new SoftwareVersion(1, 0, 0);

    private String[] stringValue = new String[] { "", "" };

    private int sendNum;

    private int currentFeature;

    private SortedSet features = new TreeSet<>();

    private BidibRequestFactory requestFactory;

    private Object responseMessageLock = new Object();

    private UsbStickConnector connector;

    private ByteArrayOutputStream sendBuffer = new ByteArrayOutputStream(100);

    private class UsbStickConnector extends AbstractBaseBidib {
        @Override
        protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {

            LOGGER.info("Send data stream: {}", ByteUtils.bytesToHex(data));
            try {
                sendBuffer.reset();

                // encode the message
                SerialMessageEncoder.encodeMessage(data, sendBuffer);

                byte[] bytes = sendBuffer.toByteArray();

                LOGGER.info("Send is called with bytes: {}", ByteUtils.bytesToHex(bytes));

                if (rawMessageListener != null) {
                    rawMessageListener.notifySend(bytes);
                }

                List bidibMessages = requestFactory.create(bytes);
                for (BidibMessageInterface bidibMessage : bidibMessages) {

                    synchronized (responseMessageLock) {

                        byte[] response = processRequest(bidibMessage);
                        if (response != null) {
                            publishResponse(response);
                        }
                    }
                }
            }
            catch (Exception ex) {
                LOGGER.warn("Process request failed.", ex);
            }
            finally {
                sendBuffer.reset();
            }
        }

        @Override
        protected void internalOpen(String portName, Context context)
            throws PortNotFoundException, PortNotOpenedException {

            super.internalOpen(portName, context);

            LOGGER.info("Internal open the port.");

        }

        public void doReceive(byte[] content, int length) {
            receive(content, length);
        }

        @Override
        public boolean close() {
            super.close();

            if (usbStickBasisAdapter != null) {
                LOGGER.info("Close the connection of the usbStickBasisAdapter: {}", usbStickBasisAdapter);
                try {
                    usbStickBasisAdapter.closeConnection();
                }
                catch (Exception ex) {
                    LOGGER.warn("Close connection to USB stick basis failed.", ex);
                }

                usbStickBasisAdapter = null;
                return true;
            }

            return false;
        }
    }

    /**
     * Get a new initialized instance of UsbStickBasisBidib.
     *
     * @return the instance of UsbStickBasisBidib
     */
    public static BidibInterface createInstance(final Context context) {
        LOGGER.info("Create new instance of UsbStickBasisBidib.");

        UsbStickBasisBidib instance = new UsbStickBasisBidib();
        instance.initialize(context);

        return instance;
    }

    @Override
    public void initialize(final Context context) {
        super.initialize(context);

        // prepare features
        features.add(new Feature(BidibLibrary.FEATURE_STRING_SIZE, 24));
        features.add(new Feature(BidibLibrary.FEATURE_RELEVANT_PID_BITS, 16));

        connector = new UsbStickConnector();

        final MessageReceiver messageReceiver = getMessageReceiver();
        connector.setMessageReceiver(messageReceiver);

        initializeConnector(connector);
    }

    public long getUniqueId() {
        return uniqueId;
    }

    @Override
    public void setConnectionListener(final ConnectionListener connectionListener) {

        super.setConnectionListener(connectionListener);
    }

    @Override
    public void open(
        String portName, ConnectionListener connectionListener, Set nodeListeners,
        Set messageListeners, Set transferListeners, Context context)
        throws PortNotFoundException, PortNotOpenedException {

        LOGGER.info("Open the port, portName: {}, connectionListener: {}", portName, connectionListener);

        setConnectionListener(connectionListener);

        // register the listeners
        registerListeners(nodeListeners, messageListeners, transferListeners);

        requestedPortName = portName;

        requestFactory = new BidibRequestFactory();
        requestFactory.initialize();
        // initialize the message type map
        BidibRequestFactoryMessageMapInitializer.loadMessageTypeMap(requestFactory);

        usbStickBasisModel = new UsbStickBasisModel();
        usbStickBasisModel.setBaudRate(19200);
        usbStickBasisModel.setSelectedPort(portName);

        final Object startupLock = new Object();

        responseInterface = new UsbStickBasisResponseInterface() {

            @Override
            public void addLog(String message) {
                LOGGER.info("Received log message: {}", message.trim());
            }

            @Override
            public void publishReponse(BidibMessageInterface bidibMessage) {
                LOGGER.info("Publish response: {}", bidibMessage);

                if (bidibMessage instanceof SysSwVersionResponse) {
                    SysSwVersionResponse sysSwVersionResponse = (SysSwVersionResponse) bidibMessage;
                    setSoftwareVersion(sysSwVersionResponse.getVersion().toString());
                }
                prepareMessage(bidibMessage);
            }

            @Override
            public void publishProductName(String productName) {
                LOGGER.info("Publish productName: {}", productName);

                if (StringUtils.isNotBlank(productName)) {
                    setProductName(productName);
                }

                synchronized (startupLock) {
                    startupLock.notifyAll();
                }
            }

            @Override
            public SelectedCar getSelectedCarModel() {
                return selectedCar;
            }

            @Override
            public void publishPomRepeat(int pomRepeat) {
                LOGGER.info("Publish the POM repeat: {}", pomRepeat);

                setPomRepeat(pomRepeat);

                synchronized (startupLock) {
                    startupLock.notifyAll();
                }
            }
        };
        try {
            usbStickBasisAdapter = new UsbStickBasisAdapter(responseInterface, usbStickBasisModel);
            usbStickBasisAdapter.openConnection(portName, "rxtx");

        }
        catch (PortNotFoundException | PortNotOpenedException ex) {
            LOGGER.warn("Open connection to USB stick basis failed.", ex);

            throw new PortNotOpenedException("Open connection to USB stick basis failed.", ex);
        }

        LOGGER.info("Opened the connection to the USB Stick Basis.");
        connector.startReceiverAndQueues(getMessageReceiver(), context);

        try {
            usbStickBasisAdapter.clearReceiveBuffer();

            LOGGER.info("Query the info from the usbStickBasis.");
            usbStickBasisAdapter.transmit("info");

            Thread.sleep(1000);
            // get the FEATURE_GEN_POM_REPEAT from the basis with "CP"
            usbStickBasisAdapter.transmit("CP");
        }
        catch (Exception ex) {
            LOGGER.warn("Send 'help' to USB stick basis failed.", ex);
        }

        try {
            LOGGER.info("Wait for the startupLock.");
            synchronized (startupLock) {
                startupLock.wait(2000);
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Wait for notify startup lock was interrupted.", ex);
        }
        if (getConnectionListener() != null) {
            LOGGER.info("Notify that the port was opened: {}", requestedPortName);
            getConnectionListener().opened(requestedPortName);
        }

    }

    protected void setProductName(String productName) {
        LOGGER.info("Set the product name: {}", productName);
        this.stringValue[0] = productName;
    }

    protected void setSoftwareVersion(String softwareVersion) {
        LOGGER.info("Set the software version: {}", softwareVersion);
        this.softwareVersion = SoftwareVersion.parse(softwareVersion);
    }

    protected void setPomRepeat(int pomRepeat) {
        LOGGER.info("Set the pomRepeat: {}", pomRepeat);

        try {
            LOGGER.info("Set the FEATURE_GEN_POM_REPEAT: {}", pomRepeat);
            features.add(new Feature(BidibLibrary.FEATURE_GEN_POM_REPEAT, pomRepeat));
        }
        catch (Exception ex) {
            LOGGER.warn("Set the feature POM repeat failed.", ex);
        }
    }

    @Override
    public boolean isOpened() {
        return usbStickBasisAdapter != null;
    }

    @Override
    public void close() {

        LOGGER.info("Close is called, usbStickBasisAdapter: {}", usbStickBasisAdapter);

        if (connector.close()) {
            super.close();

            // clear the requestedPortName
            requestedPortName = null;
            cleanupAfterClose(null);
        }
        LOGGER.info("Close the port finished.");

    }

    @Override
    public List getPortIdentifiers() {
        return Collections.emptyList();
    }

    @Override
    protected MessageReceiver createMessageReceiver(
        final NodeRegistry nodeRegistry, final RawMessageListener rawMessageListener, final Context context) {

        final ResponseFactory responseFactory = new BidibResponseFactory();
        responseFactory.initialize();

        AbstractMessageReceiver messageReceiver = new UsbStickBasisMessageReceiver(nodeRegistry, responseFactory, true);
        messageReceiver.setRawMessageListener(rawMessageListener);
        messageReceiver.init(context);

        LOGGER.info("Get the root node into the registry.");
        nodeRegistry.getRootNode();

        return messageReceiver;
    }

    @Override
    protected void cleanupAfterClose(final MessageReceiver messageReceiver) {
        releaseRootNode();

        firstPacketSent = false;

        InvalidConfigurationException ice = null;
        if (messageReceiver instanceof SerialMessageReceiver) {
            SerialMessageReceiver serialMessageReceiver = (SerialMessageReceiver) messageReceiver;
            serialMessageReceiver.clearMessageListeners();
            serialMessageReceiver.clearNodeListeners();

            LOGGER.info("Purge the received data in the message buffer.");

            try {
                serialMessageReceiver.purgeReceivedDataInBuffer();
            }
            catch (InvalidConfigurationException ex) {
                LOGGER.warn("Purge output stream has signaled an error.", ex);

                if ("debug-interface-active".equals(ex.getReason())) {
                    ice = ex;
                }
            }
        }
        else {
            LOGGER.warn("No message receiver to purge received data buffer available.");
        }

        if (getConnectionListener() != null) {
            LOGGER.info("Notify that the port was closed: {}", requestedPortName);
            getConnectionListener().closed(requestedPortName);
        }
        else {
            LOGGER
                .info("No connection listener available to publish the closed report for port: {}", requestedPortName);
        }

        requestedPortName = null;

        super.cleanupAfterClose(messageReceiver);

        if (ice != null) {
            LOGGER.warn("Signal the invalid configuration exception to the caller.");
            throw ice;
        }
    }

    // @Override
    protected void sendData(final ByteArrayOutputStream data, final RawMessageListener rawMessageListener) {

        if (!firstPacketSent) {
            LOGGER.info("Send initial sequence.");

            try {
                byte[] initialSequence = new byte[] { ByteUtils.MAGIC };
                final ByteArrayOutputStream firstPacketSendBuffer = new ByteArrayOutputStream(20);
                firstPacketSendBuffer.write(initialSequence);

                if (MSG_RAW_LOGGER.isInfoEnabled()) {
                    MSG_RAW_LOGGER
                        .info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(firstPacketSendBuffer));
                }
                // output.write(initialSequence);
                connector.sendData(firstPacketSendBuffer, rawMessageListener);

                Thread.sleep(10);
                if (MSG_RAW_LOGGER.isInfoEnabled()) {
                    MSG_RAW_LOGGER
                        .info(">> [{}] - {}", initialSequence.length, ByteUtils.bytesToHex(firstPacketSendBuffer));
                }
                // output.write(initialSequence);
                connector.sendData(firstPacketSendBuffer, rawMessageListener);

                firstPacketSent = true;

                LOGGER.info("Send initial sequence passed.");
            }
            catch (Exception ex) {
                LOGGER.warn("Send initial sequence failed.", ex);
            }
        }

        connector.sendData(data, rawMessageListener);
    }

    private void prepareMessage(BidibMessageInterface message) {

        synchronized (responseMessageLock) {

            message.setSendMsgNum(getNextResponseSendNum());
            publishResponse(message.getContent());

        }
    }

    protected void publishResponse(byte[] content) {

        LOGGER.info("Publish response: {}", ByteUtils.bytesToHex(content));

        connector.doReceive(content, content.length);
    }

    /////////////////////////////////////////////
    protected byte[] processRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        switch (ByteUtils.getInt(bidibMessage.getType())) {
            case BidibLibrary.MSG_SYS_GET_MAGIC:
                response = processSysGetMagicRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_CLOCK:
                processSysClockRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_GET_P_VERSION:
                response = processSysGetPVersionRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_GET_SW_VERSION:
                response = processSysGetSwVersionRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_ENABLE:
                processSysEnableRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_DISABLE:
                processSysDisableRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_GET_UNIQUE_ID:
                response = processSysGetUniqueIdRequest(bidibMessage);
                break;
            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_QUERY:
                response = processCsQueryRequest(bidibMessage);
                break;
            // case BidibLibrary.MSG_CS_BIN_STATE:
            // response = processCsBinStateRequest(bidibMessage);
            // break;
            case BidibLibrary.MSG_FEATURE_GET:
                response = processFeatureGetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_FEATURE_SET:
                response = processFeatureSetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_FEATURE_GETALL:
                response = processFeatureGetAllRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_FEATURE_GETNEXT:
                response = processFeatureGetNextRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_VENDOR_ENABLE:
                response = processVendorEnableRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_VENDOR_DISABLE:
                response = processVendorDisableRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_VENDOR_SET:
                response = processVendorSetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_VENDOR_GET:
                response = processVendorGetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_STRING_SET:
                response = processStringSetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_STRING_GET:
                response = processStringGetRequest(bidibMessage);
                break;
            case BidibLibrary.MSG_SYS_RESET:
                processResetRequest(bidibMessage);
                break;
            default:
                LOGGER.warn("Unprocessed bidibMessage: {}", bidibMessage);
                // response = super.prepareResponse(bidibMessage);
                break;
        }
        return response;
    }

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

        byte[] response = null;
        try {
            SysUniqueIdResponse sysUniqueIdResponse =
                new SysUniqueIdResponse(bidibMessage.getAddr(), getNextResponseSendNum(), getUniqueId());
            response = sysUniqueIdResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create sysUniqueId response failed.", ex);
        }
        return response;
    }

    protected byte[] processSysGetPVersionRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the SysGetPVersion request: {}, do nothing ...", bidibMessage);
        byte[] response = null;
        try {
            LOGGER.info("Current protocolVersion: {}", protocolVersion);
            SysPVersionResponse sysPVersionResponse =
                new SysPVersionResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
                    protocolVersion.getMajorVersion(), protocolVersion.getMinorVersion());
            response = sysPVersionResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create sysPVersion response failed.", ex);
        }
        return response;
    }

    protected byte[] processSysGetSwVersionRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the SysGetSwVersion request: {}", bidibMessage);
        byte[] response = null;
        try {
            LOGGER.info("Current softwareVersion: {}", softwareVersion);
            SysSwVersionResponse sysSwVersionResponse =
                new SysSwVersionResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
                    softwareVersion.asByteArray());
            response = sysSwVersionResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create sysSwVersion response failed.", ex);
        }
        return response;
    }

    protected void processSysEnableRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the SysEnable request: {}, do nothing ...", bidibMessage);
    }

    protected void processSysDisableRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the SysDisable request: {}, do nothing ...", bidibMessage);
    }

    protected void processSysIdentifyRequest(BidibMessageInterface bidibMessage) {
        // TODO Process the SysIdentify request
        LOGGER.info("Process the SysIdentify request: {}", bidibMessage);
    }

    protected byte[] processSysGetMagicRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            LOGGER.info("Reset the sendNum because the SYS_MAGIC is tranferred with 0.");
            resetSendNum();
            SysMagicResponse magicResponse =
                new SysMagicResponse(bidibMessage.getAddr(), getNextResponseSendNum(), (byte) 0xFE, (byte) 0xAF);
            response = magicResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create magic response failed.", ex);
        }
        return response;
    }

    protected void processSysClockRequest(BidibMessageInterface bidibMessage) {
        LOGGER.info("Process the SysClockMessage: {}, do nothing ...", bidibMessage);
    }

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

            String commandStationStateCommand = null;

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

            if (commandStationStateCommand != null) {
                usbStickBasisAdapter.transmit(commandStationStateCommand);
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Create CommandStationState response failed.", ex);
        }
        return response;
    }

    private static final int SLEEP_BETWEEN_FUNCTIONS = 10;

    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 speed = commandStationDriveMessage.getSpeed();

            int activeOutputBits = commandStationDriveMessage.getOutputActiveBits();

            // speed
            LOGGER
                .info("Received addressData: {}, speed: {}, activeOutputBits: {}", addressData, speed,
                    activeOutputBits);

            // make sure the car was addressed once
            Integer currentAddress = selectedCar.getDecoderAddress();

            if (currentAddress == null || addressData.getAddress() != currentAddress) {
                currentAddress = addressData.getAddress();
                LOGGER.info("Updated the decoder address: {}", currentAddress);

                selectedCar.setDecoderAddress(currentAddress);
            }
            else {
                // reset to prevent transmit
                currentAddress = null;
            }

            if ((activeOutputBits & 0x01) == 0x01) {
                String speedCommand = "A " + addressData.getAddress() + "," + speed;
                LOGGER.info("Prepared speed command: {}", speedCommand);

                usbStickBasisAdapter.transmit(speedCommand);
                Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
            }
            else if (currentAddress != null) {
                String addressCommand = "A " + currentAddress;

                usbStickBasisAdapter.transmit(addressCommand);
                Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
            }

            final Map functionMap = selectedCar.getFunctionMap();
            LOGGER.info("Current functionMap: {}", functionMap);

            if ((activeOutputBits & 0x02) == 0x02) {
                // lights and F1-F4
                int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F0_F4];
                String functionCommand = null;

                int newVal = ((functions & 0x10) >> 4);
                Integer fKey = Integer.valueOf(0);
                Integer fVal = functionMap.get(fKey);

                LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                if (fVal == null || fVal != newVal) {

                    functionCommand = "F 0," + newVal;
                    usbStickBasisAdapter.transmit(functionCommand);

                    functionMap.put(fKey, newVal);

                    Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                }

                for (int bit = 3; bit > -1; bit--) {

                    newVal = ((functions & (1 << bit)) >> bit);
                    fKey = Integer.valueOf(bit + 1);
                    fVal = functionMap.get(fKey);

                    LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                    if (fVal == null || fVal != newVal) {
                        functionCommand = "F " + fKey + "," + newVal;
                        usbStickBasisAdapter.transmit(functionCommand);

                        functionMap.put(fKey, newVal);

                        Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                    }
                }
            }

            // @formatter:off

            // 17:52:14.044 [INFO] org.bidib.jbidibc.usbstickbasis.UsbStickBasisBidib [sendQueueWorker] - Send data
            // stream: 0C 00 12 64 03 00 03 04 80 00 04 00 00
            //
            // 03 00   Address
            // 03      DATA 0
            // 04      DATA 1 --> Active output bits
            // 80
            // 00      DATA 3
            // 04      DATA 4 --> F12 - F5 0000 0100 <<-- F7 is 1
            // 00      DATA 5
            // 00      DATA 6
            
            // @formatter:on

            // send the F5 .. F28
            if ((activeOutputBits & 0x04) == 0x04) {
                // F5-F8
                int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F5_F12];

                for (int bit = 0; bit < 4; bit++) {

                    int newVal = ((functions & (1 << bit)) >> bit);
                    Integer fKey = Integer.valueOf(bit + 5);
                    Integer fVal = functionMap.get(fKey);

                    LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                    if (fVal == null || fVal != newVal) {

                        String functionCommand = "F " + (bit + 5) + "," + ((functions & (1 << bit)) >> bit);
                        usbStickBasisAdapter.transmit(functionCommand);

                        functionMap.put(fKey, newVal);

                        Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                    }
                }
            }
            if ((activeOutputBits & 0x08) == 0x08) {
                // F9-F12
                int functions = commandStationDriveMessage.getFunctionBits()[0];

                for (int bit = 4; bit < 8; bit++) {

                    int newVal = ((functions & (1 << bit)) >> bit);
                    Integer fKey = Integer.valueOf(bit + 9);
                    Integer fVal = functionMap.get(fKey);

                    LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                    if (fVal == null || fVal != newVal) {

                        String functionCommand = "F " + (bit + 9) + "," + ((functions & (1 << bit)) >> bit);
                        usbStickBasisAdapter.transmit(functionCommand);

                        functionMap.put(fKey, newVal);

                        Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                    }
                }
            }
            if ((activeOutputBits & 0x10) == 0x10) {
                // F13-F20
                int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F13_F20];

                for (int bit = 0; bit < 8; bit++) {

                    int newVal = ((functions & (1 << bit)) >> bit);
                    Integer fKey = Integer.valueOf(bit + 13);
                    Integer fVal = functionMap.get(fKey);

                    LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                    if (fVal == null || fVal != newVal) {

                        String functionCommand = "F " + (bit + 13) + "," + ((functions & (1 << bit)) >> bit);
                        usbStickBasisAdapter.transmit(functionCommand);

                        functionMap.put(fKey, newVal);

                        Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                    }
                }
            }
            if ((activeOutputBits & 0x20) == 0x20) {
                // F21-F28
                int functions = commandStationDriveMessage.getFunctionBits()[DriveState.FUNCTIONS_INDEX_F21_F28];

                for (int bit = 0; bit < 8; bit++) {

                    int newVal = ((functions & (1 << bit)) >> bit);
                    Integer fKey = Integer.valueOf(bit + 21);
                    Integer fVal = functionMap.get(fKey);

                    LOGGER.info("Current newVal: {}, fKey: {}, fVal: {}", newVal, fKey, fVal);

                    if (fVal == null || fVal != newVal) {

                        String functionCommand = "F " + (bit + 21) + "," + ((functions & (1 << bit)) >> bit);
                        usbStickBasisAdapter.transmit(functionCommand);

                        functionMap.put(fKey, newVal);

                        Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
                    }
                }
            }

            CommandStationDriveAcknowledgeResponse commandStationDriveAckResponse =
                new CommandStationDriveAcknowledgeResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
                    addressData, (byte) 1, null);
            response = commandStationDriveAckResponse.getContent();
        }
        catch (ProtocolException | InterruptedException 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:
                    String carQueryCommand = "A";
                    usbStickBasisAdapter.transmit(carQueryCommand);

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

        }
        catch (Exception ex) {
            LOGGER.warn("Process the CsQuery request failed.", ex);
        }
        return response;
    }

    protected byte[] processFeatureGetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            FeatureGetMessage featureGetMessage = (FeatureGetMessage) bidibMessage;
            int featureNum = featureGetMessage.getNumber();
            LOGGER.info("Get feature with number: {}", featureNum);

            Feature foundFeature = null;

            for (Feature feature : features) {
                if (feature.getType() == featureNum) {
                    foundFeature = feature;
                    LOGGER.info("Found feature: {}", foundFeature);
                    break;
                }
            }

            if (foundFeature != null) {
                FeatureResponse featureResponse =
                    new FeatureResponse(featureGetMessage.getAddr(), getNextResponseSendNum(), featureNum,
                        foundFeature.getValue());
                response = featureResponse.getContent();
                LOGGER.info("Prepared response: {}", ByteUtils.bytesToHex(response));
            }
            else {
                FeatureNotAvailableResponse featureResponse =
                    new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), featureNum);
                response = featureResponse.getContent();
            }
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create feature response failed.", ex);
        }
        return response;
    }

    protected byte[] processFeatureSetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            FeatureSetMessage featureSetMessage = (FeatureSetMessage) bidibMessage;
            int featureNum = featureSetMessage.getNumber();
            int featureValue = featureSetMessage.getValue();
            LOGGER.info("Set feature with number: {}, value: {}", featureNum, featureValue);

            Feature foundFeature = null;

            for (Feature feature : features) {
                if (feature.getType() == featureNum) {
                    foundFeature = updateFeatureValue(feature, featureValue);

                    LOGGER.info("Found feature: {}", foundFeature);
                    break;
                }
            }

            if (foundFeature != null) {
                FeatureResponse featureResponse =
                    new FeatureResponse(featureSetMessage.getAddr(), getNextResponseSendNum(), featureNum,
                        foundFeature.getValue());
                response = featureResponse.getContent();
                LOGGER.info("Prepared response: {}", ByteUtils.bytesToHex(response));
            }
            else {
                FeatureNotAvailableResponse featureResponse =
                    new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), featureNum);
                response = featureResponse.getContent();
            }
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create feature response failed.", ex);
        }
        return response;
    }

    protected Feature updateFeatureValue(Feature feature, int featureValue) {

        // TODO set the feature on the speedometer
        if (feature.getFeatureEnum() != null) {
            switch (feature.getFeatureEnum()) {
                case FEATURE_GEN_POM_REPEAT:
                    if (featureValue > 4) {
                        // limit the max value
                        featureValue = 4;
                    }
                    else if (featureValue < 1) {
                        // limit the min value
                        featureValue = 1;
                    }
                    LOGGER.info("Set the POM repeat value on the speedometer: {}", featureValue);
                    usbStickBasisAdapter.transmit("CP " + featureValue);
                    break;
                default:
                    break;
            }
        }

        feature.setValue(featureValue);

        return feature;
    }

    protected Feature autoAddFeature(int featureNum, int featureValue) {

        Feature foundFeature = new Feature(featureNum, featureValue);
        features.add(foundFeature);

        return foundFeature;
    }

    protected byte[] processFeatureGetAllRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            FeatureCountResponse featureResponse =
                new FeatureCountResponse(bidibMessage.getAddr(), getNextResponseSendNum(), features.size());
            response = featureResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create feature count response failed.", ex);
        }
        currentFeature = 0;

        return response;
    }

    protected byte[] processFeatureGetNextRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        if (currentFeature >= features.size()) {
            try {
                FeatureNotAvailableResponse featureResponse =
                    new FeatureNotAvailableResponse(bidibMessage.getAddr(), getNextResponseSendNum(), 255);
                response = featureResponse.getContent();
            }
            catch (ProtocolException ex) {
                LOGGER.warn("Create feature N/A response failed.", ex);
            }
        }
        else {
            try {
                Feature feature = IterableUtils.get(features, currentFeature);
                // Feature feature = features.toArray(new Feature[0])[currentFeature];
                FeatureResponse featureResponse =
                    new FeatureResponse(bidibMessage.getAddr(), getNextResponseSendNum(), feature.getType(),
                        feature.getValue());
                response = featureResponse.getContent();
            }
            catch (ProtocolException ex) {
                LOGGER.warn("Create feature response failed.", ex);
            }
            catch (Exception ex) {
                LOGGER.warn("Create feature response failed.", ex);
            }
        }
        currentFeature++;
        return response;
    }

    protected byte[] processVendorEnableRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            VendorEnableMessage vendorEnableMessage = (VendorEnableMessage) bidibMessage;
            long uniqueId = vendorEnableMessage.getUniqueId();
            LOGGER.info("Enable the user config mode for uniqueId: {}", uniqueId);
            byte userConfigModeActive = 1;
            VendorAckResponse vendorAckResponse =
                new VendorAckResponse(bidibMessage.getAddr(), getNextResponseSendNum(), userConfigModeActive);
            response = vendorAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create vendor ack response failed.", ex);
        }
        return response;
    }

    protected byte[] processVendorDisableRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            // VendorDisableMessage vendorDisableMessage = (VendorDisableMessage) bidibMessage;
            LOGGER.info("Disable the user config mode");
            byte userConfigModeActive = 0;
            VendorAckResponse vendorAckResponse =
                new VendorAckResponse(bidibMessage.getAddr(), getNextResponseSendNum(), userConfigModeActive);
            response = vendorAckResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create vendor ack response failed.", ex);
        }
        return response;
    }

    protected byte[] processVendorSetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            VendorSetMessage vendorSetMessage = (VendorSetMessage) bidibMessage;
            VendorData vendorData = vendorSetMessage.getVendorData();
            LOGGER.info("Set the vendor data: {}", vendorData);

            if ("Scale".equals(vendorData.getName())) {
                String scale = vendorData.getValue();
                LOGGER.info("Set the scale value of the selected car: {}", scale);
                Integer scaleValue = Integer.valueOf(scale);
                selectedCar.setScale(scaleValue != null && scaleValue.intValue() > 0 ? scaleValue : null);

                byte[] nodeAddress = vendorSetMessage.getAddr();
                response = prepareCvResponse(nodeAddress, vendorData.getName(), scale);
            }
            else if ("SpeedMeasurement".equals(vendorData.getName())) {
                String speedMeasurementCommand = vendorData.getValue();
                LOGGER.info("Prepared speed measurement command: {}", speedMeasurementCommand);

                usbStickBasisAdapter.transmit(speedMeasurementCommand);

                // TODO remove if the response from the speedo is changed
                byte[] nodeAddress = vendorSetMessage.getAddr();
                response = prepareCvResponse(nodeAddress, vendorData.getName(), speedMeasurementCommand);
            }
            else {
                String bcvReadCommand = "BCV " + vendorData.getName() + "," + vendorData.getValue();
                LOGGER.info("Prepared BCV set command: {}", bcvReadCommand);

                usbStickBasisAdapter.transmit(bcvReadCommand);
            }
        }
        catch (Exception ex) {
            LOGGER.warn("Process vendorSet request failed.", ex);
        }
        return response;
    }

    private byte[] prepareCvResponse(byte[] nodeAddress, String cvName, String cvValue) throws ProtocolException {

        try {
            BidibMessage bidibMessage = new VendorResponse(nodeAddress, getNextResponseSendNum(), cvName, cvValue);
            return bidibMessage.getContent();
        }
        catch (Exception ex) {
            LOGGER.warn("Create VendorResponse failed, cvName: {}, cvValue", cvName, cvValue, ex);
            throw new ProtocolException("Create VendorResponse failed, cvName: " + cvName + ", cvValue: " + cvValue);
        }
    }

    protected byte[] processVendorGetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            VendorGetMessage vendorGetMessage = (VendorGetMessage) bidibMessage;
            String vendorDataName = vendorGetMessage.getVendorDataName();
            LOGGER.info("Get the vendor data with name: {}", vendorDataName);

            String bcvReadCommand = "BCV " + vendorDataName;
            LOGGER.info("Prepared BCV read command: {}", bcvReadCommand);

            usbStickBasisAdapter.transmit(bcvReadCommand);
        }
        catch (Exception ex) {
            LOGGER.warn("Process vendorGet request failed.", ex);
        }
        return response;
    }

    protected byte[] processStringSetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            StringSetMessage stringSetMessage = (StringSetMessage) bidibMessage;
            int stringId = stringSetMessage.getStringId();
            stringValue[stringId] = stringSetMessage.getString();
            StringResponse stringResponse =
                new StringResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
                    ByteUtils.getLowByte(stringSetMessage.getNamespace()),
                    ByteUtils.getLowByte(stringSetMessage.getStringId()), stringValue[stringId]);
            response = stringResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create string response failed.", ex);
        }
        return response;
    }

    protected byte[] processStringGetRequest(BidibMessageInterface bidibMessage) {

        byte[] response = null;
        try {
            StringGetMessage stringGetMessage = (StringGetMessage) bidibMessage;
            int stringId = stringGetMessage.getStringId();

            LOGGER.info("Get STRING[{}]: {}", stringId, stringValue[stringId]);

            StringResponse stringResponse =
                new StringResponse(bidibMessage.getAddr(), getNextResponseSendNum(),
                    ByteUtils.getLowByte(stringGetMessage.getNamespace()),
                    ByteUtils.getLowByte(stringGetMessage.getStringId()), stringValue[stringId]);
            response = stringResponse.getContent();
        }
        catch (ProtocolException ex) {
            LOGGER.warn("Create string 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);

            // make sure the car was addressed once
            Integer currentAddress = selectedCar.getDecoderAddress();

            if (currentAddress == null || addressData.getAddress() != currentAddress) {
                currentAddress = addressData.getAddress();
                LOGGER.info("Updated the decoder address: {}", currentAddress);

                selectedCar.setDecoderAddress(currentAddress);

                String addressCommand = "A " + currentAddress;
                usbStickBasisAdapter.transmit(addressCommand);

                Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);
            }

            String cvCommand = null;
            int cvNumber = commandStationPomMessage.getCvNumber();
            int cvValue = 0;
            CommandStationPom opCode =
                CommandStationPom.valueOf(ByteUtils.getLowByte(commandStationPomMessage.getOpCode()));
            switch (opCode) {
                case WR_BYTE:
                    cvValue = commandStationPomMessage.getCvValue();

                    cvCommand = "CV " + cvNumber + "," + cvValue;
                    break;
                default:
                    cvCommand = "CV " + cvNumber;
                    break;
            }

            LOGGER.info("Prepared CV command: {}", cvCommand);

            usbStickBasisAdapter.transmit(cvCommand);

            Thread.sleep(SLEEP_BETWEEN_FUNCTIONS);

            CommandStationPomAcknowledgeResponse commandStationPomAckResponse =
                new CommandStationPomAcknowledgeResponse(bidibMessage.getAddr(), /* getNextResponseSendNum() */0,
                    addressData, (byte) 1);

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

        return response;
    }

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

        if (!initialResetSkipped) {
            LOGGER.warn("Skip the initial reset command as the Speedometer boots on connect.");
            initialResetSkipped = true;

            return;
        }

        resetSendNum();

        String restartCommand = "REBOOT";
        LOGGER.info("Prepared restart command: {}", restartCommand);

        usbStickBasisAdapter.transmit(restartCommand);
    }

    protected void resetSendNum() {
        LOGGER.info("Reset the sendNum to 0.");
        sendNum = 0;
    }

    protected int getNextSendNum() {
        return 0;
    }

    protected int getCurrentSendNum() {
        return sendNum;
    }

    protected int getNextResponseSendNum() {
        int nextSendNum = sendNum;
        sendNum++;
        if (sendNum > 255) {
            sendNum = 0;
        }
        return nextSendNum;
    }

    @Override
    public void send(byte[] data) {
        connector.send(data);
    }

    @Override
    protected int contactInterface() {
        // TODO Auto-generated method stub
        return 0;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy