001package jmri.jmrix.roco.z21.simulator; 002 003import jmri.jmrix.lenz.XNetConstants; 004import jmri.jmrix.lenz.XNetMessage; 005import jmri.jmrix.lenz.XNetReply; 006import jmri.jmrix.roco.z21.Z21Constants; 007import org.slf4j.Logger; 008import org.slf4j.LoggerFactory; 009 010/** 011 * Provide access to a simulated z21 XpressNet sub-system. 012 * <p> 013 * This shares some code with the XpressNet simulator, but it's 014 * not a derived class because it isn't a real connection. 015 * 016 * @author Paul Bender, Copyright (C) 2015 017 */ 018public class Z21XNetSimulatorAdapter { 019 020 private int csStatus; 021 // status values from the z21 Documentation. 022 private final static int csEmergencyStop = 0x01; 023 // 0x00 means normal mode. 024 private final static int csNormalMode = 0x00; 025 026 // package protected array of Z21SimulatorLocoData objects. 027 Z21SimulatorLocoData[] locoData; 028 int locoCount; // counter for locoData array. 029 int locoPosition; // Position for locoData array. 030 031 public Z21XNetSimulatorAdapter() { 032 csStatus = csNormalMode; 033 locoData = new Z21SimulatorLocoData[20]; 034 } 035 036 // generateReply is the heart of the simulation. It translates an 037 // incoming XNetMessage into an outgoing XNetReply. 038 XNetReply generateReply(XNetMessage m) { 039 log.debug("Generating Reply"); 040 XNetReply reply; 041 switch (m.getElement(0)&0xff) { 042 case XNetConstants.CS_REQUEST: 043 log.debug("CS Request Received"); 044 switch (m.getElement(1)) { 045 case XNetConstants.CS_VERSION: 046 reply=xNetVersionReply(); 047 break; 048 case XNetConstants.RESUME_OPS: 049 csStatus = csNormalMode; 050 reply=normalOpsReply(); 051 break; 052 case XNetConstants.EMERGENCY_OFF: 053 csStatus = csEmergencyStop; 054 reply=everythingOffReply(); 055 break; 056 case XNetConstants.CS_STATUS: 057 reply=csStatusReply(); 058 break; 059 case XNetConstants.SERVICE_MODE_CSRESULT: 060 default: 061 log.debug("Unsupported requested received: {}", m.toString()); 062 reply=notSupportedReply(); 063 } 064 break; 065 case XNetConstants.LI_VERSION_REQUEST: 066 log.debug("LI Version Request Received"); 067 reply=new XNetReply(); 068 reply.setOpCode(XNetConstants.LI_VERSION_RESPONSE); 069 reply.setElement(1, 0x00); // set the hardware type to 0 070 reply.setElement(2, 0x00); // set the firmware version to 0 071 reply.setElement(3, 0x00); // set the parity byte to 0 072 reply.setParity(); 073 break; 074 case XNetConstants.LOCO_OPER_REQ: 075 log.debug("Locomotive Operations Request received"); 076 switch(m.getElement(1)&0xff) { 077 case XNetConstants.LOCO_SPEED_14: 078 // z21 specific locomotive information reply is expected. 079 reply = new XNetReply(); 080 reply.setOpCode(Z21Constants.LAN_X_LOCO_INFO_RESPONSE); 081 reply.setElement(1, m.getElement(2)); // address msb from 082 // message. 083 reply.setElement(2, m.getElement(3)); // address lsb from 084 // message. 085 reply.setElement(3, 0x00); // set speed step mode to 14 086 reply.setElement(4, m.getElement(4) & 0xff); // set the speed and direction to the sent value. 087 reply.setElement(5, 0x00); // set function group a off 088 reply.setElement(6, 0x00); // set function group b off 089 reply.setElement(7, 0x00); // set F13-F20 off 090 reply.setElement(8, 0x00); // set F21-F28 off 091 reply.setElement(9, 0x00); // filler 092 reply.setElement(10, 0x00); // filler 093 reply.setElement(11, 0x00); // filler 094 reply.setElement(12, 0x00); // filler 095 reply.setElement(13, 0x00); // filler 096 reply.setElement(14, 0x00); // filler 097 reply.setElement(15, 0x00); // filler 098 reply.setElement(16, 0x00); // set the parity byte to 0 099 reply.setParity(); // set the parity correctly. 100 // save the address and speed information for 101 // the simulator's RailCom values. 102 locoData[locoPosition] = new Z21SimulatorLocoData((byte) (m.getElement(2) & 0xff), (byte) (m.getElement(3) & 0xff), (byte) (m.getElement(4) & 0xff)); 103 locoCount = (locoCount + 1) % 19; 104 if (locoCount < 19) // 19 is the limit, set by the protocol. 105 locoCount++; 106 locoPosition = (locoPosition + 1) % 19; 107 break; 108 case XNetConstants.LOCO_SPEED_28: 109 // z21 specific locomotive information reply is expected. 110 reply = new XNetReply(); 111 reply.setOpCode(Z21Constants.LAN_X_LOCO_INFO_RESPONSE); 112 reply.setElement(1, m.getElement(2)); // address msb from 113 // message. 114 reply.setElement(2, m.getElement(3)); // address lsb from 115 // message. 116 reply.setElement(3, 0x02); // set speed step mode to 28 117 reply.setElement(4, m.getElement(4)); // set the speed and direction to the sent value. 118 reply.setElement(5, 0x00); // set function group a off 119 reply.setElement(6, 0x00); // set function group b off 120 reply.setElement(7, 0x00); // set F13-F20 off 121 reply.setElement(8, 0x00); // set F21-F28 off 122 reply.setElement(9, 0x00); // filler 123 reply.setElement(10, 0x00); // filler 124 reply.setElement(11, 0x00); // filler 125 reply.setElement(12, 0x00); // filler 126 reply.setElement(13, 0x00); // filler 127 reply.setElement(14, 0x00); // filler 128 reply.setElement(15, 0x00); // filler 129 reply.setElement(16, 0x00); // set the parity byte to 0 130 reply.setParity(); // set the parity correctly. 131 locoData[locoPosition] = new Z21SimulatorLocoData((byte) (m.getElement(2) & 0xff), (byte) (m.getElement(3) & 0xff), (byte) (m.getElement(4) & 0xff)); 132 if (locoCount < 19) // 19 is the limit, set by the protocol. 133 locoCount++; 134 locoPosition = (locoPosition + 1) % 19; 135 break; 136 case XNetConstants.LOCO_SPEED_128: 137 // z21 specific locomotive information reply is expected. 138 reply = new XNetReply(); 139 reply.setOpCode(Z21Constants.LAN_X_LOCO_INFO_RESPONSE); 140 reply.setElement(1, m.getElement(2)); // address msb from 141 // message. 142 reply.setElement(2, m.getElement(3)); // address lsb from 143 // message. 144 reply.setElement(3, 0x04); // set speed step mode to 128 145 reply.setElement(4, m.getElement(4)); // set the speed and direction to the sent value. 146 reply.setElement(5, 0x00); // set function group a off 147 reply.setElement(6, 0x00); // set function group b off 148 reply.setElement(7, 0x00); // set F13-F20 off 149 reply.setElement(8, 0x00); // set F21-F28 off 150 reply.setElement(9, 0x00); // filler 151 reply.setElement(10, 0x00); // filler 152 reply.setElement(11, 0x00); // filler 153 reply.setElement(12, 0x00); // filler 154 reply.setElement(13, 0x00); // filler 155 reply.setElement(14, 0x00); // filler 156 reply.setElement(15, 0x00); // filler 157 reply.setElement(16, 0x00); // set the parity byte to 0 158 reply.setParity(); // set the parity correctly. 159 reply.setParity(); // set the parity correctly. 160 locoData[locoPosition] = new Z21SimulatorLocoData((byte) (m.getElement(2) & 0xff), (byte) (m.getElement(3) & 0xff), (byte) (m.getElement(4) & 0xff)); 161 if (locoCount < 19) // 19 is the limit, set by the protocol. 162 locoCount++; 163 locoPosition = (locoPosition + 1) % 19; 164 break; 165 case Z21Constants.LAN_X_SET_LOCO_FUNCTION: 166 // z21 specific locomotive information reply is expected. 167 reply = new XNetReply(); 168 reply.setOpCode(Z21Constants.LAN_X_LOCO_INFO_RESPONSE); 169 reply.setElement(1, m.getElement(2)); // address msb from 170 // message. 171 reply.setElement(2, m.getElement(3)); // address lsb from 172 // message. 173 reply.setElement(3, 0x04); // set speed step mode to 128 174 reply.setElement(4, 0x00); // set the speed and direction to the sent value. 175 reply.setElement(5, 0x00); // set function group a off 176 reply.setElement(6, 0x00); // set function group b off 177 reply.setElement(7, 0x00); // set F13-F20 off 178 reply.setElement(8, 0x00); // set F21-F28 off 179 reply.setElement(9, 0x00); // filler 180 reply.setElement(10, 0x00); // filler 181 reply.setElement(11, 0x00); // filler 182 reply.setElement(12, 0x00); // filler 183 reply.setElement(13, 0x00); // filler 184 reply.setElement(14, 0x00); // filler 185 reply.setElement(15, 0x00); // filler 186 reply.setElement(16, 0x00); // set the parity byte to 0 187 reply.setParity(); // set the parity correctly. 188 break; 189 case XNetConstants.LOCO_SET_FUNC_GROUP1: 190 // XpressNet set Function Group 1. 191 // We need to find out what a Z21 actually sends in response. 192 case XNetConstants.LOCO_SET_FUNC_GROUP2: 193 // XpressNet set Function Group 2. 194 // We need to find out what a Z21 actually sends in response. 195 case XNetConstants.LOCO_SET_FUNC_GROUP3: 196 // XpressNet set Function Group 3. 197 // We need to find out what a Z21 actually sends in response. 198 case XNetConstants.LOCO_SET_FUNC_GROUP4: 199 // XpressNet set Function Group 4. 200 // We need to find out what a Z21 actually sends in response. 201 case XNetConstants.LOCO_SET_FUNC_GROUP5: 202 // XpressNet set Function Group 5. 203 // We need to find out what a Z21 actually sends in response. 204 case XNetConstants.LOCO_SET_FUNC_GROUP1_MOMENTARY: 205 // XpressNet set Function Momentary Group 1. 206 // We need to find out what a Z21 actually sends in response. 207 case XNetConstants.LOCO_SET_FUNC_GROUP2_MOMENTARY: 208 // XpressNet set Function Momentary Group 2. 209 // We need to find out what a Z21 actually sends in response. 210 case XNetConstants.LOCO_SET_FUNC_GROUP3_MOMENTARY: 211 // XpressNet set Function Momentary Group 3. 212 // We need to find out what a Z21 actually sends in response. 213 case XNetConstants.LOCO_SET_FUNC_GROUP4_MOMENTARY: 214 // XpressNet set Function Momentary Group 4. 215 // We need to find out what a Z21 actually sends in response. 216 case XNetConstants.LOCO_SET_FUNC_GROUP5_MOMENTARY: 217 // XpressNet set Function Momentary Group 5. 218 // We need to find out what a Z21 actually sends in response. 219 reply = okReply(); 220 break; 221 case XNetConstants.LOCO_ADD_MULTI_UNIT_REQ: 222 case XNetConstants.LOCO_REM_MULTI_UNIT_REQ: 223 case XNetConstants.LOCO_IN_MULTI_UNIT_REQ_FORWARD: 224 case XNetConstants.LOCO_IN_MULTI_UNIT_REQ_BACKWARD: 225 case XNetConstants.LOCO_SPEED_27: 226 default: 227 log.debug("Unsupported requested received: {}", m.toString()); 228 reply = notSupportedReply(); 229 break; 230 } 231 break; 232 case XNetConstants.ALL_ESTOP: 233 log.debug("Emergency Stop Received"); 234 reply = emergencyStopReply(); 235 break; 236 case XNetConstants.EMERGENCY_STOP: 237 case XNetConstants.EMERGENCY_STOP_XNETV1V2: 238 reply = okReply(); 239 break; 240 case XNetConstants.ACC_OPER_REQ: 241 log.debug("Accessory Operations Request received"); 242 reply = okReply(); 243 break; 244 case XNetConstants.LOCO_STATUS_REQ: 245 log.debug("Locomotive Status Request received"); 246 switch (m.getElement(1)&0xff) { 247 case XNetConstants.LOCO_INFO_REQ_V3: 248 reply=new XNetReply(); 249 reply.setOpCode(XNetConstants.LOCO_INFO_NORMAL_UNIT); 250 reply.setElement(1, 0x04); // set to 128 speed step mode 251 reply.setElement(2, 0x00); // set the speed to 0 252 // direction reverse 253 reply.setElement(3, 0x00); // set function group a off 254 reply.setElement(4, 0x00); // set function group b off 255 reply.setElement(5, 0x00); // set the parity byte to 0 256 reply.setParity(); // set the parity correctly. 257 break; 258 case Z21Constants.LAN_X_LOCO_INFO_REQUEST_Z21: 259 // z21 specific locomotive information request. 260 reply=new XNetReply(); 261 reply.setOpCode(Z21Constants.LAN_X_LOCO_INFO_RESPONSE); 262 reply.setElement(1,m.getElement(2)); // address msb from 263 // message. 264 reply.setElement(2,m.getElement(3)); // address lsb from 265 // message. 266 reply.setElement(3,0x04); // set speed step mode to 128 267 reply.setElement(4,0x00); // set the speed and direction to 0 and reverse. 268 reply.setElement(5, 0x00); // set function group a off 269 reply.setElement(6, 0x00); // set function group b off 270 reply.setElement(7, 0x00); // set F13-F20 off 271 reply.setElement(8, 0x00); // set F21-F28 off 272 reply.setElement(9, 0x00); // filler 273 reply.setElement(10, 0x00); // filler 274 reply.setElement(11, 0x00); // filler 275 reply.setElement(12, 0x00); // filler 276 reply.setElement(13, 0x00); // filler 277 reply.setElement(14, 0x00); // filler 278 reply.setElement(15, 0x00); // filler 279 reply.setElement(16, 0x00); // set the parity byte to 0 280 reply.setParity(); // set the parity correctly. 281 break; 282 case XNetConstants.LOCO_INFO_REQ_FUNC: 283 case XNetConstants.LOCO_INFO_REQ_FUNC_HI_ON: 284 case XNetConstants.LOCO_INFO_REQ_FUNC_HI_MOM: 285 default: 286 log.debug("Unsupoorted requested received: {}", m.toString()); 287 reply=notSupportedReply(); 288 } 289 break; 290 case XNetConstants.ACC_INFO_REQ: 291 log.debug("Accessory Information Request Received"); 292 reply=new XNetReply(); 293 reply.setOpCode(XNetConstants.ACC_INFO_RESPONSE); 294 reply.setElement(1, m.getElement(1)); 295 if (m.getElement(1) < 64) { 296 // treat as turnout feedback request. 297 if (m.getElement(2) == 0x80) { 298 reply.setElement(2, 0x00); 299 } else { 300 reply.setElement(2, 0x10); 301 } 302 } else { 303 // treat as feedback encoder request. 304 if (m.getElement(2) == 0x80) { 305 reply.setElement(2, 0x40); 306 } else { 307 reply.setElement(2, 0x50); 308 } 309 } 310 reply.setElement(3, 0x00); 311 reply.setParity(); 312 break; 313 case Z21Constants.LAN_X_GET_TURNOUT_INFO: 314 log.debug("Get Turnout Info Request Received"); 315 reply=lanXTurnoutInfoReply(m.getElement(1),m.getElement(2), 316 true); // always sends "thrown". 317 break; 318 case Z21Constants.LAN_X_SET_TURNOUT: 319 log.debug("Set Turnout Request Received"); 320 reply=lanXTurnoutInfoReply(m.getElement(1),m.getElement(2), 321 (0x01 & m.getElement(3))==0x01); 322 break; 323 case XNetConstants.OPS_MODE_PROG_REQ: 324 if(m.getElement(1) == XNetConstants.OPS_MODE_PROG_WRITE_REQ){ 325 int operation = m.getElement(4) & 0xFC; 326 switch(operation) { 327 case 0xEC: 328 log.debug("Write CV in Ops Mode Request Received"); 329 reply = okReply(); 330 break; 331 case 0xE4: 332 log.debug("Verify CV in Ops Mode Request Received"); 333 reply = new XNetReply(); 334 reply.setOpCode(Z21Constants.LAN_X_CV_RESULT_XHEADER); 335 reply.setElement(1,Z21Constants.LAN_X_CV_RESULT_DB0); 336 reply.setElement(2,(m.getElement(4)&0x03)); 337 reply.setElement(3,m.getElement(5)); 338 reply.setElement(4,m.getElement(6)); 339 reply.setElement(5,0x00); 340 reply.setParity(); 341 break; 342 case 0xE8: 343 log.debug("Ops Mode Bit Request Received"); 344 reply = okReply(); 345 break; 346 default: 347 reply=notSupportedReply(); 348 } 349 } else { 350 reply=notSupportedReply(); 351 } 352 break; 353 case XNetConstants.LI101_REQUEST: 354 case XNetConstants.CS_SET_POWERMODE: 355 //case XNetConstants.PROG_READ_REQUEST: //PROG_READ_REQUEST 356 //and CS_SET_POWERMODE 357 //have the same value 358 case XNetConstants.PROG_WRITE_REQUEST: 359 case XNetConstants.LOCO_DOUBLEHEAD: 360 default: 361 log.debug("Unsupported requested received: {}", m.toString()); 362 reply=notSupportedReply(); 363 } 364 log.debug("generated reply {}",reply); 365 return reply; 366 } 367 368 // We have a few canned response messages. 369 370 /** 371 * Create an Unsupported XNetReply message. 372 */ 373 private XNetReply notSupportedReply() { 374 XNetReply r = new XNetReply(); 375 r.setOpCode(XNetConstants.CS_INFO); 376 r.setElement(1, XNetConstants.CS_NOT_SUPPORTED); 377 r.setElement(2, 0x00); // set the parity byte to 0 378 r.setParity(); 379 return r; 380 } 381 382 /** 383 * Create an OK XNetReply message. 384 */ 385 private XNetReply okReply() { 386 XNetReply r = new XNetReply(); 387 r.setOpCode(XNetConstants.LI_MESSAGE_RESPONSE_HEADER); 388 r.setElement(1, XNetConstants.LI_MESSAGE_RESPONSE_SEND_SUCCESS); 389 r.setElement(2, 0x00); // set the parity byte to 0 390 r.setParity(); 391 return r; 392 } 393 394 /** 395 * Create a "Normal Operations Resumed" message. 396 */ 397 private XNetReply normalOpsReply() { 398 XNetReply r = new XNetReply(); 399 r.setOpCode(XNetConstants.CS_INFO); 400 r.setElement(1, XNetConstants.BC_NORMAL_OPERATIONS); 401 r.setElement(2, 0x00); // set the parity byte to 0 402 r.setParity(); 403 return r; 404 } 405 406 /** 407 * Create a broadcast "Everything Off" reply. 408 */ 409 private XNetReply everythingOffReply() { 410 XNetReply r = new XNetReply(); 411 r.setOpCode(XNetConstants.CS_INFO); 412 r.setElement(1, XNetConstants.BC_EVERYTHING_OFF); 413 r.setElement(2, 0x00); // set the parity byte to 0 414 r.setParity(); 415 return r; 416 } 417 418 /** 419 * Create a broadcast "Emergecy Stop" reply. 420 */ 421 private XNetReply emergencyStopReply() { 422 XNetReply r = new XNetReply(); 423 r.setOpCode(XNetConstants.BC_EMERGENCY_STOP); 424 r.setElement(1, XNetConstants.BC_EVERYTHING_STOP); 425 r.setElement(2, 0x00); // set the parity byte to 0 426 r.setParity(); 427 return r; 428 } 429 430 /** 431 * Create a reply to a request for the XpressNet Version. 432 */ 433 private XNetReply xNetVersionReply(){ 434 XNetReply reply=new XNetReply(); 435 reply.setOpCode(XNetConstants.CS_SERVICE_MODE_RESPONSE); 436 reply.setElement(1, XNetConstants.CS_SOFTWARE_VERSION); 437 reply.setElement(2, 0x30 & 0xff); // indicate we are version 3.0 438 reply.setElement(3, 0x12 & 0xff); // indicate we are a Z21; 439 reply.setElement(4, 0x00); // set the parity byte to 0 440 reply.setParity(); 441 return reply; 442 } 443 444 /** 445 * Create a reply to a request for the Command Station Status. 446 */ 447 private XNetReply csStatusReply(){ 448 XNetReply reply=new XNetReply(); 449 reply.setOpCode(XNetConstants.CS_REQUEST_RESPONSE); 450 reply.setElement(1, XNetConstants.CS_STATUS_RESPONSE); 451 reply.setElement(2, csStatus); 452 reply.setElement(3, 0x00); // set the parity byte to 0 453 reply.setParity(); 454 return reply; 455 } 456 457 /** 458 * Create a LAN_X_TURNOUT_INFO reply. 459 */ 460 private XNetReply lanXTurnoutInfoReply(int FAdr_MSB,int FAdr_LSB,boolean thrown){ 461 XNetReply reply=new XNetReply(); 462 reply.setOpCode(Z21Constants.LAN_X_TURNOUT_INFO); 463 reply.setElement(1, FAdr_MSB & 0xff ); 464 reply.setElement(2, FAdr_LSB & 0xff ); 465 reply.setElement(3, thrown?0x02:0x01); // the turnout direction. 466 reply.setElement(4, 0x00); // set the parity byte to 0. 467 reply.setParity(); 468 return reply; 469 } 470 471 private final static Logger log = LoggerFactory.getLogger(Z21XNetSimulatorAdapter.class); 472}