001package jmri.jmrix.loconet; 002 003import org.slf4j.Logger; 004import org.slf4j.LoggerFactory; 005 006/** 007 * Only change compared to standard LocoNet SlotManager is CV programming. The 008 * Uhlenbrock IB-COM / Intellibox II uses some special and undocumented means 009 * (OPC_IMM_PACKET). 010 * 011 * {@code PC -> IB: BB 7F 00 3B OPC_RQ_SL_DATA, 127(Command Station Options ), 0 012 * IB -> PC: B4 3B 00 70 OPC_LONG_ACK, on OPC_RQ_SL_DATA, 0} 013 * 014 * {@code # start of programming session PC -> IB: E5 07 01 49 42 41 56 015 * OPC_PEER_XFER, src=7, dst=9345, ?? PC -> IB: 82 7D OPC_GPOFF} 016 * 017 * {@code # read cv 1 R CV CV PC -> IB: ED 1F 01 49 42 71 72 01 00 00 70 00 00 018 * 00 00 10 OPC_IMM_PACKET 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 IB -> 019 * PC: B4 6D 01 27 OPC_LONG_ACK, on OPC_IMM_PACKET # cv 1 has value 3 VV IB -> 020 * PC: E7 0E 7C 00 00 00 72 06 00 00 03 00 00 1D OPC_SL_RD_DATA, len, PT slot,} 021 * 022 * {@code # end off programming session PC -> IB: E5 07 01 49 42 40 57 023 * OPC_PEER_XFER, src=7, dst=} 024 * 025 * {@code # start of programming session PC -> IB: E5 07 01 49 42 41 56 026 * OPC_PEER_XFER, src=7, dst= PC -> IB: 82 7D OPC_GPOFF} 027 * 028 * {@code # write cv 1 W CV CV VV PC -> IB: ED 1F 01 49 42 71 71 01 00 03 70 00 029 * 00 00 00 10 OPC_IMM_PACKET 00 00 00 00 00 00 00 00 00 00 00 00 00 00 65 IB 030 * -> PC: B4 6D 01 27 OPC_LONG_ACK, on OPC_IMM_PACKET # cv 1 has value 3 VV IB 031 * -> PC: E7 0E 7C 00 00 00 71 06 00 00 03 00 00 1E OPC_SL_RD_DATA, len, PT 032 * slot,} 033 * 034 * {@code # end off programming session PC -> IB: E5 07 01 49 42 40 57 035 * OPC_PEER_XFER, src=7, dst=} 036 * 037 * {@code # write 254 in cv 27 HB W CV CV VV ED 1F 01 49 42 79 71 1B 00 7E 70 00 038 * 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 0A # HB = high bit for 039 * CV value} 040 * 041 * {@code # response HB VV E7 0E 7C 00 00 00 71 06 02 00 7E 00 00 61} 042 * 043 * {@code # write 255 in cv 545 HB W CV CV VV ED 1F 01 49 42 79 71 21 02 7F 70 044 * 00 00 00 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 33} 045 * 046 * {@code # read cv 393 HB R CV CV VV ED 1F 01 49 42 73 72 09 02 00 70 00 00 00 047 * 00 10 00 00 00 00 00 00 00 00 00 00 00 00 00 00 6D} 048 * 049 * @author Lisby Copyright (C) 2014 050 */ 051public class UhlenbrockSlotManager extends SlotManager { 052 053 public UhlenbrockSlotManager(LnTrafficController tc) { 054 super(tc); 055 } 056 057 /* 058 * NUM_SLOTS fixed for Uhlenbrock 059 */ 060 // private final int NUM_SLOTS = 128; 061 /** 062 * Provide Uhlenbrock-specific slot implementation 063 * @param initialize - not used by Uhlenbrock 064 */ 065 @Override 066 protected void loadSlots(boolean initialize) { 067 // initialize slot array 068 // TODO does UhlenBrock support extended slots? 069 for (int i = 0; i < getNumSlots(); i++) { 070 _slots[i] = new UhlenbrockSlot(i); 071 } 072 } 073 074 @Override 075 protected boolean checkLackByte1(int Byte1) { 076 // log.info("Uhlenbrock checkLackByte1 "+Byte1); 077 if ((Byte1 & 0xED) == 0x6D) { 078 return true; 079 } else { 080 return false; 081 } 082 } 083 084 @Override 085 protected boolean checkLackTaskAccepted(int Byte2) { 086 // log.info("Uhlenbrock checkLackTaskAccepted "+Byte2); 087 if (Byte2 == 1 // task accepted 088 || Byte2 == 0x23 || Byte2 == 0x2B || Byte2 == 0x6B)// added as DCS51 fix 089 { 090 return true; 091 } else { 092 return false; 093 } 094 } 095 096 @Override 097 protected boolean checkLackAcceptedBlind(int Byte2) { 098 // log.info("Uhlenbrock checkLackAcceptedBlind "+Byte2); 099 if (Byte2 == 0x40 || Byte2 == 0x7F) { 100 return true; 101 } else { 102 return false; 103 } 104 } 105 106 /** 107 * Look for IB-specific messages on the LocoNet, deferring all others to the 108 * parent SlotManager implementation. 109 * 110 * @param m incoming message 111 */ 112 @Override 113 public void message(LocoNetMessage m) { 114 115 // see if message for Intellibox-II functions F9 thru F12 116 if (m.getOpCode() == LnConstants.RE_OPC_IB2_F9_F12) { 117 UhlenbrockSlot slot = (UhlenbrockSlot) slot(m.getElement(1)); 118 slot.iB2functionMessage(m); 119 } 120 121 // see if message for Intellibox_I and -II functions F9 thru F128 122 if (m.getOpCode() == LnConstants.OPC_EXP_SLOT_MOVE_RE_OPC_IB2_SPECIAL && m.getElement(1) == LnConstants.RE_IB2_SPECIAL_FUNCS_TOKEN) { 123 UhlenbrockSlot slot = (UhlenbrockSlot) slot(m.getElement(2)); 124 slot.iBfunctionMessage(m); 125 } 126 127 super.message(m); 128 } 129 130 /** 131 * Internal method to create the LocoNetMessage for programming on main The 132 * table below contains value observed from an Intellibox II when doing 133 * programming on main. 134 * 135 * {@literal 136 * Address CV Value Element Decimal Hex Decimal Hex Decimal Hex 4 5 137 * 60 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 1 138 * 01 2 02 1 01 ED 1F 1 49 42 71 5E 1 0 2 70 0 1 0 0 10 0 0 0 0 0 0 0 0 0 0 139 * 0 0 0 0 4A 1 01 115 73 127 7F ED 1F 1 49 42 71 5E 1 0 73 70 0 7F 0 0 10 0 140 * 0 0 0 0 0 0 0 0 0 0 0 0 0 45 56 38 2 02 1 01 ED 1F 1 49 42 71 5E 38 0 2 141 * 70 0 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 73 87 57 255 FF 1 01 ED 1F 1 49 142 * 42 79 5E 57 0 7F 70 0 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 69 87 57 255 143 * FF 127 7F ED 1F 1 49 42 79 5E 57 0 7F 70 0 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 144 * 0 0 0 0 17 87 57 255 FF 255 FF ED 1F 1 49 42 79 5E 57 0 7F 72 0 7F 0 0 10 145 * 0 0 0 0 0 0 0 0 0 0 0 0 0 0 15 87 57 256 100 1 01 ED 1F 1 49 42 71 5E 57 146 * 0 0 70 1 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1F 87 57 256 100 127 7F ED 147 * 1F 1 49 42 71 5E 57 0 0 70 1 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 61 87 148 * 57 256 100 255 FF ED 1F 1 49 42 71 5E 57 0 0 72 1 7F 0 0 10 0 0 0 0 0 0 0 149 * 0 0 0 0 0 0 0 63 87 57 513 201 1 01 ED 1F 1 49 42 71 5E 57 0 1 70 2 1 0 0 150 * 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1D 87 57 513 201 127 7F ED 1F 1 49 42 71 151 * 5E 57 0 1 70 2 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 63 87 57 513 201 255 152 * FF ED 1F 1 49 42 71 5E 57 0 1 72 2 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 153 * 61 87 57 1024 400 1 01 ED 1F 1 49 42 71 5E 57 0 0 70 4 1 0 0 10 0 0 0 0 0 154 * 0 0 0 0 0 0 0 0 0 1A 87 57 1024 400 127 7F ED 1F 1 49 42 71 5E 57 0 0 70 155 * 4 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 64 87 57 1024 400 255 FF ED 1F 1 156 * 49 42 71 5E 57 0 0 72 4 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 66 120 78 157 * 127 7F 127 7F ED 1F 1 49 42 71 5E 78 0 7F 70 0 7F 0 0 10 0 0 0 0 0 0 0 0 158 * 0 0 0 0 0 0 30 120 78 255 FF 127 7F ED 1F 1 49 42 79 5E 78 0 7F 70 0 7F 0 159 * 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 38 120 78 255 FF 128 80 ED 1F 1 49 42 79 160 * 5E 78 0 7F 72 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 45 120 78 255 FF 254 161 * FE ED 1F 1 49 42 79 5E 78 0 7F 72 0 7E 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 162 * 3B 120 78 255 FF 255 FF ED 1F 1 49 42 79 5E 78 0 7F 72 0 7F 0 0 10 0 0 0 163 * 0 0 0 0 0 0 0 0 0 0 0 3A 127 7F 3 03 1 01 ED 1F 1 49 42 71 5E 7F 0 3 70 0 164 * 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 35 127 7F 3 03 127 7F ED 1F 1 49 42 165 * 71 5E 7F 0 3 70 0 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 4B 127 7F 3 03 166 * 128 80 ED 1F 1 49 42 71 5E 7F 0 3 72 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 167 * 0 36 255 FF 3 03 1 01 ED 1F 1 49 42 73 5E 7F 0 3 70 0 1 0 0 10 0 0 0 0 0 168 * 0 0 0 0 0 0 0 0 0 37 255 FF 255 FF 127 7F ED 1F 1 49 42 7B 5E 7F 0 7F 70 169 * 0 7F 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 3D 255 FF 255 FF 128 80 ED 1F 1 170 * 49 42 7B 5E 7F 0 7F 72 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 40 256 100 171 * 2 02 1 01 ED 1F 1 49 42 71 5E 0 1 2 70 0 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 172 * 0 0 4A 256 100 255 FF 255 FF ED 1F 1 49 42 79 5E 0 1 7F 72 0 7F 0 0 10 0 173 * 0 0 0 0 0 0 0 0 0 0 0 0 0 43 1000 3E8 3 03 1 01 ED 1F 1 49 42 73 5E 68 3 174 * 3 70 0 1 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 23 175 * } 176 * <p> 177 * Element 0: OPC_IMM_PACKET Element 1: Always 1F Element 2: Always 01 178 * Element 3: Always 49 Element 4: Always 42 Element 5: Basic value 71. Bit 179 * 1 (value 02) equals bit 7 in address. Bit 3 (value 08) equals bit 7 in CV 180 * number. Element 6: 5E=Write on main, 71=Write on PT, 72=Read on PT 181 * Element 7: LOPSA, i.e. Bit 0-6 of address Element 8: Bit 8-14 of address. 182 * This is not equal to HOPSA, since bit 7 is stored in element 5. Element 183 * 9: Bit 0-6 of CV number. Element 10: Basic value 70. Bit 1 (value 02) 184 * equals bit 7 in CV value. Element 11: Bit 8-11 of CV number. Element 12: 185 * Bit 0-6 in CV value. Element 15: Always 10 Element 30: Checksum 186 * 187 * @param hopsa high status byte for message 188 * @param lopsa low status byte for message 189 * @param val Value for programming operation 190 * @param cvnum CV number for programming operation 191 * @return formatted message 192 */ 193 protected LocoNetMessage progOnMainMessage(int hopsa, int lopsa, int val, int cvnum) { 194 LocoNetMessage m = new LocoNetMessage(0x1F); 195 m.setOpCode(LnConstants.OPC_IMM_PACKET); 196 m.setElement(1, 0x1F); 197 m.setElement(2, 0x01); 198 m.setElement(3, 0x49); 199 m.setElement(4, 0x42); 200 m.setElement(5, 0x71 | (hopsa & 0x01) << 1 | (cvnum & 0x80) >> 4); 201 m.setElement(6, 0x5E); 202 m.setElement(7, lopsa); 203 m.setElement(8, hopsa / 2); 204 m.setElement(9, cvnum & 0x7F); 205 m.setElement(10, 0x70 | ((val & 0x80) >> 6)); 206 m.setElement(11, cvnum / 256); 207 m.setElement(12, val & 0x7F); 208 m.setElement(15, 0x10); 209 return m; 210 } 211 212 /** 213 * Internal method to create the LocoNetMessage for programming on 214 * programming track. The table below contains value observed from an 215 * Intellibox II when doing programming on programming track. 216 * 217 * {@literal 218 * Operation CV Value Byte # Decimal Hex Decimal Hex 0 1 2 3 4 5 6 7 8 9 10 219 * 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 Read 1 01 ED 220 * 1F 1 49 42 71 72 1 0 0 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65 Write 221 * 1 01 3 03 ED 1F 1 49 42 71 71 1 0 3 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 222 * 0 0 65 Write 27 1B 254 FE ED 1F 1 49 42 79 71 1B 0 7E 70 0 0 0 0 10 0 0 0 223 * 0 0 0 0 0 0 0 0 0 0 0 0A Write 545 221 255 FF ED 1F 1 49 42 79 71 21 2 7F 224 * 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 33 Read 393 189 ED 1F 1 49 42 225 * 73 72 9 1 0 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6D Write n 226 * 01 ED 1F 1 49 42 73 71 7F 0 1 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 227 * 1B Write 255 FF 127 7F ED 1F 1 49 42 73 71 7F 0 7F 70 0 0 0 0 10 0 0 0 0 228 * 0 0 0 0 0 0 0 0 0 0 65 Write 255 FF 255 FF ED 1F 1 49 42 7B 71 7F 0 7F 70 229 * 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 6D Write 256 100 1 01 ED 1F 1 49 230 * 42 71 71 0 1 1 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 67 Write 256 100 231 * 127 7F ED 1F 1 49 42 71 71 0 1 7F 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 232 * 0 19 Write 256 100 255 FF ED 1F 1 49 42 79 71 0 1 7F 70 0 0 0 0 10 0 0 0 233 * 0 0 0 0 0 0 0 0 0 0 0 11 Write 513 201 1 01 ED 1F 1 49 42 71 71 1 2 1 70 234 * 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 65 Write 513 201 127 7F ED 1F 1 49 235 * 42 71 71 1 2 7F 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1B Write 513 236 * 201 255 FF ED 1F 1 49 42 79 71 1 2 7F 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 237 * 0 0 0 13 Write 1024 400 1 01 ED 1F 1 49 42 71 71 0 4 1 70 0 0 0 0 10 0 0 238 * 0 0 0 0 0 0 0 0 0 0 0 0 62 Write 1024 400 127 7F ED 1F 1 49 42 71 71 0 4 239 * 7F 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1C Write 1024 400 255 FF ED 240 * 1F 1 49 42 79 71 0 4 7F 70 0 0 0 0 10 0 0 0 0 0 0 0 0 0 0 0 0 0 0 14 241 * } 242 * <p> 243 * Element 0: OPC_IMM_PACKET Element 1: Always 1F Element 2: Always 01 244 * Element 3: Always 49 Element 4: Always 42 Element 5: Basic value 71. Bit 245 * 1 (value 02) equals bit 7 in CV number. Bit 3 (value 08) equals bit 7 in 246 * CV value. Element 6: 5E=Write on main, 71=Write on PT, 72=Read on PT 247 * Element 7: Bit 0-6 of CV number Element 8: Bit 8-11 of CV number. Element 248 * 9: Bit 0-6 of CV value. Element 10: Always 70. Element 15: Always 10 249 * Element 30: Checksum 250 * 251 * @param element6 Byte from message 252 * @param val Value for programming operation 253 * @param cvnum CV number for programming operation 254 * @return formatted message 255 */ 256 protected LocoNetMessage progOnProgrammingTrackMessage(int element6, int val, int cvnum) { 257 // log.error("About to create read/write CV command for IB-COM.", new Excception()); 258 LocoNetMessage m = new LocoNetMessage(0x1F); 259//log.info("--------SENDING OPC_IMM_PACKET TO IB-COM PROGRAMMING TRACK. pcmd=" + pcmd + "val="+val+" cvnum="+cvnum+" write="+write); 260 m.setOpCode(LnConstants.OPC_IMM_PACKET); 261 m.setElement(1, 0x1F); 262 m.setElement(2, 0x01); 263 m.setElement(3, 0x49); 264 m.setElement(4, 0x42); 265 m.setElement(5, 0x71 | (val & 0x80) >> 4 | (cvnum & 0x80) >> 6); 266 m.setElement(6, element6); 267 m.setElement(7, cvnum & 0x7F); 268 m.setElement(8, cvnum / 256); 269 m.setElement(9, val & 0x7F); 270 m.setElement(10, 0x70); 271 m.setElement(15, 0x10); 272 return m; 273 } 274 275 /* 276 * Internal method to create the LocoNetMessage for programmer task start 277 * @param pcmd command byte values 278 * @param val Value to write if writing 279 * @parma cvnum CV number to access 280 * @param write is this a write? 281 * @return Message to send to do this function 282 */ 283 @Override 284 protected LocoNetMessage progTaskStart(int pcmd, int val, int cvnum, boolean write) { 285 switch (pcmd) { 286 case 0x67: 287 return progOnMainMessage(hopsa, lopsa, val, cvnum); // write on main 288 289 case 0x63: 290 return progOnProgrammingTrackMessage(0x6F, val, cvnum); // write on PT in PageMode 291 case 0x6B: 292 return progOnProgrammingTrackMessage(0x71, val, cvnum); // write on PT in DirectByteMode 293 case 0x53: 294 return progOnProgrammingTrackMessage(0x6D, val, cvnum); // write on PT in RegisterMode or AddressMode 295 296 case 0x23: 297 return progOnProgrammingTrackMessage(0x6E, 0, cvnum); // read on PT in PageMode 298 case 0x2B: 299 return progOnProgrammingTrackMessage(0x72, 0, cvnum); // read on PT in DirectByteMode 300 case 0x13: 301 return progOnProgrammingTrackMessage(0x6C, 0, cvnum); // read on PT in RegisterMoode or AddressMode 302 default: 303 log.warn("Unhandled programming type: {}", pcmd); 304 break; 305 } 306 // We are probably being asked to read CV on main track, which is not suppoorted by IB. So get out of programming mode. 307 return stopIBComPT(); 308 } 309 310 /** 311 * Internal method to create the LocoNetMessage for enabling programming 312 * track in IB-COM / Intellibox II Note: This method is specific to 313 * Uhlenbrock 314 * @return Message to send to do this function 315 */ 316 protected LocoNetMessage startIBComPT() { 317 // log.error("About to initiate programming track for IB-COM.", new Exception()); 318 319 LocoNetMessage m = new LocoNetMessage(7); 320//log.info("--------startIBComPT"); 321 m.setOpCode(LnConstants.OPC_PEER_XFER); 322 m.setElement(1, 0x07); 323 m.setElement(2, 0x01); 324 m.setElement(3, 0x49); 325 m.setElement(4, 0x42); 326 m.setElement(5, 0x41); 327 return m; 328 } 329 330 /** 331 * Internal method to create the LocoNetMessage for disabling programming 332 * track in IB-COM / Intellibox II Note: This method is currently not used 333 * @return Message to send to do this function 334 */ 335 protected LocoNetMessage stopIBComPT() { 336 // log.error("About to stop using programming track for IB-COM.", new Exception()); 337 338 LocoNetMessage m = new LocoNetMessage(7); 339 340 m.setOpCode(LnConstants.OPC_PEER_XFER); 341 m.setElement(1, 0x07); 342 m.setElement(2, 0x01); 343 m.setElement(3, 0x49); 344 m.setElement(4, 0x42); 345 m.setElement(5, 0x40); 346 return m; 347 } 348 349 // internal method to remember who's using the programmer 350 // Note: Overridden in order to also call the startIBComPT method 351 @Override 352 protected void useProgrammer(jmri.ProgListener p) throws jmri.ProgrammerException { 353 super.useProgrammer(p); 354 tc.sendLocoNetMessage(startIBComPT()); 355 } 356 357 // A couple of seconds after the last programming command, power is meant to be turned on. 358 // However, for the Uhlenbrock IB-COM / Intellibox II, the command station is taken out of programming mode instead. 359 @Override 360 synchronized protected void doEndOfProgramming() { 361 log.debug("Uhlenbrock doEndOfProgramming"); 362 tc.sendLocoNetMessage(stopIBComPT()); 363 } 364 365 // initialize logging 366 private final static Logger log = LoggerFactory.getLogger(UhlenbrockSlotManager.class); 367 368}