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 additions and edits 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)";
095                                case 10:
096                                    return "TMCC2 - Engine " + A + " - AUX1 Option 2";
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 On/Off";
103                                case 14:
104                                    return "TMCC2 - Engine " + A + " - AUX2 Option 2";
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)";
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 On/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                return "Unrecognized Train(TR) Command with A= " + A + " C= " + C + " D= " + D;
345
346
347            } else if ((val & 0xC000) == 0x8000) {
348                // TMCC1 Accessory Commands
349                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
350                int C = (val / 32) & 0x03; // C is TMCC Command Code
351                int D = val & 0x1F; // D is TMCC Data Code
352                switch (C) {
353                    case 0: // If C (TMCC Command Code) == 0
354                        switch (D) {
355                            case 0:
356                            case 1:
357                            case 2:
358                            case 3:
359                            case 4:
360                            case 5:
361                            case 6:
362                            case 7:
363                            case 8:
364                                return "Aux 1 - ACC " + A + " - OFF";
365                            case 9:
366                                return "Aux 1 - ACC " + A + " - OPTION 1";
367                            case 10:
368                                return "Aux 1 - ACC " + A + " - OPTION 2";
369                            case 11:
370                                return "Aux 1 - ACC " + A + " - ON";
371                            case 12:
372                                return "Aux 2 - ACC " + A + " - OFF";
373                            case 13:
374                                return "Aux 2 - ACC " + A + " - OPTION 1";
375                            case 14:
376                                return "Aux 2 - ACC " + A + " - OPTION 2";
377                            case 15:
378                                return "Aux 2 - ACC " + A + " - ON";
379                            default:
380                                return "Unrecognized Accessory(ACC) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
381                        }
382                        
383                    case 1: // If C (TMCC Command Code) == 1
384                        switch (D) {
385                            case 0:
386                                return "ALL ACC OFF";
387                            case 11:
388                                return "Accessory ID " + A + " - Set";
389                            case 15:
390                                return "ALL ACC ON";
391//                } else if ((C == 1) && (D == 0x??)) {
392//                    return "Assign Aux 1 to Group D " + A + " - 0-9";
393//                } else if ((C == 1) && (D == 0x??)) {
394//                    return "Assign Aux 2 to Group D " + A + " - 0-9"";
395                            default:
396                                return "Unrecognized Accessory(ACC) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
397                        }
398
399               default:
400                    return "Unrecognized Accessory(ACC) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
401                }
402
403
404            } else if ((val & 0xF800) == 0xC000) {
405                // TMCC1 Group Commands
406                int A = (val / 128) & 0x0F; // A is TMCC Adddress Code
407                int C = (val / 32) & 0x03; // C is TMCC Command Code
408                int D = val & 0x1F; // D is TMCC Data Code
409                switch (C) {
410                    case 0: // If C (TMCC Command Code) == 0
411                        switch (D) {
412                            case 0:
413                            case 1:
414                            case 2:
415                            case 3:
416                            case 4:
417                            case 5:
418                            case 6:
419                            case 7:
420                            case 8:
421                                return "GROUP - ACC " + A + " - OFF";
422                            case 9:
423                                return "GROUP - ACC " + A + " - OPTION 1";
424                            case 10:
425                                return "GROUP - ACC " + A + " - OPTION 2";
426                            case 11:
427                                return "GROUP - ACC " + A + " - ON";
428                            default:
429                                return "Unrecognized Group(GR) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
430                        }
431
432                    case 1: // If C (TMCC Command Code) == 1
433                        switch (D) {
434                            case 12:
435                                return "GROUP - ACC " + A + " - CLEAR";
436                            default:
437                                return "Unrecognized Group(GR) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;                              
438                        }
439
440                default:
441                    return "Unrecognized Group(GR) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
442                }
443            }            
444
445            if (val == 0xFFFF) {
446                return "TMCC1 - HALT - Emergency System Stop; ALL";
447            }
448
449        }
450
451
452        // TMCC Error parsing
453        if (opCode == 0x00) {
454            int C = (val / 32) & 0x03; // C is TMCC Command Code
455            int D = val & 0x1F; // D is TMCC Data Code
456            switch (C) {
457                case 0: // If C (TMCC Command Code) == 0
458                    switch (D) {
459                        case 0:
460                            return "Address Must be Between 1-98 for TMCC";
461                        case 1:
462                            return "CV Must Equal 1 for Programming TMCC Loco/Engine, Switch, Accessory ID#s";
463                        case 2:
464                            return "CV Must Equal 2 for Programming TMCC Feature Type";
465                        case 3:
466                            return "Value Entered is Not a TMCC1 Feature Type";
467                        case 4:
468                            return "Value Entered is Not a TMCC2 Feature Type";
469                        default:
470                            return "Unrecognized TMCC Error (Case C=0) - with C= " + C + " D= " + D;
471                    }
472            default:
473                return "Unrecognized TMCC Error (Case C) - with C= " + C + " D= " + D;
474            }
475        }
476        
477        return "TMCC - CV#, Loco ID#/Address/Feature Value - Out of Range";
478
479    }
480}
481