001package jmri.implementation;
002
003import java.util.regex.Matcher;
004import java.util.regex.Pattern;
005
006import javax.annotation.Nonnull;
007
008import jmri.*;
009import jmri.util.PhysicalLocation;
010
011import org.slf4j.Logger;
012import org.slf4j.LoggerFactory;
013
014/**
015 * Extend AbstractReporter for IdTag reporters
016 * <p>
017 * This file is based on @{link jmri.jmrix.rfid.RfidReporter}
018 *
019 * @author Matthew Harris Copyright (c) 2011
020 * @author Paul Bender Copyright (c) 2016, 2019
021 * @since 4.15.3
022 */
023public class AbstractIdTagReporter extends AbstractReporter
024        implements IdTagListener, PhysicalLocationReporter {
025
026    public AbstractIdTagReporter(String systemName) {
027        super(systemName);
028    }
029
030    public AbstractIdTagReporter(String systemName, String userName) {
031        super(systemName, userName);
032    }
033
034    /**
035     * {@inheritDoc}
036     */
037    @Override
038    public void notify(IdTag id) {
039        log.debug("Notify: {}",mSystemName);
040        if (id != null) {
041            log.debug("Tag {} notified in {}",id, this);
042            // do not update last reporter and last seen if this is an "exit" report
043            // Only happens on LocoNet transponding reports
044            var entryexit = id.getProperty("entryexit");
045            if (entryexit == null || ! entryexit.equals("exits")) {
046                Reporter r = id.getWhereLastSeen();
047                if (r != null) {
048                    log.trace("{} notifyPreviousReporter {}", id, r);
049                    notifyPreviousReporter(r,id);
050                }
051                id.setWhereLastSeen(this);
052                log.trace("{} last seen here: {}",id, this.mSystemName);
053            } else {
054                log.trace("{} skipping setWhereLastSeen on {} exits report", this, id);
055            }
056        }
057        setReport(id);
058        setState(id != null ? IdTag.SEEN : IdTag.UNSEEN);
059    }
060
061    private void notifyPreviousReporter(Reporter r, IdTag id) {
062        log.debug("Previous reporter: {}",r.getSystemName());
063        if (!(r.equals(this)) && r.getCurrentReport() == id
064           && (r instanceof IdTagListener)) {
065            log.debug("Notify previous");
066            ((IdTagListener)r).notify(null);
067        } else {
068            log.debug("Current report was: {}",r.getCurrentReport());
069        }
070    }
071
072    private int state = UNKNOWN;
073
074    /** {@inheritDoc} */
075    @Override
076    public void setState(int s) {
077        state = s;
078    }
079
080    /** {@inheritDoc} */
081    @Override
082    public int getState() {
083        return state;
084    }
085
086    /** {@inheritDoc} */
087    @Override
088    @Nonnull
089    public String describeState(int state) {
090        switch (state) {
091            case IdTag.SEEN:
092                return Bundle.getMessage("IdTagReporterStateSeen");
093            case IdTag.UNSEEN:
094                return Bundle.getMessage("IdTagReporterStateUnSeen");
095            default:
096                return super.describeState(state);
097        }
098    }
099
100    // Methods to support PhysicalLocationReporter interface
101
102    /**
103     * Get the locomotive address we're reporting about from the current report.
104     * {@inheritDoc}
105     * @param rep ignored, IdTag Reporters don't send String type reports.
106     */
107    @Override
108    public LocoAddress getLocoAddress(String rep) {
109        // For now, we assume the current report.
110        // IdTag.getTagID() is a system-name-ized version of the loco address. I think.
111        // Matcher.group(1) : loco address (I think)
112        IdTag cr = (IdTag) this.getCurrentReport();
113        ReporterManager rm = InstanceManager.getDefault(jmri.ReporterManager.class);
114        Pattern p = Pattern.compile("" + rm.getSystemPrefix() + rm.typeLetter() + "(\\d+)");
115        Matcher m = p.matcher(cr.getTagID());
116        if (m.find()) {
117            log.debug("Parsed address: {}", m.group(1));
118            // I have no idea what kind of loco address an Ecos reporter uses,
119            // so we'll default to DCC for now.
120            return (new DccLocoAddress(Integer.parseInt(m.group(1)), LocoAddress.Protocol.DCC));
121        } else {
122            return (null);
123        }
124    }
125
126    /**
127     * Gets the direction (ENTER/EXIT) of the report.
128     * <p>
129     * Because of the way
130     * IdTag Reporters work, all reports are ENTER type.
131     * {@inheritDoc}
132     */
133    @Override
134    public PhysicalLocationReporter.Direction getDirection(String rep) {
135        // TEMPORARY:  Assume we're always Entering, if asked.
136        return (PhysicalLocationReporter.Direction.ENTER);
137    }
138
139    /**
140     * Get the PhysicalLocation of the Reporter
141     *
142     * Reports its own location, for now.
143     * Not sure if that's the right thing or
144     * not. NOT DONE YET
145     *
146     * {@inheritDoc}
147     */
148    @Override
149    public PhysicalLocation getPhysicalLocation() {
150        return (this.getPhysicalLocation(null));
151    }
152
153    /**
154     * Get the PhysicalLocation of the Reporter.
155     *
156     * {@inheritDoc}
157     * @param s unused.
158     */
159    @Override
160    public PhysicalLocation getPhysicalLocation(String s) {
161        return (PhysicalLocation.getBeanPhysicalLocation(this));
162    }
163
164    private static final Logger log = LoggerFactory.getLogger(AbstractIdTagReporter.class);
165
166}