001package jmri.jmrix.rfid.protocol.coreid;
002
003import jmri.jmrix.AbstractMRReply;
004import jmri.jmrix.rfid.RfidProtocol;
005import org.slf4j.Logger;
006import org.slf4j.LoggerFactory;
007
008/**
009 * Common routines to extract the Tag information and validate checksum for
010 * implementations that use the CORE-ID / ID-Innovations protocol.
011 * <hr>
012 * This file is part of JMRI.
013 * <p>
014 * JMRI is free software; you can redistribute it and/or modify it under the
015 * terms of version 2 of the GNU General Public License as published by the Free
016 * Software Foundation. See the "COPYING" file for a copy of this license.
017 * <p>
018 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY
019 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
020 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
021 *
022 * @author Matthew Harris Copyright (C) 2014
023 */
024public class CoreIdRfidProtocol extends RfidProtocol {
025
026    private static final int SPECIFICMAXSIZE = 16;
027
028    /**
029     * Constructor for CORE-ID RFID Protocol. Used when a single reader is
030     * connected directly to a port, not via a concentrator.
031     */
032    public CoreIdRfidProtocol() {
033        super();
034    }
035
036    /**
037     * Constructor for CORE-ID RFID Protocol. Supports the use of concentrators
038     * where a character range is used to determine the specific reader port.
039     *
040     * @param concentratorFirst  character representing first concentrator port
041     * @param concentratorLast   character representing last concentrator port
042     * @param portPosition       position of port character in reply string
043     */
044    public CoreIdRfidProtocol(char concentratorFirst, char concentratorLast, int portPosition) {
045        super(concentratorFirst, concentratorLast, portPosition);
046    }
047
048    public static final int getMaxSize() {
049        return SPECIFICMAXSIZE;
050    }
051
052    @Override
053    public String initString() {
054        // None required for CORE-ID
055        return "";
056    }
057
058    @Override
059    public String getTag(AbstractMRReply msg) {
060        StringBuilder sb = new StringBuilder(10);
061
062        for (int i = 1; i < 11; i++) {
063            sb.append((char) msg.getElement(i));
064        }
065
066        return sb.toString();
067    }
068
069    @Override
070    public boolean providesChecksum() {
071        return true;
072    }
073
074    @Override
075    public String getCheckSum(AbstractMRReply msg) {
076        StringBuilder sb = new StringBuilder(2);
077
078        for (int i = 11; i < 13; i++) {
079            sb.append((char) msg.getElement(i));
080        }
081
082        return sb.toString();
083    }
084
085    @Override
086    public boolean isValid(AbstractMRReply msg) {
087        return (((!isConcentrator && msg.getElement(0) == 0x02
088                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x03)
089                || (isConcentrator
090                && msg.getElement(portPosition) >= concentratorFirst
091                && msg.getElement(portPosition) <= concentratorLast
092                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x3E))
093                && (msg.getElement(SPECIFICMAXSIZE - 2) & 0xFF) == 0x0A
094                && (msg.getElement(SPECIFICMAXSIZE - 3) & 0xFF) == 0x0D
095                && isCheckSumValid(msg));
096    }
097
098    public boolean isCheckSumValid(AbstractMRReply msg) {
099        byte[] tag = convertHexString(getTag(msg));
100        int checksum = 0;
101        for (int i = 0; i < 5; i++) {
102            checksum = checksum ^ tag[i];
103            log.debug("read {}", tag[i]);
104        }
105        log.debug("Checksum: {} converted: {}", getCheckSum(msg), convertHexString(getCheckSum(msg))[0]);
106        return checksum == convertHexString(getCheckSum(msg))[0];
107    }
108
109    @Override
110    public boolean endOfMessage(AbstractMRReply msg) {
111        if (msg.getNumDataElements() == SPECIFICMAXSIZE) {
112            if (((msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x03
113                    || (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x3E)
114                    && (msg.getElement(SPECIFICMAXSIZE - 2) & 0xFF) == 0x0A
115                    && (msg.getElement(SPECIFICMAXSIZE - 3) & 0xFF) == 0x0D) {
116                return true;
117            }
118            if (log.isDebugEnabled()) {
119                log.debug("Not a correctly formed message");
120            }
121            return true;
122        } else if (isConcentrator && (msg.getNumDataElements() == 1) && (msg.getElement(0) & 0xFF) == 0x3E) {
123            log.debug("Init message from Concentrator: {}", msg);
124            return true;
125        }
126        return false;
127    }
128
129    @Override
130    public String toMonitorString(AbstractMRReply msg) {
131        // check for valid message
132        if (isValid(msg)) {
133            StringBuilder sb = new StringBuilder();
134            sb.append("Reply from CORE-ID reader.");
135            if (isConcentrator) {
136                sb.append(" Reply from port ");
137                sb.append(getReaderPort(msg));
138            }
139            sb.append(" Tag read ");
140            sb.append(getTag(msg));
141            sb.append(" checksum ");
142            sb.append(getCheckSum(msg));
143            sb.append(" valid? ");
144            sb.append(isCheckSumValid(msg) ? "yes" : "no");
145            return sb.toString();
146        } else {
147            return super.toMonitorString(msg);
148        }
149    }
150
151    private static final Logger log = LoggerFactory.getLogger(CoreIdRfidProtocol.class);
152
153}