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 ((val & 0x0100) == 0x0100) {
071                switch (C) {
072                    case 0: // If C (TMCC Command Code) == 0                    
073                        switch (D) {
074                            case 0:
075                                return "TMCC2 - Engine " + A + " - Forward Direction";
076                            case 1:
077                                return "TMCC2 - Engine " + A + " - Toggle Direction";
078                            case 2:
079                                
080                            case 3:
081                                return "TMCC2 - Engine " + A + " - Reverse Direction";
082                            case 4:
083                                return "TMCC2 - Engine " + A + " - Boost";
084                            case 5:
085                                return "TMCC2 - Engine " + A + " - Open Front Coupler";
086                            case 6:
087                                return "TMCC2 - Engine " + A + " - Open Rear Coupler";
088                            case 7:
089                                return "TMCC2 - Engine " + A + " - Brake";
090                            case 8:
091                                return "TMCC2 - Engine " + A + " - AUX1 Off";
092                            case 9:
093                                return "TMCC2 - Engine " + A + " - AUX1 Option 1 (CAB AUX1 button)";
094                            case 10:
095                                return "TMCC2 - Engine " + A + " - AUX1 Option 2";
096                            case 11:
097                                return "TMCC2 - Engine " + A + " - AUX1 On";
098                            case 12:
099                                return "TMCC2 - Engine " + A + " - AUX2 Off";
100                            case 13:
101                                return "TMCC2 - Engine " + A + " - AUX2 Option 1 (CAB AUX2 button) Headlight On/Off";
102                            case 14:
103                                return "TMCC2 - Engine " + A + " - AUX2 Option 2";
104                            case 15:
105                                return "TMCC2 - Engine " + A + " - AUX2 On";
106                            case 16:
107                                return "TMCC2 - Engine " + A + " - Num 0 - Engine Reset - Needed to toggle ERR 100 Speed Steps - TMCC2 Feature Type 0 ";
108                            case 17:
109                                return "TMCC2 - Engine " + A + " - Num 1 - Sound Volume Increase - TMCC2 Feature Type 1";
110                            case 18:
111                                return "TMCC2 - Engine " + A + " - Num 2 - Crew Talk - TMCC2 Feature Type 2";
112                            case 19:
113                                return "TMCC2 - Engine " + A + " - Num 3 - Sound On w/Start-Up Sequence/RPM Increase";
114                            case 20:
115                                return "TMCC2 - Engine " + A + " - Num 4 - Sound Volume Decrease";
116                            case 21:
117                                return "TMCC2 - Engine " + A + " - Num 5 - Sound Off w/Shut-Down Sequence";
118                            case 22:
119                                return "TMCC2 - Engine " + A + " - Num 6 - Steam Release/RPM Decrease";
120                            case 23:
121                                return "TMCC2 - Engine " + A + " - Num 7 - Tower Com Announcement";
122                            case 24:
123                                return "TMCC2 - Engine " + A + " - Num 8 - Feature Off (Smoke/Aux Lighting)";
124                            case 25:
125                                return "TMCC2 - Engine " + A + " - Num 9 - Feature On (Smoke/Aux Lighting)";
126                            case 26:
127                                
128                            case 27:
129                                
130                            case 28:
131                                return "TMCC2 - Engine " + A + " - Blow Whistle/Horn 1";
132                            case 29:
133                                return "TMCC2 - Engine " + A + " - Ring Bell";
134                            case 30:
135                                return "TMCC2 - Engine " + A + " - Letoff Sound";
136                            case 31:
137                                return "TMCC2 - Engine " + A + " - Blow Horn 2";
138                            default:
139                                return "TMCC2 - Engine " + A + " - Unassigned FnKey TMCC2 (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
140                        }
141
142                    case 1: // If C (TMCC Command Code) == 1
143                        switch (D & 0x17) {
144                            case 0:
145                                return "TMCC2 - Engine " + A + " - Momentum Low";
146                            case 1:
147                                return "TMCC2 - Engine " + A + " - Momentum Medium";
148                            case 2:
149                                return "TMCC2 - Engine " + A + " - Momentum High";
150                            case 3:
151                                return "TMCC2 - Engine ID " + A + " - Set";
152                            default:
153                                return "TMCC2 - Engine " + A + " - Unassigned FnKey TMCC2 (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
154                        }
155                    
156                    //$FALL-THROUGH$
157                    case 2: // If C (TMCC Command Code) == 2
158                        return "TMCC2 - Engine " + A + " - Change Speed (Relative) by " + (D - 5);
159                        
160                    case 3: // If C (TMCC Command Code) == 3
161                    default:    // to let the compiler know there are only 3 cases
162                        return "TMCC2 (32 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + D;
163                }
164            }
165
166            return "TMCC2 (200 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + (val & 0xFF);
167        }
168        
169        
170        // TMCC 1 parsing
171        if (opCode == 0xFE) {
172            if ((val & 0xC000) == 0x4000) {
173                // TMCC1 Switch Commands
174                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
175                int C = (val / 32) & 0x03; // C is TMCC Command Code
176                int D = val & 0x1F; // D is TMCC Data Code
177                switch (C) {
178                    case 0: // If C (TMCC Command Code) == 0
179                        switch (D) {
180                            case 0:
181                                return "Throw Switch " + A + " - THROUGH/CLOSED";
182                            case 31:
183                                return "Throw Switch " + A + " - OUT/THROWN";
184                            default:
185                                return "Unrecognized Switch(SW) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
186                        }
187
188                    case 1: // If C (TMCC Command Code) == 1
189                        switch (D) {
190                            case 11:
191                                return "Switch ID " + A + " - Set";
192                            default:
193                                return "Unrecognized Switch(SW) Command (Cases C=1) - with A= " + A + " C= " + C + " D= " + D;                                
194                        }
195
196                    //$FALL-THROUGH$
197                    case 2: // If C (TMCC Command Code) == 2
198                        return "Assign switch " + A + " to route " + D + " - THROUGH";
199
200                    case 3: // If C (TMCC Command Code) == 3
201                        return "Assign switch " + A + " to route " + D + " - OUT";
202                    default:
203                        return "Unrecognized Switch(SW) Command (Cases C= 2-3) - with A= " + A + " C= " + C + " D= " + D;
204                }
205
206
207            } else if ((val & 0xF000) == 0xD000) {
208                // TMCC1 Route Commands
209                int A = (val / 128) & 0x1F; // A is TMCC Adddress Code
210                int C = (val / 32) & 0x03; // C is TMCC Command Code
211                int D = val & 0x1F; // D is TMCC Data Code
212                switch (C) {
213                    case 0: // If C (TMCC Command Code) == 0
214                        switch (D) {
215                            case 15:
216                                return "Route " + A + " - THROW";
217                            default:
218                                return "Unrecognized Route(RTE) Command (Cases C=0) - with A= " + A + " C= " + C + " D= " + D;
219                        }
220
221                    case 1: // If C (TMCC Command Code) == 0
222                        switch (D) {
223                            case 12:
224                                return "Route " + A + " - CLEAR";
225                            default:
226                                return "Unrecognized Route(RTE) Command (Cases C=1) - with A= " + A + " C= " + C + " D= " + D;
227                           
228                        }
229
230                default:
231                    return "Unrecognized Route(RTE) Command (Cases C) - with A= " + A + " C= " + C + " D= " + D;
232                }
233
234            } else if ((val & 0xC000) == 0x0000) {
235                // TMCC1 Engine Commands
236                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
237                int C = (val / 32) & 0x03; // C is TMCC Command Code
238                int D = val & 0x1F; // D is TMCC Data Code
239                switch (C) {
240                    case 0: // If C (TMCC Command Code) == 0
241                        switch (D) {
242                            case 0:
243                                return "TMCC1 - Engine " + A + " - Forward Direction";
244                            case 1:
245                                return "TMCC1 - Engine " + A + " - Toggle Direction";
246                            case 2:
247                            
248                            case 3:
249                                return "TMCC1 - Engine " + A + " - Reverse Direction";
250                            case 4:
251                                return "TMCC1 - Engine " + A + " - Boost";
252                            case 5:
253                                return "TMCC1 - Engine " + A + " - Open Front Coupler";
254                            case 6:
255                                return "TMCC1 - Engine " + A + " - Open Rear Coupler";
256                            case 7:
257                                return "TMCC1 - Engine " + A + " - Brake";
258                            case 8:
259                            
260                            case 9:
261                                return "TMCC1 - Engine " + A + " - AUX1 Option 1 (CAB AUX1 button)";
262                            case 10:
263                            
264                            case 11:
265                            
266                            case 12:
267                            
268                            case 13:
269                                return "TMCC1 - Engine " + A + " - AUX2 Option 1 (CAB AUX2 button) Headlight On/Off";
270                            case 14:
271                            
272                            case 15:
273                            
274                            case 16:
275                                return "TMCC1 - Engine " + A + " - Num 0 - Engine Reset (Needed to toggle ERR 100 Speed Steps)";
276                            case 17:
277                                return "TMCC1 - Engine " + A + " - Num 1 - Sound Volume Increase";
278                            case 18:
279                                return "TMCC1 - Engine " + A + " - Num 2 - Crew Talk";
280                            case 19:
281                                return "TMCC1 - Engine " + A + " - Num 3 - Sound On w/Start-Up Sequence";
282                            case 20:
283                                return "TMCC1 - Engine " + A + " - Num 4 - Sound Volume Decrease - TMCC1 Feature Type 4";
284                            case 21:
285                                return "TMCC1 - Engine " + A + " - Num 5 - Sound Off w/Shut-Down Sequence - TMCC1 Feature Type 5";
286                            case 22:
287                                return "TMCC1 - Engine " + A + " - Num 6 - Steam Release/RPM Decrease - TMCC1 Feature Type 6";
288                            case 23:
289                                return "TMCC1 - Engine " + A + " - Num 7 - Tower Com Announcement";
290                            case 24:
291                                return "TMCC1 - Engine " + A + " - Num 8 - Feature Off (Smoke/Aux Lighting) - TMCC1 Feature Type 8";
292                            case 25:
293                                return "TMCC1 - Engine " + A + " - Num 9 - Feature On (Smoke/Aux Lighting)";
294                            case 26:
295                            
296                            case 27:
297                            
298                            case 28:
299                                return "TMCC1 - Engine " + A + " - Blow Whistle/Horn 1";
300                            case 29:
301                                return "TMCC1 - Engine " + A + " - Ring Bell";
302                            case 30:
303                                return "TMCC1 - Engine " + A + " - Letoff Sound";
304                            case 31:
305                                return "TMCC1 - Engine " + A + " - Blow Horn 2";
306                            default:
307                                return "TMCC1 - Engine " + A + " - Unassigned FnKey TMCC1 (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
308                        }
309
310                    case 1: // If C (TMCC Command Code) == 1
311                        switch (D & 0x17) {
312                            case 0:
313                                return "TMCC1 - Engine " + A + " - Momentum Low";
314                            case 1:
315                                return "TMCC1 - Engine " + A + " - Momentum Medium";
316                            case 2:
317                                return "TMCC1 - Engine " + A + " - Momentum High";
318                            case 3:
319                                return "TMCC1 - Engine ID " + A + " - Set";
320                            default:
321                                return "TMCC1 - Engine " + A + " - Unassigned FnKey TMCC1 (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
322                        }
323                    
324                    //$FALL-THROUGH$
325                    case 2: // If C (TMCC Command Code) == 2
326                        return "TMCC1 - Engine " + A + " - Change Speed (Relative) by " + (D - 5);
327
328                    case 3: // If C (TMCC Command Code) == 3
329                    default:    // to let the compiler know there are only 3 cases
330                        return "TMCC1 (32 Speed Steps) - Engine " + A + " - Speed (Absolute) = " + D;
331                }
332
333
334            } else if ((val & 0xF800) == 0xC800) {
335                // TMCC1 Train Commands
336                int A = (val / 128) & 0x0F; // A is TMCC Adddress Code
337                int C = (val / 32) & 0x03; // C is TMCC Command Code
338                int D = val & 0x1F; // D is TMCC Data Code
339                return "Unrecognized Train(TR) Command with A= " + A + " C= " + C + " D= " + D;
340
341
342            } else if ((val & 0xC000) == 0x8000) {
343                // TMCC1 Accessory Commands
344                int A = (val / 128) & 0x7F; // A is TMCC Adddress Code
345                int C = (val / 32) & 0x03; // C is TMCC Command Code
346                int D = val & 0x1F; // D is TMCC Data Code
347                switch (C) {
348                    case 0: // If C (TMCC Command Code) == 0
349                        switch (D) {
350                            case 0:
351                            case 1:
352                            case 2:
353                            case 3:
354                            case 4:
355                            case 5:
356                            case 6:
357                            case 7:
358                            case 8:
359                                return "Aux 1 - ACC " + A + " - OFF";
360                            case 9:
361                                return "Aux 1 - ACC " + A + " - OPTION 1";
362                            case 10:
363                                return "Aux 1 - ACC " + A + " - OPTION 2";
364                            case 11:
365                                return "Aux 1 - ACC " + A + " - ON";
366                            case 12:
367                                return "Aux 2 - ACC " + A + " - OFF";
368                            case 13:
369                                return "Aux 2 - ACC " + A + " - OPTION 1";
370                            case 14:
371                                return "Aux 2 - ACC " + A + " - OPTION 2";
372                            case 15:
373                                return "Aux 2 - ACC " + A + " - ON";
374                            default:
375                                return "Unrecognized Accessory(ACC) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
376                        }
377                        
378                    case 1: // If C (TMCC Command Code) == 1
379                        switch (D) {
380                            case 0:
381                                return "ALL ACC OFF";
382                            case 11:
383                                return "Accessory ID " + A + " - Set";
384                            case 15:
385                                return "ALL ACC ON";
386//                } else if ((C == 1) && (D == 0x??)) {
387//                    return "Assign Aux 1 to Group D " + A + " - 0-9";
388//                } else if ((C == 1) && (D == 0x??)) {
389//                    return "Assign Aux 2 to Group D " + A + " - 0-9"";
390                            default:
391                                return "Unrecognized Accessory(ACC) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;
392                        }
393
394               default:
395                    return "Unrecognized Accessory(ACC) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
396                }
397
398
399            } else if ((val & 0xF800) == 0xC000) {
400                // TMCC1 Group Commands
401                int A = (val / 128) & 0x0F; // A is TMCC Adddress Code
402                int C = (val / 32) & 0x03; // C is TMCC Command Code
403                int D = val & 0x1F; // D is TMCC Data Code
404                switch (C) {
405                    case 0: // If C (TMCC Command Code) == 0
406                        switch (D) {
407                            case 0:
408                            case 1:
409                            case 2:
410                            case 3:
411                            case 4:
412                            case 5:
413                            case 6:
414                            case 7:
415                            case 8:
416                                return "GROUP - ACC " + A + " - OFF";
417                            case 9:
418                                return "GROUP - ACC " + A + " - OPTION 1";
419                            case 10:
420                                return "GROUP - ACC " + A + " - OPTION 2";
421                            case 11:
422                                return "GROUP - ACC " + A + " - ON";
423                            default:
424                                return "Unrecognized Group(GR) Command (Case C=0) - with A= " + A + " C= " + C + " D= " + D;
425                        }
426
427                    case 1: // If C (TMCC Command Code) == 1
428                        switch (D) {
429                            case 12:
430                                return "GROUP - ACC " + A + " - CLEAR";
431                            default:
432                                return "Unrecognized Group(GR) Command (Case C=1) - with A= " + A + " C= " + C + " D= " + D;                              
433                        }
434
435                default:
436                    return "Unrecognized Group(GR) Command (Case C) - with A= " + A + " C= " + C + " D= " + D;
437                }
438            }            
439        }
440
441
442        // TMCC Error parsing
443        if (opCode == 0x00) {
444            int C = (val / 32) & 0x03; // C is TMCC Command Code
445            int D = val & 0x1F; // D is TMCC Data Code
446            switch (C) {
447                case 0: // If C (TMCC Command Code) == 0
448                    switch (D) {
449                        case 0:
450                            return "Address Must be Between 1-98 for TMCC";
451                        case 1:
452                            return "CV Must Equal 1 for Programming TMCC Loco/Engine, Switch, Accessory ID#s";
453                        case 2:
454                            return "CV Must Equal 2 for Programming TMCC Feature Type";
455                        case 3:
456                            return "Value Entered is Not a TMCC1 Feature Type";
457                        case 4:
458                            return "Value Entered is Not a TMCC2 Feature Type";
459                        default:
460                            return "Unrecognized TMCC Error (Case C=0) - with C= " + C + " D= " + D;
461                    }
462            default:
463                return "Unrecognized TMCC Error (Case C) - with C= " + C + " D= " + D;
464            }
465        }
466        
467        return "TMCC - CV#, Loco ID#/Address/Feature Value - Out of Range";
468
469    }
470}
471