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        result.put(88, "CANCDU_U");
330        result.put(89, "CANGATEX");
331        result.put(90, "CANGATEXL");
332
333        result.put(252, "VLCB");
334        result.put(253, "CANUSB"); // NOI18N
335        result.put(254, "EMPTY"); // NOI18N
336        result.put(255, "CAN_SW"); // NOI18N
337        return Collections.unmodifiableMap(result);
338    }
339
340    /*
341     * Populate hashmap with format strings
342     *
343     */
344    private static Map<Integer, String> createType70Map() {
345        Map<Integer, String> result = new HashMap<>();
346        result.put(1, "CANGC1"); // NOI18N
347        result.put(2, "CANGC2"); // NOI18N
348        result.put(3, "CANGC3"); // NOI18N
349        result.put(4, "CANGC4"); // NOI18N
350        result.put(5, "CANGC5"); // NOI18N
351        result.put(6, "CANGC6"); // NOI18N
352        result.put(7, "CANGC7"); // NOI18N
353        result.put(11, "CANGC1e"); // NOI18N
354        return Collections.unmodifiableMap(result);
355    }
356
357    
358    /*
359     * Populate hashmap with format strings
360     *
361     */
362    private static Map<Integer, String> createType80Map() {
363        Map<Integer, String> result = new HashMap<>();
364        result.put(1, "AMCTRLR"); // NOI18N
365        result.put(2, "DUALCAB"); // NOI18N
366        return Collections.unmodifiableMap(result);
367    }
368    
369    
370    /*
371     * Populate hashmap with format strings.
372     * Visible name of module, not the CBUS NAME OPC Response.
373     */
374    private static Map<Integer, String> createType44Map() {
375        Map<Integer, String> result = new HashMap<>();
376        result.put(1, "Pi-SPROG 3"); // NOI18N
377        result.put(2, "CANSPROG3P"); // NOI18N
378        result.put(3, "CANSPROG"); // NOI18N
379        result.put(4, "SBOOST"); // NOI18N
380        result.put(5, "Unsupported"); // NOI18N
381
382        result.put(8, "CANSOLNOID"); // NOI18N. Matches MERG CANSOL
383        result.put(50, "CANSERVOIO"); // NOI18N. Matches MERG canmio-svo
384        
385        result.put(100, "CANISB"); // NOI18N
386        result.put(101, "CANSOLIO"); // NOI18N
387        return Collections.unmodifiableMap(result);
388    }
389    
390    
391    /**
392     * Return a string representation of extra module info
393     * @param man int manufacturer code
394     * @param type int module type
395     * @return string value of extra module info
396     */
397    public static String getModuleTypeExtra(int man, int type) {
398        String format="";
399        switch (man) {
400            case MANU_MERG:
401                format = extra165Map.get(type);
402                break;
403            case MANU_ROCRAIL:
404                format = extra70Map.get(type);
405                break;
406            case MANU_SPECTRUM:
407                format = extra80Map.get(type);
408                break;
409            case SPROG_DCC:
410                format = extra44Map.get(type);
411                break;
412            default:
413                break;
414        }
415        return format;
416    }
417    
418    /**
419     * Hashmap for decoding Module extra info
420     */
421    private static final Map<Integer, String> extra165Map = createExtra165Map();
422    private static final Map<Integer, String> extra70Map = createExtra70Map();
423    private static final Map<Integer, String> extra80Map = createExtra80Map();
424    private static final Map<Integer, String> extra44Map = createExtra44Map();
425    
426    /*
427     * Populate hashmap with format strings
428     */
429    private static Map<Integer, String> createExtra165Map() {
430        Map<Integer, String> result = new HashMap<>();
431        result.put(0, "Default for SLiM nodes");
432        result.put(1, "Solenoid point driver");
433        result.put(2, "Motorised point driver");
434        result.put(3, "8 digital outputs ( + 8 inputs if modded)");
435        result.put(4, "Control panel switch/button encoder");
436        result.put(5, "8 digital inputs");
437        result.put(6, "64 led driver");
438        result.put(7, "64 led driver (multi leds per event)");
439        result.put(8, "12v version of CANACC4 Solenoid point driver");
440        result.put(9, "CANCAB hand throttle");
441        result.put(10, "Command Station");
442        result.put(11, "8 servo driver (on canacc8 or similar hardware)");
443        result.put(12, "BC1a Command Station");
444        result.put(13, "RPI and RFID interface");
445        result.put(14, "Turntable controller (turntable end)");
446        result.put(15, "Turntable controller (control panel end)");
447        result.put(16, "Handset controller for old BC1a type handsets");
448        result.put(17, "Track occupancy detector");
449        result.put(18, "8 inputs 8 outputs");
450        result.put(19, "Canservo with servo position feedback");
451        result.put(20, "RFID input");
452        result.put(21, "CANTC4");
453        result.put(22, "16 inputs");
454        result.put(23, "8 way I/O");
455        result.put(24, "CANSNDX");
456        result.put(25, "Ethernet interface");
457        result.put(26, "Multiple aspect signalling for CANLED module");
458        result.put(27, "Multiple aspect signalling for CANACC8 module");
459        result.put(28, "Conditional event generation");
460        result.put(29, "Control panel 32 Outputs + 32 Inputs");
461        result.put(30, "Newer version of CANACE3 firmware");
462        result.put(31, "Control panel 64 Inputs / 64 Outputs");
463        result.put(32, "Multiple I/O – Universal CANMIO firmware");
464        result.put(33, "Multiple IO module 16 inputs emulating CANACE8C on CANMIO hardware");
465        result.put(34, "Solenoid driver module");
466        result.put(35, "Universal CANBIP firmware - Bipolar IO module with additional 8 I/O pins (CANMIO family)");
467        result.put(36, "Solenoid driver module with additional 6 I/O pins (CANMIO family)");
468        result.put(37, "CANACC4 firmware ported to CANCDU");
469        result.put(38, "CAN to MiWi base station");
470        result.put(39, "Wireless cab using MiWi protocol");
471        result.put(40, "CAN to WiFi connection with Withrottle to CBUS protocol conversion");
472        result.put(41, "Turntable controller configured using FLiM");
473        result.put(42, "Handset (alternative to CANCAB)");
474        result.put(43, "Touchscreen handset");
475        result.put(44, "multi-channel RFID reader");
476        result.put(45, "either a 2ch or 8ch RFID reader");
477        result.put(46, "Raspberry Pi based module for WiFi");
478        result.put(47, "DC train controller");
479        result.put(48, "Nelevator controller");
480        result.put(49, "128 switch inputs");
481        result.put(50, "16MHz 25k80 version of CANSERVO8c on CANMIO hardware");
482        result.put(51, "16MHz 25k80 version of CANACE8MIO on CANMIO hardware");
483        result.put(52, "16MHz 25k80 version of CANACC8 on CANMIO hardware");
484        result.put(53, "16MHz 25k80 version of CANACC5 on CANMIO hardware");
485        result.put(54, "DCC stop generator");
486        result.put(55, "Command Station with 3A booster");
487        result.put(56, "Magnet on Track detector");
488        result.put(57, "16 input equivaent to CANACE8C");
489        result.put(58, "CBUS module based on Raspberry Pi");
490        result.put(59, "25K80 version of CANLED64");
491        result.put(60, "Compute Event processing engine");
492        result.put(61, "Read/Write from/to RC522 RFID tags");
493        result.put(62, "8 inputs module (2g version of CANACE8c)");
494        result.put(63, "8 outputs module (2g version of CANACC8)");
495        result.put(64, "Extended CANMIO (24 I/O ports)");
496        result.put(65, "DC cab");
497        result.put(66, "DCC Railcom detector/reader");
498        result.put(67, "MP3 sound player in response to events (eg: station announcements)");
499        result.put(68, "Addressed RGB LED driver");
500        result.put(69, "Servo setting box");
501        result.put(70, "DC Command station");
502        result.put(71, "Text message display");
503        result.put(72, "Signal controller");
504        result.put(73, "DCC cab with slider control");
505        result.put(74, "DC ATC module");
506        result.put(75, "Logic module using and/or gates");
507        result.put(76, "Q series PIC input module");
508        result.put(77, "Q series PIC output module");
509        result.put(78, "Q series PIC BIP module");
510        result.put(79, "Message buffer");
511        result.put(80, "Lever frame module");
512        result.put(81, "Kit 110 Arduino shield test firmware");
513        result.put(82, "4 inputs 4 outputs (Arduino module)");
514        result.put(83, "CANCMD with built in booster");
515        result.put(84, "neopixel driver");
516        result.put(85, "Cab2 with pot or encoder");
517        result.put(86, "Smart train detector");
518        result.put(87, "Addressable LEDs");
519        result.put(88, "CANCDU (universal)");
520        result.put(89, "Logic module using boolean logic gates");
521        result.put(90, "Extended version of CANGATEX");
522
523        result.put(252, "All VLCB modules have the same ID");
524        result.put(253, "USB interface");
525        result.put(254, "Empty module, bootloader only");
526        result.put(255, "Software nodes");
527        return Collections.unmodifiableMap(result);
528    }
529    
530    /*
531     * Populate hashmap with format strings
532     * extra text for Rocrail Modules
533     */
534    private static Map<Integer, String> createExtra70Map() {
535        Map<Integer, String> result = new HashMap<>();
536        result.put(1, "RS232 PC interface.");
537        result.put(2, "16 I/O.");
538        result.put(3, "Command station (derived from cancmd).");
539        result.put(4, "8 channel RFID reader.");        
540        result.put(5, "Cab for fixed panels (derived from cancab).");        
541        result.put(6, "4 channel servo controller.");        
542        result.put(7, "Fast clock module.");        
543        result.put(11, "CAN Ethernet interface.");
544        return Collections.unmodifiableMap(result);
545    }    
546    
547    /*
548     * Populate hashmap with format strings
549     * extra text for Animated Modeller module types
550     */
551    private static Map<Integer, String> createExtra80Map() {
552        Map<Integer, String> result = new HashMap<>();
553        result.put(1, "Animation controller (firmware derived from cancmd).");
554        result.put(2, "Dual cab based on cancab.");
555        return Collections.unmodifiableMap(result);
556    }   
557
558    
559    /*
560     * Populate hashmap with format strings
561     * extra text for Animated Modeller module types
562     */
563    private static Map<Integer, String> createExtra44Map() {
564        Map<Integer, String> result = new HashMap<>();
565        result.put(1, "Pi-SPROG 3 programmer/command station.");
566        result.put(2, "SPROG 3 Plus programmer/command station.");
567        result.put(3, "CAN SPROG programmer/command station.");
568        result.put(4, "System booster");
569        result.put(5, "Unsupported module type");
570        
571        result.put(8, "8-channel twin-coil solenoid, like MERG CANACC4_2.");
572        result.put(50, "8-channel servo I/O, like MERG CANMIO_SVO.");
573        
574        result.put(100, "Isolated USB to CAN interface with CBUS node.");
575        result.put(101, "8-channel twin-coil solenoid I/O.");
576        return Collections.unmodifiableMap(result);
577    }   
578
579  
580    /**
581     * Return a string representation of Module Support Link
582     * @param man int manufacturer ID
583     * @param type int module type ID
584     * @return string module support link, else empty string
585     */
586    public static String getModuleSupportLink(int man, int type) {
587        String format="";
588        if (man == MANU_MERG) {
589            format = link165Map.get(type);
590        }
591        else if (man == MANU_ROCRAIL) {
592            format = link70Map.get(type);
593        }
594        else if (man == SPROG_DCC) {
595            format = link44Map.get(type);
596        }
597        if ( format == null ){
598            return ("");
599        }
600        return format;
601    }
602    
603    private static final Map<Integer, String> link165Map = createLink165Map();
604    private static final Map<Integer, String> link70Map = createLink70Map();
605    private static final Map<Integer, String> link44Map = createLink44Map();
606    
607    /*
608     * Populate hashmap with merg module support links
609     */
610    private static Map<Integer, String> createLink165Map() {
611        Map<Integer, String> result = new HashMap<>();
612        
613        result.put(1, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
614        result.put(2, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc5"); // NOI18N
615        result.put(3, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc8"); // NOI18N
616        result.put(4, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
617        result.put(5, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
618        // result.put(6, "CANLED"); // NOI18N
619        result.put(7, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canled64"); // NOI18N
620        result.put(8, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canacc4"); // NOI18N
621        result.put(9, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancab"); // NOI18N
622        result.put(10, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancmd"); // NOI18N
623        // result.put(11, "CANSERVO"); // NOI18N
624        // result.put(12, "CANBC"); // NOI18N
625        // result.put(13, "CANRPI"); // NOI18N
626        result.put(14, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
627        result.put(15, "https://www.merg.org.uk/merg_wiki/doku.php?id=other_download:turntable"); // NOI18N
628        // result.put(16, "CANHS"); // NOI18N
629        result.put(17, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace8c"); // NOI18N
630        // result.put(18, "CAN8I8O"); // NOI18N
631        result.put(19, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canservo8"); // NOI18N
632        result.put(20, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid"); // NOI18N
633        // result.put(21, "CANTC4"); // NOI18N
634        // result.put(22, "CANACE16C"); // NOI18N
635        // result.put(23, "CANIO8"); // NOI18N
636        // result.put(24, "CANSNDX"); // NOI18N
637        result.put(25, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canether"); // NOI18N
638        result.put(26, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
639        result.put(27, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansig"); // NOI18N
640        result.put(28, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canccond8c"); // NOI18N
641        result.put(29, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpan"); // NOI18N
642        result.put(30, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canace3"); // NOI18N
643        result.put(31, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canpanel"); // NOI18N
644        result.put(32, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
645        // result.put(33, "CANACE8MIO"); // NOI18N
646        result.put(34, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cansol"); // NOI18N
647        result.put(35, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canbip"); // NOI18N
648        result.put(36, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancdu"); // NOI18N
649        // result.put(37, "CANACC4CDU"); // NOI18N
650        // result.put(38, "CANWiBase"); // NOI18N
651        // result.put(39, "WiCAB"); // NOI18N
652        // result.put(40, "CANWiFi"); // NOI18N
653        // result.put(41, "CANFTT"); // NOI18N
654        // result.put(42, "CANHNDST"); // NOI18N
655        // result.put(43, "CANTCHNDST"); // NOI18N
656        result.put(44, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrfid8"); // NOI18N
657        result.put(45, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmchrfid"); // NOI18N
658        result.put(46, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canwi"); // NOI18N
659        result.put(47, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:can4dc"); // NOI18N
660        // result.put(48, "CANELEV"); // NOI18N
661        result.put(49, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canscan"); // NOI18N
662        result.put(50, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
663        result.put(51, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
664        result.put(52, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canmio"); // NOI18N
665        // result.put(53, "CANBIP_OUT"); // NOI18N
666        result.put(54, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canastop"); // NOI18N
667        result.put(55, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancsb"); // NOI18N
668        // result.put(56, "CANMAGOT"); // NOI18N
669        // result.put(57, "CANACE16CMIO"); // NOI18N
670        // result.put(58, "CANPiNODE"); // NOI18N
671        result.put(59, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:candisp"); // NOI18N
672        result.put(60, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancompute"); // NOI18N
673        result.put(61, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canrc522"); // NOI18N
674        result.put(62, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:caninp"); // NOI18N
675        result.put(63, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:canout"); // NOI18N
676        // result.put(64, "CANEMIO"); // NOI18N
677        result.put(65, "https://merg.org.uk/merg_wiki/doku.php?id=cbus:cancabdc"); // NOI18N
678        result.put(66, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:canrcom"); // NOI18N
679        // result.put(67, "CANMP3");
680        result.put(68, "https://www.merg.org.uk/merg_wiki/doku.php?id=projects:canxmas");
681        // result.put(69, "CANSVOSET");
682        // result.put(70, "CANCMDDC");
683        // result.put(71, "CANTEXT");
684        // result.put(72, "CANASIGNAL");
685        result.put(73, "https://www.merg.org.uk/merg_wiki/doku.php?id=projects:canslider");
686        // result.put(74, "CANDCATC");
687        result.put(75, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cangate");
688        // result.put(76, "CANSINP");
689        // result.put(77, "CANSOUT");
690        // result.put(78, "CANSBIP");
691        // result.put(79, "CANBUFFER");
692        // result.put(80, "CANLEVER");
693        // result.put(81, "CANSHIELD");
694        // result.put(82, "CAN4IN4OUT");
695        result.put(83, "https://www.merg.org.uk/merg_wiki/doku.php?id=cbus:cancmdb");
696        // result.put(84, "CANPIXEL");
697        // result.put(85, "CANCABPE");
698        // result.put(86, "CANSMARTTD");
699        // result.put(87, "CANARGB");
700
701        // result.put(252, "VLCB");
702        // result.put(253, "CANUSB"); // NOI18N
703        // result.put(254, "EMPTY"); // NOI18N
704        // result.put(255, "CAN_SW"); // NOI18N        
705        
706        return Collections.unmodifiableMap(result);
707    }
708    
709    /*
710     * Populate hashmap with rocrail module support links
711     */
712    private static Map<Integer, String> createLink70Map() {
713        Map<Integer, String> result = new HashMap<>();
714        result.put(1, "https://wiki.rocrail.net/doku.php?id=can-gca1-en"); // NOI18N
715        result.put(2, "https://wiki.rocrail.net/doku.php?id=can-gca2-en"); // NOI18N
716        result.put(3, "https://wiki.rocrail.net/doku.php?id=can-gc3-en"); // NOI18N
717        result.put(4, "https://wiki.rocrail.net/doku.php?id=can-gc4-en"); // NOI18N
718        result.put(5, "https://wiki.rocrail.net/doku.php?id=can-gca5-en"); // NOI18N
719        result.put(6, "https://wiki.rocrail.net/doku.php?id=can-gc6-en"); // NOI18N
720        result.put(7, "https://wiki.rocrail.net/doku.php?id=can-gc7-en"); // NOI18N
721        result.put(11, "https://wiki.rocrail.net/doku.php?id=can-gca1e-en"); // NOI18N
722        return Collections.unmodifiableMap(result);
723    }
724    
725    
726    /*
727     * Populate hashmap with SPROG module support links
728     */
729    private static Map<Integer, String> createLink44Map() {
730        Map<Integer, String> result = new HashMap<>();
731        result.put(1, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
732        result.put(2, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
733        result.put(3, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
734        result.put(4, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
735        result.put(5, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
736        result.put(8, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
737        result.put(50, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
738        result.put(100, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
739        result.put(101, "https://www.sprog-dcc.co.uk/download-page"); // NOI18N
740        return Collections.unmodifiableMap(result);
741    }
742    
743    
744    /**
745     * Return a string representation of a reserved node number
746     * @param modnum node number
747     * @return reserved node number reason
748     */
749    public static String getReservedModule(int modnum) {
750        // look for the opcode
751        String format = resMod.get(modnum);
752        if (format == null) {
753            return "";
754        } else {
755            return format; 
756        }
757    }
758    
759    /**
760     * Hashmap for fixed Module Numbers
761     */
762    private static final Map<Integer, String> resMod = createModMap();
763
764    /*
765     * Populate hashmap with format strings
766     *
767     */
768    private static Map<Integer, String> createModMap() {
769        Map<Integer, String> result = new HashMap<>();
770
771        for (int i = 100; i < 126; i++) {
772            result.put(i, Bundle.getMessage("NdNumReserveFixed")); // NOI18N
773        }
774        // use html with br so text fits into dialogue
775        result.put(120, "Reserved / Command Station Shuttles");
776        result.put(126, "Reserved for CAN_RS Modules");
777        result.put(127, "Reserved for CAN_USB Modules");
778        result.put(162, "Used in Command Station Shuttles");
779        result.put(163, "Used in Command Station Shuttles");
780        result.put(164, "Used in Command Station Shuttles");
781        result.put(167, "Used in Command Station Shuttles");
782        result.put(65534, "Reserved for Command Station");
783        result.put(65535, "Reserved, used by all CABS");
784        return Collections.unmodifiableMap(result);
785    }
786
787    private static final Map<String, BackupType> nameIndex =
788            new HashMap<>(BackupType.values().length);
789    static {
790        for (BackupType t : BackupType.values()) {
791            nameIndex.put(t.name(), t);
792        }
793    }
794
795    private static final Map<BackupType, String> displayPhraseIndex =
796            new HashMap<>(BackupType.values().length);
797    static {
798        displayPhraseIndex.put(BackupType.INCOMPLETE, Bundle.getMessage("BackupIncomplete"));
799        displayPhraseIndex.put(BackupType.COMPLETE, Bundle.getMessage("BackupComplete"));
800        displayPhraseIndex.put(BackupType.COMPLETEDWITHERROR, Bundle.getMessage("BackupCompleteError"));
801        displayPhraseIndex.put(BackupType.NOTONNETWORK, Bundle.getMessage("BackupNotOnNetwork"));
802        displayPhraseIndex.put(BackupType.OUTSTANDING, Bundle.getMessage("BackupOutstanding"));
803        displayPhraseIndex.put(BackupType.SLIM, Bundle.getMessage("NodeInSlim"));
804    }
805    
806    /*
807     * Get the display phrase for an enum value
808     * <p>
809     * eg. displayPhrase(BackupType.INCOMPLETE) will return "Backup InComplete"
810     *
811     * @param type The enum to translate
812     * @return The phrase
813     *
814     */
815    public static String displayPhrase(BackupType type) {
816        return displayPhraseIndex.get(type);
817    }
818    
819    /*
820     * Get the enum type for a String value
821     * <p>
822     * eg. lookupByName("Complete") will return BackupType.COMPLETE
823     *
824     * @param name The String to lookup
825     * @return The BackupType enum, else null
826     *
827     */
828    public static BackupType lookupByName(String name) {
829        return nameIndex.get(name);
830    }
831    
832    /*
833     * enum to represent Node Backup Conditions in a CBUS Node XML File
834     *
835     */
836    public enum BackupType{
837        INCOMPLETE(0),
838        COMPLETE(1),
839        COMPLETEDWITHERROR(2),
840        NOTONNETWORK(3),
841        OUTSTANDING(4),
842        SLIM(5);
843        
844        private final int v;
845
846        BackupType(final int v) {
847            this.v = v;
848        }
849    
850        public int getValue() {
851            return v;
852        }
853    
854    }
855
856    // private static final Logger log = LoggerFactory.getLogger(CbusNodeConstants.class);
857}