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}