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                case 83 : // or CANCMDB
090                    if ( node.getNodeParamManager().getParameter(MAJOR_VER_IDX) == 4 ) { // v4 Firmware
091                        node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
092                        node.setStatResponseFlagsAccurate(false);
093                    }
094                    break;
095                case 46: // CANPiWi
096                    if ( node.getNodeParamManager().getParameter(MAJOR_VER_IDX) == 1 ) { // v1 Firmware
097                        node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
098                    }
099                    break;
100                case 9: // CANCAB
101                    node.getNodeEventManager().resetNodeEventsToZero(); // sets num events to 0 as does not respond to RQEVN
102                    break;
103                case 50: // CANMIO-SVO
104                case 19: // CANSERVO8C
105                    node.setnvWriteInLearnOnly(true);
106                    break;
107                default:
108                    break;
109            }
110        } else if ( node.getNodeParamManager().getParameter(MANU_ID_IDX) == SPROG_DCC ) {    // SPROG DCC module
111            switch (node.getNodeParamManager().getParameter(MODULE_ID_IDX)) {           // Module Type ID Number
112                case MTYP_CANSERVOIO:
113                    node.setnvWriteInLearnOnly(true);
114                    break;
115                    
116                default:
117                    break;
118            }
119        }
120    }
121
122    /**
123     * Return a string representation of a decoded Module Manufacturer
124     * @param man manufacturer int
125     * @return decoded CBUS message
126     */
127    public static String getManu(int man) {
128        if (man < 1 ) {
129            return ("");
130        }
131        // look for the manufacturer
132        String format = manMap.get(man);
133        if (format == null) {
134            return "Manufacturer " + man;
135        } else {
136            return format; 
137        }
138    }
139    
140    /**
141     * Hashmap for decoding Module Manufacturers
142     */
143    private static final Map<Integer, String> manMap = createManMap();
144
145    /**
146     * Populate hashmap with format strings
147     *
148     */
149    private static Map<Integer, String> createManMap() {
150        Map<Integer, String> result = new HashMap<>();
151        result.put(MANU_ROCRAIL, "ROCRAIL"); // NOI18N
152        result.put(MANU_SPECTRUM, "SPECTRUM"); // NOI18N
153        result.put(MANU_MERG, "MERG"); // NOI18N
154        result.put(SPROG_DCC, "SPROG DCC"); // NOI18N
155        return Collections.unmodifiableMap(result);
156    }
157    
158    /**
159     * Return a string representation of a decoded Bus Type
160     * @param type Bus type
161     * @return decoded CBUS message
162     */
163    public static String getBusType(int type) {
164        // look for the opcode
165        String format = busMap.get(type);
166        if (format == null) {
167            return "Unknown";
168        } else {
169            return format; 
170        }
171    }
172    
173    /**
174     * Hashmap for decoding Bus Type
175     */
176    private static final Map<Integer, String> busMap = createBusMap();
177
178    /**
179     * Populate hashmap with format strings
180     *
181     */
182    private static Map<Integer, String> createBusMap() {
183        Map<Integer, String> result = new HashMap<>();
184        result.put(0, "None"); // NOI18N
185        result.put(1, "CAN"); // NOI18N
186        result.put(2, "ETH"); // NOI18N
187        result.put(3, "MIWI"); // NOI18N
188        result.put(4, "USB"); // NOI18N
189        return Collections.unmodifiableMap(result);
190    }
191    
192    // manufacturer specific stuff from here down
193    // do not rely on these as defs, may be moved to module config file.
194    
195    // getModuleTypeExtra
196    // getModuleSupportLink
197
198    /**
199     * Return a string representation of a decoded Module Name for
200     * manufacturer MERG.
201     * @param man int manufacturer
202     * @param type module type int
203     * @return decoded String module type name else empty string
204     */
205    public static String getModuleType(int man, int type) {
206        String format="";
207        if (man == MANU_MERG) {
208            format = type165Map.get(type);
209        }
210        else if (man == MANU_ROCRAIL) {
211            format = type70Map.get(type);
212        }
213        else if (man == MANU_SPECTRUM) {
214            format = type80Map.get(type);
215        }
216        else if (man == SPROG_DCC) {
217            format = type44Map.get(type);
218        }
219        
220        if ( format == null ){
221            return ("");
222        }
223        else {
224            return format;
225        }
226    }
227    
228    /**
229     * Hashmap for decoding Module Names
230     */
231    private static final Map<Integer, String> type165Map = createType165Map();
232    private static final Map<Integer, String> type70Map = createType70Map();
233    private static final Map<Integer, String> type80Map = createType80Map();
234    private static final Map<Integer, String> type44Map = createType44Map();
235    
236    /**
237     * Populate hashmap with format strings for manufacturer MERG
238     */
239    private static Map<Integer, String> createType165Map() {
240        Map<Integer, String> result = new HashMap<>();
241        result.put(0, "SLIM"); // NOI18N
242        result.put(1, "CANACC4"); // NOI18N
243        result.put(2, "CANACC5"); // NOI18N
244        result.put(3, "CANACC8"); // NOI18N
245        result.put(4, "CANACE3"); // NOI18N
246        result.put(5, "CANACE8C"); // NOI18N
247        result.put(6, "CANLED"); // NOI18N
248        result.put(7, "CANLED64"); // NOI18N
249        result.put(8, "CANACC4_2"); // NOI18N
250        result.put(9, "CANCAB"); // NOI18N
251        result.put(10, "CANCMD"); // NOI18N
252        result.put(11, "CANSERVO"); // NOI18N
253        result.put(12, "CANBC"); // NOI18N
254        result.put(13, "CANRPI"); // NOI18N
255        result.put(14, "CANTTCA"); // NOI18N
256        result.put(15, "CANTTCB"); // NOI18N
257        result.put(16, "CANHS"); // NOI18N
258        result.put(17, "CANTOTI"); // NOI18N
259        result.put(18, "CAN8I8O"); // NOI18N
260        result.put(19, "CANSERVO8C"); // NOI18N
261        result.put(20, "CANRFID"); // NOI18N
262        result.put(21, "CANTC4"); // NOI18N
263        result.put(22, "CANACE16C"); // NOI18N
264        result.put(23, "CANIO8"); // NOI18N
265        result.put(24, "CANSNDX"); // NOI18N
266        result.put(25, "CANEther"); // NOI18N
267        result.put(26, "CANSIG64"); // NOI18N
268        result.put(27, "CANSIG8"); // NOI18N
269        result.put(28, "CANCOND8C"); // NOI18N
270        result.put(29, "CANPAN"); // NOI18N
271        result.put(30, "CANACE3C"); // NOI18N
272        result.put(31, "CANPanel"); // NOI18N
273        result.put(32, "CANMIO"); // NOI18N
274        result.put(33, "CANACE8MIO"); // NOI18N
275        result.put(34, "CANSOL"); // NOI18N
276        result.put(35, "CANBIP"); // NOI18N
277        result.put(36, "CANCDU"); // NOI18N
278        result.put(37, "CANACC4CDU"); // NOI18N
279        result.put(38, "CANWiBase"); // NOI18N
280        result.put(39, "WiCAB"); // NOI18N
281        result.put(40, "CANWiFi"); // NOI18N
282        result.put(41, "CANFTT"); // NOI18N
283        result.put(42, "CANHNDST"); // NOI18N
284        result.put(43, "CANTCHNDST"); // NOI18N
285        result.put(44, "CANRFID8"); // NOI18N
286        result.put(45, "CANmchRFID"); // NOI18N
287        result.put(46, "CANPiWi"); // NOI18N
288        result.put(47, "CAN4DC"); // NOI18N
289        result.put(48, "CANELEV"); // NOI18N
290        result.put(49, "CANSCAN"); // NOI18N
291        result.put(50, "CANMIO_SVO"); // NOI18N
292        result.put(51, "CANMIO_INP"); // NOI18N
293        result.put(52, "CANMIO_OUT"); // NOI18N
294        result.put(53, "CANBIP_OUT"); // NOI18N
295        result.put(54, "CANASTOP"); // NOI18N
296        result.put(55, "CANCSB"); // NOI18N
297        result.put(56, "CANMAG"); // NOI18N
298        result.put(57, "CANACE16CMIO"); // NOI18N
299        result.put(58, "CANPiNODE"); // NOI18N
300        result.put(59, "CANDISP"); // NOI18N
301        result.put(60, "CANCOMPUTE"); // NOI18N
302        result.put(61, "CANRC522"); // NOI18N
303        result.put(62, "CANINP"); // NOI18N
304        result.put(63, "CANOUT"); // NOI18N
305        result.put(64, "CANEMIO"); // NOI18N
306        result.put(65, "CANCABDC"); // NOI18N
307        result.put(66, "CANRCOM"); // NOI18N
308        result.put(67, "CANMP3");
309        result.put(68, "CANXMAS");
310        result.put(69, "CANSVOSET");
311        result.put(70, "CANCMDDC");
312        result.put(71, "CANTEXT");
313        result.put(72, "CANASIGNAL");
314        result.put(73, "CANSLIDER");
315        result.put(74, "CANDCATC");
316        result.put(75, "CANGATE");
317        result.put(76, "CANSINP");
318        result.put(77, "CANSOUT");
319        result.put(78, "CANSBIP");
320        result.put(79, "CANBUFFER");
321        result.put(80, "CANLEVER");
322        result.put(81, "CANSHIELD");
323        result.put(82, "CAN4IN4OUT");
324        result.put(83, "CANCMDB");
325        result.put(84, "CANPIXEL");
326        result.put(85, "CANCABPE");
327        result.put(86, "CANSMARTTD");
328        result.put(87, "CANARGB");
329
330        result.put(252, "VLCB");
331        result.put(253, "CANUSB"); // NOI18N
332        result.put(254, "EMPTY"); // NOI18N
333        result.put(255, "CAN_SW"); // NOI18N
334        return Collections.unmodifiableMap(result);
335    }
336
337    /*
338     * Populate hashmap with format strings
339     *
340     */
341    private static Map<Integer, String> createType70Map() {
342        Map<Integer, String> result = new HashMap<>();
343        result.put(1, "CANGC1"); // NOI18N
344        result.put(2, "CANGC2"); // NOI18N
345        result.put(3, "CANGC3"); // NOI18N
346        result.put(4, "CANGC4"); // NOI18N
347        result.put(5, "CANGC5"); // NOI18N
348        result.put(6, "CANGC6"); // NOI18N
349        result.put(7, "CANGC7"); // NOI18N
350        result.put(11, "CANGC1e"); // NOI18N
351        return Collections.unmodifiableMap(result);
352    }
353
354    
355    /*
356     * Populate hashmap with format strings
357     *
358     */
359    private static Map<Integer, String> createType80Map() {
360        Map<Integer, String> result = new HashMap<>();
361        result.put(1, "AMCTRLR"); // NOI18N
362        result.put(2, "DUALCAB"); // NOI18N
363        return Collections.unmodifiableMap(result);
364    }
365    
366    
367    /*
368     * Populate hashmap with format strings.
369     * Visible name of module, not the CBUS NAME OPC Response.
370     */
371    private static Map<Integer, String> createType44Map() {
372        Map<Integer, String> result = new HashMap<>();
373        result.put(1, "Pi-SPROG 3"); // NOI18N
374        result.put(2, "CANSPROG3P"); // NOI18N
375        result.put(3, "CANSPROG"); // NOI18N
376        result.put(4, "SBOOST"); // NOI18N
377        result.put(5, "Unsupported"); // NOI18N
378
379        result.put(8, "CANSOLNOID"); // NOI18N. Matches MERG CANSOL
380        result.put(50, "CANSERVOIO"); // NOI18N. Matches MERG canmio-svo
381        
382        result.put(100, "CANISB"); // NOI18N
383        result.put(101, "CANSOLIO"); // NOI18N
384        return Collections.unmodifiableMap(result);
385    }
386    
387    
388    /**
389     * Return a string representation of extra module info
390     * @param man int manufacturer code
391     * @param type int module type
392     * @return string value of extra module info
393     */
394    public static String getModuleTypeExtra(int man, int type) {
395        String format="";
396        switch (man) {
397            case MANU_MERG:
398                format = extra165Map.get(type);
399                break;
400            case MANU_ROCRAIL:
401                format = extra70Map.get(type);
402                break;
403            case MANU_SPECTRUM:
404                format = extra80Map.get(type);
405                break;
406            case SPROG_DCC:
407                format = extra44Map.get(type);
408                break;
409            default:
410                break;
411        }
412        return format;
413    }
414    
415    /**
416     * Hashmap for decoding Module extra info
417     */
418    private static final Map<Integer, String> extra165Map = createExtra165Map();
419    private static final Map<Integer, String> extra70Map = createExtra70Map();
420    private static final Map<Integer, String> extra80Map = createExtra80Map();
421    private static final Map<Integer, String> extra44Map = createExtra44Map();
422    
423    /*
424     * Populate hashmap with format strings
425     */
426    private static Map<Integer, String> createExtra165Map() {
427        Map<Integer, String> result = new HashMap<>();
428        result.put(0, "Default for SLiM nodes");
429        result.put(1, "Solenoid point driver");
430        result.put(2, "Motorised point driver");
431        result.put(3, "8 digital outputs ( + 8 inputs if modded)");
432        result.put(4, "Control panel switch/button encoder");
433        result.put(5, "8 digital inputs");
434        result.put(6, "64 led driver");
435        result.put(7, "64 led driver (multi leds per event)");
436        result.put(8, "12v version of CANACC4 Solenoid point driver");
437        result.put(9, "CANCAB hand throttle");
438        result.put(10, "Command Station");
439        result.put(11, "8 servo driver (on canacc8 or similar hardware)");
440        result.put(12, "BC1a Command Station");
441        result.put(13, "RPI and RFID interface");
442        result.put(14, "Turntable controller (turntable end)");
443        result.put(15, "Turntable controller (control panel end)");
444        result.put(16, "Handset controller for old BC1a type handsets");
445        result.put(17, "Track occupancy detector");
446        result.put(18, "8 inputs 8 outputs");
447        result.put(19, "Canservo with servo position feedback");
448        result.put(20, "RFID input");
449        result.put(21, "CANTC4");
450        result.put(22, "16 inputs");
451        result.put(23, "8 way I/O");
452        result.put(24, "CANSNDX");
453        result.put(25, "Ethernet interface");
454        result.put(26, "Multiple aspect signalling for CANLED module");
455        result.put(27, "Multiple aspect signalling for CANACC8 module");
456        result.put(28, "Conditional event generation");
457        result.put(29, "Control panel 32 Outputs + 32 Inputs");
458        result.put(30, "Newer version of CANACE3 firmware");
459        result.put(31, "Control panel 64 Inputs / 64 Outputs");
460        result.put(32, "Multiple I/O – Universal CANMIO firmware");
461        result.put(33, "Multiple IO module 16 inputs emulating CANACE8C on CANMIO hardware");
462        result.put(34, "Solenoid driver module");
463        result.put(35, "Universal CANBIP firmware - Bipolar IO module with additional 8 I/O pins (CANMIO family)");
464        result.put(36, "Solenoid driver module with additional 6 I/O pins (CANMIO family)");
465        result.put(37, "CANACC4 firmware ported to CANCDU");
466        result.put(38, "CAN to MiWi base station");
467        result.put(39, "Wireless cab using MiWi protocol");
468        result.put(40, "CAN to WiFi connection with Withrottle to CBUS protocol conversion");
469        result.put(41, "Turntable controller configured using FLiM");
470        result.put(42, "Handset (alternative to CANCAB)");
471        result.put(43, "Touchscreen handset");
472        result.put(44, "multi-channel RFID reader");
473        result.put(45, "either a 2ch or 8ch RFID reader");
474        result.put(46, "Raspberry Pi based module for WiFi");
475        result.put(47, "DC train controller");
476        result.put(48, "Nelevator controller");
477        result.put(49, "128 switch inputs");
478        result.put(50, "16MHz 25k80 version of CANSERVO8c on CANMIO hardware");
479        result.put(51, "16MHz 25k80 version of CANACE8MIO on CANMIO hardware");
480        result.put(52, "16MHz 25k80 version of CANACC8 on CANMIO hardware");
481        result.put(53, "16MHz 25k80 version of CANACC5 on CANMIO hardware");
482        result.put(54, "DCC stop generator");
483        result.put(55, "Command Station with 3A booster");
484        result.put(56, "Magnet on Track detector");
485        result.put(57, "16 input equivaent to CANACE8C");
486        result.put(58, "CBUS module based on Raspberry Pi");
487        result.put(59, "25K80 version of CANLED64");
488        result.put(60, "Compute Event processing engine");
489        result.put(61, "Read/Write from/to RC522 RFID tags");
490        result.put(62, "8 inputs module (2g version of CANACE8c)");
491        result.put(63, "8 outputs module (2g version of CANACC8)");
492        result.put(64, "Extended CANMIO (24 I/O ports)");
493        result.put(65, "DC cab");
494        result.put(66, "DCC Railcom detector/reader");
495        result.put(67, "MP3 sound player in response to events (eg: station announcements)");
496        result.put(68, "Addressed RGB LED driver");
497        result.put(69, "Servo setting box");
498        result.put(70, "DC Command station");
499        result.put(71, "Text message display");
500        result.put(72, "Signal controller");
501        result.put(73, "DCC cab with slider control");
502        result.put(74, "DC ATC module");
503        result.put(75, "Logic module using and/or gates");
504        result.put(76, "Q series PIC input module");
505        result.put(77, "Q series PIC output module");
506        result.put(78, "Q series PIC BIP module");
507        result.put(79, "Message buffer");
508        result.put(80, "Lever frame module");
509        result.put(81, "Kit 110 Arduino shield test firmware");
510        result.put(82, "4 inputs 4 outputs (Arduino module)");
511        result.put(83, "CANCMD with built in booster");
512        result.put(84, "neopixel driver");
513        result.put(85, "Cab2 with pot or encoder");
514        result.put(86, "Smart train detector");
515        result.put(87, "Addressable LEDs");
516
517        result.put(252, "All VLCB modules have the same ID");
518        result.put(253, "USB interface");
519        result.put(254, "Empty module, bootloader only");
520        result.put(255, "Software nodes");
521        return Collections.unmodifiableMap(result);
522    }
523    
524    /*
525     * Populate hashmap with format strings
526     * extra text for Rocrail Modules
527     */
528    private static Map<Integer, String> createExtra70Map() {
529        Map<Integer, String> result = new HashMap<>();
530        result.put(1, "RS232 PC interface.");
531        result.put(2, "16 I/O.");
532        result.put(3, "Command station (derived from cancmd).");
533        result.put(4, "8 channel RFID reader.");        
534        result.put(5, "Cab for fixed panels (derived from cancab).");        
535        result.put(6, "4 channel servo controller.");        
536        result.put(7, "Fast clock module.");        
537        result.put(11, "CAN Ethernet interface.");
538        return Collections.unmodifiableMap(result);
539    }    
540    
541    /*
542     * Populate hashmap with format strings
543     * extra text for Animated Modeller module types
544     */
545    private static Map<Integer, String> createExtra80Map() {
546        Map<Integer, String> result = new HashMap<>();
547        result.put(1, "Animation controller (firmware derived from cancmd).");
548        result.put(2, "Dual cab based on cancab.");
549        return Collections.unmodifiableMap(result);
550    }   
551
552    
553    /*
554     * Populate hashmap with format strings
555     * extra text for Animated Modeller module types
556     */
557    private static Map<Integer, String> createExtra44Map() {
558        Map<Integer, String> result = new HashMap<>();
559        result.put(1, "Pi-SPROG 3 programmer/command station.");
560        result.put(2, "SPROG 3 Plus programmer/command station.");
561        result.put(3, "CAN SPROG programmer/command station.");
562        result.put(4, "System booster");
563        result.put(5, "Unsupported module type");
564        
565        result.put(8, "8-channel twin-coil solenoid, like MERG CANACC4_2.");
566        result.put(50, "8-channel servo I/O, like MERG CANMIO_SVO.");
567        
568        result.put(100, "Isolated USB to CAN interface with CBUS node.");
569        result.put(101, "8-channel twin-coil solenoid I/O.");
570        return Collections.unmodifiableMap(result);
571    }   
572
573  
574    /**
575     * Return a string representation of Module Support Link
576     * @param man int manufacturer ID
577     * @param type int module type ID
578     * @return string module support link, else empty string
579     */
580    public static String getModuleSupportLink(int man, int type) {
581        String format="";
582        if (man == MANU_MERG) {
583            format = link165Map.get(type);
584        }
585        else if (man == MANU_ROCRAIL) {
586            format = link70Map.get(type);
587        }
588        else if (man == SPROG_DCC) {
589            format = link44Map.get(type);
590        }
591        if ( format == null ){
592            return ("");
593        }
594        return format;
595    }
596    
597    private static final Map<Integer, String> link165Map = createLink165Map();
598    private static final Map<Integer, String> link70Map = createLink70Map();
599    private static final Map<Integer, String> link44Map = createLink44Map();
600    
601    /*
602     * Populate hashmap with merg module support links
603     */
604    private static Map<Integer, String> createLink165Map() {
605        Map<Integer, String> result = new HashMap<>();
606        
607        result.put(1, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
608        result.put(2, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc5"); // NOI18N
609        result.put(3, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc8"); // NOI18N
610        result.put(4, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
611        result.put(5, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
612        // result.put(6, "CANLED"); // NOI18N
613        result.put(7, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canled64"); // NOI18N
614        result.put(8, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
615        result.put(9, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancab"); // NOI18N
616        result.put(10, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancmd"); // NOI18N
617        // result.put(11, "CANSERVO"); // NOI18N
618        // result.put(12, "CANBC"); // NOI18N
619        // result.put(13, "CANRPI"); // NOI18N
620        result.put(14, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
621        result.put(15, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
622        // result.put(16, "CANHS"); // NOI18N
623        result.put(17, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
624        // result.put(18, "CAN8I8O"); // NOI18N
625        result.put(19, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canservo8"); // NOI18N
626        result.put(20, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid"); // NOI18N
627        // result.put(21, "CANTC4"); // NOI18N
628        // result.put(22, "CANACE16C"); // NOI18N
629        // result.put(23, "CANIO8"); // NOI18N
630        // result.put(24, "CANSNDX"); // NOI18N
631        result.put(25, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canether"); // NOI18N
632        result.put(26, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
633        result.put(27, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
634        result.put(28, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canccond8c"); // NOI18N
635        result.put(29, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpan"); // NOI18N
636        result.put(30, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
637        result.put(31, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpanel"); // NOI18N
638        result.put(32, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
639        // result.put(33, "CANACE8MIO"); // NOI18N
640        result.put(34, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansol"); // NOI18N
641        result.put(35, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canbip"); // NOI18N
642        result.put(36, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancdu"); // NOI18N
643        // result.put(37, "CANACC4CDU"); // NOI18N
644        // result.put(38, "CANWiBase"); // NOI18N
645        // result.put(39, "WiCAB"); // NOI18N
646        // result.put(40, "CANWiFi"); // NOI18N
647        // result.put(41, "CANFTT"); // NOI18N
648        // result.put(42, "CANHNDST"); // NOI18N
649        // result.put(43, "CANTCHNDST"); // NOI18N
650        result.put(44, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid8"); // NOI18N
651        result.put(45, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmchrfid"); // NOI18N
652        result.put(46, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canwi"); // NOI18N
653        result.put(47, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:can4dc"); // NOI18N
654        // result.put(48, "CANELEV"); // NOI18N
655        result.put(49, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canscan"); // NOI18N
656        result.put(50, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
657        result.put(51, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
658        result.put(52, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
659        // result.put(53, "CANBIP_OUT"); // NOI18N
660        result.put(54, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canastop"); // NOI18N
661        result.put(55, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancsb"); // NOI18N
662        // result.put(56, "CANMAGOT"); // NOI18N
663        // result.put(57, "CANACE16CMIO"); // NOI18N
664        // result.put(58, "CANPiNODE"); // NOI18N
665        result.put(59, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:candisp"); // NOI18N
666        result.put(60, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancompute"); // NOI18N
667        result.put(61, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canrc522"); // NOI18N
668        result.put(62, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:caninp"); // NOI18N
669        result.put(63, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canout"); // NOI18N
670        // result.put(64, "CANEMIO"); // NOI18N
671        result.put(65, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:cancabdc"); // NOI18N
672        result.put(66, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrcom"); // NOI18N
673        // result.put(67, "CANMP3");
674        result.put(68, "https://www.merg.org.uk/merg_wiki/doku.php?id=projects:canxmas");
675        // result.put(69, "CANSVOSET");
676        // result.put(70, "CANCMDDC");
677        // result.put(71, "CANTEXT");
678        // result.put(72, "CANASIGNAL");
679        result.put(73, "https://www.merg.org.uk/merg_wiki/doku.php?id=projects:canslider");
680        // result.put(74, "CANDCATC");
681        result.put(75, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cangate");
682        // result.put(76, "CANSINP");
683        // result.put(77, "CANSOUT");
684        // result.put(78, "CANSBIP");
685        // result.put(79, "CANBUFFER");
686        // result.put(80, "CANLEVER");
687        // result.put(81, "CANSHIELD");
688        // result.put(82, "CAN4IN4OUT");
689        result.put(83, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancmdb");
690        // result.put(84, "CANPIXEL");
691        // result.put(85, "CANCABPE");
692        // result.put(86, "CANSMARTTD");
693        // result.put(87, "CANARGB");
694
695        // result.put(252, "VLCB");
696        // result.put(253, "CANUSB"); // NOI18N
697        // result.put(254, "EMPTY"); // NOI18N
698        // result.put(255, "CAN_SW"); // NOI18N        
699        
700        return Collections.unmodifiableMap(result);
701    }
702    
703    /*
704     * Populate hashmap with rocrail module support links
705     */
706    private static Map<Integer, String> createLink70Map() {
707        Map<Integer, String> result = new HashMap<>();
708        result.put(1, "https://wiki.rocrail.net/doku.php?id=can-gca1-en"); // NOI18N
709        result.put(2, "https://wiki.rocrail.net/doku.php?id=can-gca2-en"); // NOI18N
710        result.put(3, "https://wiki.rocrail.net/doku.php?id=can-gc3-en"); // NOI18N
711        result.put(4, "https://wiki.rocrail.net/doku.php?id=can-gc4-en"); // NOI18N
712        result.put(5, "https://wiki.rocrail.net/doku.php?id=can-gca5-en"); // NOI18N
713        result.put(6, "https://wiki.rocrail.net/doku.php?id=can-gc6-en"); // NOI18N
714        result.put(7, "https://wiki.rocrail.net/doku.php?id=can-gc7-en"); // NOI18N
715        result.put(11, "https://wiki.rocrail.net/doku.php?id=can-gca1e-en"); // NOI18N
716        return Collections.unmodifiableMap(result);
717    }
718    
719    
720    /*
721     * Populate hashmap with SPROG module support links
722     */
723    private static Map<Integer, String> createLink44Map() {
724        Map<Integer, String> result = new HashMap<>();
725        result.put(1, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
726        result.put(2, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
727        result.put(3, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
728        result.put(4, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
729        result.put(5, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
730        result.put(8, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
731        result.put(50, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
732        result.put(100, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
733        result.put(101, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
734        return Collections.unmodifiableMap(result);
735    }
736    
737    
738    /**
739     * Return a string representation of a reserved node number
740     * @param modnum node number
741     * @return reserved node number reason
742     */
743    public static String getReservedModule(int modnum) {
744        // look for the opcode
745        String format = resMod.get(modnum);
746        if (format == null) {
747            return "";
748        } else {
749            return format; 
750        }
751    }
752    
753    /**
754     * Hashmap for fixed Module Numbers
755     */
756    private static final Map<Integer, String> resMod = createModMap();
757
758    /*
759     * Populate hashmap with format strings
760     *
761     */
762    private static Map<Integer, String> createModMap() {
763        Map<Integer, String> result = new HashMap<>();
764
765        for (int i = 100; i < 126; i++) {
766            result.put(i, Bundle.getMessage("NdNumReserveFixed")); // NOI18N
767        }
768        // use html with br so text fits into dialogue
769        result.put(120, "Reserved / Command Station Shuttles");
770        result.put(126, "Reserved for CAN_RS Modules");
771        result.put(127, "Reserved for CAN_USB Modules");
772        result.put(162, "Used in Command Station Shuttles");
773        result.put(163, "Used in Command Station Shuttles");
774        result.put(164, "Used in Command Station Shuttles");
775        result.put(167, "Used in Command Station Shuttles");
776        result.put(65534, "Reserved for Command Station");
777        result.put(65535, "Reserved, used by all CABS");
778        return Collections.unmodifiableMap(result);
779    }
780
781    private static final Map<String, BackupType> nameIndex =
782            new HashMap<>(BackupType.values().length);
783    static {
784        for (BackupType t : BackupType.values()) {
785            nameIndex.put(t.name(), t);
786        }
787    }
788
789    private static final Map<BackupType, String> displayPhraseIndex =
790            new HashMap<>(BackupType.values().length);
791    static {
792        displayPhraseIndex.put(BackupType.INCOMPLETE, Bundle.getMessage("BackupIncomplete"));
793        displayPhraseIndex.put(BackupType.COMPLETE, Bundle.getMessage("BackupComplete"));
794        displayPhraseIndex.put(BackupType.COMPLETEDWITHERROR, Bundle.getMessage("BackupCompleteError"));
795        displayPhraseIndex.put(BackupType.NOTONNETWORK, Bundle.getMessage("BackupNotOnNetwork"));
796        displayPhraseIndex.put(BackupType.OUTSTANDING, Bundle.getMessage("BackupOutstanding"));
797        displayPhraseIndex.put(BackupType.SLIM, Bundle.getMessage("NodeInSlim"));
798    }
799    
800    /*
801     * Get the display phrase for an enum value
802     * <p>
803     * eg. displayPhrase(BackupType.INCOMPLETE) will return "Backup InComplete"
804     *
805     * @param type The enum to translate
806     * @return The phrase
807     *
808     */
809    public static String displayPhrase(BackupType type) {
810        return displayPhraseIndex.get(type);
811    }
812    
813    /*
814     * Get the enum type for a String value
815     * <p>
816     * eg. lookupByName("Complete") will return BackupType.COMPLETE
817     *
818     * @param name The String to lookup
819     * @return The BackupType enum, else null
820     *
821     */
822    public static BackupType lookupByName(String name) {
823        return nameIndex.get(name);
824    }
825    
826    /*
827     * enum to represent Node Backup Conditions in a CBUS Node XML File
828     *
829     */
830    public enum BackupType{
831        INCOMPLETE(0),
832        COMPLETE(1),
833        COMPLETEDWITHERROR(2),
834        NOTONNETWORK(3),
835        OUTSTANDING(4),
836        SLIM(5);
837        
838        private final int v;
839
840        BackupType(final int v) {
841            this.v = v;
842        }
843    
844        public int getValue() {
845            return v;
846        }
847    
848    }
849
850    // private final static Logger log = LoggerFactory.getLogger(CbusNodeConstants.class);
851}