/
CbusNode.java
2544 lines (2250 loc) · 82.5 KB
/
CbusNode.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
package jmri.jmrix.can.cbus.node;
import java.util.ArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.Iterator;
import javax.annotation.Nonnull;
import jmri.jmrix.can.CanListener;
import jmri.jmrix.can.CanMessage;
import jmri.jmrix.can.CanReply;
import jmri.jmrix.can.CanSystemConnectionMemo;
import jmri.jmrix.can.cbus.CbusConstants;
import jmri.jmrix.can.cbus.CbusMessage;
import jmri.jmrix.can.cbus.CbusSend;
import jmri.jmrix.can.cbus.node.CbusNodeConstants.BackupType;
import jmri.jmrix.can.cbus.swing.nodeconfig.CbusNodeEditEventFrame;
import jmri.jmrix.can.cbus.swing.nodeconfig.CbusNodeInfoPane;
import jmri.jmrix.can.cbus.swing.nodeconfig.NodeConfigToolPane;
import jmri.jmrix.can.cbus.swing.nodeconfig.CbusNodeBackupsPane.CbusNodeBackupTableModel;
import jmri.jmrix.can.TrafficController;
import jmri.util.ThreadingUtil;
import java.util.TimerTask;
import jmri.util.TimerUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Class to represent a node.
*
* @author Steve Young Copyright (C) 2019
*/
public class CbusNode implements CanListener {
private CanSystemConnectionMemo memo;
private TrafficController tc;
private int _nodeNumber;
private String _nodeUserName;
private String _nodeNameFromName;
private ArrayList<CbusNodeEvent> _nodeEvents;
private CopyOnWriteArraySet<CbusNodeNVTableDataModel> _nvTableModels;
private CopyOnWriteArraySet<CbusNodeEventTableDataModel> _evTableModels;
private CopyOnWriteArraySet<CbusNodeBackupTableModel> _backupTableModels;
private CopyOnWriteArraySet<CbusNodeInfoPane> _infoPanes;
private int _canId;
private int[] _nvArray;
private int[] _parameters;
private int _flags;
private int _fwMaj;
private int _fwMin;
private int _fwBuild;
private int _manu;
private int _type;
private boolean nodeTraitsSet;
private boolean _inSetupMode;
private boolean _inlearnMode;
private boolean _inFLiMMode;
private boolean _sendsWRACKonNVSET;
private boolean _eventIndexValid;
public CbusSend send;
private CbusNodeTableDataModel tableModel;
private int _csNum;
private boolean _StatResponseFlagsAccurate;
private boolean _commandStationIdentified;
public static int SINGLE_MESSAGE_TIMEOUT_TIME = 1500;
/**
* Create a new CbusNode
*
* @param connmemo The CAN Connection to use
* @param nodenumber The Node Number
*/
public CbusNode ( CanSystemConnectionMemo connmemo, int nodenumber ){
memo = connmemo;
_nodeNumber = nodenumber;
_nodeUserName = "";
_nodeNameFromName = "";
setFW( -1, -1, -1);
_parameters = null;
_canId = -1;
_nodeEvents = null;
_inSetupMode = false;
_inlearnMode = false;
_inFLiMMode = true;
_sendsWRACKonNVSET = true;
_csNum = -1;
_StatResponseFlagsAccurate=false;
_commandStationIdentified = false;
nodeTraitsSet = false;
_manu = -1;
_type = -1;
_eventIndexValid = false;
_flags = -1;
_nvTableModels = new CopyOnWriteArraySet<CbusNodeNVTableDataModel>();
_evTableModels = new CopyOnWriteArraySet<CbusNodeEventTableDataModel>();
_backupTableModels = new CopyOnWriteArraySet<CbusNodeBackupTableModel>();
_infoPanes = new CopyOnWriteArraySet<CbusNodeInfoPane>();
if (memo != null) {
tc = memo.getTrafficController();
tc.addCanListener(this);
}
send = new CbusSend(memo);
}
/**
* Set the main Node Table Model to receive updates when things change
* @param model the Node Table Model to get updates
*/
protected void setTableModel( CbusNodeTableDataModel model){
tableModel = model;
}
/**
* Add a Node Event Table Model to receive updates when events change
* <p>
* Multiple Node Event Tables can follow the same node
* @param model the Event Table Model to get updates
*/
protected void setNodeEventTable(CbusNodeEventTableDataModel model) {
_evTableModels.add(model);
}
/**
* Remove a Node Event Table Model
* @param model the Node Event Table Model to stop getting updates
*/
protected void removeNodeEventTable(CbusNodeEventTableDataModel model) {
_evTableModels.remove(model);
}
/**
* Add an NV Table Model to receive updates when NVs change
* <p>
* Multiple NV Tables can follow the same node
* @param model the NV Table Model to get updates
*/
protected void setNodeNVTable( CbusNodeNVTableDataModel model ) {
_nvTableModels.add(model);
}
/**
* Remove an NV Table Model
* @param model the NV Table Model to stop getting updates
*/
protected void removeNodeNVTable(CbusNodeNVTableDataModel model){
_nvTableModels.remove(model);
}
/**
* Add an Backup Table Model to receive updates when Backups change
* <p>
* Multiple Backup Tables can follow the same node
* @param model the Backup Table Model to get updates
*/
public void addBackupTableModel( CbusNodeBackupTableModel model ) {
_backupTableModels.add(model);
}
/**
* Remove a Backup Table Model to receive updates when Backups change
* <p>
* Multiple Backup Tables can follow the same node
* @param model the Backup Table Model to get updates
*/
public void removeBackupTableModel( CbusNodeBackupTableModel model ) {
_backupTableModels.remove(model);
}
/**
* Add a CbusNodeInfoPane to receive updates when parameters change
* Parameters are not editable though JMRI may not have yet downloaded from the node
* hence the need to update the GUI
* <p>
* Multiple Node Info Panes can follow the same node
* @param infopane the CbusNodeInfoPane to get updates
*/
public void addInfoPane( CbusNodeInfoPane infopane ) {
_infoPanes.add(infopane);
}
/**
* Remove a CbusNodeInfoPane to receive updates when parameters change
* @param infopane the CbusNodeInfoPane to stop getting updates
*/
public void removeInfoPane( CbusNodeInfoPane infopane ) {
_infoPanes.remove(infopane);
}
/**
* Node Type Name, if available.
* @return If Node Parameters 1 and 3 are set eg. "CANPAN", else potentially a NAME OPC return, else empty string.
*
*/
public String getNodeTypeName() {
if (!CbusNodeConstants.getModuleType(getParameter(1),getParameter(3)).isEmpty() ){
return CbusNodeConstants.getModuleType(getParameter(1),getParameter(3));
}
else {
return getNodeNameFromName();
}
}
/**
* Returns Node Number
*
*/
public int getNodeNumber() {
return _nodeNumber;
}
/**
* Returns node Username
* @return eg. "John Smith"
*
*/
public String getUserName() {
return _nodeUserName;
}
/**
* Get Node Number and name
* @return string eg "1234 UserName", no trailing space.
*/
public String getNodeNumberName() {
if ( !getUserName().isEmpty() ){
return "" + getNodeNumber() + " " + getUserName();
}
else if ( !getNodeTypeName().isEmpty() ){
return "" + getNodeNumber() + " " + getNodeTypeName();
}
else {
return String.valueOf(getNodeNumber());
}
}
/**
* Set Node UserName
* Updates Node XML File
* @param newName UserName of the node
*/
public void setUserName( String newName ) {
_nodeUserName = newName;
if (backupStarted) {
if (!thisNodeBackupFile.doStore(false) ) {
log.error("Unable to save Node Name to Node Backup File");
}
}
}
/**
* Set Node UserName only if UserName not currently set
* used in RestoreFromFCU
* @param newName UserName of the node
*/
public void setNameIfNoName( String newName ) {
if ( getUserName().isEmpty() ){
_nodeUserName = newName;
notifyModelIfExists(CbusNodeTableDataModel.NODE_USER_NAME_COLUMN);
}
}
/**
* Get Module Type Name from a previous NAME OPC response
* Used when a module type is not identified within JMRI.
* @return Module type name, NOT prefixed with CAN or ETH, may be empty.
*/
public String getNodeNameFromName(){
return _nodeNameFromName;
}
/**
* Set Module Type Name
* Used when a module is not identified within JMRI so the NAME is requested.
* @param newName The module type name, should NOT be prefixed with CAN or ETH
*/
public void setNodeNameFromName( String newName ){
_nodeNameFromName = newName;
notifyModelIfExists(CbusNodeTableDataModel.NODE_TYPE_NAME_COLUMN);
}
/**
* Set Node Number
* @param newnumber Node Number, should be 1-65535
*/
public void setNodeNumber ( int newnumber ) {
_nodeNumber = newnumber;
}
/**
* Set Node CAN ID
* @param newcanid CAN ID of the node
*/
public void setCanId ( int newcanid ) {
_canId = newcanid;
notifyModelIfExists(CbusNodeTableDataModel.CANID_COLUMN);
}
/**
* Node CAN ID, min 1 , ( max 128? )
* @return CAN ID of the node, -1 if unset
*/
public int getNodeCanId() {
return _canId;
}
public void setNodeFlags(int flags) {
_flags = flags;
}
/**
* Get Node Flags
* <p>
* Captured from a PNN node response
* @return flags in int form, will need decoding to bit, -1 if unset
*/
public int getNodeFlags() {
return _flags;
}
/**
* Node Parameters
* <p>
* Para 0 Number of parameters
* <p>
* Para 1 The manufacturer ID
* <p>
* Para 2 Minor code version as an alphabetic character (ASCII)
* <p>
* Para 3 Manufacturer module identifier as a HEX numeric
* <p>
* Para 4 Number of supported events as a HEX numeric
* <p>
* Para 5 Number of Event Variables per event as a HEX numeric
* <p>
* Para 6 Number of supported Node Variables as a HEX numeric
* <p>
* Para 7 Major version
* <p>
* Para 8 Node flags
* <p>
* Para 9 Processor type
* <p>
* Para 10 Bus type
* <p>
* Para 11-14 load address, 4 bytes
* <p>
* Para 15-18 CPU manufacturer's id as read from the chip config space, 4 bytes
* <p>
* Para 19 CPU manufacturer code
* <p>
* Para 20 Beta revision (numeric), or 0 if release
*
* @param newparams set the node parameters
*
*/
public void setParameters( int[] newparams ) {
// log.warn("new params {}",newparams);
_parameters = new int [(newparams[0]+1)];
for (int i = 0; i < _parameters.length; i++) {
setParameter(i,newparams[i]);
}
if ( getParameter(6) > -1 ) {
int [] myarray = new int[(getParameter(6)+1)]; // +1 to account for index 0 being the NV count
java.util.Arrays.fill(myarray, -1);
myarray[0] = getParameter(6);
setNVs(myarray);
}
}
/**
*
*
*/
public void setParameter( int index, int newval ) {
if ( _parameters == null ){
return;
}
log.debug("set parameter tot:{} index:{} newval:{}",_parameters.length,index,newval);
if ( index <= _parameters.length ) {
_parameters[index] = newval;
// log.info("set ok to {}",newval);
notifyModelIfExists(CbusNodeTableDataModel.NODE_TYPE_NAME_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.NODE_TOTAL_BYTES_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.BYTES_REMAINING_COLUMN);
notifyInfoPane();
}
}
/**
* Get Number of outstanding unknown Parameters to be fetched from a CbusNode
*
* @return Number of outstanding Paramteters, else 8
*/
public int getOutstandingParams(){
if (_parameters == null){
return 8; // CBUS Spec minimum 8 parameters, likely value 20
}
int count = 0;
for (int i = 1; i < _parameters.length; i++) {
if ( _parameters[i] == -1 ) {
count++;
}
}
return count;
}
/**
* Send a request for the next unknown parameter to the physical node
*
*/
protected void sendRequestNextParam(){
if ( hasActiveTimers() ){
return;
}
if ( _parameters == null ) {
requestParam(0);
return;
}
if ( getParameter(1) < 0 ) {
requestParam(1);
return;
}
if ( getParameter(3) < 0 ) {
requestParam(3);
return;
}
if ( ( getCsNum() > -1 ) && ( _commandStationIdentified == false ) ) {
// notify command station located
log.info("{}",getNodeTypeString() );
_commandStationIdentified = true;
}
// initialise NV's
if ( getParameter(6) < 0 ) {
// log.info("requesting param 6");
requestParam(6);
return;
}
// get number event variables per event
if ( getParameter(5) < 0 ) {
// log.info("requesting param 5");
requestParam(5);
return;
}
// get firmware pt1
if ( getParameter(7) < 0 ) {
// log.info("requesting param 7");
requestParam(7);
return;
}
// get firmware pt2
if ( getParameter(2) < 0 ) {
// log.info("requesting param 2");
requestParam(2);
return;
}
// set node traits, eg CANPAN v1 send wrack on nv set, CANCMD v4 numevents 0
// only do this once
if (!nodeTraitsSet) {
CbusNodeConstants.setTraits(this);
nodeTraitsSet = true;
}
// now traits are known request num. of events
if ( getTotalNodeEvents()<0 ){
setNumEvTimeout();
send.rQEVN( getNodeNumber() );
return;
}
for (int i = 1; i < _parameters.length; i++) {
if ( _parameters[i] == -1 ) {
requestParam(i);
return;
}
}
}
/**
* Get the total number of bytes to store in a backup file
*
* @return total number, else 0 if still waiting for a total number of events
*/
public int totalNodeFileBytes(){
return Math.max(0,getParameter(0))+ Math.max(0,getNV(0)) + Math.max(0,getParameter(5) * getTotalNodeEvents());
}
/**
* Get the total bytes to transfer all data currently on module
*
* @return total number, else -1 if still waiting for a total number of events
*/
public int totalNodeBytes() {
if ( ( getParameter(0) < 0 )
|| ( getParameter(6) < 0 )
|| ( getParameter(5) < 0 )
|| ( getTotalNodeEvents() < 0 ) ){
return -1;
}
return getParameter(0) + /* Total Parameters */
getParameter(6) + /* Total NV's */
( getParameter(5) * getTotalNodeEvents() ) + /* Ev Variables for All Events */
( getTotalNodeEvents() ); /* Events from index return */
}
/**
* Get the number of data bytes outstanding to fetch from a node
*
* @return total number, else -1 if still waiting for a total number of events
*/
public int totalRemainingNodeBytes(){
if ( ( getOutstandingParams() < 0 )
|| ( getOutstandingNvCount() < 0 )
|| ( getOutstandingEvVars() < 0 )
|| ( getOutstandingIndexNodeEvents() < 0 ) ){
return -1;
}
return getOutstandingParams() + /* Total Parameters */
getOutstandingNvCount() + /* Total NV's */
getOutstandingEvVars() +
( getOutstandingIndexNodeEvents() ); /* Events from index return */
}
/**
* Get the amount of Node data known to JMRI
* in terms of percentage of total data fetch done so far.
*
* @return float min 0 max 1
*/
public float floatPercentageRemaining(){
float remain = ( 1.0f * ( totalNodeBytes() - totalRemainingNodeBytes() ) ) / ( totalNodeBytes() );
if ( remain > 0 && remain < 1.000001 ) {
return remain;
}
return 0.0f;
}
/**
* Check if node has finished loading all available data
*
* The first time that all data is loaded, saves new backup.
*
*/
protected void checkNodeFinishedLoad(){
if ((!backupStarted) && totalRemainingNodeBytes() == 0) {
if (!thisNodeBackupFile.doStore(true) ) {
log.error("Unable to save Finished Load to Node Backup File");
}
}
// reset value if node comes online after being offline
if (backupStarted && totalRemainingNodeBytes()>0) {
backupStarted = false;
notifyNodeBackupTable();
}
}
/**
* Start loading the Node backup xml file
* <p>
* Happens early on a node being added to the table so that the
* Node Name, user comments etc. are avaiable.
* Sets internal flag so can only be triggered once
*
*/
protected void startLoadFromXml() {
if (thisNodeBackupFile==null && !backupInit) {
log.debug("Finding node xml file");
backupInit = true;
ThreadingUtil.runOnLayout( () -> {
thisNodeBackupFile = new CbusNodeXml(this);
});
}
}
private CbusNodeXml thisNodeBackupFile = null;
/**
* Get the CbusNodeXml for Node Backup Details and operations
* <p>
* @return the CbusNodeXml instance for the node, may be null
*/
public CbusNodeXml getNodeBackupFile() {
return thisNodeBackupFile;
}
private boolean backupStarted = false; // auto startup backup started
private boolean backupInit = false; // node details loaded from file
/**
* Notify the core node model, there is only 1 master model per connection
*
* @param col the Column number in the CbusNodeTableDataModel
*/
private void notifyModelIfExists ( int col) {
if ( tableModel != null ) {
tableModel.updateFromNode( getNodeNumber(), col);
}
}
/**
* Notify any NV Table Models registered with the Node of a change
*
* @param row is the NV index (0 is NV1, 5 is NV6)
* @param col the Column number in the CbusNodeNVTableDataModel
*/
private void notifyNodeEventTable( int row, int col ) {
Iterator itr = _evTableModels.iterator();
while (itr.hasNext()) {
CbusNodeEventTableDataModel model = (CbusNodeEventTableDataModel)itr.next();
if ( model != null) {
log.debug("node notifys, event model {} row {} col {}",model,row,col);
ThreadingUtil.runOnGUI( ()->{
model.fireTableCellUpdated(row, col);
});
}
}
}
/**
* Notify any NV Table Models registered with the Node
*
* @param row is the NV index (0 is NV1, 5 is NV6)
* @param col the Column number in the CbusNodeNVTableDataModel
*/
private void notifyNodeNvTable( int row, int col ) {
Iterator itr = _nvTableModels.iterator();
while (itr.hasNext()) {
CbusNodeNVTableDataModel model = (CbusNodeNVTableDataModel)itr.next();
if ( model != null) {
log.debug("node notifys, nv edit model {} arrayID {} col {}",model,row,col);
ThreadingUtil.runOnGUI( ()->{
model.fireTableCellUpdated(row, col);
model.setValueAt(
model.getValueAt(row,CbusNodeNVTableDataModel.NV_CURRENT_VAL_COLUMN),
row,CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
});
}
}
}
/**
* Notify any NV Table Models registered with the Node
* that the core node has changed (eg. coming online after starting offline)
*/
private void notifyNodeNvModelResetNvs(){
Iterator itr = _nvTableModels.iterator();
while (itr.hasNext()) {
CbusNodeNVTableDataModel model = (CbusNodeNVTableDataModel)itr.next();
if ( model != null) {
model.resetNewNvs();
model.fireTableDataChanged();
}
}
}
/**
* Notify any Backup Table Models registered with the Node
*
*/
protected void notifyNodeBackupTable(){
Iterator itr = _backupTableModels.iterator();
while (itr.hasNext()) {
CbusNodeBackupTableModel model = (CbusNodeBackupTableModel)itr.next();
if ( model != null) {
ThreadingUtil.runOnGUI( ()->{
model.fireTableDataChanged();
});
}
}
notifyModelIfExists(CbusNodeTableDataModel.SESSION_BACKUP_STATUS_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.NUMBER_BACKUPS_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.LAST_BACKUP_COLUMN);
notifyInfoPane();
}
/**
* Notify any InfoPane registered with the Node
* of a change in Parameters
*
*/
protected void notifyInfoPane(){
Iterator itr = _infoPanes.iterator();
while (itr.hasNext()) {
CbusNodeInfoPane pane = (CbusNodeInfoPane)itr.next();
ThreadingUtil.runOnGUI( ()->{
pane.paramsHaveUpdated();
});
}
}
/**
* Get a Single Parameter value
* <p>
* eg. for param. array [3,1,2,3] index 2 returns 2
*
* @param index of which parameter, 0 gives the total parameters
* @return Full Parameter value for a particular index, -1 if unknown
*/
public int getParameter(int index) {
if ( _parameters == null ) {
return -1;
}
try {
return _parameters[index];
}
catch (java.lang.ArrayIndexOutOfBoundsException e) {
return -1;
}
}
/**
* Get array of All parameters
*
* @return Full Parameter array, index 0 is total parameters
*/
public int[] getParameters() {
return _parameters;
}
/**
* Get the Parameter String in Hex Byte Format
* <p>
* eg. for param. array [3,1,2,3] returns "010203"
*
* @return Full Parameter String WITHOUT leading number of parameters
*/
public String getParameterHexString() {
if (getParameters()==null) {
return "";
} else {
return jmri.util.StringUtil.hexStringFromInts(getParameters()).substring(2);
}
}
/**
* Get the NV String in Hex Byte Format
* <p>
* eg. for NV array [3,1,2,255] returns "0102FF"
*
* @return Full NV String WITHOUT leading number of NVs
*/
public String getNvHexString() {
if ( getNvArray() == null ) {
return "";
} else {
return jmri.util.StringUtil.hexStringFromInts(getNvArray()).substring(2);
}
}
/**
* Temporarily store Node Firmware version obtained from a CBUS_STAT Response
* <p>
* Parameter array is not created until total number of parameters is known.
* This saves asking the Node for them.
*
* @param fwMaj Major Firmware Type
* @param fwMin Minor Firmware Type
* @param fwBuild Firmware Build Number
*/
public void setFW( int fwMaj, int fwMin, int fwBuild ){
_fwMaj = fwMaj;
_fwMin = fwMin;
_fwBuild = fwBuild;
}
/**
* Temporarily store Node Manufacturer and Module Type obtained from a PNN Response
* <p>
* Parameter array is not created until total number of parameters is known.
* This saves asking the Node for them.
*
* @param manu Manufacturer
* @param modtype Module Type
*/
protected void setManuModule(int manu, int modtype){
_manu = manu;
_type = modtype;
}
private int[] tempSetupParams;
/**
* Temporarily store Node Parameters obtained from a Node requesting a Node Number
* <p>
* Parameter array is not created until total number of parameters is known.
* This saves asking the Node for them.
*
* @param setupParams an int array in order of final 7 bytes of the CBUS_PARAMS node response
*/
public void setParamsFromSetup(int[] setupParams) {
log.debug("setup parameters received {}",setupParams);
tempSetupParams = setupParams;
}
/**
* Set the Node Variables
* <p>
* 0th NV is total NVs
* so length of newnvs should already be num. of NV's +1
*
* @param newnvs an int array, the first value being the total number
*
*/
public void setNVs( int[] newnvs ) {
log.debug(" setNVs newnvs arr length {} ",newnvs.length);
_nvArray = new int [(newnvs.length)]; // no need to compensate for index 0 being total
for (int i = 0; i < newnvs.length; i++) {
setNV(i,newnvs[i]);
}
notifyModelIfExists(CbusNodeTableDataModel.BYTES_REMAINING_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.NODE_TOTAL_BYTES_COLUMN);
notifyNodeNvModelResetNvs();
}
/**
* Set a single Node Variable
* <p>
* 0th NV is total NVs
* so Index 1 is NV1 .. Index 255 is NV255
*
* @param index NV Index
* @param newnv min 0, max 255
*
*/
public void setNV( int index, int newnv ) {
if ( _nvArray == null ){
log.error("Attempted to set NV {} on a null NV Array on Node {}",index,this);
return;
}
if (index < 0 || index > 255) { // 0 is total
log.error("Attempted to set Invalid NV {} on Node {}",index,this);
return;
}
if (newnv < -1 || newnv > 255) { // -1 is unset
log.error("Attempted to set NV {} Invalid Value {} on Node {}",index,newnv,this);
return;
}
_nvArray[index]=newnv;
notifyNodeNvTable(( index -1),CbusNodeNVTableDataModel.NV_CURRENT_VAL_COLUMN);
notifyModelIfExists(CbusNodeTableDataModel.BYTES_REMAINING_COLUMN);
}
/**
* Get the Node Variable int Array
* <p>
* 0th Index is total NVs
* so Index 1 is NV1 .. Index 255 is NV255
*
* @return Array of NV's, first in index is Total NV's
*/
public int[] getNvArray() {
return _nvArray;
}
/**
* Number of Node Variables on the node.
* @return 0 if number of NV's unknown, else number of NV's.
*/
public int getTotalNVs() {
if ( _nvArray==null){
return 0;
}
return _nvArray[0];
}
/**
* Get a specific Node Variable
* @return -1 if NV's unknown, else Node Variable value.
*
*/
public int getNV ( int index ) {
if ( getTotalNVs() < 1 ){
return -1;
}
return _nvArray[index];
}
public int getNvDifference(CbusNode testAgainst){
int count = 0;
for (int i = 0; i < _nvArray.length; i++) {
if (getNV(i) != testAgainst.getNV(i)){
count++;
}
}
return count;
}
/**
* Number of unknown Node Variables, ie not yet fetched from a physical node.
* @return -1 if number of NV's unknown, 0 if all NV's known, else number of outstanding.
*
*/
public int getOutstandingNvCount(){
int count = 0;
if ( _nvArray == null ){
return -1;
}
for (int i = 0; i < _nvArray.length; i++) {
if ( _nvArray[i] < 0 ) {
count ++;
}
}
return count;
}
/**
* Send a request for the next unknown Node Variable.
* <p>
* Only triggered from CBUS Node Manager.
* <p>
* Does NOT send if the node has existing outstanding requests.
* Expected response from node NVANS
*/
protected void sendNextNVToFetch(){
if ( hasActiveTimers() ) {
return;
}
for (int i = 0; i < _nvArray.length; i++) {
if ( _nvArray[i] < 0 ) {
// start NV request timer
setNextNvVarTimeout();
send.nVRD( getNodeNumber(), i );
return;
}
}
}
private NodeConfigToolPane _mainPane;
int[] newNvsToTeach;
int nextNvInLoop;
boolean TEACH_OUTSTANDING_NVS = false;
int sendNVErrorCount;
/**
* Send and teach updated Node Variables to this node
*
* @param newnv array of variables, index 0 i the array is total variables
* @param mainPane The main NodeConfigToolPane for GUI feedback, can be null
*/
public void sendNvsToNode( int[] newnv , NodeConfigToolPane mainPane ) {
// log.info("start loop to send nv's , nv 1 is {}",newnv[1]);
_mainPane = mainPane; // may be null
newNvsToTeach = newnv;
nextNvInLoop = 1; // start from 1 not 0 as 0 is the total num. nv's
TEACH_OUTSTANDING_NVS = true;
sendNVErrorCount = 0 ;
// check length of new array
// log.info("array size {}",newNvsToTeach.length);
sendNextNvToNode();
}
/**
* Loop for NV teaching
*/
private void sendNextNvToNode() {
for (int i = nextNvInLoop; i < _nvArray.length; i++) {
if ( newNvsToTeach[i] != _nvArray[i] ) {
setsendEditNvTimeout();
send.nVSET( getNodeNumber() ,i, newNvsToTeach[i] );
nextNvInLoop = i;
return;
}
}
log.info( Bundle.getMessage("NdCompleteNVar", String.valueOf(sendNVErrorCount) , getNodeNumberName() ) );
TEACH_OUTSTANDING_NVS = false;
if ( _mainPane != null ){
_mainPane.nVTeachComplete(sendNVErrorCount);
_mainPane = null;
}