001package jmri.jmrix.tmcc.serialmon;
002
003import jmri.jmrix.tmcc.SerialListener;
004import jmri.jmrix.tmcc.SerialMessage;
005import jmri.jmrix.tmcc.SerialReply;
006import jmri.jmrix.tmcc.TmccSystemConnectionMemo;
007
008/**
009 * Frame displaying (and logging) TMCC serial command messages.
010 *
011 * @author Bob Jacobsen Copyright (C) 2001, 2006
012 * with edits/additions by
013 * @author Timothy Jump Copyright (C) 2025
014 */
015public class SerialMonFrame extends jmri.jmrix.AbstractMonFrame implements SerialListener {
016
017    private TmccSystemConnectionMemo _memo = null;
018
019    public SerialMonFrame(TmccSystemConnectionMemo memo) {
020        super();
021        _memo = memo;
022    }
023
024    @Override
025    protected String title() {
026        return Bundle.getMessage("MonitorXTitle", "TMCC");
027    }
028
029    @Override
030    protected void init() {
031        // connect to TrafficController
032        _memo.getTrafficController().addSerialListener(this);
033    }
034
035    @Override
036    public void dispose() {
037        _memo.getTrafficController().removeSerialListener(this);
038        super.dispose();
039    }
040
041    @Override
042    public synchronized void message(SerialMessage l) { // receive a message and log it
043        // check for valid length
044        if (l.getNumDataElements() < 3) {
045            nextLine("Truncated message of length " + l.getNumDataElements() + "\n",
046                    l.toString());
047        } else {
048            nextLine("Cmd: " + parse(l.getOpCode(), l.getAsWord()) + "\n", l.toString());
049        }
050    }
051
052    @Override
053    public synchronized void reply(SerialReply l) { // receive a reply message and log it
054        // check for valid length
055        if (l.getNumDataElements() < 2) {
056            nextLine("Truncated reply of length " + l.getNumDataElements() + ": \"" + l.toString() + "\"\n",
057                    l.toString());
058        } else {
059            nextLine("Rep: " + parse(l.getOpCode(), l.getAsWord()) + "\n", l.toString());
060        }
061    }
062
063    String parse(int opCode, int val) {
064        // TMCC 2 parsing
065        if (opCode == 0xF8 || opCode == 0xF9 || opCode == 0xFB) {
066            // TMCC2 Engine Commands
067            int A = (val / 512) & 0x7F; // A is TMCC Adddress Code
068            int C = (val / 32) & 0x03; // C is TMCC Command Code
069            int D = val & 0x1F; // D is TMCC Data Code
070            if (A < 99) {
071                if ((val & 0x0100) == 0x0100) {
072                    switch (C) {
073                        case 0: // If C (TMCC Command Code) == 0                    
074                            switch (D) {
075                                case 0:
076                                    return "TMCC2 - Engine " + A + " - Forward Direction";
077                                case 1:
078                                    return "TMCC2 - Engine " + A + " - Toggle Direction";
079                                case 2:
080                                    
081                                case 3:
082                                    return "TMCC2 - Engine " + A + " - Reverse Direction";
083                                case 4:
084                                    return "TMCC2 - Engine " + A + " - Boost";
085                                case 5:
086                                    return "TMCC2 - Engine " + A + " - Open Front Coupler";
087                                case 6:
088                                    return "TMCC2 - Engine " + A + " - Open Rear Coupler";
089                                case 7:
090                                    return "TMCC2 - Engine " + A + " - Brake";
091                                case 8:
092                                    return "TMCC2 - Engine " + A + " - AUX1 Off";
093                                case 9:
094                                    return "TMCC2 - Engine " + A + " - AUX1 Option 1 (CAB AUX1 Button; On While Pressed/On Until Next Button Pressed)";
095                                case 10:
096                                    return "TMCC2 - Engine " + A + " - AUX1 Option 2 (Toggle On/Toggle Off)";
097                                case 11:
098                                    return "TMCC2 - Engine " + A + " - AUX1 On";
099                                case 12:
100                                    return "TMCC2 - Engine " + A + " - AUX2 Off";
101                                case 13:
102                                    return "TMCC2 - Engine " + A + " - AUX2 Option 1 (CAB AUX2 Button; Headlight Toggle On/Toggle Off)";
103                                case 14:
104                                    return "TMCC2 - Engine " + A + " - AUX2 Option 2 (On While Pressed)";
105                                case 15:
106                                    return "TMCC2 - Engine " + A + " - AUX2 On";
107                                case 16:
108                                    return "TMCC2 - Engine " + A + " - Num 0 - Engine Reset - Needed to toggle ERR 100 Speed Steps - TMCC2 Feature Type 0 ";
109                                case 17:
110                                    return "TMCC2 - Engine " + A + " - Num 1 - Sound Volume Increase - TMCC2 Feature Type 1";
111                                case 18:
112                                    return "TMCC2 - Engine " + A + " - Num 2 - Crew Talk - TMCC2 Feature Type 2";
113                                case 19:
114                                    return "TMCC2 - Engine " + A + " - Num 3 - Sound On w/Start-Up Sequence/RPM Increase";
115                                case 20:
116                                    return "TMCC2 - Engine " + A + " - Num 4 - Sound Volume Decrease";
117                                case 21:
118                                    return "TMCC2 - Engine " + A + " - Num 5 - Sound Off w/Shut-Down Sequence";
119                                case 22:
120                                    return "TMCC2 - Engine " + A + " - Num 6 - Steam Release/RPM Decrease";
121                                case 23:
122                                    return "TMCC2 - Engine " + A + " - Num 7 - Tower Com Announcement";
123                                case 24:
124                                    return "TMCC2 - Engine " + A + " - Num 8 - Feature Off (Smoke/Aux Lighting)";
125                                case 25:
126                                    return "TMCC2 - Engine " + A + " - Num 9 - Feature On (Smoke/Aux Lighting)";
127                                case 26:
128                                    
129                                case 27:
130                                    
131                                case 28:
132                                    return "TMCC2 - Engine " + A + " - Blow Whistle/Horn 1";
133                                case 29:
134                                    return "TMCC2 - Engine " + A + " - Ring Bell";
135                                case 30:
136                                    return "TMCC2 - Engine " + A + " - Letoff Sound";
137                                case 31:
138                                    return "TMCC2 - Engine " + A + " - Blow Horn 2";
139                                default:
140                                    return "TMCC2 - Engine " + A + " - Unassigned FnKey TMCC2 (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
141                            }
142
143                        case 1: // If C (TMCC Command Code) == 1
144                            switch (D & 0x17) {
145                                case 0:
146                                    return "TMCC2 - Engine " + A + " - Momentum Low";
147                                case 1:
148                                    return "TMCC2 - Engine " + A + " - Momentum Medium";
149                                case 2:
150                                    return "TMCC2 - Engine " + A + " - Momentum High";
151                                case 3:
152                                    return "TMCC2 - Engine ID " + A + " - Set";
153                                default:
154                                    return "TMCC2 - Engine " + A + " - Unassigned FnKey TMCC2 (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
155                            }
156                    
157                        //$FALL-THROUGH$
158                        case 2: // If C (TMCC Command Code) == 2
159                            // return "TMCC2 - Engine " + A + " - Change Speed (Relative) by " + (D - 5);
160                        
161                        case 3: // If C (TMCC Command Code) == 3
162                        default:    // to let the compiler know there are only 3 cases
163                            return "TMCC2 (32 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + D;
164                    }
165                }
166            }
167
168            if (val == 0xFF8B) {
169                return "TMCC2 - HALT - Emergency System Stop; ALL";
170            } else {
171                return "TMCC2 (200 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + (val & 0xFF);
172            }
173        }        
174        
175        // TMCC 1 parsing
176        if (opCode == 0xFE) {
177            if ((val & 0xC000) == 0x4000) {
178                // TMCC1 Switch Commands
179                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
180                int C = (val / 32) & 0x03; // C is TMCC Command Code
181                int D = val & 0x1F; // D is TMCC Data Code
182                switch (C) {
183                    case 0: // If C (TMCC Command Code) == 0
184                        switch (D) {
185                            case 0:
186                                return "Throw Switch " + A + " - THROUGH/CLOSED";
187                            case 31:
188                                return "Throw Switch " + A + " - OUT/THROWN";
189                            default:
190                                return "Unrecognized Switch(SW) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
191                        }
192
193                    case 1: // If C (TMCC Command Code) == 1
194                        switch (D) {
195                            case 11:
196                                return "Switch ID " + A + " - Set";
197                            default:
198                                return "Unrecognized Switch(SW) Command (Cases C=1) - with A= " + A + " C= " + C + " D= " + D;                                
199                        }
200
201                    //$FALL-THROUGH$
202                    case 2: // If C (TMCC Command Code) == 2
203                        return "Assign switch " + A + " to route " + D + " - THROUGH";
204
205                    case 3: // If C (TMCC Command Code) == 3
206                        return "Assign switch " + A + " to route " + D + " - OUT";
207                    default:
208                        return "Unrecognized Switch(SW) Command (Cases C= 2-3) - with A= " + A + " C= " + C + " D= " + D;
209                }
210
211
212            } else if ((val & 0xF000) == 0xD000) {
213                // TMCC1 Route Commands
214                int A = (val / 128) & 0x1F; // A is TMCC Adddress Code
215                int C = (val / 32) & 0x03; // C is TMCC Command Code
216                int D = val & 0x1F; // D is TMCC Data Code
217                switch (C) {
218                    case 0: // If C (TMCC Command Code) == 0
219                        switch (D) {
220                            case 15:
221                                return "Route " + A + " - THROW";
222                            default:
223                                return "Unrecognized Route(RTE) Command (Cases C=0) - with A= " + A + " C= " + C + " D= " + D;
224                        }
225
226                    case 1: // If C (TMCC Command Code) == 0
227                        switch (D) {
228                            case 12:
229                                return "Route " + A + " - CLEAR";
230                            default:
231                                return "Unrecognized Route(RTE) Command (Cases C=1) - with A= " + A + " C= " + C + " D= " + D;
232                           
233                        }
234
235                default:
236                    return "Unrecognized Route(RTE) Command (Cases C) - with A= " + A + " C= " + C + " D= " + D;
237                }
238
239            } else if ((val & 0xC000) == 0x0000) {
240                // TMCC1 Engine Commands
241                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
242                int C = (val / 32) & 0x03; // C is TMCC Command Code
243                int D = val & 0x1F; // D is TMCC Data Code
244                switch (C) {
245                    case 0: // If C (TMCC Command Code) == 0
246                        switch (D) {
247                            case 0:
248                                return "TMCC1 - Engine " + A + " - Forward Direction";
249                            case 1:
250                                return "TMCC1 - Engine " + A + " - Toggle Direction";
251                            case 2:
252                            
253                            case 3:
254                                return "TMCC1 - Engine " + A + " - Reverse Direction";
255                            case 4:
256                                return "TMCC1 - Engine " + A + " - Boost";
257                            case 5:
258                                return "TMCC1 - Engine " + A + " - Open Front Coupler";
259                            case 6:
260                                return "TMCC1 - Engine " + A + " - Open Rear Coupler";
261                            case 7:
262                                return "TMCC1 - Engine " + A + " - Brake";
263                            case 8:
264                            
265                            case 9:
266                                return "TMCC1 - Engine " + A + " - AUX1 Option 1 (CAB AUX1 Button; On While Pressed/On Until Next Button Pressed)";
267                            case 10:
268                            
269                            case 11:
270                            
271                            case 12:
272                            
273                            case 13:
274                                return "TMCC1 - Engine " + A + " - AUX2 Option 1 (CAB AUX2 Button; Headlight Toggle On/Toggle Off)";
275                            case 14:
276                            
277                            case 15:
278                            
279                            case 16:
280                                return "TMCC1 - Engine " + A + " - Num 0 - Engine Reset (Needed to toggle ERR 100 Speed Steps)";
281                            case 17:
282                                return "TMCC1 - Engine " + A + " - Num 1 - Sound Volume Increase";
283                            case 18:
284                                return "TMCC1 - Engine " + A + " - Num 2 - Crew Talk";
285                            case 19:
286                                return "TMCC1 - Engine " + A + " - Num 3 - Sound On w/Start-Up Sequence";
287                            case 20:
288                                return "TMCC1 - Engine " + A + " - Num 4 - Sound Volume Decrease - TMCC1 Feature Type 4";
289                            case 21:
290                                return "TMCC1 - Engine " + A + " - Num 5 - Sound Off w/Shut-Down Sequence - TMCC1 Feature Type 5";
291                            case 22:
292                                return "TMCC1 - Engine " + A + " - Num 6 - Steam Release/RPM Decrease - TMCC1 Feature Type 6";
293                            case 23:
294                                return "TMCC1 - Engine " + A + " - Num 7 - Tower Com Announcement";
295                            case 24:
296                                return "TMCC1 - Engine " + A + " - Num 8 - Feature Off (Smoke/Aux Lighting) - TMCC1 Feature Type 8";
297                            case 25:
298                                return "TMCC1 - Engine " + A + " - Num 9 - Feature On (Smoke/Aux Lighting)";
299                            case 26:
300                            
301                            case 27:
302                            
303                            case 28:
304                                return "TMCC1 - Engine " + A + " - Blow Whistle/Horn 1";
305                            case 29:
306                                return "TMCC1 - Engine " + A + " - Ring Bell";
307                            case 30:
308                                return "TMCC1 - Engine " + A + " - Letoff Sound";
309                            case 31:
310                                return "TMCC1 - Engine " + A + " - Blow Horn 2";
311                            default:
312                                return "TMCC1 - Engine " + A + " - Unassigned FnKey TMCC1 (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
313                        }
314
315                    case 1: // If C (TMCC Command Code) == 1
316                        switch (D & 0x17) {
317                            case 0:
318                                return "TMCC1 - Engine " + A + " - Momentum Low";
319                            case 1:
320                                return "TMCC1 - Engine " + A + " - Momentum Medium";
321                            case 2:
322                                return "TMCC1 - Engine " + A + " - Momentum High";
323                            case 3:
324                                return "TMCC1 - Engine ID " + A + " - Set";
325                            default:
326                                return "TMCC1 - Engine " + A + " - Unassigned FnKey TMCC1 (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
327                        }
328                    
329                    //$FALL-THROUGH$
330                    case 2: // If C (TMCC Command Code) == 2
331                        // return "TMCC1 - Engine " + A + " - Change Speed (Relative) by " + (D - 5);
332
333                    case 3: // If C (TMCC Command Code) == 3
334                    default: // to let the compiler know there are only 3 cases
335                        return "TMCC1 (32 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + D;
336                }
337
338
339            } else if ((val & 0xF800) == 0xC800) {
340                // TMCC1 Train Commands
341                int A = (val / 128) & 0x0F; // A is TMCC Adddress Code
342                int C = (val / 32) & 0x03; // C is TMCC Command Code
343                int D = val & 0x1F; // D is TMCC Data Code
344                switch (C) {
345                    case 1: // If C (TMCC Command Code) == 1
346                    default: // To let the compiler know this should be blank
347                        switch (D & 0x17) {
348                            case 3:
349                                return "TMCC1 - Track/Train ID " + A + " - Set";
350                            default:
351                                return "Unrecognized Train(TR) Command with A= " + A + " C= " + C + " D= " + D;
352                        }
353                }
354
355            } else if ((val & 0xC000) == 0x8000) {
356                // TMCC1 Accessory Commands
357                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
358                int C = (val / 32) & 0x03; // C is TMCC Command Code
359                int D = val & 0x1F; // D is TMCC Data Code
360                switch (C) {
361                    case 0: // If C (TMCC Command Code) == 0
362                        switch (D) {
363                            case 0:
364                            case 1:
365                            case 2:
366                            case 3:
367                            case 4:
368                            case 5:
369                            case 6:
370                            case 7:
371                            case 8:
372                                return "Aux 1 - ACC " + A + " - OFF";
373                            case 9:
374                                return "Aux 1 - ACC " + A + " - OPTION 1 (On While Pressed)";
375                            case 10:
376                                return "Aux 1 - ACC " + A + " - OPTION 2 (Toggle On/Toggle Off)";
377                            case 11:
378                                return "Aux 1 - ACC " + A + " - ON";
379                            case 12:
380                                return "Aux 2 - ACC " + A + " - OFF";
381                            case 13:
382                                return "Aux 2 - ACC " + A + " - OPTION 1 (Toggle On/Toggle Off)";
383                            case 14:
384                                return "Aux 2 - ACC " + A + " - OPTION 2 (On While Pressed)";
385                            case 15:
386                                return "Aux 2 - ACC " + A + " - ON";
387                            default:
388                                return "Unrecognized Accessory(ACC) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
389                        }
390                        
391                    case 1: // If C (TMCC Command Code) == 1
392                        switch (D) {
393                            case 0:
394                                return "ALL ACC OFF";
395                            case 11:
396                                return "Accessory ID " + A + " - Set";
397                            case 15:
398                                return "ALL ACC ON";
399//                } else if ((C == 1) && (D == 0x??)) {
400//                    return "Assign Aux 1 to Group D " + A + " - 0-9";
401//                } else if ((C == 1) && (D == 0x??)) {
402//                    return "Assign Aux 2 to Group D " + A + " - 0-9"";
403                            default:
404                                return "Unrecognized Accessory(ACC) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
405                        }
406
407               default:
408                    return "Unrecognized Accessory(ACC) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
409                }
410
411            } else if ((val & 0xF800) == 0xC000) {
412                // TMCC1 Group Commands
413                int A = (val / 128) & 0x0F; // A is TMCC Adddress Code
414                int C = (val / 32) & 0x03; // C is TMCC Command Code
415                int D = val & 0x1F; // D is TMCC Data Code
416                switch (C) {
417                    case 0: // If C (TMCC Command Code) == 0
418                        switch (D) {
419                            case 0:
420                            case 1:
421                            case 2:
422                            case 3:
423                            case 4:
424                            case 5:
425                            case 6:
426                            case 7:
427                            case 8:
428                                return "GROUP - ACC " + A + " - OFF";
429                            case 9:
430                                return "GROUP - ACC " + A + " - OPTION 1 (On While Pressed)";
431                            case 10:
432                                return "GROUP - ACC " + A + " - OPTION 2 (Toggle On/Toggle Off)";
433                            case 11:
434                                return "GROUP - ACC " + A + " - ON";
435                            default:
436                                return "Unrecognized Group(GR) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
437                        }
438
439                    case 1: // If C (TMCC Command Code) == 1
440                        switch (D) {
441                            case 12:
442                                return "GROUP - ACC " + A + " - CLEAR";
443                            default:
444                                return "Unrecognized Group(GR) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;                              
445                        }
446
447                default:
448                    return "Unrecognized Group(GR) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
449                }
450            }            
451
452            if (val == 0xFFFF) {
453                return "TMCC1 - HALT - Emergency System Stop; ALL";
454            }
455
456        }
457
458        // TMCC Error parsing
459        if (opCode == 0x00) {
460            int C = (val / 32) & 0x03; // C is TMCC Command Code
461            int D = val & 0x1F; // D is TMCC Data Code
462            switch (C) {
463                case 0: // If C (TMCC Command Code) == 0
464                    switch (D) {
465                        case 0:
466                            return "Address Must be Between 1-98 for TMCC1 and TMCC2 ENG/SW/ACC and TMCC2 TR. Address Must be Between 1-9 for TMCC1 TR";
467                        case 1:
468                            return "CV Must Equal 1 for Programming TMCC Loco/Engine, Switch, Accessory ID#s";
469                        case 2:
470                            return "CV Must Equal 2 for Programming TMCC Feature Type";
471                        case 3:
472                            return "Value Entered is Not a TMCC1 Feature Type";
473                        case 4:
474                            return "Value Entered is Not a TMCC2 Feature Type";
475                        default:
476                            return "Unrecognized TMCC Error (Case C=0) - with C= " + C + " D= " + D;
477                    }
478            default:
479                return "Unrecognized TMCC Error (Case C) - with C= " + C + " D= " + D;
480            }
481        }
482        
483        return "TMCC - CV#, Loco ID#/Address/Feature Value - Out of Range";
484
485    }
486}
487