001package jmri.jmrix.rfid.protocol.em18;
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 EM-18 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 * @author Oscar A. Pruitt Copyright (C) 2015
024 * @since 4.1.2
025 */
026public class Em18RfidProtocol extends RfidProtocol {
027
028    private static final int SPECIFICMAXSIZE = 12;
029
030    /**
031     * Constructor for EM-18 RFID Protocol. Used when a single reader is
032     * connected directly to a port, not via a concentrator.
033     */
034    public Em18RfidProtocol() {
035        super();
036    }
037
038    /**
039     * Constructor for EM-18 RFID Protocol. Supports the use of concentrators
040     * where a character range is used to determine the specific reader port.
041     *
042     * @param concentratorFirst  character representing first concentrator port
043     * @param concentratorLast   character representing last concentrator port
044     * @param portPosition       position of port character in reply string
045     */
046    public Em18RfidProtocol(char concentratorFirst, char concentratorLast, int portPosition) {
047        super(concentratorFirst, concentratorLast, portPosition);
048    }
049
050    public static final int getMaxSize() {
051        return SPECIFICMAXSIZE;
052    }
053
054    @Override
055    public String initString() {
056        // None required for EM-18
057        return "";
058    }
059
060    @Override
061    public String getTag(AbstractMRReply msg) {
062        StringBuilder sb = new StringBuilder(10);
063
064        for (int i = 0; i < 10; i++) {
065            sb.append((char) msg.getElement(i));
066        }
067
068        return sb.toString();
069    }
070
071    @Override
072    public boolean providesChecksum() {
073        return true;
074    }
075
076    @Override
077    public String getCheckSum(AbstractMRReply msg) {
078        StringBuilder sb = new StringBuilder(2);
079
080        for (int i = 10; i < 12; i++) {
081            sb.append((char) msg.getElement(i));
082        }
083
084        return sb.toString();
085    }
086
087    @Override
088    public boolean isValid(AbstractMRReply msg) {
089        return (((!isConcentrator && msg.getElement(0) != 0x02
090                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) != 0x03)
091                || (isConcentrator
092                && msg.getElement(portPosition) >= concentratorFirst
093                && msg.getElement(portPosition) <= concentratorLast
094                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) != 0x3E))
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        }
123        return false;
124    }
125
126    @Override
127    public String toMonitorString(AbstractMRReply msg) {
128        // check for valid message
129        if (isValid(msg)) {
130            StringBuilder sb = new StringBuilder();
131            sb.append("Reply from EM-18 reader.");
132            if (isConcentrator) {
133                sb.append(" Reply from port ");
134                sb.append(getReaderPort(msg));
135            }
136            sb.append(" Tag read ");
137            sb.append(getTag(msg));
138            sb.append(" checksum ");
139            sb.append(getCheckSum(msg));
140            sb.append(" valid? ");
141            sb.append(isCheckSumValid(msg) ? "yes" : "no");
142            return sb.toString();
143        } else {
144            return super.toMonitorString(msg);
145        }
146    }
147
148    private static final Logger log = LoggerFactory.getLogger(Em18RfidProtocol.class);
149
150}