001package jmri.jmrix.rfid.protocol.seeedstudio;
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 SeeedStudio 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 SeeedStudioRfidProtocol extends RfidProtocol {
025
026    private static final int SPECIFICMAXSIZE = 14;
027
028    /**
029     * Constructor for SeeedStudio RFID Protocol. Used when a single reader is
030     * connected directly to a port, not via a concentrator.
031     */
032    public SeeedStudioRfidProtocol() {
033        super();
034    }
035
036    /**
037     * Constructor for SeeedStudio RFID Protocol. Supports the use of
038     * concentrators where a character range is used to determine the specific
039     * reader port.
040     *
041     * @param concentratorFirst  character representing first concentrator port
042     * @param concentratorLast   character representing last concentrator port
043     * @param portPosition       position of port character in reply string
044     */
045    public SeeedStudioRfidProtocol(char concentratorFirst, char concentratorLast, int portPosition) {
046        super(concentratorFirst, concentratorLast, portPosition);
047    }
048
049    public static final int getMaxSize() {
050        return SPECIFICMAXSIZE;
051    }
052
053    @Override
054    public String initString() {
055        // None required for SeeedStudio
056        return "";
057    }
058
059    @Override
060    public String getTag(AbstractMRReply msg) {
061        StringBuilder sb = new StringBuilder(10);
062
063        for (int i = 1; i < 11; i++) {
064            sb.append((char) msg.getElement(i));
065        }
066
067        return sb.toString();
068    }
069
070    @Override
071    public boolean providesChecksum() {
072        return true;
073    }
074
075    @Override
076    public String getCheckSum(AbstractMRReply msg) {
077        StringBuilder sb = new StringBuilder(2);
078
079        for (int i = 11; i < 13; i++) {
080            sb.append((char) msg.getElement(i));
081        }
082
083        return sb.toString();
084    }
085
086    @Override
087    public boolean isValid(AbstractMRReply msg) {
088        return (((!isConcentrator && msg.getElement(0) == 0x02
089                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x03)
090                || (isConcentrator
091                && msg.getElement(portPosition) >= concentratorFirst
092                && msg.getElement(portPosition) <= concentratorLast
093                && (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x3E))
094                && isCheckSumValid(msg));
095    }
096
097    public boolean isCheckSumValid(AbstractMRReply msg) {
098        byte[] tag = convertHexString(getTag(msg));
099        int checksum = 0;
100        for (int i = 0; i < 5; i++) {
101            checksum = checksum ^ tag[i];
102            log.debug("read {}", tag[i]);
103        }
104        log.debug("Checksum: {} converted: {}", getCheckSum(msg), convertHexString(getCheckSum(msg))[0]);
105        return checksum == convertHexString(getCheckSum(msg))[0];
106    }
107
108    @Override
109    public boolean endOfMessage(AbstractMRReply msg) {
110        if (msg.getNumDataElements() == SPECIFICMAXSIZE) {
111            if (((msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x03
112                    || (msg.getElement(SPECIFICMAXSIZE - 1) & 0xFF) == 0x3E)
113                    && (msg.getElement(SPECIFICMAXSIZE - 2) & 0xFF) == 0x0A
114                    && (msg.getElement(SPECIFICMAXSIZE - 3) & 0xFF) == 0x0D) {
115                return true;
116            }
117            if (log.isDebugEnabled()) {
118                log.debug("Not a correctly formed message");
119            }
120            return true;
121        }
122        return false;
123    }
124
125    @Override
126    public String toMonitorString(AbstractMRReply msg) {
127        // check for valid message
128        if (isValid(msg)) {
129            StringBuilder sb = new StringBuilder();
130            sb.append("Reply from SeeedStudio reader.");
131            if (isConcentrator) {
132                sb.append(" Reply from port ");
133                sb.append(getReaderPort(msg));
134            }
135            sb.append(" Tag read ");
136            sb.append(getTag(msg));
137            sb.append(" checksum ");
138            sb.append(getCheckSum(msg));
139            sb.append(" valid? ");
140            sb.append(isCheckSumValid(msg) ? "yes" : "no");
141            return sb.toString();
142        } else {
143            return super.toMonitorString(msg);
144        }
145    }
146
147    private static final Logger log = LoggerFactory.getLogger(SeeedStudioRfidProtocol.class);
148
149}