001package jmri.jmrix.loconet.slotmon;
002
003import javax.swing.JButton;
004import javax.swing.JCheckBox;
005import javax.swing.JLabel;
006import javax.swing.JTable;
007import javax.swing.JTextField;
008
009import jmri.Throttle;
010import jmri.jmrix.loconet.LnConstants;
011import jmri.jmrix.loconet.LocoNetMessage;
012import jmri.jmrix.loconet.LocoNetSlot;
013import jmri.jmrix.loconet.LocoNetSystemConnectionMemo;
014import jmri.jmrix.loconet.SlotListener;
015import jmri.util.StringUtil;
016import jmri.util.swing.JmriJOptionPane;
017
018/**
019 * Table data model for display of slot manager contents.
020 *
021 * @author Bob Jacobsen Copyright (C) 2001
022 * @author Jeffrey Machacek 2013
023 */
024public class SlotMonDataModel extends javax.swing.table.AbstractTableModel implements SlotListener {
025
026    static public final int SLOTCOLUMN = 0;
027    static public final int ESTOPCOLUMN = 1;
028    static public final int ADDRCOLUMN = 2;
029    static public final int SPDCOLUMN = 3;
030    static public final int TYPECOLUMN = 4;
031    static public final int STATCOLUMN = 5;  // status: free, common, etc
032    static public final int DISPCOLUMN = 6;  // originally "dispatch" button, now "free"
033    static public final int CONSCOLUMN = 7;  // consist state
034    static public final int CONSISTADDRESS = 8; //consist address
035    static public final int THROTCOLUMN = 9;
036    static public final int DIRCOLUMN = 10;
037    static public final int F0COLUMN = 11;
038    static public final int F1COLUMN = 12;
039    static public final int F2COLUMN = 13;
040    static public final int F3COLUMN = 14;
041    static public final int F4COLUMN = 15;
042    static public final int F5COLUMN = 16;
043    static public final int F6COLUMN = 17;
044    static public final int F7COLUMN = 18;
045    static public final int F8COLUMN = 19;
046    static public final int F9COLUMN = 20;
047    static public final int F10COLUMN = 21;
048    static public final int F11COLUMN = 22;
049    static public final int F12COLUMN = 23;
050    static public final int F13COLUMN = 24;
051    static public final int F14COLUMN = 25;
052    static public final int F15COLUMN = 26;
053    static public final int F16COLUMN = 27;
054    static public final int F17COLUMN = 28;
055    static public final int F18COLUMN = 29;
056    static public final int F19COLUMN = 30;
057    static public final int F20COLUMN = 31;
058    static public final int F21COLUMN = 32;
059    static public final int F22COLUMN = 33;
060    static public final int F23COLUMN = 34;
061    static public final int F24COLUMN = 35;
062    static public final int F25COLUMN = 36;
063    static public final int F26COLUMN = 37;
064    static public final int F27COLUMN = 38;
065    static public final int F28COLUMN = 39;
066
067    //static public final int NUMCOLUMN = 40; Number of columns comes from the pane.
068
069    private int numRows = 128;
070    private int columns;
071
072    private final transient LocoNetSystemConnectionMemo memo;
073
074    SlotMonDataModel(int row, int column, LocoNetSystemConnectionMemo memo) {
075        this.memo = memo;
076
077        // number of columns
078        this.columns = column;
079        // set number of rows;
080        numRows = row;
081
082        // connect to SlotManager for updates
083        memo.getSlotManager().addSlotListener(SlotMonDataModel.this);
084
085        // start update process
086        memo.getSlotManager().update();
087    }
088
089    /**
090     * Forces a refresh of the slots
091     */
092    public void refreshSlots() {
093        memo.getSlotManager().update();
094    }
095
096    /**
097     * Return the number of rows to be displayed. This can vary depending on
098     * whether only active rows are displayed, and whether the system slots
099     * should be displayed.
100     * <p>
101     * This should probably use a local cache instead of counting/searching each
102     * time.
103     *
104     * @return the number of rows
105     */
106    @Override
107    public int getRowCount() {
108        return numRows;
109    }
110
111    @Override
112    public int getColumnCount() {
113        return columns;
114    }
115
116    @Override
117    public String getColumnName(int col) {
118        switch (col) {
119            case SLOTCOLUMN:
120                return Bundle.getMessage("SlotCol");
121            case ESTOPCOLUMN:
122                return "";     // no heading, as button is clear
123            case ADDRCOLUMN:
124                return Bundle.getMessage("AddressCol");
125            case SPDCOLUMN:
126                return Bundle.getMessage("SpeedCol");
127            case TYPECOLUMN:
128                return Bundle.getMessage("StatusCol");
129            case STATCOLUMN:
130                return Bundle.getMessage("UseCol");
131            case CONSCOLUMN:
132                return Bundle.getMessage("ConsistedCol");
133            case CONSISTADDRESS:
134                return Bundle.getMessage("ConsistAddress");
135            case DIRCOLUMN:
136                return Bundle.getMessage("DirectionCol");
137            case DISPCOLUMN:
138                return "";     // no heading, as button is clear
139            case F0COLUMN:
140                return Throttle.getFunctionString(0);
141            case F1COLUMN:
142                return Throttle.getFunctionString(1);
143            case F2COLUMN:
144                return Throttle.getFunctionString(2);
145            case F3COLUMN:
146                return Throttle.getFunctionString(3);
147            case F4COLUMN:
148                return Throttle.getFunctionString(4);
149            case F5COLUMN:
150                return Throttle.getFunctionString(5);
151            case F6COLUMN:
152                return Throttle.getFunctionString(6);
153            case F7COLUMN:
154                return Throttle.getFunctionString(7);
155            case F8COLUMN:
156                return Throttle.getFunctionString(8);
157            case F9COLUMN:
158                return Throttle.getFunctionString(9);
159            case F10COLUMN:
160                return Throttle.getFunctionString(10);
161            case F11COLUMN:
162                return Throttle.getFunctionString(11);
163            case F12COLUMN:
164                return Throttle.getFunctionString(12);
165            case F13COLUMN:
166                return Throttle.getFunctionString(13);
167            case F14COLUMN:
168                return Throttle.getFunctionString(14);
169            case F15COLUMN:
170                return Throttle.getFunctionString(15);
171            case F16COLUMN:
172                return Throttle.getFunctionString(16);
173            case F17COLUMN:
174                return Throttle.getFunctionString(17);
175            case F18COLUMN:
176                return Throttle.getFunctionString(16);
177            case F19COLUMN:
178                return Throttle.getFunctionString(19);
179            case F20COLUMN:
180                return Throttle.getFunctionString(20);
181            case F21COLUMN:
182                return Throttle.getFunctionString(21);
183            case F22COLUMN:
184                return Throttle.getFunctionString(22);
185            case F23COLUMN:
186                return Throttle.getFunctionString(23);
187            case F24COLUMN:
188                return Throttle.getFunctionString(24);
189            case F25COLUMN:
190                return Throttle.getFunctionString(25);
191            case F26COLUMN:
192                return Throttle.getFunctionString(26);
193            case F27COLUMN:
194                return Throttle.getFunctionString(27);
195            case F28COLUMN:
196                return Throttle.getFunctionString(28);
197            case THROTCOLUMN:
198                return Bundle.getMessage("ThrottleIDCol");
199            default:
200                return "unknown"; // NOI18N
201        }
202    }
203
204    @Override
205    public Class<?> getColumnClass(int col) {
206        switch (col) {
207            case SLOTCOLUMN:
208            case ADDRCOLUMN:
209            case CONSISTADDRESS:
210                return Integer.class;
211            case SPDCOLUMN:
212            case TYPECOLUMN:
213            case STATCOLUMN:
214            case CONSCOLUMN:
215            case DIRCOLUMN:
216            case THROTCOLUMN:
217                return String.class;
218            case ESTOPCOLUMN:
219            case DISPCOLUMN:
220                return JButton.class;
221            case F0COLUMN:
222            case F1COLUMN:
223            case F2COLUMN:
224            case F3COLUMN:
225            case F4COLUMN:
226            case F5COLUMN:
227            case F6COLUMN:
228            case F7COLUMN:
229            case F8COLUMN:
230            case F9COLUMN:
231            case F10COLUMN:
232            case F11COLUMN:
233            case F12COLUMN:
234            case F13COLUMN:
235            case F14COLUMN:
236            case F15COLUMN:
237            case F16COLUMN:
238            case F17COLUMN:
239            case F18COLUMN:
240            case F19COLUMN:
241            case F20COLUMN:
242            case F21COLUMN:
243            case F22COLUMN:
244            case F23COLUMN:
245            case F24COLUMN:
246            case F25COLUMN:
247            case F26COLUMN:
248            case F27COLUMN:
249            case F28COLUMN:
250                return Boolean.class;
251            default:
252                return null;
253        }
254    }
255
256    @Override
257    public boolean isCellEditable(int row, int col) {
258        LocoNetSlot s = memo.getSlotManager().slot(row);
259        switch (col) {
260            case ESTOPCOLUMN:
261            case DISPCOLUMN:
262            case F0COLUMN:
263            case F1COLUMN:
264            case F2COLUMN:
265            case F3COLUMN:
266            case F4COLUMN:
267            case F5COLUMN:
268            case F6COLUMN:
269            case F7COLUMN:
270            case F8COLUMN:
271            case F9COLUMN:
272            case F10COLUMN:
273            case F11COLUMN:
274            case F12COLUMN:
275            case F13COLUMN:
276            case F14COLUMN:
277            case F15COLUMN:
278            case F16COLUMN:
279            case F17COLUMN:
280            case F18COLUMN:
281            case F19COLUMN:
282            case F20COLUMN:
283            case F21COLUMN:
284            case F22COLUMN:
285            case F23COLUMN:
286            case F24COLUMN:
287            case F25COLUMN:
288            case F26COLUMN:
289            case F27COLUMN:
290            case F28COLUMN:
291                // only loco slots to be marked writeable only, system slot are read only
292                return !s.isSystemSlot();
293            default:
294                return false;
295        }
296    }
297
298    @Override
299    public Object getValueAt(int row, int col) {
300        LocoNetSlot s = memo.getSlotManager().slot(row);
301        String t;
302        if (s == null) {
303            log.error("slot pointer was null for slot row: {} col: {}", row, col);
304            return null;
305        }
306
307        switch (col) {
308            case SLOTCOLUMN:  // slot number
309                return row;
310            case ESTOPCOLUMN:
311                return Bundle.getMessage("ButtonEstop"); // will be name of button in default GUI
312            case ADDRCOLUMN:
313                return s.locoAddr();
314            case SPDCOLUMN:
315                switch (s.consistStatus()) {
316                    case LnConstants.CONSIST_TOP:
317                    case LnConstants.CONSIST_NO:
318                        if (s.speed() == 1) {
319                            t = "(estop) 1";
320                        } else {
321                            t = "          " + s.speed();
322                        }
323                        return t.substring(t.length() - 9, t.length()); // 9 comes from length of the "(estop)" prefix
324                    case LnConstants.CONSIST_MID:
325                    case LnConstants.CONSIST_SUB:
326                        return Bundle.getMessage("SlotSpeedConsist");
327                    default:
328                        return Bundle.getMessage("StateError");
329                }
330            case TYPECOLUMN:
331                switch (s.decoderType()) {
332                    case LnConstants.DEC_MODE_128A:
333                        return "128 step adv";
334                    case LnConstants.DEC_MODE_28A:
335                        return " 28 step adv";
336                    case LnConstants.DEC_MODE_128:
337                        return "128 step";
338                    case LnConstants.DEC_MODE_14:
339                        return " 14 step";
340                    case LnConstants.DEC_MODE_28TRI:
341                        return " 28 step trinary";
342                    case LnConstants.DEC_MODE_28:
343                        return " 28 step";
344                    default:
345                        return Bundle.getMessage("StateUnknown");
346                }
347            case STATCOLUMN:
348                switch (s.slotStatus()) {
349                    case LnConstants.LOCO_IN_USE:
350                        return Bundle.getMessage("StateInUse");
351                    case LnConstants.LOCO_IDLE:
352                        return Bundle.getMessage("StateIdle");
353                    case LnConstants.LOCO_COMMON:
354                        return Bundle.getMessage("StateCommon");
355                    case LnConstants.LOCO_FREE:
356                        return Bundle.getMessage("StateFree");
357                    default:
358                        return Bundle.getMessage("StateError");
359                }
360            case CONSCOLUMN:
361                switch (s.consistStatus()) {
362                    case LnConstants.CONSIST_MID:
363                        if (s.getProtocol() == LnConstants.LOCONETPROTOCOL_TWO) {
364                            return Bundle.getMessage("SlotConsistMidX", s.getLeadSlot());
365                        }
366                        t = Bundle.getMessage("SlotConsistMidX", s.speed());
367                        return t;
368                    case LnConstants.CONSIST_TOP:
369                        return Bundle.getMessage("SlotConsistTop");
370                    case LnConstants.CONSIST_SUB:
371                        if (s.getProtocol() == LnConstants.LOCONETPROTOCOL_TWO) {
372                            return Bundle.getMessage("SlotConsistSubX", s.getLeadSlot());
373                        }
374                        t = Bundle.getMessage("SlotConsistSubX", s.speed());
375                        return t;
376                    case LnConstants.CONSIST_NO:
377                        return Bundle.getMessage("SlotConsistNone");
378                    default:
379                        return Bundle.getMessage("StateError");
380                }
381            case CONSISTADDRESS:
382                switch (s.consistStatus()) {
383                    case LnConstants.CONSIST_TOP:
384                        return s.locoAddr();
385                    case LnConstants.CONSIST_MID:
386                    case LnConstants.CONSIST_SUB:
387                        if (s.getProtocol() == LnConstants.LOCONETPROTOCOL_TWO) {
388                            return memo.getSlotManager().slot(s.getLeadSlot()).locoAddr();
389                        }
390                        return memo.getSlotManager().slot(s.speed()).locoAddr();
391                    case LnConstants.CONSIST_NO:
392                        return 0;
393                    default:
394                        return 0;
395                }
396            case DISPCOLUMN:
397                return Bundle.getMessage("ButtonRelease"); // will be name of button in default GUI
398            case DIRCOLUMN:
399                return s.isForward() ? Bundle.getMessage("DirColForward") : Bundle.getMessage("DirColReverse");
400            case F0COLUMN:
401                return s.isF0();
402            case F1COLUMN:
403                return s.isF1();
404            case F2COLUMN:
405                return s.isF2();
406            case F3COLUMN:
407                return s.isF3();
408            case F4COLUMN:
409                return s.isF4();
410            case F5COLUMN:
411                return s.isF5();
412            case F6COLUMN:
413                return s.isF6();
414            case F7COLUMN:
415                return s.isF7();
416            case F8COLUMN:
417                return s.isF8();
418            case F9COLUMN:
419                return s.isF9();
420            case F10COLUMN:
421                return s.isF10();
422            case F11COLUMN:
423                return s.isF11();
424            case F12COLUMN:
425                return s.isF12();
426            case F13COLUMN:
427                return s.isF13();
428            case F14COLUMN:
429                return s.isF14();
430            case F15COLUMN:
431                return s.isF15();
432            case F16COLUMN:
433                return s.isF16();
434            case F17COLUMN:
435                return s.isF17();
436            case F18COLUMN:
437                return s.isF18();
438            case F19COLUMN:
439                return s.isF19();
440            case F20COLUMN:
441                return s.isF20();
442            case F21COLUMN:
443                return s.isF21();
444            case F22COLUMN:
445                return s.isF22();
446            case F23COLUMN:
447                return s.isF23();
448            case F24COLUMN:
449                return s.isF24();
450            case F25COLUMN:
451                return s.isF25();
452            case F26COLUMN:
453                return s.isF26();
454            case F27COLUMN:
455                return s.isF27();
456            case F28COLUMN:
457                return s.isF28();
458            case THROTCOLUMN:
459                int upper = (s.id() >> 7) & 0x7F;
460                int lower = s.id() & 0x7F;
461                return StringUtil.twoHexFromInt(upper) + " " + StringUtil.twoHexFromInt(lower);
462
463            default:
464                log.error("internal state inconsistent with table requst for {} {}", row, col);
465                return null;
466        }
467    }
468
469    public int getPreferredWidth(int col) {
470        switch (col) {
471            case SLOTCOLUMN:
472                return new JTextField(3).getPreferredSize().width;
473            case ESTOPCOLUMN:
474                return new JButton(Bundle.getMessage("ButtonEstop")).getPreferredSize().width;
475            case ADDRCOLUMN:
476                return new JTextField(5).getPreferredSize().width;
477            case SPDCOLUMN:
478            case STATCOLUMN:
479                return new JTextField(6).getPreferredSize().width;
480            case TYPECOLUMN:
481                return new JTextField(12).getPreferredSize().width;
482            case CONSCOLUMN:
483                return new JTextField(4).getPreferredSize().width;
484            case CONSISTADDRESS:
485                return new JTextField(" 0000 ").getPreferredSize().width;
486            case DIRCOLUMN:
487                // the length of an empty JTextField works on more GUIs
488                int m = Math.max(Bundle.getMessage("DirColForward").length(), Bundle.getMessage("DirColReverse").length());
489                m = Math.max(m, Bundle.getMessage("DirectionCol").length());
490                return new JTextField(m).getPreferredSize().width;
491            case DISPCOLUMN:
492                return new JButton(Bundle.getMessage("ButtonRelease")).getPreferredSize().width;
493            case THROTCOLUMN:
494                return new JTextField(7).getPreferredSize().width;
495            case F0COLUMN:
496            case F1COLUMN:
497            case F2COLUMN:
498            case F3COLUMN:
499            case F4COLUMN:
500            case F5COLUMN:
501            case F6COLUMN:
502            case F7COLUMN:
503            case F8COLUMN:
504            case F9COLUMN:
505            case F10COLUMN:
506            case F11COLUMN:
507            case F12COLUMN:
508            case F13COLUMN:
509            case F14COLUMN:
510            case F15COLUMN:
511            case F16COLUMN:
512            case F17COLUMN:
513            case F18COLUMN:
514            case F19COLUMN:
515            case F20COLUMN:
516            case F21COLUMN:
517            case F22COLUMN:
518            case F23COLUMN:
519            case F24COLUMN:
520            case F25COLUMN:
521            case F26COLUMN:
522            case F27COLUMN:
523            case F28COLUMN:
524                // to show checkboxes and Text
525                return Math.max(new JCheckBox().getPreferredSize().width, new JTextField("F99").getPreferredSize().width);
526            default:
527                return new JLabel(" <unknown> ").getPreferredSize().width; // NOI18N
528        }
529    }
530
531    @Override
532    public void setValueAt(Object value, int row, int col) {
533        LocoNetMessage msg;
534        LocoNetSlot s = memo.getSlotManager().slot(row);
535        if (s == null) {
536            log.error("slot pointer was null for slot row: {} col: {}", row, col);
537            return;
538        }
539
540        switch (col) {
541            case ESTOPCOLUMN:
542                log.debug("Start estop in slot {}", row);
543                if ((s.consistStatus() == LnConstants.CONSIST_SUB)
544                        || (s.consistStatus() == LnConstants.CONSIST_MID)) {
545                    Object[] options = {Bundle.getMessage("ButtonOK"), Bundle.getMessage("ButtonCancel")};
546                    int result = JmriJOptionPane.showOptionDialog(null,
547                                    Bundle.getMessage("SlotEstopWarning"),
548                                    Bundle.getMessage("WarningTitle"),
549                                    JmriJOptionPane.DEFAULT_OPTION,
550                                    JmriJOptionPane.WARNING_MESSAGE,
551                                    null, options, options[1]);
552                    if ( result != 0 ) { // not array position 0 ButtonOK
553                        return;
554                    }
555                }
556                msg = s.writeSpeed(1);
557                memo.getLnTrafficController().sendLocoNetMessage(msg);
558                fireTableRowsUpdated(row, row);
559                break;
560            case F0COLUMN:
561                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
562                    sendFunctionGroup1(s, col, row);
563                } else {
564                    sendExpFunctionGroup1(s, col, row);
565                }
566                fireTableRowsUpdated(row, row);
567                break;
568            case F1COLUMN:
569                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
570                    sendFunctionGroup1(s, col, row);
571                } else {
572                    sendExpFunctionGroup1(s, col, row);
573                }
574                fireTableRowsUpdated(row, row);
575                break;
576            case F2COLUMN:
577                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
578                    sendFunctionGroup1(s, col, row);
579                } else {
580                    sendExpFunctionGroup1(s, col, row);
581                }
582                fireTableRowsUpdated(row, row);
583                break;
584            case F3COLUMN:
585                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
586                    sendFunctionGroup1(s, col, row);
587                } else {
588                    sendExpFunctionGroup1(s, col, row);
589                }
590                fireTableRowsUpdated(row, row);
591                break;
592            case F4COLUMN:
593                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
594                    sendFunctionGroup1(s, col, row);
595                } else {
596                    sendExpFunctionGroup1(s, col, row);
597                }
598                fireTableRowsUpdated(row, row);
599                break;
600            case F5COLUMN:
601                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
602                    sendFunctionGroup2(s, col, row);
603                } else {
604                    sendExpFunctionGroup1(s, col, row);
605                }
606                fireTableRowsUpdated(row, row);
607                break;
608            case F6COLUMN:
609                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
610                    sendFunctionGroup2(s, col, row);
611                } else {
612                    sendExpFunctionGroup1(s, col, row);
613                }
614                fireTableRowsUpdated(row, row);
615                break;
616            case F7COLUMN:
617                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
618                    sendFunctionGroup2(s, col, row);
619                } else {
620                    sendExpFunctionGroup2(s, col, row);
621                }
622                fireTableRowsUpdated(row, row);
623                break;
624            case F8COLUMN:
625                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
626                    sendFunctionGroup2(s, col, row);
627                } else {
628                    sendExpFunctionGroup2(s, col, row);
629                }
630                fireTableRowsUpdated(row, row);
631                break;
632            case F9COLUMN:
633                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
634                    sendFunctionGroup3(s, col, row);
635                } else {
636                    sendExpFunctionGroup2(s, col, row);
637                }
638                fireTableRowsUpdated(row, row);
639                break;
640            case F10COLUMN:
641                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
642                    sendFunctionGroup3(s, col, row);
643                } else {
644                    sendExpFunctionGroup2(s, col, row);
645                }
646                fireTableRowsUpdated(row, row);
647                break;
648            case F11COLUMN:
649                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
650                    sendFunctionGroup3(s, col, row);
651                } else {
652                    sendExpFunctionGroup2(s, col, row);
653                }
654                fireTableRowsUpdated(row, row);
655                break;
656            case F12COLUMN:
657                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
658                    sendFunctionGroup3(s, col, row);
659                } else {
660                    sendExpFunctionGroup2(s, col, row);
661                }
662                fireTableRowsUpdated(row, row);
663                break;
664            case F13COLUMN:
665                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
666                    sendFunctionGroup4(s, col, row);
667                } else {
668                    sendExpFunctionGroup2(s, col, row);
669                }
670                fireTableRowsUpdated(row, row);
671                break;
672            case F14COLUMN:
673                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
674                    sendFunctionGroup4(s, col, row);
675                } else {
676                    sendExpFunctionGroup3(s, col, row);
677                }
678                fireTableRowsUpdated(row, row);
679                break;
680            case F15COLUMN:
681                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
682                    sendFunctionGroup4(s, col, row);
683                } else {
684                    sendExpFunctionGroup3(s, col, row);
685                }
686                fireTableRowsUpdated(row, row);
687                break;
688            case F16COLUMN:
689                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
690                    sendFunctionGroup4(s, col, row);
691                } else {
692                    sendExpFunctionGroup3(s, col, row);
693                }
694                fireTableRowsUpdated(row, row);
695                break;
696            case F17COLUMN:
697                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
698                    sendFunctionGroup4(s, col, row);
699                } else {
700                    sendExpFunctionGroup3(s, col, row);
701                }
702                fireTableRowsUpdated(row, row);
703                break;
704            case F18COLUMN:
705                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
706                    sendFunctionGroup4(s, col, row);
707                } else {
708                    sendExpFunctionGroup3(s, col, row);
709                }
710                fireTableRowsUpdated(row, row);
711                break;
712            case F19COLUMN:
713                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
714                    sendFunctionGroup4(s, col, row);
715                } else {
716                    sendExpFunctionGroup3(s, col, row);
717                }
718                fireTableRowsUpdated(row, row);
719                break;
720            case F20COLUMN:
721                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
722                    sendFunctionGroup4(s, col, row);
723                } else {
724                    sendExpFunctionGroup3(s, col, row);
725                }
726                fireTableRowsUpdated(row, row);
727                break;
728            case F21COLUMN:
729                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
730                    sendFunctionGroup5(s, col, row);
731                } else {
732                    sendExpFunctionGroup4(s, col, row);
733                }
734                fireTableRowsUpdated(row, row);
735                break;
736            case F22COLUMN:
737                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
738                    sendFunctionGroup5(s, col, row);
739                } else {
740                    sendExpFunctionGroup4(s, col, row);
741                }
742                fireTableRowsUpdated(row, row);
743                break;
744            case F23COLUMN:
745                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
746                    sendFunctionGroup5(s, col, row);
747                } else {
748                    sendExpFunctionGroup4(s, col, row);
749                }
750                fireTableRowsUpdated(row, row);
751                break;
752            case F24COLUMN:
753                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
754                    sendFunctionGroup5(s, col, row);
755                } else {
756                    sendExpFunctionGroup4(s, col, row);
757                }
758                fireTableRowsUpdated(row, row);
759                break;
760            case F25COLUMN:
761                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
762                    sendFunctionGroup5(s, col, row);
763                } else {
764                    sendExpFunctionGroup4(s, col, row);
765                }
766                fireTableRowsUpdated(row, row);
767                break;
768            case F26COLUMN:
769                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
770                    sendFunctionGroup5(s, col, row);
771                } else {
772                    sendExpFunctionGroup4(s, col, row);
773                }
774                fireTableRowsUpdated(row, row);
775                break;
776            case F27COLUMN:
777                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
778                    sendFunctionGroup5(s, col, row);
779                } else {
780                    sendExpFunctionGroup4(s, col, row);
781                }
782                fireTableRowsUpdated(row, row);
783                break;
784            case F28COLUMN:
785                if (s.getProtocol() != LnConstants.LOCONETPROTOCOL_TWO) {
786                    sendFunctionGroup5(s, col, row);
787                } else {
788                    sendExpFunctionGroup4(s, col, row);
789                }
790                fireTableRowsUpdated(row, row);
791                break;
792            case DISPCOLUMN:
793                log.debug("Start freeing slot {}", row);
794                if (s.slotStatus() != LnConstants.LOCO_FREE) {
795                    if (s.consistStatus() != LnConstants.CONSIST_NO) {
796                        // Freeing a member takes it out of the consist
797                        // entirely (i.e., while the slot is LOCO_FREE, it
798                        // still reads the former consist information, but
799                        // the next time that loco is selected, it comes
800                        // back as CONSIST_NO).  Freeing the CONSIST_TOP
801                        // will kill the entire consist.
802                        Object[] options = {Bundle.getMessage("ButtonOK"), Bundle.getMessage("ButtonCancel")};
803                        int result = JmriJOptionPane.showOptionDialog(null,
804                                        "Freeing a consist member will destroy the consist.\n\nAre you sure you want to do that?",
805                                        Bundle.getMessage("WarningTitle"),
806                                        JmriJOptionPane.DEFAULT_OPTION,
807                                        JmriJOptionPane.WARNING_MESSAGE,
808                                        null, options, options[1]);
809                        if ( result != 0 ) { // not array position 0, ButtonOK
810                            return;
811                        }
812                    }
813                    // send status to free
814                    memo.getLnTrafficController().sendLocoNetMessage(
815                            s.writeStatus(LnConstants.LOCO_FREE));
816                } else {
817                    log.debug("Slot not in use");
818                }
819                fireTableRowsUpdated(row, row);
820                break;
821            default:
822                // nothing to do if column not recognized
823                break;
824        }
825    }
826
827    //Added by Jeffrey Machacek, date: 2013
828    //changed 8/22/2013
829    public void clearAllSlots() {
830        int count = getRowCount();
831
832        for (int row = 0; row < (count - 1); row++) {
833            LocoNetSlot s = memo.getSlotManager().slot(row);
834
835            if ((s.slotStatus() != LnConstants.LOCO_IN_USE) && (s.consistStatus() == LnConstants.CONSIST_NO)) {
836                log.debug("Freeing {} from slot {}, old status: {}", s.locoAddr(), s.getSlot(), s.slotStatus());
837                memo.getLnTrafficController().sendLocoNetMessage(
838                        s.writeStatus(LnConstants.LOCO_FREE
839                        ));
840                fireTableRowsUpdated(row, row);
841            }
842            count = getRowCount();
843        }
844    }
845
846    /**
847     * Configure a table to have our standard rows and columns. This is
848     * optional, in that other table formats can use this table model. But we
849     * put it here to help keep it consistent.
850     *
851     * @param slotTable the table to configure
852     */
853    public void configureTable(JTable slotTable) {
854    }
855
856    // methods to communicate with SlotManager
857    @Override
858    public synchronized void notifyChangedSlot(LocoNetSlot s) {
859        // update model from this slot
860        int slotNum = s.getSlot();
861        int slotStatus2;
862
863        if (slotNum == LnConstants.CFG_SLOT) {
864            slotStatus2 = s.ss2() & 0x78; // Bit 3-6 of SS2 contains SW36-39 of the CFG_SLOT
865            if (slotStatus2 > 0) {
866                memo.getSlotManager().update();
867            }
868        } else {
869            slotNum = -1; // all rows
870        }
871
872        // notify the JTable object that a row has changed; do that in the Swing thread!
873        javax.swing.SwingUtilities.invokeLater(new Notify(slotNum, this));
874    }
875
876    static private class Notify implements Runnable {
877
878        private final int _row;
879        javax.swing.table.AbstractTableModel _model;
880
881        public Notify(int row, javax.swing.table.AbstractTableModel model) {
882            _row = row;
883            _model = model;
884        }
885
886        @Override
887        public void run() {
888            if (-1 == _row) {  // notify about entire table
889                _model.fireTableDataChanged();  // just that row
890            } else {
891                // notify that _row has changed
892                _model.fireTableRowsUpdated(_row, _row);  // just that row
893            }
894        }
895    }
896
897    protected LocoNetSlot getSlot(int row) {
898        return memo.getSlotManager().slot(row);
899    }
900
901    /**
902     * This is a convenience method that makes it easier for classes using this
903     * model to set all in-use slots to emergency stop
904     */
905    public void estopAll() {
906        for (int slotNum = 0; slotNum < 120; slotNum++) {
907            LocoNetSlot s = memo.getSlotManager().slot(slotNum);
908            if (s.slotStatus() != LnConstants.LOCO_FREE
909                    && (s.consistStatus() == LnConstants.CONSIST_NO
910                    || s.consistStatus() == LnConstants.CONSIST_TOP)
911                    && s.speed() != 1) {
912                // send message to estop this loco
913                LocoNetMessage msg = s.writeSpeed(1) ; // emergency stop
914                memo.getLnTrafficController().sendLocoNetMessage(msg);
915            }
916        }
917    }
918
919    /**
920     * Send the LocoNet message to set the state of locomotive direction and
921     * functions F0, F1, F2, F3, F4
922     * @param slot loconet slot
923     * @param col  grid col
924     * @param row  grid row
925     */
926    protected void sendFunctionGroup1(LocoNetSlot slot, int col, int row) {
927        log.debug("F0-F4 change requested {}", row);
928        if (slot == null) {
929            log.error("slot pointer was null for slot row: {} col: {}", row, col);
930            return;
931        }
932        boolean tempF0 = (col == F0COLUMN) ? !slot.isF0() : slot.isF0();
933        boolean tempF1 = (col == F1COLUMN) ? !slot.isF1() : slot.isF1();
934        boolean tempF2 = (col == F2COLUMN) ? !slot.isF2() : slot.isF2();
935        boolean tempF3 = (col == F3COLUMN) ? !slot.isF3() : slot.isF3();
936        boolean tempF4 = (col == F4COLUMN) ? !slot.isF4() : slot.isF4();
937
938        int new_dirf = ((slot.isForward() ? 0 : LnConstants.DIRF_DIR)
939                | (tempF0 ? LnConstants.DIRF_F0 : 0)
940                | (tempF1 ? LnConstants.DIRF_F1 : 0)
941                | (tempF2 ? LnConstants.DIRF_F2 : 0)
942                | (tempF3 ? LnConstants.DIRF_F3 : 0)
943                | (tempF4 ? LnConstants.DIRF_F4 : 0));
944
945        // set status to 'In Use' if other
946        int status = slot.slotStatus();
947        if (status != LnConstants.LOCO_IN_USE) {
948            memo.getLnTrafficController().sendLocoNetMessage(
949                    slot.writeStatus(LnConstants.LOCO_IN_USE
950                    ));
951        }
952        LocoNetMessage msg = new LocoNetMessage(4);
953        msg.setOpCode(LnConstants.OPC_LOCO_DIRF);
954        msg.setElement(1, slot.getSlot());
955        msg.setElement(2, new_dirf);       // 1 here is estop
956        memo.getLnTrafficController().sendLocoNetMessage(msg);
957        // Delay here allows command station time to xmit on the rails.
958        try {
959            Thread.sleep(100);
960        } catch (InterruptedException ex) {
961            log.error("Interuppted Sleep", ex);
962        }
963        // reset status to original value if not previously 'in use'
964        if (status != LnConstants.LOCO_IN_USE) {
965            memo.getLnTrafficController().sendLocoNetMessage(
966                    slot.writeStatus(status));
967        }
968    }
969
970    /**
971     * Send the LocoNet message to set the state of functions F5, F6, F7, F8
972     * @param slot loconet slot
973     * @param col  grid col
974     * @param row  grid row
975     */
976    protected void sendFunctionGroup2(LocoNetSlot slot, int col, int row) {
977        boolean tempF5 = (col == F5COLUMN) ? !slot.isF5() : slot.isF5();
978        boolean tempF6 = (col == F6COLUMN) ? !slot.isF6() : slot.isF6();
979        boolean tempF7 = (col == F7COLUMN) ? !slot.isF7() : slot.isF7();
980        boolean tempF8 = (col == F8COLUMN) ? !slot.isF8() : slot.isF8();
981
982        int new_snd = ((tempF8 ? LnConstants.SND_F8 : 0)
983                | (tempF7 ? LnConstants.SND_F7 : 0)
984                | (tempF6 ? LnConstants.SND_F6 : 0)
985                | (tempF5 ? LnConstants.SND_F5 : 0));
986
987        // set status to 'In Use' if other
988        int status = slot.slotStatus();
989        if (status != LnConstants.LOCO_IN_USE) {
990            memo.getLnTrafficController().sendLocoNetMessage(
991                    slot.writeStatus(LnConstants.LOCO_IN_USE
992                    ));
993        }
994
995        LocoNetMessage msg = new LocoNetMessage(4);
996        msg.setOpCode(LnConstants.OPC_LOCO_SND);
997        msg.setElement(1, slot.getSlot());
998        msg.setElement(2, new_snd);       // 1 here is estop
999        memo.getLnTrafficController().sendLocoNetMessage(msg);
1000        // Delay here allows command station time to xmit on the rails.
1001        try {
1002            Thread.sleep(100);
1003        } catch (InterruptedException ex) {
1004            log.error("Interuppted Sleep", ex);
1005        }
1006
1007        // reset status to original value if not previously 'in use'
1008        if (status != LnConstants.LOCO_IN_USE) {
1009            memo.getLnTrafficController().sendLocoNetMessage(
1010                    slot.writeStatus(status));
1011        }
1012    }
1013
1014    /**
1015     * Sends Function Group 3 values - F9 thru F12, using an "OPC_IMM_PACKET" LocoNet
1016     * Message.
1017     * @param slot loconet slot
1018     * @param col  grid col
1019     * @param row  grid row
1020     */
1021     protected void sendFunctionGroup3(LocoNetSlot slot, int col, int row) {
1022        // LocoNet practice is to send F9-F12 as a DCC packet
1023        boolean tempF9 = (col == F9COLUMN) ? !slot.isF9() : slot.isF9();
1024        boolean tempF10 = (col == F10COLUMN) ? !slot.isF10() : slot.isF10();
1025        boolean tempF11 = (col == F11COLUMN) ? !slot.isF11() : slot.isF11();
1026        boolean tempF12 = (col == F12COLUMN) ? !slot.isF12() : slot.isF12();
1027        byte[] result = jmri.NmraPacket.function9Through12Packet(slot.locoAddr(), (slot.locoAddr() >= 128),
1028                tempF9, tempF10, tempF11, tempF12);
1029
1030        log.debug("sendFunctionGroup3 sending {} to LocoNet slot {}", result, slot.getSlot());
1031        memo.get(jmri.CommandStation.class).sendPacket(result, 4); // repeat = 4
1032    }
1033
1034    /**
1035     * Sends Function Group 4 values - F13 thru F20, using an "OPC_IMM_PACKET" LocoNet
1036     * Message.
1037     * @param slot loconet slot
1038     * @param col  grid col
1039     * @param row  grid row
1040     */
1041    protected void sendFunctionGroup4(LocoNetSlot slot, int col, int row) {
1042        // LocoNet practice is to send F13-F20 as a DCC packet
1043        boolean tempF13 = (col == F13COLUMN) ? !slot.isF13() : slot.isF13();
1044        boolean tempF14 = (col == F14COLUMN) ? !slot.isF14() : slot.isF14();
1045        boolean tempF15 = (col == F15COLUMN) ? !slot.isF15() : slot.isF15();
1046        boolean tempF16 = (col == F16COLUMN) ? !slot.isF16() : slot.isF16();
1047        boolean tempF17 = (col == F17COLUMN) ? !slot.isF17() : slot.isF17();
1048        boolean tempF18 = (col == F18COLUMN) ? !slot.isF18() : slot.isF18();
1049        boolean tempF19 = (col == F19COLUMN) ? !slot.isF19() : slot.isF19();
1050        boolean tempF20 = (col == F20COLUMN) ? !slot.isF20() : slot.isF20();
1051        byte[] result = jmri.NmraPacket.function13Through20Packet(slot.locoAddr(), (slot.locoAddr() >= 128),
1052                tempF13, tempF14, tempF15, tempF16,
1053                tempF17, tempF18, tempF19, tempF20);
1054
1055        log.debug("sendFunctionGroup4 sending {} to LocoNet slot {}", result, slot.getSlot());
1056        memo.get(jmri.CommandStation.class).sendPacket(result, 4); // repeat = 4
1057    }
1058
1059    /**
1060     * Sends Function Group 5 values - F21 thru F28, using an "OPC_IMM_PACKET" LocoNet
1061     * Message.
1062     * @param slot loconet slot
1063     * @param col  grid col
1064     * @param row  grid row
1065     */
1066    protected void sendFunctionGroup5(LocoNetSlot slot, int col, int row) {
1067        // LocoNet practice is to send F21-F28 as a DCC packet
1068        // LocoNet practice is to send F13-F20 as a DCC packet
1069        boolean tempF21 = (col == F21COLUMN) ? !slot.isF21() : slot.isF21();
1070        boolean tempF22 = (col == F22COLUMN) ? !slot.isF22() : slot.isF22();
1071        boolean tempF23 = (col == F23COLUMN) ? !slot.isF23() : slot.isF23();
1072        boolean tempF24 = (col == F24COLUMN) ? !slot.isF24() : slot.isF24();
1073        boolean tempF25 = (col == F25COLUMN) ? !slot.isF25() : slot.isF25();
1074        boolean tempF26 = (col == F26COLUMN) ? !slot.isF26() : slot.isF26();
1075        boolean tempF27 = (col == F27COLUMN) ? !slot.isF27() : slot.isF27();
1076        boolean tempF28 = (col == F28COLUMN) ? !slot.isF28() : slot.isF28();
1077        byte[] result = jmri.NmraPacket.function21Through28Packet(slot.locoAddr(), (slot.locoAddr() >= 128),
1078                tempF21, tempF22, tempF23, tempF24,
1079                tempF25, tempF26, tempF27, tempF28);
1080
1081        log.debug("sendFunctionGroup5 sending {} to LocoNet slot {}", result, slot.getSlot());
1082        memo.get(jmri.CommandStation.class).sendPacket(result, 4); // repeat = 4
1083    }
1084
1085    /**
1086     * Send the Expanded LocoNet message to set the state of locomotive direction and
1087     * functions F0, F1, F2, F3, F4, F5, F6
1088     * @param slot loconet slot
1089     * @param col  grid col
1090     * @param row  grid row
1091     */
1092    protected void sendExpFunctionGroup1(LocoNetSlot slot, int col, int row) {
1093        boolean tempF0 = (col == F0COLUMN) ? !slot.isF0() : slot.isF0();
1094        boolean tempF1 = (col == F1COLUMN) ? !slot.isF1() : slot.isF1();
1095        boolean tempF2 = (col == F2COLUMN) ? !slot.isF2() : slot.isF2();
1096        boolean tempF3 = (col == F3COLUMN) ? !slot.isF3() : slot.isF3();
1097        boolean tempF4 = (col == F4COLUMN) ? !slot.isF4() : slot.isF4();
1098        boolean tempF5 = (col == F5COLUMN) ? !slot.isF5() : slot.isF5();
1099        boolean tempF6 = (col == F6COLUMN) ? !slot.isF6() : slot.isF6();
1100        int new_F0F6 = ((tempF5 ? 0b00100000 : 0) | (tempF6 ? 0b01000000 : 0)
1101                | (tempF0 ? LnConstants.DIRF_F0 : 0)
1102                | (tempF1 ? LnConstants.DIRF_F1 : 0)
1103                | (tempF2 ? LnConstants.DIRF_F2 : 0)
1104                | (tempF3 ? LnConstants.DIRF_F3 : 0)
1105                | (tempF4 ? LnConstants.DIRF_F4 : 0));
1106            LocoNetMessage msg = new LocoNetMessage(6);
1107            msg.setOpCode(0xd5);
1108            msg.setElement(1, (slot.getSlot() / 128) | 0b00010000 );
1109            msg.setElement(2,slot.getSlot() & 0b01111111);
1110            msg.setElement(3,slot.id() & 0x7F);
1111            msg.setElement(4, new_F0F6);
1112            memo.getLnTrafficController().sendLocoNetMessage(msg);
1113    }
1114
1115    /**
1116     * Send the Expanded LocoNet message to set the state of functions F7, F8, F8, F9, F10, F11, F12, F13
1117     * @param slot loconet slot
1118     * @param col  grid col
1119     * @param row  grid row
1120     */
1121    protected void sendExpFunctionGroup2(LocoNetSlot slot, int col, int row) {
1122        boolean tempF7 = (col == F7COLUMN) ? !slot.isF7() : slot.isF7();
1123        boolean tempF8 = (col == F8COLUMN) ? !slot.isF8() : slot.isF8();
1124        boolean tempF9 = (col == F9COLUMN) ? !slot.isF9() : slot.isF9();
1125        boolean tempF10 = (col == F10COLUMN) ? !slot.isF10() : slot.isF10();
1126        boolean tempF11 = (col == F11COLUMN) ? !slot.isF11() : slot.isF11();
1127        boolean tempF12 = (col == F12COLUMN) ? !slot.isF12() : slot.isF12();
1128        boolean tempF13 = (col == F13COLUMN) ? !slot.isF13() : slot.isF13();
1129            int new_F7F13 = ((tempF7 ? 0b00000001 : 0) | (tempF8 ? 0b00000010 : 0)
1130                    | (tempF9  ? 0b00000100 : 0)
1131                    | (tempF10 ? 0b00001000 : 0)
1132                    | (tempF11 ? 0b00010000 : 0)
1133                    | (tempF12 ? 0b00100000 : 0)
1134                    | (tempF13 ? 0b01000000 : 0));
1135                LocoNetMessage msg = new LocoNetMessage(6);
1136                msg.setOpCode(0xd5);
1137                msg.setElement(1, (slot.getSlot() / 128) | 0b00011000 );
1138                msg.setElement(2,slot.getSlot() & 0b01111111);
1139                msg.setElement(3,slot.id() & 0x7F);
1140                msg.setElement(4, new_F7F13);
1141                memo.getLnTrafficController().sendLocoNetMessage(msg);
1142    }
1143
1144    /**
1145     * Sends expanded loconet message F14 thru F20
1146     * Message.
1147     * @param slot loconet slot
1148     * @param col  grid col
1149     * @param row  grid row
1150     */
1151    protected void sendExpFunctionGroup3(LocoNetSlot slot, int col, int row) {
1152        boolean tempF14 = (col == F14COLUMN) ? !slot.isF14() : slot.isF14();
1153        boolean tempF15 = (col == F15COLUMN) ? !slot.isF15() : slot.isF15();
1154        boolean tempF16 = (col == F16COLUMN) ? !slot.isF16() : slot.isF16();
1155        boolean tempF17 = (col == F17COLUMN) ? !slot.isF17() : slot.isF17();
1156        boolean tempF18 = (col == F18COLUMN) ? !slot.isF18() : slot.isF18();
1157        boolean tempF19 = (col == F19COLUMN) ? !slot.isF19() : slot.isF19();
1158        boolean tempF20 = (col == F20COLUMN) ? !slot.isF20() : slot.isF20();
1159        int new_F14F20 = ((tempF14 ? 0b00000001 : 0) | (tempF15 ? 0b00000010 : 0)
1160                | (tempF16  ? 0b00000100 : 0)
1161                | (tempF17 ? 0b00001000 : 0)
1162                | (tempF18 ? 0b00010000 : 0)
1163                | (tempF19 ? 0b00100000 : 0)
1164                | (tempF20 ? 0b01000000 : 0));
1165            LocoNetMessage msg = new LocoNetMessage(6);
1166            msg.setOpCode(0xd5);
1167            msg.setElement(1, (slot.getSlot() / 128) | 0b00100000 );
1168            msg.setElement(2,slot.getSlot() & 0b01111111);
1169            msg.setElement(3,slot.id() & 0x7F);
1170            msg.setElement(4, new_F14F20);
1171            memo.getLnTrafficController().sendLocoNetMessage(msg);
1172    }
1173
1174    /**
1175     * Sends Expanded loconet message F21 thru F28 Message.
1176     * @param slot loconet slot
1177     * @param col  grid col
1178     * @param row  grid row
1179     */
1180    protected void sendExpFunctionGroup4(LocoNetSlot slot, int col, int row) {
1181        boolean tempF21 = (col == F21COLUMN) ? !slot.isF21() : slot.isF21();
1182        boolean tempF22 = (col == F22COLUMN) ? !slot.isF22() : slot.isF22();
1183        boolean tempF23 = (col == F23COLUMN) ? !slot.isF23() : slot.isF23();
1184        boolean tempF24 = (col == F24COLUMN) ? !slot.isF24() : slot.isF24();
1185        boolean tempF25 = (col == F25COLUMN) ? !slot.isF25() : slot.isF25();
1186        boolean tempF26 = (col == F26COLUMN) ? !slot.isF26() : slot.isF26();
1187        boolean tempF27 = (col == F27COLUMN) ? !slot.isF27() : slot.isF27();
1188        boolean tempF28 = (col == F28COLUMN) ? !slot.isF28() : slot.isF28();
1189        int new_F14F20 = ((tempF21 ? 0b00000001 : 0) |
1190                (tempF22 ? 0b00000010 : 0) |
1191                (tempF23 ? 0b00000100 : 0) |
1192                (tempF24 ? 0b00001000 : 0) |
1193                (tempF25 ? 0b00010000 : 0) |
1194                (tempF26 ? 0b00100000 : 0) |
1195                (tempF27 ? 0b01000000 : 0));
1196        LocoNetMessage msg = new LocoNetMessage(6);
1197        msg.setOpCode(0xd5);
1198        if (!tempF28) {
1199            msg.setElement(1, (slot.getSlot() / 128) | LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28OFF);
1200        } else {
1201            msg.setElement(1, (slot.getSlot() / 128) | LnConstants.OPC_EXP_SEND_FUNCTION_GROUP_F21F28_F28ON);
1202        }
1203        msg.setElement(2, slot.getSlot() &  0x7F);
1204        msg.setElement(3, slot.id() & 0x7F);
1205        msg.setElement(4, new_F14F20);
1206        memo.getLnTrafficController().sendLocoNetMessage(msg);
1207    }
1208
1209    // gets called on SlotMonPane.dispose
1210    public void dispose() {
1211        memo.getSlotManager().removeSlotListener(this);
1212    }
1213
1214    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SlotMonDataModel.class);
1215
1216}