001package jmri.jmrix.can.cbus; 002 003import javax.annotation.CheckForNull; 004import javax.annotation.Nonnull; 005import jmri.jmrix.can.CanMessage; 006import jmri.jmrix.can.CanReply; 007import jmri.jmrix.can.CanSystemConnectionMemo; 008import jmri.jmrix.can.TrafficController; 009import jmri.jmrix.can.cbus.node.CbusNodeTableDataModel; 010import jmri.util.swing.TextAreaFIFO; 011import jmri.util.ThreadingUtil; 012 013/** 014 * Class to send CAN Frames. 015 * <p> 016 * Auto adds CBUS priority. 017 * 018 * @author Steve Young (C) 2019 019 */ 020public class CbusSend { 021 022 private TrafficController tc; 023 private final CanSystemConnectionMemo memo; 024 private final TextAreaFIFO ta; 025 private final String newLine = System.getProperty("line.separator"); 026 027 /** 028 * Constructor 029 * @param systemMemo System Connection 030 * @param txta a Text Area for any feedback messages 031 */ 032 public CbusSend( @Nonnull CanSystemConnectionMemo systemMemo, TextAreaFIFO txta){ 033 memo = systemMemo; 034 tc = memo.getTrafficController(); 035 ta = txta; 036 } 037 038 /** 039 * Constructor 040 * @param systemMemo System Connection 041 */ 042 public CbusSend(CanSystemConnectionMemo systemMemo){ 043 memo = systemMemo; 044 if (memo!=null) { 045 tc = memo.getTrafficController(); 046 } 047 ta = null; 048 } 049 050 /** 051 * Sends an outgoing CanMessage or incoming CanReply from a CanReply with a specified delay 052 * @param r A CanReply Can Frame which will be sent 053 * @param sendReply true to send as incoming CcanReply 054 * @param sendMessage true to send as outgoing CanMessage 055 * @param delay delay in ms 056 */ 057 public void sendWithDelay( CanReply r, Boolean sendReply, Boolean sendMessage, int delay ){ 058 CbusMessage.setId(r, tc.getCanid() ); 059 CbusMessage.setPri(r, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 060 ThreadingUtil.runOnLayoutDelayed( () -> { 061 if (sendReply) { 062 tc.sendCanReply(r, null); 063 } 064 if (sendMessage) { 065 CanMessage m = new CanMessage(r); 066 tc.sendCanMessage(m, null); 067 } 068 },delay ); 069 } 070 071 /** 072 * Sends NNULN OPC , node exit learn mode 073 * @param nn Node Number 074 */ 075 public void nodeExitLearnEvMode( int nn ) { 076 CanMessage m = new CanMessage(tc.getCanid()); 077 m.setNumDataElements(3); 078 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 079 m.setElement(0, CbusConstants.CBUS_NNULN); 080 m.setElement(1, nn >> 8); 081 m.setElement(2, nn & 0xff); 082 tc.sendCanMessage(m, null); 083 if (ta != null){ 084 ta.append(newLine + Bundle.getMessage("NdReqExitLearn", nn)); // future lookup from node table 085 } 086 } 087 088 /** 089 * Sends NNLRN OPC , node enter learn mode. 090 * @param nn Node Number 091 */ 092 public void nodeEnterLearnEvMode( int nn ) { 093 CanMessage m = new CanMessage(tc.getCanid()); 094 m.setNumDataElements(3); 095 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 096 m.setElement(0, CbusConstants.CBUS_NNLRN); 097 m.setElement(1, nn >> 8); 098 m.setElement(2, nn & 0xff); 099 tc.sendCanMessage(m, null); 100 if (ta != null){ 101 ta.append(newLine + Bundle.getMessage("NdReqEnterLearn", nn)); 102 } 103 } 104 105 /** 106 * Sends SNN OPC , Set Node Number to Node in Setup. 107 * @param nn Node Number 108 */ 109 public void nodeSetNodeNumber( int nn ) { 110 CanMessage mn = new CanMessage(tc.getCanid()); 111 mn.setNumDataElements(3); 112 CbusMessage.setPri(mn, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 113 mn.setElement(0, CbusConstants.CBUS_SNN); 114 mn.setElement(1, nn >> 8); 115 mn.setElement(2, nn & 0xff); 116 tc.sendCanMessage(mn, null); 117 } 118 119 /** 120 * Sends RQNP OPC , Request Parameters from node in setup. 121 */ 122 public void nodeRequestParamSetup() { 123 CanMessage m = new CanMessage(tc.getCanid()); 124 m.setNumDataElements(1); 125 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 126 m.setElement(0, CbusConstants.CBUS_RQNP); 127 tc.sendCanMessage(m, null); 128 } 129 130 /** 131 * Sends EVLRN OPC , Event Learn 132 * when a node is in learn mode. 133 * @param newvalnd event variable node 134 * @param newevent event variable event 135 * @param varindex event variable index 136 * @param newval event variable value 137 */ 138 public void nodeTeachEventLearnMode(int newvalnd, int newevent, int varindex, int newval) { 139 CanMessage m = new CanMessage(tc.getCanid()); 140 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 141 m.setNumDataElements(7); 142 m.setElement(0, CbusConstants.CBUS_EVLRN); 143 m.setElement(1, newvalnd >> 8); 144 m.setElement(2, newvalnd & 0xff); 145 m.setElement(3, newevent >> 8); 146 m.setElement(4, newevent & 0xff); 147 m.setElement(5, varindex); 148 m.setElement(6, newval); 149 tc.sendCanMessage(m, null); 150 } 151 152 /** 153 * Sends EVULN OPC , Event Unlearn. 154 * when a node is in learn mode 155 * @param newvalnd event variable node 156 * @param newevent event variable event 157 */ 158 public void nodeUnlearnEvent( int newvalnd, int newevent ){ 159 CanMessage m = new CanMessage(tc.getCanid()); 160 m.setNumDataElements(5); 161 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 162 m.setElement(0, CbusConstants.CBUS_EVULN); 163 m.setElement(1, newvalnd >> 8); 164 m.setElement(2, newvalnd & 0xff); 165 m.setElement(3, newevent >> 8); 166 m.setElement(4, newevent & 0xff); 167 tc.sendCanMessage(m, null); 168 } 169 170 /** 171 * Sends REVAL OPC , Request for read of an event variable. 172 * @param nodeinsetup Node Number 173 * @param nextev event index number 174 * @param nextevvar event variable number 175 */ 176 public void rEVAL( int nodeinsetup, int nextev, int nextevvar ){ 177 CanMessage m = new CanMessage(tc.getCanid()); 178 m.setNumDataElements(5); 179 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 180 m.setElement(0, CbusConstants.CBUS_REVAL); 181 m.setElement(1, nodeinsetup >> 8); 182 m.setElement(2, nodeinsetup & 0xff); 183 m.setElement(3, nextev); 184 m.setElement(4, nextevvar); 185 tc.sendCanMessage(m, null); 186 } 187 188 /** 189 * Sends RQNPN OPC , Request read of a node parameter by index. 190 * @param nodeinsetup Node Number 191 * @param nextnodeparam parameter index number 192 */ 193 public void rQNPN( int nodeinsetup, int nextnodeparam ) { 194 CanMessage m = new CanMessage(tc.getCanid()); 195 m.setNumDataElements(4); 196 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 197 m.setElement(0, CbusConstants.CBUS_RQNPN); 198 m.setElement(1, nodeinsetup >> 8); 199 m.setElement(2, nodeinsetup & 0xff); 200 m.setElement(3, nextnodeparam); // 0 gets total parameters for this module 201 tc.sendCanMessage(m, null); 202 } 203 204 /** 205 * Sends CanMessage QNN OPC to get all nodes. 206 */ 207 public void searchForNodes() { 208 CanMessage m = new CanMessage(tc.getCanid()); 209 m.setNumDataElements(1); 210 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 211 m.setElement(0, CbusConstants.CBUS_QNN); 212 tc.sendCanMessage(m, null); 213 } 214 215 /** 216 * Sends an RSTAT message to request details of any connected command stations. 217 * Responses are received by the CBUS node table. 218 */ 219 public void searchForCommandStations(){ 220 CanMessage m = new CanMessage(tc.getCanid()); 221 m.setNumDataElements(1); 222 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 223 m.setElement(0, CbusConstants.CBUS_RSTAT); 224 tc.sendCanMessage(m, null); 225 } 226 227 /** 228 * Sends NVRD OPC , Request read of a node variable. 229 * @param nodeinsetup Node Number 230 * @param nextnodenv variable number 231 */ 232 public void nVRD( int nodeinsetup, int nextnodenv ) { 233 CanMessage m = new CanMessage(tc.getCanid()); 234 m.setNumDataElements(4); 235 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 236 m.setElement(0, CbusConstants.CBUS_NVRD); 237 m.setElement(1, nodeinsetup >> 8); 238 m.setElement(2, nodeinsetup & 0xff); 239 m.setElement(3, nextnodenv); // get total parameters for this module 240 tc.sendCanMessage(m, null); 241 } 242 243 /** 244 * Sends NVSET OPC , Node set individual NV. 245 * If (contrary to CBUS spec), the node is required to be in Event Learn Mode 246 * before setting a NV, this will be done within this function, 247 * assuming that the node is visible to the memo CbusNodeTableDataModel . 248 * @param nodeinsetup Node Number 249 * @param nv Node variable number 250 * @param newval Node variable number value 251 */ 252 public void nVSET(int nodeinsetup,int nv,int newval ) { 253 254 if ( getNvWriteLearnMode(nodeinsetup) ) { 255 nodeEnterLearnEvMode(nodeinsetup); 256 } 257 258 CanMessage m = new CanMessage(tc.getCanid()); 259 m.setNumDataElements(5); 260 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 261 m.setElement(0, CbusConstants.CBUS_NVSET); 262 m.setElement(1, nodeinsetup >> 8); 263 m.setElement(2, nodeinsetup & 0xff); 264 m.setElement(3, nv); 265 m.setElement(4, newval); 266 tc.sendCanMessage(m, null); 267 268 if ( getNvWriteLearnMode(nodeinsetup) ) { 269 nodeExitLearnEvMode(nodeinsetup); 270 } 271 } 272 273 /** 274 * Test if Node needs to be in learn mode for teaching NVs. 275 * @param nodeinsetup node number 276 * @return if required, else false. 277 */ 278 private boolean getNvWriteLearnMode(int nodeinsetup) { 279 CbusNodeTableDataModel model = getNodeModel(); 280 if ( model !=null ) { 281 jmri.jmrix.can.cbus.node.CbusNode nd = model.getNodeByNodeNum(nodeinsetup); 282 if ( nd !=null ) { 283 return nd.getnvWriteInLearnOnly(); 284 } 285 } 286 return false; 287 } 288 289 @CheckForNull 290 private CbusNodeTableDataModel getNodeModel(){ 291 if ( memo != null ) { 292 return memo.getFromMap(CbusNodeTableDataModel.class); 293 } 294 return null; 295 } 296 297 /** 298 * Sends RQEVN OPC , Read number of stored events in node. 299 * <p> 300 * nb, NOT max events capable 301 * @param nodeinsetup Node Number 302 */ 303 public void rQEVN( int nodeinsetup ) { 304 CanMessage m = new CanMessage(tc.getCanid()); 305 m.setNumDataElements(3); 306 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 307 m.setElement(0, CbusConstants.CBUS_RQEVN); 308 m.setElement(1, nodeinsetup >> 8); 309 m.setElement(2, nodeinsetup & 0xff); 310 tc.sendCanMessage(m, null); 311 } 312 313 /** 314 * Sends NERD OPC , Request to read all node events. 315 * @param nodeinsetup Node Number 316 */ 317 public void nERD(int nodeinsetup ){ 318 CanMessage m = new CanMessage(tc.getCanid()); 319 m.setNumDataElements(3); 320 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 321 m.setElement(0, CbusConstants.CBUS_NERD); 322 m.setElement(1, nodeinsetup >> 8); 323 m.setElement(2, nodeinsetup & 0xff); 324 tc.sendCanMessage(m, null); 325 } 326 327 /** 328 * Sends a System Reset ARST OPC. 329 * Full system reset 330 */ 331 public void aRST(){ 332 CanMessage m = new CanMessage(tc.getCanid()); 333 m.setNumDataElements(1); 334 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 335 m.setElement(0, CbusConstants.CBUS_ARST); 336 tc.sendCanMessage(m, null); 337 } 338 339 /** 340 * Sends ENUM OPC , Force a self enumeration cycle for use with CAN. 341 * @param nodeinsetup Node Number 342 */ 343 public void eNUM(int nodeinsetup ){ 344 CanMessage m = new CanMessage(tc.getCanid()); 345 m.setNumDataElements(3); 346 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 347 m.setElement(0, CbusConstants.CBUS_ENUM); 348 m.setElement(1, nodeinsetup >> 8); 349 m.setElement(2, nodeinsetup & 0xff); 350 tc.sendCanMessage(m, null); 351 } 352 353 /** 354 * Sends CANID OPC , Teach node a specific CANID. 355 * @param nodeinsetup Node Number 356 * @param canid new CAN ID ( min 1, max 99 ) 357 */ 358 public void cANID(int nodeinsetup, int canid ){ 359 CanMessage m = new CanMessage(tc.getCanid()); 360 m.setNumDataElements(4); 361 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 362 m.setElement(0, CbusConstants.CBUS_CANID); 363 m.setElement(1, nodeinsetup >> 8); 364 m.setElement(2, nodeinsetup & 0xff); 365 m.setElement(3, canid & 0xff); 366 tc.sendCanMessage(m, null); 367 } 368 369 370 /** 371 * Sends NNCLR OPC , Clear all events from a node. 372 * <p> 373 * Node must be in Learn Mode to take effect 374 * @param nodeinsetup Node Number 375 * 376 */ 377 public void nNCLR(int nodeinsetup ){ 378 CanMessage m = new CanMessage(tc.getCanid()); 379 m.setNumDataElements(3); 380 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 381 m.setElement(0, CbusConstants.CBUS_NNCLR); 382 m.setElement(1, nodeinsetup >> 8); 383 m.setElement(2, nodeinsetup & 0xff); 384 tc.sendCanMessage(m, null); 385 } 386 387 /** 388 * Sends RQMN OPC , Request name from node. 389 * <p> 390 * Node must be in Setup Mode to take effect 391 */ 392 public void rQmn(){ 393 CanMessage m = new CanMessage(tc.getCanid()); 394 m.setNumDataElements(1); 395 CbusMessage.setPri(m, CbusConstants.DEFAULT_DYNAMIC_PRIORITY * 4 + CbusConstants.DEFAULT_MINOR_PRIORITY); 396 m.setElement(0, CbusConstants.CBUS_RQMN); 397 tc.sendCanMessage(m, null); 398 } 399 400 // private final static org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusSend.class); 401}