001package jmri.jmrix.ecos; 002 003import java.util.Hashtable; 004import javax.annotation.Nonnull; 005import jmri.Sensor; 006import org.slf4j.Logger; 007import org.slf4j.LoggerFactory; 008 009/** 010 * Implement sensor manager for ECoS systems. The Manager handles all the state 011 * changes. 012 * <p> 013 * System names are "USnnn:yy", Dcc4PcBoardManager nnn is the ECoS Object Number for a given 014 * s88 Bus Module and yy is the port on that module. 015 * 016 * @author Kevin Dickerson Copyright (C) 2009 017 */ 018public class EcosSensorManager extends jmri.managers.AbstractSensorManager 019 implements EcosListener { 020 021 public EcosSensorManager(@Nonnull EcosSystemConnectionMemo memo) { 022 super(memo); 023 tc = memo.getTrafficController(); 024 init(); 025 } 026 027 private void init() { 028 // listen for sensor creation 029 // connect to the TrafficManager 030 tc.addEcosListener(this); 031 032 // ask to be notified 033 //Not sure if we need to worry about newly created sensors on the layout. 034 /*EcosMessage m = new EcosMessage("request(26, view)"); 035 tc.sendEcosMessage(m, this);*/ 036 // get initial state 037 EcosMessage m = new EcosMessage("queryObjects(26, ports)"); 038 tc.sendEcosMessage(m, this); 039 } 040 041 private final EcosTrafficController tc; 042 //The hash table simply holds the object number against the EcosSensor ref. 043 private final Hashtable<Integer, EcosSensor> _tecos = new Hashtable<>(); // stores known Ecos Object ids to DCC 044 private final Hashtable<Integer, Integer> _sport = new Hashtable<>(); // stores known Ecos Object ids to DCC 045 046 /** 047 * {@inheritDoc} 048 */ 049 @Override 050 @Nonnull 051 public EcosSystemConnectionMemo getMemo() { 052 return (EcosSystemConnectionMemo) memo; 053 } 054 055 /** 056 * {@inheritDoc} 057 */ 058 @Override 059 @Nonnull 060 protected Sensor createNewSensor(@Nonnull String systemName, String userName) throws IllegalArgumentException { 061 //int ports = Integer.parseInt(systemName.substring(getSystemPrefix().length() + 1)); 062 return new EcosSensor(systemName, userName); 063 } 064 065 // to listen for status changes from Ecos system 066 @Override 067 public void reply(EcosReply m) { 068 // is this a list of sensors? 069 EcosSensor es; 070 if (m.getResultCode() == 0) { 071 int ecosObjectId = m.getEcosObjectId(); 072 if ((ecosObjectId != 26) && ((ecosObjectId < 100) || (ecosObjectId > 300))) { 073 log.debug("message receieved that is not within the valid Sensor object range"); 074 return; 075 } 076 if (m.isUnsolicited() || m.getReplyType().equals("get")) { //<Event Messages are unsolicited 077 String[] lines = m.getContents(); 078 for (int i = 0; i < lines.length; i++) { 079 int start = lines[i].indexOf("[") + 1; 080 int end = lines[i].indexOf("]"); 081 if (lines[i].contains("state")) { 082 start = start + 2; 083 if (start > 0 && end > 0) { 084 String val = lines[i].substring(start, end); 085 int intState = Integer.parseInt(val, 16); 086 decodeSensorState(ecosObjectId, intState); 087 } 088 } 089 if (lines[i].contains("railcom")) { 090 //int newstate = UNKNOWN; 091 if (start > 0 && end > 0) { 092 String val = lines[i].substring(start, lines[i].indexOf(",")).trim(); 093 int j = Integer.parseInt(val); 094 j++; 095 StringBuilder sb = new StringBuilder(); 096 sb.append(getSystemPrefix()); 097 sb.append("R"); 098 sb.append(ecosObjectId); 099 sb.append(":"); 100 //Little work around to pad single digit address out. 101 if (j < 10) { 102 sb.append("0"); 103 } 104 sb.append(j); 105 try { 106 EcosReporter rp = (EcosReporter) getMemo().getReporterManager().provideReporter(sb.toString()); 107 rp.decodeDetails(lines[i]); 108 } catch (IllegalArgumentException ex) { 109 log.warn("Failed to provide Reporter \"{}\" in reply", sb.toString()); 110 } 111 } 112 113 } 114 } 115 //With the sensor manager we don't keep an eye on the manager, which we proabaly need to do. 116 } else { 117 if (m.getReplyType().equals("queryObjects") && ecosObjectId == 26) { 118 String[] lines = m.getContents(); 119 for (int i = 0; i < lines.length; i++) { 120 if (lines[i].contains("ports[")) { // skip odd lines 121 int start = 0; 122 int end = lines[i].indexOf(' '); 123 int object = Integer.parseInt(lines[i].substring(start, end)); 124 125 if ((100 <= object) && (object < 300)) { // only physical sensors 126 start = lines[i].indexOf('[') + 1; 127 end = lines[i].indexOf(']'); 128 int ports = Integer.parseInt(lines[i].substring(start, end)); 129 log.debug("Found sensor object {} ports {}", object, ports); 130 131 if ((ports == 8) || (ports == 16)) { 132 Sensor s; 133 String sensorprefix = getSystemPrefix() + "S" + object + ":"; 134 _sport.put(object, ports); 135 //ports 1, 5, 9 13 on a ECoS detector are railcom enabled., but value in messages is returned 0, 4, 8, 12 136 for (int j = 1; j <= ports; j++) { 137 StringBuilder sb = new StringBuilder(); 138 sb.append(sensorprefix); 139 //Little work around to pad single digit address out. 140 if (j < 10) { 141 sb.append("0"); 142 } 143 sb.append(j); 144 s = getSensor(sb.toString()); 145 if (s == null) { 146 es = (EcosSensor) provideSensor(sb.toString()); 147 es.setObjectNumber(object); 148 _tecos.put(object, es); 149 if (object >= 200 && (j == 1 || j == 5 || j == 9 || j == 13)) { 150 sb = new StringBuilder(); 151 sb.append(getSystemPrefix()); 152 sb.append("R"); 153 sb.append(object); 154 sb.append(":"); 155 //Little work around to pad single digit address out. 156 if (j < 10) { 157 sb.append("0"); 158 } 159 sb.append(j); 160 try { 161 EcosReporter rp = (EcosReporter) getMemo().getReporterManager().provideReporter(sb.toString()); 162 rp.setObjectPort(object, (j - 1)); 163 es.setReporter(rp); 164 EcosMessage em = new EcosMessage("get(" + object + ", railcom[" + (j - 1) + "])"); 165 tc.sendEcosMessage(em, this); 166 } catch (IllegalArgumentException ex) { 167 log.warn("Failed to provide Reporter \"{}\"", sb.toString()); 168 } 169 } 170 } 171 } 172 EcosMessage em = new EcosMessage("request(" + object + ", view)"); 173 tc.sendEcosMessage(em, this); 174 175 em = new EcosMessage("get(" + object + ",state)"); 176 tc.sendEcosMessage(em, this); 177 } else { 178 log.debug("Invalid number of ports returned for Module {}", object); 179 } 180 } 181 } 182 } 183 } 184 } 185 } 186 } 187 188 @Override 189 public void message(EcosMessage m) { 190 // messages are ignored 191 } 192 193 private void decodeSensorState(int object, int intState) { 194 EcosSensor es; 195 int k = 1; 196 int result; 197 String sensorprefix = getSystemPrefix() + "S" + object + ":"; 198 for (int port = 1; port <= _sport.get(object); port++) { 199 result = intState & k; 200 //Little work around to pad single digit address out. 201 StringBuilder sb = new StringBuilder(); 202 sb.append(sensorprefix); 203 //Little work around to pad single digit address out. 204 if (port < 10) { 205 sb.append("0"); 206 } 207 sb.append(port); 208 es = (EcosSensor) provideSensor(sb.toString()); 209 if (result == 0) { 210 es.setOwnState(Sensor.INACTIVE); 211 } else { 212 es.setOwnState(Sensor.ACTIVE); 213 } 214 k = k * 2; 215 } 216 } 217 218 public void refreshItems() { 219 /*ask to be notified about newly created sensors on the layout. 220 Doing the request to view the list, will also kick off a request to 221 view each individual sensor*/ 222 EcosMessage m = new EcosMessage("queryObjects(26, ports)"); 223 tc.sendEcosMessage(m, this); 224 } 225 226 /** 227 * Validates to contain at least 1 number . . . 228 * <p> 229 * TODO: Custom validation for EcosSensorManager could be improved. 230 * {@inheritDoc} 231 */ 232 @Override 233 @Nonnull 234 public String validateSystemNameFormat(@Nonnull String name, @Nonnull java.util.Locale locale) throws jmri.NamedBean.BadSystemNameException { 235 return validateTrimmedMin1NumberSystemNameFormat(name,locale); 236 } 237 238 @Override 239 public void dispose() { 240 tc.removeEcosListener(this); 241 super.dispose(); 242 } 243 244 private final static Logger log = LoggerFactory.getLogger(EcosSensorManager.class); 245 246}