001package jmri.jmrix.can.cbus.node;
002
003import java.util.Collections;
004import java.util.HashMap;
005import java.util.Map;
006
007import javax.annotation.Nonnull;
008
009import static jmri.jmrix.can.cbus.CbusConstants.*;
010
011// import org.slf4j.Logger;
012// import org.slf4j.LoggerFactory;
013
014/**
015 * Static Methods relating to nodes ( modules ).
016 *
017 * @author Steve Young (C) 2019
018 */
019public class CbusNodeConstants {
020
021    /**
022     * Node Parameters
023     *
024     * Para 0 Number of parameters
025     * Para 1 The manufacturer ID
026     * Para 2 Minor code version as an alphabetic character (ASCII)
027     * Para 3 Manufacturer module identifier as a HEX numeric
028     * Para 4 Number of supported events as a HEX numeric
029     * Para 5 Number of Event Variables per event as a HEX numeric
030     * Para 6 Number of supported Node Variables as a HEX numeric
031     * Para 7 Major version
032     * Para 8 Node flags
033     * Para 9 Processor type
034     * Para 10 Bus type
035     * Para 11 load address, 4 bytes
036     * Para 15 CPU manufacturer's id as read from the chip config space, 4 bytes
037     * Para 19 CPU manufacturer code
038     * Para 20 Beta revision (numeric), or 0 if release
039     * Para 21 - 24 Zero filled spare
040     * Not readable by index:
041     * Para 25 - 26 Number of paranmeters can be read as parameter 0
042     * Para 27 - 30 Name string base address
043     * Para 31 - 32 Checksum. Para 1 - 32 must sum to zero
044     */
045    public static final int NUM_PARAM_IDX = 0;      // Para 0 Number of parameters
046    public static final int MANU_ID_IDX = 1;        // Para 1 The manufacturer ID
047    public static final int MINOR_VER_IDX = 2;      // Para 2 Minor code version as an alphabetic character (ASCII)
048    public static final int MODULE_ID_IDX = 3;      // Para 3 Manufacturer module identifier as a HEX numeric
049    public static final int NUM_EV_IDX = 4;         // Para 4 Number of supported events as a HEX numeric
050    public static final int EV_PER_EN_IDX = 5;      // Para 5 Number of Event Variables per event as a HEX numeric
051    public static final int NUM_NV_IDX = 6;         // Para 6 Number of supported Node Variables as a HEX numeric
052    public static final int MAJOR_VER_IDX = 7;      // Para 7 Major version
053    public static final int FLAGS_IDX = 8;          // Para 8 Node flags
054    public static final int PROC_TYPE_IDX = 9;      // Para 9 Processor type
055    public static final int BUS_TYPE_IDX = 10;      // Para 10 Bus type
056    public static final int LOAD_ADDR_IDX = 11;     // Para 11 load address, 4 bytes
057    public static final int CPU_ID_IDX = 15;        // Para 15 CPU manufacturer's id as read from the chip config space, 4 bytes, only firs two used for PIC18
058    public static final int CPU_CODE_IDX = 19;      // Para 19 CPU manufacturer code
059    public static final int BETA_REV_IDX = 20;      // Para 20 Beta revision (numeric), or 0 if release
060    public static final int SPARE_IDX = 21;         // Para 21 - 24 
061    // Following are available from hex data but not readable by index
062    public static final int PARAM_COUNT_IDX = 25;    // Para 25 - 26 parameter count high byte
063    public static final int NAME_STRING_BASE_IDX = 27; // Para 27 - 30 parameter count high byte
064    public static final int PARAM_CHECK_IDX = 31;    // Para 31 - 32 parameter count high byte
065   
066    
067    /**
068     * Set traits for a node where there is a minor deviance to MERG CBUS protocol
069     * or provide extra info. which is missing for a known module firmware.
070     * @param node The CbusNode object we are setting the traits for
071     */
072    public static void setTraits(@Nonnull CbusNode node ){
073        
074        // defaults
075        node.setsendsWRACKonNVSET(true);
076        
077        if ( node.getNodeParamManager().getParameter(MANU_ID_IDX) == MANU_MERG ) { // MERG MODULE
078            switch (node.getNodeParamManager().getParameter(MODULE_ID_IDX)) { // Module Type ID Number
079                case 1: // CANACC4
080                case 8: // CANACC4_2
081                case 29: // CANPAN
082                case 34: // CANSOL
083                case 37: // CANACC4CDU
084                    node.setsendsWRACKonNVSET(false);
085                    break;
086                case 10 : // CANCMD
087                case 55 : // or CANCSB 
088                case 12 : // or CANBC
089                    if ( node.getNodeParamManager().getParameter(MAJOR_VER_IDX) == 4 ) { // v4 Firmware
090                        node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
091                        node.setStatResponseFlagsAccurate(false);
092                    }
093                    break;
094                case 46: // CANPiWi
095                    if ( node.getNodeParamManager().getParameter(MAJOR_VER_IDX) == 1 ) { // v1 Firmware
096                        node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
097                    }
098                    break;
099                case 9: // CANCAB
100                    node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
101                    break;
102                case 50: // CANMIO-SVO
103                case 19: // CANSERVO8C
104                    node.setnvWriteInLearnOnly(true);
105                    break;
106                default:
107                    break;
108            }
109        } else if ( node.getNodeParamManager().getParameter(MANU_ID_IDX) == SPROG_DCC ) {    // SPROG DCC module
110            switch (node.getNodeParamManager().getParameter(MODULE_ID_IDX)) {           // Module Type ID Number
111                case MTYP_CANSERVOIO:
112                    node.setnvWriteInLearnOnly(true);
113                    break;
114                    
115                default:
116                    break;
117            }
118        }
119    }
120
121    /**
122     * Return a string representation of a decoded Module Manufacturer
123     * @param man manufacturer int
124     * @return decoded CBUS message
125     */
126    public static String getManu(int man) {
127        if (man < 1 ) {
128            return ("");
129        }
130        // look for the manufacturer
131        String format = manMap.get(man);
132        if (format == null) {
133            return "Manufacturer " + man;
134        } else {
135            return format; 
136        }
137    }
138    
139    /**
140     * Hashmap for decoding Module Manufacturers
141     */
142    private static final Map<Integer, String> manMap = createManMap();
143
144    /**
145     * Populate hashmap with format strings
146     *
147     */
148    private static Map<Integer, String> createManMap() {
149        Map<Integer, String> result = new HashMap<>();
150        result.put(MANU_ROCRAIL, "ROCRAIL"); // NOI18N
151        result.put(MANU_SPECTRUM, "SPECTRUM"); // NOI18N
152        result.put(MANU_MERG, "MERG"); // NOI18N
153        result.put(SPROG_DCC, "SPROG DCC"); // NOI18N
154        return Collections.unmodifiableMap(result);
155    }
156    
157    /**
158     * Return a string representation of a decoded Bus Type
159     * @param type Bus type
160     * @return decoded CBUS message
161     */
162    public static String getBusType(int type) {
163        // look for the opcode
164        String format = busMap.get(type);
165        if (format == null) {
166            return "Unknown";
167        } else {
168            return format; 
169        }
170    }
171    
172    /**
173     * Hashmap for decoding Bus Type
174     */
175    private static final Map<Integer, String> busMap = createBusMap();
176
177    /**
178     * Populate hashmap with format strings
179     *
180     */
181    private static Map<Integer, String> createBusMap() {
182        Map<Integer, String> result = new HashMap<>();
183        result.put(0, "None"); // NOI18N
184        result.put(1, "CAN"); // NOI18N
185        result.put(2, "ETH"); // NOI18N
186        result.put(3, "MIWI"); // NOI18N
187        return Collections.unmodifiableMap(result);
188    }
189    
190    // manufacturer specific stuff from here down
191    // do not rely on these as defs, may be moved to module config file.
192    
193    // getModuleTypeExtra
194    // getModuleSupportLink
195
196    /**
197     * Return a string representation of a decoded Module Name for
198     * manufacturer MERG.
199     * @param man int manufacturer
200     * @param type module type int
201     * @return decoded String module type name else empty string
202     */
203    public static String getModuleType(int man, int type) {
204        String format="";
205        if (man == MANU_MERG) {
206            format = type165Map.get(type);
207        }
208        else if (man == MANU_ROCRAIL) {
209            format = type70Map.get(type);
210        }
211        else if (man == MANU_SPECTRUM) {
212            format = type80Map.get(type);
213        }
214        else if (man == SPROG_DCC) {
215            format = type44Map.get(type);
216        }
217        
218        if ( format == null ){
219            return ("");
220        }
221        else {
222            return format;
223        }
224    }
225    
226    /**
227     * Hashmap for decoding Module Names
228     */
229    private static final Map<Integer, String> type165Map = createType165Map();
230    private static final Map<Integer, String> type70Map = createType70Map();
231    private static final Map<Integer, String> type80Map = createType80Map();
232    private static final Map<Integer, String> type44Map = createType44Map();
233    
234    /**
235     * Populate hashmap with format strings for manufacturer MERG
236     */
237    private static Map<Integer, String> createType165Map() {
238        Map<Integer, String> result = new HashMap<>();
239        result.put(0, "SLIM"); // NOI18N
240        result.put(1, "CANACC4"); // NOI18N
241        result.put(2, "CANACC5"); // NOI18N
242        result.put(3, "CANACC8"); // NOI18N
243        result.put(4, "CANACE3"); // NOI18N
244        result.put(5, "CANACE8C"); // NOI18N
245        result.put(6, "CANLED"); // NOI18N
246        result.put(7, "CANLED64"); // NOI18N
247        result.put(8, "CANACC4_2"); // NOI18N
248        result.put(9, "CANCAB"); // NOI18N
249        result.put(10, "CANCMD"); // NOI18N
250        result.put(11, "CANSERVO"); // NOI18N
251        result.put(12, "CANBC"); // NOI18N
252        result.put(13, "CANRPI"); // NOI18N
253        result.put(14, "CANTTCA"); // NOI18N
254        result.put(15, "CANTTCB"); // NOI18N
255        result.put(16, "CANHS"); // NOI18N
256        result.put(17, "CANTOTI"); // NOI18N
257        result.put(18, "CAN8I8O"); // NOI18N
258        result.put(19, "CANSERVO8C"); // NOI18N
259        result.put(20, "CANRFID"); // NOI18N
260        result.put(21, "CANTC4"); // NOI18N
261        result.put(22, "CANACE16C"); // NOI18N
262        result.put(23, "CANIO8"); // NOI18N
263        result.put(24, "CANSNDX"); // NOI18N
264        result.put(25, "CANEther"); // NOI18N
265        result.put(26, "CANSIG64"); // NOI18N
266        result.put(27, "CANSIG8"); // NOI18N
267        result.put(28, "CANCOND8C"); // NOI18N
268        result.put(29, "CANPAN"); // NOI18N
269        result.put(30, "CANACE3C"); // NOI18N
270        result.put(31, "CANPanel"); // NOI18N
271        result.put(32, "CANMIO"); // NOI18N
272        result.put(33, "CANACE8MIO"); // NOI18N
273        result.put(34, "CANSOL"); // NOI18N
274        result.put(35, "CANBIP"); // NOI18N
275        result.put(36, "CANCDU"); // NOI18N
276        result.put(37, "CANACC4CDU"); // NOI18N
277        result.put(38, "CANWiBase"); // NOI18N
278        result.put(39, "WiCAB"); // NOI18N
279        result.put(40, "CANWiFi"); // NOI18N
280        result.put(41, "CANFTT"); // NOI18N
281        result.put(42, "CANHNDST"); // NOI18N
282        result.put(43, "CANTCHNDST"); // NOI18N
283        result.put(44, "CANRFID8"); // NOI18N
284        result.put(45, "CANmchRFID"); // NOI18N
285        result.put(46, "CANPiWi"); // NOI18N
286        result.put(47, "CAN4DC"); // NOI18N
287        result.put(48, "CANELEV"); // NOI18N
288        result.put(49, "CANSCAN"); // NOI18N
289        result.put(50, "CANMIO_SVO"); // NOI18N
290        result.put(51, "CANMIO_INP"); // NOI18N
291        result.put(52, "CANMIO_OUT"); // NOI18N
292        result.put(53, "CANBIP_OUT"); // NOI18N
293        result.put(54, "CANASTOP"); // NOI18N
294        result.put(55, "CANCSB"); // NOI18N
295        result.put(56, "CANMAG"); // NOI18N
296        result.put(57, "CANACE16CMIO"); // NOI18N
297        result.put(58, "CANPiNODE"); // NOI18N
298        result.put(59, "CANDISP"); // NOI18N
299        result.put(60, "CANCOMPUTE"); // NOI18N
300        result.put(61, "CANRC522"); // NOI18N
301        result.put(62, "CANINP"); // NOI18N
302        result.put(63, "CANOUT"); // NOI18N
303        result.put(64, "CANEMIO"); // NOI18N
304        result.put(65, "CANCABDC"); // NOI18N
305        result.put(66, "CANRCOM"); // NOI18N
306        
307        result.put(253, "CANUSB"); // NOI18N
308        result.put(254, "EMPTY"); // NOI18N
309        result.put(255, "CAN_SW"); // NOI18N
310        return Collections.unmodifiableMap(result);
311    }
312
313    /*
314     * Populate hashmap with format strings
315     *
316     */
317    private static Map<Integer, String> createType70Map() {
318        Map<Integer, String> result = new HashMap<>();
319        result.put(1, "CANGC1"); // NOI18N
320        result.put(2, "CANGC2"); // NOI18N
321        result.put(3, "CANGC3"); // NOI18N
322        result.put(4, "CANGC4"); // NOI18N
323        result.put(5, "CANGC5"); // NOI18N
324        result.put(6, "CANGC6"); // NOI18N
325        result.put(7, "CANGC7"); // NOI18N
326        result.put(11, "CANGC1e"); // NOI18N
327        return Collections.unmodifiableMap(result);
328    }
329
330    
331    /*
332     * Populate hashmap with format strings
333     *
334     */
335    private static Map<Integer, String> createType80Map() {
336        Map<Integer, String> result = new HashMap<>();
337        result.put(1, "AMCTRLR"); // NOI18N
338        result.put(2, "DUALCAB"); // NOI18N
339        return Collections.unmodifiableMap(result);
340    }
341    
342    
343    /*
344     * Populate hashmap with format strings.
345     * Visible name of module, not the CBUS NAME OPC Response.
346     */
347    private static Map<Integer, String> createType44Map() {
348        Map<Integer, String> result = new HashMap<>();
349        result.put(1, "Pi-SPROG 3"); // NOI18N
350        result.put(2, "CANSPROG3P"); // NOI18N
351        result.put(3, "CANSPROG"); // NOI18N
352        result.put(4, "SBOOST"); // NOI18N
353        result.put(5, "Unsupported"); // NOI18N
354
355        result.put(8, "CANSOLNOID"); // NOI18N. Matches MERG CANSOL
356        result.put(50, "CANSERVOIO"); // NOI18N. Matches MERG canmio-svo
357        
358        result.put(100, "CANISB"); // NOI18N
359        result.put(101, "CANSOLIO"); // NOI18N
360        return Collections.unmodifiableMap(result);
361    }
362    
363    
364    /**
365     * Return a string representation of extra module info
366     * @param man int manufacturer code
367     * @param type int module type
368     * @return string value of extra module info
369     */
370    public static String getModuleTypeExtra(int man, int type) {
371        String format="";
372        if (man == MANU_MERG) {
373            format = extra165Map.get(type);
374        }
375        else if (man == MANU_ROCRAIL) {
376            format = extra70Map.get(type);
377        }
378        else if (man == MANU_SPECTRUM) {
379            format = extra80Map.get(type);
380        }
381        else if (man == SPROG_DCC) {
382            format = extra44Map.get(type);
383        }
384        return format;
385    }
386    
387    /**
388     * Hashmap for decoding Module extra info
389     */
390    private static final Map<Integer, String> extra165Map = createExtra165Map();
391    private static final Map<Integer, String> extra70Map = createExtra70Map();
392    private static final Map<Integer, String> extra80Map = createExtra80Map();
393    private static final Map<Integer, String> extra44Map = createExtra44Map();
394    
395    /*
396     * Populate hashmap with format strings
397     */
398    private static Map<Integer, String> createExtra165Map() {
399        Map<Integer, String> result = new HashMap<>();
400        result.put(0, "Default for SLiM nodes");
401        result.put(1, "Solenoid point driver");
402        result.put(2, "Motorised point driver");
403        result.put(3, "8 digital outputs ( + 8 inputs if modded)");
404        result.put(4, "Control panel switch/button encoder");
405        result.put(5, "8 digital inputs");
406        result.put(6, "64 led driver");
407        result.put(7, "64 led driver (multi leds per event)");
408        result.put(8, "12v version of CANACC4 Solenoid point driver");
409        result.put(9, "CANCAB hand throttle");
410        result.put(10, "Command Station");
411        result.put(11, "8 servo driver (on canacc8 or similar hardware)");
412        result.put(12, "BC1a Command Station");
413        result.put(13, "RPI and RFID interface");
414        result.put(14, "Turntable controller (turntable end)");
415        result.put(15, "Turntable controller (control panel end)");
416        result.put(16, "Handset controller for old BC1a type handsets");
417        result.put(17, "Track occupancy detector");
418        result.put(18, "8 inputs 8 outputs");
419        result.put(19, "Canservo with servo position feedback");
420        result.put(20, "RFID input");
421        result.put(21, "CANTC4");
422        result.put(22, "16 inputs");
423        result.put(23, "8 way I/O");
424        result.put(24, "CANSNDX");
425        result.put(25, "Ethernet interface");
426        result.put(26, "Multiple aspect signalling for CANLED module");
427        result.put(27, "Multiple aspect signalling for CANACC8 module");
428        result.put(28, "Conditional event generation");
429        result.put(29, "Control panel 32 Outputs + 32 Inputs");
430        result.put(30, "Newer version of CANACE3 firmware");
431        result.put(31, "Control panel 64 Inputs / 64 Outputs");
432        result.put(32, "Multiple I/O – Universal CANMIO firmware");
433        result.put(33, "Multiple IO module 16 inputs emulating CANACE8C on CANMIO hardware");
434        result.put(34, "Solenoid driver module");
435        result.put(35, "Universal CANBIP firmware - Bipolar IO module with additional 8 I/O pins (CANMIO family)");
436        result.put(36, "Solenoid driver module with additional 6 I/O pins (CANMIO family)");
437        result.put(37, "CANACC4 firmware ported to CANCDU");
438        result.put(38, "CAN to MiWi base station");
439        result.put(39, "Wireless cab using MiWi protocol");
440        result.put(40, "CAN to WiFi connection with Withrottle to CBUS protocol conversion");
441        result.put(41, "Turntable controller configured using FLiM");
442        result.put(42, "Handset (alternative to CANCAB)");
443        result.put(43, "Touchscreen handset");
444        result.put(44, "multi-channel RFID reader");
445        result.put(45, "either a 2ch or 8ch RFID reader");
446        result.put(46, "Raspberry Pi based module for WiFi");
447        result.put(47, "DC train controller");
448        result.put(48, "Nelevator controller");
449        result.put(49, "128 switch inputs");
450        result.put(50, "16MHz 25k80 version of CANSERVO8c on CANMIO hardware");
451        result.put(51, "16MHz 25k80 version of CANACE8MIO on CANMIO hardware");
452        result.put(52, "16MHz 25k80 version of CANACC8 on CANMIO hardware");
453        result.put(53, "16MHz 25k80 version of CANACC5 on CANMIO hardware");
454        result.put(54, "DCC stop generator");
455        result.put(55, "Command Station with 3A booster");
456        result.put(56, "Magnet on Track detector");
457        result.put(57, "16 input equivaent to CANACE8C");
458        result.put(58, "CBUS module based on Raspberry Pi");
459        result.put(59, "25K80 version of CANLED64");
460        result.put(60, "Compute Event processing engine");
461        result.put(61, "Read/Write from/to RC522 RFID tags");
462        result.put(62, "8 inputs module (2g version of CANACE8c)");
463        result.put(63, "8 outputs module (2g version of CANACC8)");
464        result.put(64, "Extended CANMIO (24 I/O ports)");
465        result.put(65, "DC cab");
466        result.put(66, "DCC Railcom detector/reader");
467        
468        result.put(253, "USB interface");
469        result.put(254, "Empty module, bootloader only");
470        result.put(255, "Software nodes");
471        return Collections.unmodifiableMap(result);
472    }
473    
474    /*
475     * Populate hashmap with format strings
476     * extra text for Rocrail Modules
477     */
478    private static Map<Integer, String> createExtra70Map() {
479        Map<Integer, String> result = new HashMap<>();
480        result.put(1, "RS232 PC interface.");
481        result.put(2, "16 I/O.");
482        result.put(3, "Command station (derived from cancmd).");
483        result.put(4, "8 channel RFID reader.");        
484        result.put(5, "Cab for fixed panels (derived from cancab).");        
485        result.put(6, "4 channel servo controller.");        
486        result.put(7, "Fast clock module.");        
487        result.put(11, "CAN Ethernet interface.");
488        return Collections.unmodifiableMap(result);
489    }    
490    
491    /*
492     * Populate hashmap with format strings
493     * extra text for Animated Modeller module types
494     */
495    private static Map<Integer, String> createExtra80Map() {
496        Map<Integer, String> result = new HashMap<>();
497        result.put(1, "Animation controller (firmware derived from cancmd).");
498        result.put(2, "Dual cab based on cancab.");
499        return Collections.unmodifiableMap(result);
500    }   
501
502    
503    /*
504     * Populate hashmap with format strings
505     * extra text for Animated Modeller module types
506     */
507    private static Map<Integer, String> createExtra44Map() {
508        Map<Integer, String> result = new HashMap<>();
509        result.put(1, "Pi-SPROG 3 programmer/command station.");
510        result.put(2, "SPROG 3 Plus programmer/command station.");
511        result.put(3, "CAN SPROG programmer/command station.");
512        result.put(4, "System booster");
513        result.put(5, "Unsupported module type");
514        
515        result.put(8, "8-channel twin-coil solenoid, like MERG CANACC4_2.");
516        result.put(50, "8-channel servo I/O, like MERG CANMIO_SVO.");
517        
518        result.put(100, "Isolated USB to CAN interface with CBUS node.");
519        result.put(101, "8-channel twin-coil solenoid I/O.");
520        return Collections.unmodifiableMap(result);
521    }   
522
523  
524    /**
525     * Return a string representation of Module Support Link
526     * @param man int manufacturer ID
527     * @param type int module type ID
528     * @return string module support link, else empty string
529     */
530    public static String getModuleSupportLink(int man, int type) {
531        String format="";
532        if (man == MANU_MERG) {
533            format = link165Map.get(type);
534        }
535        else if (man == MANU_ROCRAIL) {
536            format = link70Map.get(type);
537        }
538        else if (man == SPROG_DCC) {
539            format = link44Map.get(type);
540        }
541        if ( format == null ){
542            return ("");
543        }
544        return format;
545    }
546    
547    private static final Map<Integer, String> link165Map = createLink165Map();
548    private static final Map<Integer, String> link70Map = createLink70Map();
549    private static final Map<Integer, String> link44Map = createLink44Map();
550    
551    /*
552     * Populate hashmap with merg module support links
553     */
554    private static Map<Integer, String> createLink165Map() {
555        Map<Integer, String> result = new HashMap<>();
556        
557        result.put(1, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
558        result.put(2, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc5"); // NOI18N
559        result.put(3, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc8"); // NOI18N
560        result.put(4, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
561        result.put(5, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
562        // result.put(6, "CANLED"); // NOI18N
563        result.put(7, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canled64"); // NOI18N
564        result.put(8, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
565        result.put(9, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancab"); // NOI18N
566        result.put(10, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancmd"); // NOI18N
567        // result.put(11, "CANSERVO"); // NOI18N
568        // result.put(12, "CANBC"); // NOI18N
569        // result.put(13, "CANRPI"); // NOI18N
570        result.put(14, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
571        result.put(15, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
572        // result.put(16, "CANHS"); // NOI18N
573        result.put(17, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
574        // result.put(18, "CAN8I8O"); // NOI18N
575        result.put(19, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canservo8"); // NOI18N
576        result.put(20, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid"); // NOI18N
577        // result.put(21, "CANTC4"); // NOI18N
578        // result.put(22, "CANACE16C"); // NOI18N
579        // result.put(23, "CANIO8"); // NOI18N
580        // result.put(24, "CANSNDX"); // NOI18N
581        result.put(25, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canether"); // NOI18N
582        result.put(26, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
583        result.put(27, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
584        result.put(28, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canccond8c"); // NOI18N
585        result.put(29, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpan"); // NOI18N
586        result.put(30, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
587        result.put(31, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpanel"); // NOI18N
588        result.put(32, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
589        // result.put(33, "CANACE8MIO"); // NOI18N
590        result.put(34, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansol"); // NOI18N
591        result.put(35, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canbip"); // NOI18N
592        result.put(36, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancdu"); // NOI18N
593        // result.put(37, "CANACC4CDU"); // NOI18N
594        // result.put(38, "CANWiBase"); // NOI18N
595        // result.put(39, "WiCAB"); // NOI18N
596        // result.put(40, "CANWiFi"); // NOI18N
597        // result.put(41, "CANFTT"); // NOI18N
598        // result.put(42, "CANHNDST"); // NOI18N
599        // result.put(43, "CANTCHNDST"); // NOI18N
600        result.put(44, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid8"); // NOI18N
601        result.put(45, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmchrfid"); // NOI18N
602        result.put(46, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canwi"); // NOI18N
603        result.put(47, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:can4dc"); // NOI18N
604        // result.put(48, "CANELEV"); // NOI18N
605        result.put(49, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canscan"); // NOI18N
606        result.put(50, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
607        result.put(51, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
608        result.put(52, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
609        // result.put(53, "CANBIP_OUT"); // NOI18N
610        result.put(54, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canastop"); // NOI18N
611        result.put(55, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancsb"); // NOI18N
612        // result.put(56, "CANMAGOT"); // NOI18N
613        // result.put(57, "CANACE16CMIO"); // NOI18N
614        // result.put(58, "CANPiNODE"); // NOI18N
615        result.put(59, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:candisp"); // NOI18N
616        result.put(60, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancompute"); // NOI18N
617        result.put(61, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canrc522"); // NOI18N
618        result.put(62, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:caninp"); // NOI18N
619        result.put(63, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canout"); // NOI18N
620        // result.put(64, "CANEMIO"); // NOI18N
621        result.put(65, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:cancabdc"); // NOI18N
622        result.put(66, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrcom"); // NOI18N
623        
624        // result.put(253, "CANUSB"); // NOI18N
625        // result.put(254, "EMPTY"); // NOI18N
626        // result.put(255, "CAN_SW"); // NOI18N        
627        
628        return Collections.unmodifiableMap(result);
629    }
630    
631    /*
632     * Populate hashmap with rocrail module support links
633     */
634    private static Map<Integer, String> createLink70Map() {
635        Map<Integer, String> result = new HashMap<>();
636        result.put(1, "https://wiki.rocrail.net/doku.php?id=can-gca1-en"); // NOI18N
637        result.put(2, "https://wiki.rocrail.net/doku.php?id=can-gca2-en"); // NOI18N
638        result.put(3, "https://wiki.rocrail.net/doku.php?id=can-gc3-en"); // NOI18N
639        result.put(4, "https://wiki.rocrail.net/doku.php?id=can-gc4-en"); // NOI18N
640        result.put(5, "https://wiki.rocrail.net/doku.php?id=can-gca5-en"); // NOI18N
641        result.put(6, "https://wiki.rocrail.net/doku.php?id=can-gc6-en"); // NOI18N
642        result.put(7, "https://wiki.rocrail.net/doku.php?id=can-gc7-en"); // NOI18N
643        result.put(11, "https://wiki.rocrail.net/doku.php?id=can-gca1e-en"); // NOI18N
644        return Collections.unmodifiableMap(result);
645    }
646    
647    
648    /*
649     * Populate hashmap with SPROG module support links
650     */
651    private static Map<Integer, String> createLink44Map() {
652        Map<Integer, String> result = new HashMap<>();
653        result.put(1, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
654        result.put(2, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
655        result.put(3, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
656        result.put(4, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
657        result.put(5, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
658        result.put(8, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
659        result.put(50, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
660        result.put(100, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
661        result.put(101, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
662        return Collections.unmodifiableMap(result);
663    }
664    
665    
666    /**
667     * Return a string representation of a reserved node number
668     * @param modnum node number
669     * @return reserved node number reason
670     */
671    public static String getReservedModule(int modnum) {
672        // look for the opcode
673        String format = resMod.get(modnum);
674        if (format == null) {
675            return "";
676        } else {
677            return format; 
678        }
679    }
680    
681    /**
682     * Hashmap for fixed Module Numbers
683     */
684    private static final Map<Integer, String> resMod = createModMap();
685
686    /*
687     * Populate hashmap with format strings
688     *
689     */
690    private static Map<Integer, String> createModMap() {
691        Map<Integer, String> result = new HashMap<>();
692        // Opcodes with no data
693        
694        for (int i = 100; i < 126; i++) {
695            result.put(i, Bundle.getMessage("NdNumReserveFixed")); // NOI18N
696        }
697        result.put(126, "Reserved for CAN_RS Modules");
698        result.put(127, "Reserved for CAN_USB Modules");
699        result.put(65534, "Reserved for Command Station");
700        result.put(65535, "Reserved, used by all CABS");
701        return Collections.unmodifiableMap(result);
702    }
703    
704    private static final Map<String, BackupType> nameIndex =
705            new HashMap<String, BackupType>(BackupType.values().length);
706    static {
707        for (BackupType t : BackupType.values()) {
708            nameIndex.put(t.name(), t);
709        }
710    }
711    
712    private static final Map<BackupType, String> displayPhraseIndex =
713            new HashMap<BackupType, String>(BackupType.values().length);
714    static {
715        displayPhraseIndex.put(BackupType.INCOMPLETE, Bundle.getMessage("BackupIncomplete"));
716        displayPhraseIndex.put(BackupType.COMPLETE, Bundle.getMessage("BackupComplete"));
717        displayPhraseIndex.put(BackupType.COMPLETEDWITHERROR, Bundle.getMessage("BackupCompleteError"));
718        displayPhraseIndex.put(BackupType.NOTONNETWORK, Bundle.getMessage("BackupNotOnNetwork"));
719        displayPhraseIndex.put(BackupType.OUTSTANDING, Bundle.getMessage("BackupOutstanding"));
720        displayPhraseIndex.put(BackupType.SLIM, Bundle.getMessage("NodeInSlim"));
721    }
722    
723    /*
724     * Get the display phrase for an enum value
725     * <p>
726     * eg. displayPhrase(BackupType.INCOMPLETE) will return "Backup InComplete"
727     *
728     * @param type The enum to translate
729     * @return The phrase
730     *
731     */
732    public static String displayPhrase(BackupType type) {
733        return displayPhraseIndex.get(type);
734    }
735    
736    /*
737     * Get the enum type for a String value
738     * <p>
739     * eg. lookupByName("Complete") will return BackupType.COMPLETE
740     *
741     * @param name The String to lookup
742     * @return The BackupType enum, else null
743     *
744     */
745    public static BackupType lookupByName(String name) {
746        return nameIndex.get(name);
747    }
748    
749    /*
750     * enum to represent Node Backup Conditions in a CBUS Node XML File
751     *
752     */
753    public enum BackupType{
754        INCOMPLETE(0),
755        COMPLETE(1),
756        COMPLETEDWITHERROR(2),
757        NOTONNETWORK(3),
758        OUTSTANDING(4),
759        SLIM(5);
760        
761        private final int v;
762
763        private BackupType(final int v) {
764            this.v = v;
765        }
766    
767        public int getValue() {
768            return v;
769        }
770    
771    }
772
773    // private final static Logger log = LoggerFactory.getLogger(CbusNodeConstants.class);
774}