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