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: {}",id);
042            Reporter r = id.getWhereLastSeen();
043            if (r != null) {
044                notifyPreviousReporter(r,id);
045            }
046            id.setWhereLastSeen(this);
047            log.debug("Seen here: {}",this.mSystemName);
048        }
049        setReport(id);
050        setState(id != null ? IdTag.SEEN : IdTag.UNSEEN);
051    }
052    
053    private void notifyPreviousReporter(Reporter r, IdTag id) {
054        log.debug("Previous reporter: {}",r.getSystemName());
055        if (!(r.equals(this)) && r.getCurrentReport() == id
056           && (r instanceof IdTagListener)) {
057            log.debug("Notify previous");
058            ((IdTagListener)r).notify(null);
059        } else {
060            log.debug("Current report was: {}",r.getCurrentReport());
061        }
062    }
063
064    private int state = UNKNOWN;
065
066    /** {@inheritDoc} */
067    @Override
068    public void setState(int s) {
069        state = s;
070    }
071
072    /** {@inheritDoc} */
073    @Override
074    public int getState() {
075        return state;
076    }
077    
078    /** {@inheritDoc} */
079    @Override
080    @Nonnull
081    public String describeState(int state) {
082        switch (state) {
083            case IdTag.SEEN:
084                return Bundle.getMessage("IdTagReporterStateSeen");
085            case IdTag.UNSEEN:
086                return Bundle.getMessage("IdTagReporterStateUnSeen");
087            default:
088                return super.describeState(state);
089        }
090    }
091
092    // Methods to support PhysicalLocationReporter interface
093    
094    /**
095     * Get the locomotive address we're reporting about from the current report.
096     * {@inheritDoc}
097     * @param rep ignored, IdTag Reporters don't send String type reports.
098     */
099    @Override
100    public LocoAddress getLocoAddress(String rep) {
101        // For now, we assume the current report.
102        // IdTag.getTagID() is a system-name-ized version of the loco address. I think.
103        // Matcher.group(1) : loco address (I think)
104        IdTag cr = (IdTag) this.getCurrentReport();
105        ReporterManager rm = InstanceManager.getDefault(jmri.ReporterManager.class);
106        Pattern p = Pattern.compile("" + rm.getSystemPrefix() + rm.typeLetter() + "(\\d+)");
107        Matcher m = p.matcher(cr.getTagID());
108        if (m.find()) {
109            log.debug("Parsed address: {}", m.group(1));
110            // I have no idea what kind of loco address an Ecos reporter uses,
111            // so we'll default to DCC for now.
112            return (new DccLocoAddress(Integer.parseInt(m.group(1)), LocoAddress.Protocol.DCC));
113        } else {
114            return (null);
115        }
116    }
117
118    /**
119     * Gets the direction (ENTER/EXIT) of the report. 
120     * <p>
121     * Because of the way 
122     * IdTag Reporters work, all reports are ENTER type.
123     * {@inheritDoc}
124     */
125    @Override
126    public PhysicalLocationReporter.Direction getDirection(String rep) {
127        // TEMPORARY:  Assume we're always Entering, if asked.
128        return (PhysicalLocationReporter.Direction.ENTER);
129    }
130
131    /**
132     * Get the PhysicalLocation of the Reporter
133     *
134     * Reports its own location, for now. 
135     * Not sure if that's the right thing or
136     * not. NOT DONE YET
137     * 
138     * {@inheritDoc}
139     */
140    @Override
141    public PhysicalLocation getPhysicalLocation() {
142        return (this.getPhysicalLocation(null));
143    }
144
145    /**
146     * Get the PhysicalLocation of the Reporter.
147     *
148     * {@inheritDoc}
149     * @param s unused.
150     */
151    @Override
152    public PhysicalLocation getPhysicalLocation(String s) {
153        return (PhysicalLocation.getBeanPhysicalLocation(this));
154    }
155
156    private static final Logger log = LoggerFactory.getLogger(AbstractIdTagReporter.class);
157
158}