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

org.bidib.jbidibc.usbstickbasis.adapter.UsbStickBasisResponseFactory Maven / Gradle / Ivy

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

import java.nio.charset.Charset;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.apache.commons.lang3.StringUtils;
import org.bidib.jbidibc.messages.AddressData;
import org.bidib.jbidibc.messages.BidibLibrary;
import org.bidib.jbidibc.messages.SoftwareVersion;
import org.bidib.jbidibc.messages.enums.AddressTypeEnum;
import org.bidib.jbidibc.messages.enums.CommandStationState;
import org.bidib.jbidibc.messages.enums.DynNumEnum;
import org.bidib.jbidibc.messages.enums.SpeedStepsEnum;
import org.bidib.jbidibc.messages.exception.ProtocolException;
import org.bidib.jbidibc.messages.message.BidibMessage;
import org.bidib.jbidibc.messages.message.CommandStationDriveStateResponse;
import org.bidib.jbidibc.messages.message.CommandStationStateResponse;
import org.bidib.jbidibc.messages.message.FeatureResponse;
import org.bidib.jbidibc.messages.message.FeedbackCvResponse;
import org.bidib.jbidibc.messages.message.FeedbackDynStateResponse;
import org.bidib.jbidibc.messages.message.FeedbackSpeedResponse;
import org.bidib.jbidibc.messages.message.ResponseFactory;
import org.bidib.jbidibc.messages.message.SysSwVersionResponse;
import org.bidib.jbidibc.messages.message.VendorResponse;
import org.bidib.jbidibc.messages.utils.ByteUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class UsbStickBasisResponseFactory implements ResponseFactory {

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

    private static final String PREFIX_POM_REPEAT = "POM repeat:";

    private static final String PREFIX_CAR_RESPONSE = "Car->:";

    private static final String PREFIX_CAR_REQUEST = "->Car:";

    private final UsbStickBasisResponseInterface responseInterface;

    private Pattern patternCarCvResponse;

    private Pattern patternCarBatteryResponse;

    private Pattern patternCarSpeedResponse;

    private Pattern patternBasisCv;

    private Pattern patternBasisStatus;

    private Pattern patternSpeed;

    private Pattern patternProductline;

    private Pattern patternProductname;

    private Pattern patternFirmwareVersion;

    private Pattern patternCarSpeedstep;

    private Pattern patternCarQueryCar;

    // private Pattern patternCarQuerySpeed;

    private Pattern patternPomRepeat;

    private SelectedCar selectedCar;

    public UsbStickBasisResponseFactory(final UsbStickBasisResponseInterface responseInterface) {
        this.responseInterface = responseInterface;

        this.selectedCar = responseInterface.getSelectedCarModel();

        patternCarCvResponse = Pattern.compile(REGEX_PATTERN_CAR_CV_RESPONSE);
        patternCarBatteryResponse = Pattern.compile(REGEX_PATTERN_CAR_BATTERY_RESPONSE);
        patternCarSpeedResponse = Pattern.compile(REGEX_PATTERN_CAR_SPEED_RESPONSE);

        patternBasisCv = Pattern.compile(REGEX_PATTERN_BASIS_CV);
        patternBasisStatus = Pattern.compile(REGEX_PATTERN_BASIS_STATUS);

        patternSpeed = Pattern.compile(REGEX_PATTERN_SPEED);

        patternProductline = Pattern.compile(REGEX_PATTERN_PRODUCTLINE);

        patternProductname = Pattern.compile(REGEX_PATTERN_PRODUCTNAME);
        patternFirmwareVersion = Pattern.compile(REGEX_PATTERN_FIRMWAREVERSION);

        patternCarSpeedstep = Pattern.compile(REGEX_PATTERN_CAR_SPEEDSTEP);

        patternCarQueryCar = Pattern.compile(REGEX_PATTERN_CARQUERY_CAR);
        // patternCarQuerySpeed = Pattern.compile(REGEX_PATTERN_CARQUERY_SPEED);

        patternPomRepeat = Pattern.compile(REGEX_PATTERN_POMREPEAT);
    }

    @Override
    public void initialize() {

    }

    @Override
    public BidibMessage create(byte[] message) throws ProtocolException {

        String line = new String(message, Charset.forName("UTF-8"));
        LOGGER.info("Process the line: {}", line);

        // @formatter:off

        // write Basis CV 1 = 8[CRLF]
        // Basis CV 1 = 8[CRLF]

        // rf channel: 8
        // set rf channel: 1
        // set rf channel: 8
        
        // Status: go
        // set Status: Stop
        // ->Car:3  FS: 0
        // actual car address: 3 with speedstep: 0
        // actual speedstep: 64
        // put first!    <<<<<---- no actual car

        //
        // ->Car:3  F0,F4-F1: 10000
        // ->Car:3  F0-28 off
        // ->Car:3 POM write CV1 ,3
        // ->Car:3 POM read CV1
        // 
        // Car->:3 Msg_Nr:1 CV1 = 3
        // 
        // Car->:3 Msg_Nr:2 battery=100%
        // Car->:3 Msg_Nr:3 speed=25km/h
        // Car->:3 Msg_Nr:4 on position: 120
        // Car->:3 Msg_Nr:5 CV1 = 3
        // 
        // Speed: 10 mm/s, mean speed: 9 mm/s
        
        
        // evaluate POM repeat
        // POM repeat: 1
        
        // @formatter:on

        // if (line.startsWith(PREFIX_SET_COMMAND)) {
        // LOGGER.info("Ignore the set command response: {}", line);
        // return null;
        // }

        BidibMessage bidibMessage = null;
        if (line.startsWith(PREFIX_CAR_RESPONSE)) {
            // evaluate response from car
            bidibMessage = parseCarResponse(line);
        }
        else if (line.startsWith(PREFIX_CAR_REQUEST)) {
            // evaluate response message after send request to car
            bidibMessage = parseCarRequestResponse(line);
        }
        else if (line.startsWith("actual")) {
            // the message and response of the actual addressed car
            bidibMessage = parseCarQueryResponse(line);
        }
        else if (line.startsWith("Status") || line.startsWith("set Status")) {
            // the answer of the basis status
            bidibMessage = parseBasisResponse(line);
        }
        else if (line.startsWith("Basis") || line.startsWith("write Basis")) {
            // the answer of the basis CV
            bidibMessage = parseBasisCvResponse(line);
        }
        else if (line.startsWith("Speed:")) {
            // the speed message from the Speedometer
            bidibMessage = parseSpeedResponse(line);
        }
        else if (line.startsWith("OpenCarSystem")) {
            // the speed message from the Speedometer
            bidibMessage = parseHelpResponse(line);
        }
        else if (line.startsWith(PREFIX_POM_REPEAT)) {
            // POM repeat
            bidibMessage = parsePomRepeatResponse(line);
        }
        else if (line.startsWith("OK")) {
            // the speed message from the Speedometer
            bidibMessage = parseOkResponse(line);
        }
        LOGGER.info("Created bidibMessage: {}", bidibMessage);
        return bidibMessage;
    }

    private static final String REGEX_PATTERN_CAR_CV_RESPONSE = "Car->:(\\d+) Msg_Nr\\:(\\d+) CV(\\d+) = (.+)";

    private static final String REGEX_PATTERN_CAR_BATTERY_RESPONSE = "Car->:(\\d+) Msg_Nr\\:(\\d+) battery=(\\d+)\\%";

    private static final String REGEX_PATTERN_CAR_SPEED_RESPONSE = "Car->:(\\d+) Msg_Nr\\:(\\d+) speed=(\\d+)km/h";

    private final byte[] ROOT_ADDR = new byte[] { 0 };

    protected BidibMessage parseCarResponse(String line) throws ProtocolException {

        // @formatter:off
        // Car->:3 Msg_Nr:1 CV1 = 3
        // 
        // Car->:3 Msg_Nr:2 battery=100%
        // Car->:3 Msg_Nr:3 speed=25km/h
        // Car->:3 Msg_Nr:4 on position: 120
        // Car->:3 Msg_Nr:5 CV1 = 3
        // @formatter:on
        LOGGER.info("parseCarResponse, line: {}", line);

        BidibMessage bidibMessage = null;

        Matcher m = patternCarCvResponse.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String addr = m.group(1);
            String msgNum = m.group(2);
            String cvNum = m.group(3);
            String cvValue = m.group(4);

            LOGGER.info("Current addr: {}, msgNum: {}, cvNum: {}, cvValue: {}", addr, msgNum, cvNum, cvValue);

            try {
                int cvNumVal = Integer.parseInt(cvNum) - 1;
                bidibMessage =
                    new FeedbackCvResponse(ROOT_ADDR, Integer.parseInt(msgNum), Integer.parseInt(addr), cvNumVal,
                        Integer.parseInt(cvValue));
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackCvResponse failed, response: {}", line, ex);
                throw new ProtocolException("Create FeedbackCvResponse failed, line: " + line);
            }

            return bidibMessage;
        }

        m = patternCarBatteryResponse.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String addr = m.group(1);
            String msgNum = m.group(2);
            String batteryPercentage = m.group(3);

            LOGGER.info("Current addr: {}, msgNum: {}, batteryPercentage: {}", addr, msgNum, batteryPercentage);

            try {

                AddressData addressData = new AddressData(Integer.parseInt(addr), AddressTypeEnum.LOCOMOTIVE_FORWARD);
                int detectorNum = 0;

                bidibMessage =
                    new FeedbackDynStateResponse(ROOT_ADDR, Integer.parseInt(msgNum), detectorNum, addressData,
                        DynNumEnum.DYN_STATE_NUM_CONTAINER_1.getType(),
                        ByteUtils.getLowByte(Integer.parseInt(batteryPercentage)));
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackDynStateResponse failed, response: {}", line, ex);
                throw new ProtocolException("Create FeedbackDynStateResponse failed, line: " + line);
            }

            return bidibMessage;
        }

        m = patternCarSpeedResponse.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String carAddress = m.group(1);
            String msgNum = m.group(2);
            String carSpeed = m.group(3);

            LOGGER.info("Current carAddress: {}, msgNum: {}, carSpeed: {}", carAddress, msgNum, carSpeed);

            LOGGER.info("Create speed response from car speed message: {}", carSpeed);

            try {
                bidibMessage =
                    new FeedbackSpeedResponse(ROOT_ADDR, 0, Integer.parseInt(carAddress), Integer.parseInt(carSpeed));
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackSpeedResponse failed, response: {}", line, ex);
                throw new ProtocolException("Create FeedbackSpeedResponse failed, line: " + line);
            }
            return bidibMessage;
        }

        return bidibMessage;
    }

    private static final String REGEX_PATTERN_CAR_SPEEDSTEP = "^->Car:(\\d+) FS: (\\d+)";

    protected BidibMessage parseCarRequestResponse(String line) throws ProtocolException {
        LOGGER.info("parseCarRequestResponse, line: {}", line);

        // @formatter:off

        // ->Car:3 FS: 0
        // ->Car:3 F0,F4-F1: 10000
        // ->Car:3 F0-28 off
        // ->Car:3 POM write CV1 ,3
        // ->Car:3 POM read CV1

        // @formatter:on
        BidibMessage bidibMessage = null;

        Matcher m = patternCarCvResponse.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String addr = m.group(1);
            String msgNum = m.group(2);
            String cvNum = m.group(3);
            String cvValue = m.group(4);

            LOGGER.info("Current addr: {}, msgNum: {}, cvNum: {}, cvValue: {}", addr, msgNum, cvNum, cvValue);

            try {
                int cvNumVal = Integer.parseInt(cvNum) - 1;
                bidibMessage =
                    new FeedbackCvResponse(ROOT_ADDR, Integer.parseInt(msgNum), Integer.parseInt(addr), cvNumVal,
                        Integer.parseInt(cvValue));

                return bidibMessage;
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackCvResponse failed, response: {}", line, ex);
                throw new ProtocolException("Create FeedbackCvResponse failed, line: " + line);
            }
        }
        else {
            LOGGER.trace("The provided line does not match the CarCV response pattern: {}", line);
        }

        m = patternCarSpeedstep.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String carAddress = null;
            String carSpeed = null;
            try {
                carAddress = m.group(1);
                carSpeed = m.group(2);

                LOGGER.info("Parsed car address: {}, speed: {}", carAddress, carSpeed);

                LOGGER.info("Create speed response from car speed steps message: {}", carSpeed);

                // we should not provide the speed message if the measurement is active

                if (selectedCar.getScale() == null) {
                    bidibMessage =
                        new FeedbackSpeedResponse(ROOT_ADDR, 0, Integer.parseInt(carAddress),
                            Integer.parseInt(carSpeed));

                    return bidibMessage;
                }
                else {
                    LOGGER.info("Skip provide SPEED response from car speed message because measurement is active.");
                }
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackSpeedResponse failed, CV num: {}, CV value: {}", carAddress, carSpeed, ex);
                throw new ProtocolException("Create FeedbackSpeedResponse failed, line: " + line);
            }
        }
        else {
            LOGGER.trace("The provided line does not match the car speed step pattern: {}", line);
        }

        return bidibMessage;
    }

    private static final String REGEX_PATTERN_CARQUERY_CAR = "^actual car address: (\\d+) with speedstep: (\\d+)";

    // private static final String REGEX_PATTERN_CARQUERY_SPEED = "^actual speedstep: (\\d+)";

    protected BidibMessage parseCarQueryResponse(String line) throws ProtocolException {
        LOGGER.info("parseCarQueryResponse, line: {}", line);

        // @formatter:off

        // actual car address: 3 with speedstep: 0
        // actual speedstep: 64

        // @formatter:on
        BidibMessage bidibMessage = null;

        Matcher m = patternCarQueryCar.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String carAddress = null;
            String carSpeed = null;
            try {
                carAddress = m.group(1);
                carSpeed = m.group(2);

                LOGGER.info("Parsed car address: {}, speed: {}", carAddress, carSpeed);

                org.bidib.jbidibc.messages.AddressData addressData =
                    new AddressData(Integer.parseInt(carAddress), AddressTypeEnum.LOCOMOTIVE_FORWARD);
                byte[] functions = new byte[] { 0x00, 0x00, 0x00, 0x00 };
                bidibMessage =
                    new CommandStationDriveStateResponse(ROOT_ADDR, 0, 0x41, addressData, SpeedStepsEnum.DCC128,
                        Integer.parseInt(carSpeed), functions);

            }
            catch (Exception ex) {
                LOGGER
                    .warn("Create CommandStationStateResponse failed, CV num: {}, CV value: {}", carAddress, carSpeed,
                        ex);
                throw new ProtocolException("Create CommandStationStateResponse failed, line: " + line);
            }
        }

        return bidibMessage;
    }

    private static final String REGEX_PATTERN_BASIS_CV = "^(write Basis|Basis) CV(\\d+) = (\\d+)";

    protected BidibMessage parseBasisCvResponse(String line) throws ProtocolException {
        LOGGER.info("parseBasisCvResponse, parse line: {}", line);

        // @formatter:off

        // write Basis CV 1 = 8[CRLF]
        // Basis CV 1 = 8[CRLF]

        // @formatter:on
        BidibMessage bidibMessage = null;

        Matcher m = patternBasisCv.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String basisCvNum = null;
            String basisCvValue = null;
            try {
                basisCvNum = m.group(2);
                basisCvValue = m.group(3);

                LOGGER.info("Parsed CV num: {}, CV value: {}", basisCvNum, basisCvValue);

                bidibMessage = new VendorResponse(ROOT_ADDR, 0, basisCvNum, basisCvValue);
            }
            catch (Exception ex) {
                LOGGER
                    .warn("Create CommandStationStateResponse failed, CV num: {}, CV value: {}", basisCvNum,
                        basisCvValue, ex);
                throw new ProtocolException("Create CommandStationStateResponse failed, line: " + line);
            }
        }
        else {
            LOGGER.info("No match found: {}", line);
        }

        return bidibMessage;
    }

    public static final String REGEX_PATTERN_BASIS_STATUS = "^Status\\: (\\S+)";

    public static final String REGEX_PATTERN_BASIS_SET_STATUS = "^set Status\\: (\\S+)";

    protected BidibMessage parseBasisResponse(String line) throws ProtocolException {

        // @formatter:off

        // write Basis CV 1 = 8[CRLF]
        // Basis CV 1 = 8[CRLF]
        // rf channel: 8
        // Status: Go
        // set Status: Stop

        // @formatter:on

        if (line.startsWith("set ")) {
            line = line.substring(4);
        }

        BidibMessage bidibMessage = null;

        Matcher m = patternBasisStatus.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            String basisStatus = m.group(1);

            try {
                CommandStationState state = null;
                switch (basisStatus.toLowerCase()) {
                    case "go":
                        state = CommandStationState.GO;
                        break;
                    default:
                        state = CommandStationState.STOP;
                        break;
                }
                bidibMessage = new CommandStationStateResponse(ROOT_ADDR, 0, state.getType());
            }
            catch (Exception ex) {
                LOGGER.warn("Create CommandStationStateResponse failed, basisStatus: {}", basisStatus, ex);
                throw new ProtocolException("Create CommandStationStateResponse failed, line: " + line);
            }
        }

        return bidibMessage;
    }

    private static final String REGEX_PATTERN_SPEED = "^Speed\\: (\\d+) mm/s, mean speed: (\\d+) mm/s";

    protected BidibMessage parseSpeedResponse(String line) throws ProtocolException {

        // @formatter:off

        // Speed: 10 mm/s, mean speed: 9 mm/s

        // @formatter:on

        BidibMessage bidibMessage = null;

        Matcher m = patternSpeed.matcher(line);
        if (m.matches()) {
            LOGGER.info("A matching line was found, m: {}", m);

            try {
                Integer decoderAddress = selectedCar.getDecoderAddress();

                if (decoderAddress != null) {

                    // Integer scale = selectedCar.getScale();
                    // if (scale == null) {
                    // LOGGER.warn("No scale value available, assume 1:87");
                    // scale = 87;
                    // }

                    String currentSpeed = m.group(1);
                    String meanSpeed = m.group(2);

                    // mean speed in mm/s
                    int speedValue = Integer.parseInt(/* currentSpeed */ meanSpeed);

                    // int speedVal = 0;
                    // if (scale > 1) {
                    // speedVal = (int) (((double) speedValue * scale) / 277.778);
                    // }
                    // else {
                    // speedVal = speedValue;
                    // }

                    LOGGER
                        .info(
                            "Create speed response from speed message: {} mm/s, current speed: {} mm/s, meanSpeed: {} mm/s",
                            speedValue, currentSpeed, meanSpeed);

                    bidibMessage = new FeedbackSpeedResponse(ROOT_ADDR, 0, decoderAddress, speedValue);
                }
                else {
                    LOGGER.warn("Skip return speed response because no decoder address available.");
                }
            }
            catch (Exception ex) {
                LOGGER.warn("Create FeedbackSpeedResponse failed, line: {}", line, ex);
                throw new ProtocolException("Create FeedbackSpeedResponse failed, line: " + line);
            }
        }

        return bidibMessage;
    }

    private static final String REGEX_PATTERN_PRODUCTLINE = "^OpenCarSystem (USB-Basis|Car-Speedometer) V(\\d+).+$";

    private static final String REGEX_PATTERN_PRODUCTNAME = "^OpenCarSystem (USB-Basis|Car-Speedometer)";

    private static final String REGEX_PATTERN_FIRMWAREVERSION = "V(\\d+.*)$";

    private BidibMessage parseHelpResponse(String line) throws ProtocolException {
        LOGGER.info("Received answer: {}", line);
        BidibMessage response = null;

        String productLine = null;

        Matcher m = patternProductline.matcher(line);
        if (m.matches()) {
            productLine = line;
            LOGGER.info("Found matching result: {}", productLine);
        }

        if (StringUtils.isNotBlank(productLine)) {

            // publish the product name
            if (responseInterface != null) {

                Matcher matcher = patternProductname.matcher(productLine);
                if (matcher.find()) {
                    String productName = matcher.group();
                    LOGGER.info("Found productName: {}", productName);
                    responseInterface.publishProductName(productName);
                }
            }

            // "V((?:(?:\d{1,2}\.){1,2})(?:\d{1,2}))"

            Matcher matcher = patternFirmwareVersion.matcher(productLine);
            if (matcher.find()) {
                String firmwareVersion = matcher.group(1);

                SoftwareVersion version = SoftwareVersion.parse(firmwareVersion);
                response = new SysSwVersionResponse(ROOT_ADDR, 0, version.asByteArray());
            }
        }
        else {
            LOGGER.warn("Get the productName of the attached stick failed.");
        }
        return response;
    }

    private static final String REGEX_PATTERN_POMREPEAT = "^POM repeat: (\\d+)$";

    protected BidibMessage parsePomRepeatResponse(String line) {

        // POM repeat: 1

        LOGGER.info("Parse POM repeat response: {}", line);
        BidibMessage response = null;

        if (StringUtils.isNotBlank(line)) {

            // publish the feature POM repeat
            if (responseInterface != null) {

                Matcher matcher = patternPomRepeat.matcher(line);
                if (matcher.find()) {
                    String pomRepeat = matcher.group(1);
                    LOGGER.info("Found pomRepeat: {}", pomRepeat);

                    try {
                        int featureValuePomRepeat = Integer.parseInt(pomRepeat);
                        responseInterface.publishPomRepeat(featureValuePomRepeat);

                        LOGGER.info("Set the FEATURE_GEN_POM_REPEAT: {}", featureValuePomRepeat);

                        response =
                            new FeatureResponse(ROOT_ADDR, 0, BidibLibrary.FEATURE_GEN_POM_REPEAT,
                                featureValuePomRepeat);

                    }
                    catch (Exception ex) {
                        LOGGER.warn("Set the feature POM repeat failed.", ex);
                    }

                }
            }
        }
        return response;
    }

    private BidibMessage parseOkResponse(String line) {
        LOGGER.info("Parse OK response.");

        return null;
    }

    @Override
    public BidibMessage createRaw(byte[] message) throws ProtocolException {
        return null;
    }

}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy