001package jmri.jmrix.loconet; 002 003import javax.annotation.Nonnull; 004 005import jmri.jmrix.loconet.alm.LnSimple7thGenDevicesRoutes; 006import jmri.jmrix.loconet.alm.LnSimple7thGenDeviceRoutes; 007import jmri.jmrix.loconet.alm.LnSimple7thGenRoute; 008import jmri.jmrix.loconet.alm.LnSimpleRouteEntry; 009import jmri.jmrix.loconet.alm.RouteSwitchPositionEnum; 010 011import org.slf4j.Logger; 012import org.slf4j.LoggerFactory; 013 014/** 015 * 016 * @author aptly 017 */ 018public class Ln7gAccyRoutesManager implements LocoNetListener { 019 020 private static final String DEVICE_DS74 = "DS74"; 021 private static final String DEVICE_DS78V = "DS78V"; 022 private static final String DEVICE_PM74 = "PM74"; 023 private static final String DEVICE_SE74 = "SE74"; 024 025 private LocoNetSystemConnectionMemo memo; 026 private final LnSimple7thGenDevicesRoutes devicesRoutes; 027 private int activeDeviceType; 028 private int activeDeviceSerNum; 029 private int activeDeviceBaseAddr; 030 private int activeEntrySet; 031 private int activeRouteNum; 032 033 public Ln7gAccyRoutesManager() { 034 this.devicesRoutes = new LnSimple7thGenDevicesRoutes(); 035 } 036 037 public void initComponents(LocoNetSystemConnectionMemo c) { 038 memo = c; 039 memo.getLnTrafficController().addLocoNetListener(~0, this); 040 deselectAlmRoutesDevice(); 041 // nothing more to do 042 } 043 044 public Ln7gAccyRoutesManager initContext(Object context) { 045 if (context instanceof LocoNetSystemConnectionMemo) { 046 initComponents((LocoNetSystemConnectionMemo)context); 047 } 048 return this; 049 } 050 private void deselectAlmRoutesDevice() { 051 activeDeviceType = 0; 052 activeDeviceSerNum = 0; 053 activeDeviceBaseAddr = 0; 054 activeEntrySet = -1; 055 activeRouteNum = -1; 056 } 057 058 private void selectAlmRoutesDevice(int deviceType, int serNum, int baseAddr) { 059 activeDeviceType = deviceType; 060 activeDeviceSerNum = serNum; 061 activeDeviceBaseAddr = baseAddr; 062 activeEntrySet = -1; 063 activeRouteNum = -1; 064 } 065 066 /** 067 * Update from saved data. 068 * 069 * Read the file of devices/routes. 070 */ 071 public void initializeDeviceRoutes() { 072 loadTheXML(); 073 } 074 075 /** 076 * Get String which shows all 7th Gen Accy devices which can store routes, 077 * with any route data. 078 * @return string of 7th-gen Accy devices which can have enabled routes 079 */ 080 public String showStoredDevicesWithRoutes() { 081 if (getCountOfDevicesWithRoutes() < 1) { 082 return "No Digitrax 7th-gen Accessory devices with stored/storable routes."; 083 } 084 StringBuilder s = new StringBuilder("Known Digitrax 7th-gen Accessory devices with stored/storable routes:\n"); 085 for (int i = 0; i < getCountOfDevicesWithRoutes(); i++) { 086 LnSimple7thGenDeviceRoutes d = devicesRoutes.getDeviceRoutes(i); 087 s.append("\tDevice "); 088 s.append(d.getDeviceType()); 089 s.append(", ser. num. "); 090 s.append(d.getSerNum()); 091 s.append(", base addr. "); 092 s.append(d.getBaseAddr()); 093 094 for (int j = 0; j < d.getRoutes().length; ++j) { 095 LnSimple7thGenRoute route = d.getRoutes(j); 096 s.append("\n\tRoute "); 097 s.append(j); 098 s.append(": "); 099 for (int l = 0; l < 8; l++) { 100 s.append(", "); 101 s.append(route.getRouteEntry(l).getNumber()); 102 s.append(route.getRouteEntry(l).getPosition()); 103 } 104 } 105 s.append(".\n"); 106 } 107 return s.toString(); 108 } 109 110 111 /** 112 * Returns the manager's memo. 113 * @return a LocoNetSystemConnectionMemo 114 */ 115 @Nonnull 116 public LocoNetSystemConnectionMemo getMemo() { 117 return memo; 118 } 119 120 private void checkMessage4Byte(LocoNetMessage m) { 121 if ((m.getNumDataElements() == 4) && 122 (m.getOpCode() == 0xb4) && (m.getElement(1) == 0x6e) && 123 (m.getElement(2) == 0x7f)) { 124 // Ignore the ALM device route write long ack response 125 // rather than accepting at the write request! 126 log.trace("message: write acknowledge- Ignored."); 127 } 128 } 129 130 private boolean checkReportAlmCSRoutesCapabilities(LocoNetMessage m) { 131 if ((m.getNumDataElements() == 16) && 132 (m.getOpCode() == LnConstants.OPC_ALM_READ) && (m.getElement(1) == 0x10) && 133 (m.getElement(2) == 0x1) && (m.getElement(3) == 0x0) && 134 (m.getElement(10) == 0) && 135 (m.getElement(11) == 0) && (m.getElement(12) == 0) && 136 (m.getElement(13) == 0) && (m.getElement(14) == 0)) { 137 // [E6 10 01 00 00 02 03 02 10 7F 00 00 00 00 00 64] Command Station Routes Capabilities reply: 64 routes, 16 entries each route. 138 log.debug("Report ALM CS Routes Capabilities"); 139 // nothing to do at this time 140 return true; 141 } 142 return false; 143 } 144 145 private boolean checkRequestDeviceRoutes(LocoNetMessage m) { 146 if ((m.getNumDataElements() == 16) && 147 (m.getOpCode() == 0xeE) && (m.getElement(1) == 0x10) && 148 (m.getElement(2) == 0x2) && (m.getElement(3) == 0x0e) && 149 (m.getElement(4) == 0) && (m.getElement(5) == 0) && 150 (m.getElement(6) == 0) && (m.getElement(7) == 0) && 151 (m.getElement(8) == 0)) { 152 log.debug("request select ALM Device Routes"); 153 // Ignore byte 10: devopsw! 154 // force the device as unselected (for now)! 155 deselectAlmRoutesDevice(); 156 return true; 157 } 158 return false; 159 } 160 161 private boolean checkAlmDeviceIsSelected(LocoNetMessage m) { 162 if ((m.getNumDataElements() == 16) && 163 (m.getOpCode() == 0xe6) && (m.getElement(1) == 0x10) && 164 (m.getElement(2) == 0x2) && (m.getElement(3) == 0x0e) && 165 ((m.getElement(4) & 0x4f) == 0) && // bits 5:1 imply # routes 166 (m.getElement(5) == 0) && 167 (m.getElement(6) == 0) && (m.getElement(7) == 0x02) && 168 (m.getElement(8) == 8)) { 169 log.debug("response from device: ALM Device Routes selected"); 170 // Ignore byte 10: devopsw! 171 // selected the currect ALM Routes device 172 int dev = m.getElement(9); 173 int ser = (m.getElement(12) << 7) + m.getElement(11); 174 int base = (m.getElement(14) << 7) + m.getElement(13); 175 176 // set the device as selected for routes read/write 177 selectAlmRoutesDevice(dev, ser, base); 178 return true; 179 } 180 return false; 181 } 182 183 private boolean checkAlmDeviceIsDeselected(LocoNetMessage m) { 184 if ((m.getNumDataElements() == 16) && 185 (m.getOpCode() == 0xe6) && (m.getElement(1) == 0x10) && 186 (m.getElement(2) == 0x2) && (m.getElement(3) == 0) && 187 (m.getElement(4) == 0) && (m.getElement(5) == 0) && 188 (m.getElement(7) == 0) && (m.getElement(8) == 0) && 189 (m.getElement(9) == 0) && (m.getElement(10) == 0) && 190 (m.getElement(11) == 0) && (m.getElement(12) == 0) && 191 (m.getElement(13) == 0) && (m.getElement(14) == 0)) { 192 // Ignoring byte 6! 193 194 // deselect any currect ALM Routes device 195 log.debug("message: deselect the ALM routes device at the de-select request"); 196 deselectAlmRoutesDevice(); 197 return true; 198 } 199 return false; 200 } 201 202 private boolean checkRequestAlmDeviceReadRequest(LocoNetMessage m) { 203 if ((m.getNumDataElements() == 16) && 204 (m.getOpCode() == 0xee) && (m.getElement(1) == 0x10) && 205 (m.getElement(2) == 0x2) && (m.getElement(3) == 0x2) && 206 (m.getElement(7) == 0) && (m.getElement(8) == 0) && 207 (m.getElement(9) == 0) && (m.getElement(10) == 0) && 208 (m.getElement(11) == 0) && (m.getElement(12) == 0) && 209 (m.getElement(13) == 0) && (m.getElement(14) == 0) 210 ) { 211 // Ignoring byte 6! 212 213 // watch for ALM device route read request; capture data for later use 214 activeEntrySet = m.getElement(4) & 1; 215 activeRouteNum = (m.getElement(4) >> 1) + (m.getElement(5) << 6); 216 log.debug("message: read request - ALM 7th gen read - entry {} entrySet {}", activeRouteNum, activeEntrySet); 217 return true; 218 } 219 return false; 220 } 221 222 private boolean checkAlmDeviceReadReport(LocoNetMessage m) { 223 if ((m.getNumDataElements() == 16) && 224 (m.getOpCode() == 0xe6) && (m.getElement(1) == 0x10) && 225 (m.getElement(2) == 0x2) && (m.getElement(3) == 0x2) && 226 (m.getElement(7) == 0) && (m.getElement(8) == 0) && 227 (m.getElement(9) == 0) && (m.getElement(10) == 0) && 228 (m.getElement(11) == 0) && (m.getElement(12) == 0) && 229 (m.getElement(13) == 0) && (m.getElement(14) == 0) 230 ) { 231 // Ignoring byte 6! 232 233 // watch for ALM device route read report and keep the route data 234 activeEntrySet = m.getElement(4) & 1; 235 activeRouteNum = (m.getElement(4) >> 1) + (m.getElement(5) << 6); 236 int entrya = m.getElement(6) + (m.getElement(7) << 7); 237 int entryb = m.getElement(8) + (m.getElement(8) << 7); 238 int entryc = m.getElement(10) + (m.getElement(11) << 7); 239 int entryd = m.getElement(12) + (m.getElement(13) << 7); 240 241 log.debug("message: read report - ALM 7th gen read - entry {} entrySet {}: entryA = {}, entryB = {}, entryC = {}, entryD = {}.", 242 activeEntrySet, activeEntrySet, entrya, entryb, entryc, entryd); 243 saveData(activeDeviceType, activeDeviceSerNum, 244 activeDeviceBaseAddr, activeEntrySet, activeRouteNum, 245 entrya, entryb, entryc, entryd); 246 return true; 247 } 248 return false; 249 } 250 251 private boolean checkAlmDeviceWriteRequest(LocoNetMessage m) { 252 if ((m.getNumDataElements() == 16) && 253 (m.getOpCode() == 0xee) && (m.getElement(1) == 0x10) && 254 (m.getElement(2) == 0x2) && (m.getElement(3) == 0x3)) { 255 // Ignoring byte 6! 256 257 // watch for ALM device route write request 258 activeEntrySet = m.getElement(4) & 1; 259 activeRouteNum = (m.getElement(4)>>1) + (m.getElement(5) << 6); 260 // get 4 entries 261 int entrya = m.getElement(7) + (m.getElement(8) << 7); 262 int entryb = m.getElement(9) + (m.getElement(10) << 7); 263 int entryc = m.getElement(11) + (m.getElement(12) << 7); 264 int entryd = m.getElement(13) + (m.getElement(14) << 7); 265 266 log.debug("message: write request - ALM 7th gen write - entry {} entrySet {}: entryA = {}, entryB = {}, entryC = {}, entryD = {}.\n", 267 activeEntrySet, activeEntrySet, entrya, entryb, entryc, entryd); 268 269 saveData(activeDeviceType, activeDeviceSerNum, activeDeviceBaseAddr, 270 activeEntrySet, activeRouteNum, 271 entrya, entryb, entryc, entryd); 272 return true; 273 } 274 return false; 275 } 276 277 @Override 278 public void message(LocoNetMessage m) { 279 log.trace("RtsMgr:message - length {}", m.getNumDataElements()); 280 if (checkReportAlmCSRoutesCapabilities(m)) { 281 } else if (checkRequestDeviceRoutes(m)) { 282 } else if (checkAlmDeviceIsSelected(m)) { 283 } else if (checkAlmDeviceIsDeselected(m)) { 284 } else if (checkRequestAlmDeviceReadRequest(m)) { 285 } else if (checkAlmDeviceReadReport(m)) { 286 } else if (checkAlmDeviceWriteRequest(m)) { 287 } else { 288 checkMessage4Byte(m); 289 } 290 } 291 292 /** 293 * Save 4 entries of a route. 294 * 295 * @param deviceType Device type number 296 * @param deviceSerNum Device serial number 297 * @param deviceBaseAddr Device base address 298 * @param entrySet Entry "set" number 299 * @param routeNum Route number 300 * @param entrya Entry A 301 * @param entryb Entry B 302 * @param entryc Entry C 303 * @param entryd Entry D 304 */ 305 public void saveData(int deviceType, int deviceSerNum, 306 int deviceBaseAddr, int entrySet, int routeNum, 307 int entrya, int entryb, int entryc, int entryd) { 308 309 log.debug("saveData: updating 4 entries for device {} serNum {} routeNum {}" 310 + " entries {} to {}, \n\twith entrya = {}, entryb = {}, entryc = {}, entryd = {}.", 311 deviceType, deviceSerNum, routeNum, 312 entrySet * 4, (entrySet *4) + 3, 313 entrya, entryb, entryc, entryd); 314 315 // find/create the device's storage 316 LnSimple7thGenDeviceRoutes device = devicesRoutes.getDeviceRoutes(deviceType, deviceSerNum); 317 if (device == null) { 318 // Didn't find one. Create one 319 addDevice(new LnSimple7thGenDeviceRoutes(deviceType, deviceSerNum)); 320 321 device = devicesRoutes.getDeviceRoutes(deviceType, deviceSerNum); 322 device.setBaseAddr(deviceBaseAddr); 323 log.debug("saveData: New device's type = {}, serNum = {}, baseAddr = {}", 324 device.getDeviceType(), device.getSerNum(), device.getBaseAddr()); 325 } 326 // update the four entries at routeNum, "entrySet" half 327 device.setFourEntries(routeNum, entrySet, entrya, entryb, entryc, entryd); 328 329 LnSimple7thGenDeviceRoutes d = devicesRoutes.getDeviceRoutes(deviceType, deviceSerNum); 330 LnSimpleRouteEntry ea = d.getRoutes(routeNum).getRouteEntry(4 * entrySet); 331 LnSimpleRouteEntry eb = d.getRoutes(routeNum).getRouteEntry((4 * entrySet) + 1); 332 LnSimpleRouteEntry ec = d.getRoutes(routeNum).getRouteEntry(4 * entrySet + 2); 333 LnSimpleRouteEntry ed = d.getRoutes(routeNum).getRouteEntry(4 * entrySet + 3); 334 log.debug("saveData: device {} serNum {} route {}. entrySet {}, a {}, b {}, c {}, d {}", 335 d.getDeviceType(), d.getSerNum(), routeNum, entrySet, 336 Integer.toString(ea.getNumber())+ea.getPosition().toString(), 337 Integer.toString(eb.getNumber())+eb.getPosition().toString(), 338 Integer.toString(ec.getNumber())+ec.getPosition().toString(), 339 Integer.toString(ed.getNumber())+ed.getPosition().toString() 340 ); 341 } 342 343 public String getDevName(int devType) { 344 switch (devType) { 345 case LnConstants.RE_IPL_DIGITRAX_HOST_DS74: 346 return DEVICE_DS74; 347 case LnConstants.RE_IPL_DIGITRAX_HOST_DS78V: 348 return DEVICE_DS78V; 349 case LnConstants.RE_IPL_DIGITRAX_HOST_PM74: 350 return DEVICE_PM74; 351 case LnConstants.RE_IPL_DIGITRAX_HOST_SE74: 352 return DEVICE_SE74; 353 default: 354 throw new IllegalArgumentException("Bad Device Type: " + 355 Integer.toString(devType) + "."); 356 } 357 } 358 359 public int getDevType(String deviceName) { 360 switch (deviceName) { 361 case DEVICE_DS74: 362 return LnConstants.RE_IPL_DIGITRAX_HOST_DS74; 363 case DEVICE_DS78V: 364 return LnConstants.RE_IPL_DIGITRAX_HOST_DS78V; 365 case DEVICE_PM74: 366 return LnConstants.RE_IPL_DIGITRAX_HOST_PM74; 367 case DEVICE_SE74: 368 return LnConstants.RE_IPL_DIGITRAX_HOST_SE74; 369 default: 370 throw new IllegalArgumentException("Wrong device name: "+deviceName+"."); 371 } 372 } 373 374 public boolean loadTheXML() { 375 log.debug("loadXML: starting; current getCountOfDevicesWithRoutes is {}", 376 getCountOfDevicesWithRoutes()); 377 // get info from file 378 jmri.jmrix.loconet.configurexml.Digitrax7thGenAccyRoutesXML xmlThingy = 379 new jmri.jmrix.loconet.configurexml.Digitrax7thGenAccyRoutesXML(this); 380 xmlThingy.loadXML(); 381 382 log.debug("loadTheXML: Finished reading the 'DigitraxRoutes' file."); 383 return true; 384 } 385 386 public int getCountOfDevicesWithRoutes() { 387 return devicesRoutes.size(); 388 } 389 390 public void addDeviceRoutesRoute(String devType, int serNum, int baseNum, 391 int[][] turnouts, RouteSwitchPositionEnum[][] positions) { 392 // get device type (IPL) number 393 int idevNumber = LnSimple7thGenDeviceRoutes.getDeviceType(devType); 394 395 log.debug("addDeviceRoutesRoute: checking for device already entered - devNumber {}, serNum {}.", 396 idevNumber, serNum); 397 // get a device by number & serial number 398 LnSimple7thGenDeviceRoutes existingDevRts = 399 devicesRoutes.getDeviceRoutes(idevNumber, serNum ); 400 401 if (existingDevRts != null) { 402 devicesRoutes.removeExistingDevice(idevNumber, serNum); 403 log.debug("removed previous device in favor of the current data"); 404 } 405 LnSimple7thGenDeviceRoutes ls7gdr = new LnSimple7thGenDeviceRoutes(idevNumber, serNum); 406 ls7gdr.setBaseAddr(baseNum); 407 408 for (int i = 0; i < (devType.equalsIgnoreCase(DEVICE_DS78V)?16:8); ++i) { 409 for (int j = 0; j < 8; ++j) { 410 ls7gdr.setOneEntry(i, j, turnouts[i][j], positions[i][j]); 411 } 412 } 413 addDevice(ls7gdr); 414 } 415 416 public void addDevice(LnSimple7thGenDeviceRoutes dev) { 417 devicesRoutes.add(dev); 418 log.debug("addDevice: done adding the device!"); 419 } 420 421 public LnSimple7thGenDeviceRoutes getDevice(int i) { 422 return devicesRoutes.getDeviceRoutes(i); 423 } 424 425 426 public void dispose() { 427 memo.getLnTrafficController().removeLocoNetListener(~0, this); 428 } 429 430 private final static Logger log = LoggerFactory.getLogger(Ln7gAccyRoutesManager.class); 431}