001package jmri.jmrix.can.cbus.swing.modules.sprogdcc;
002
003import java.awt.*;
004import java.awt.event.ActionEvent;
005
006import javax.swing.*;
007import javax.swing.event.TableModelEvent;
008
009import jmri.jmrix.can.cbus.node.CbusNode;
010import jmri.jmrix.can.cbus.node.CbusNodeNVTableDataModel;
011import jmri.jmrix.can.cbus.swing.modules.*;
012
013import org.slf4j.Logger;
014import org.slf4j.LoggerFactory;
015
016/**
017 * Node Variable edit frame for a SPROG DCC Pi-SPROG 3 module
018 *
019 * @author Andrew Crosland Copyright (C) 2022
020 */
021public class PiSprog3EditNVPane extends AbstractEditNVPane {
022    
023    private static final int USER_FLAGS = 0;
024    private static final int OPS_FLAGS = 1;
025    private CmdStaFlags [] csFlags = new CmdStaFlags[2];
026    
027    private final UpdateNV currentLimitUpdateFn = new UpdateCurrentLimit();
028    private final UpdateNV accyPktUpdateFn = new UpdateAccyCount();
029    private final UpdateNV preambleUpdateFn = new UpdatePreamble();
030    private final UpdateNV modeUpdateFn = new UpdatePowerMode();
031    private final UpdateNV meterUpdateFn = new UpdateMeter();
032    private final UpdateNV flagUpdateFn = new UpdateFlags();
033    
034    private JComboBox<String> modeList ;
035    private TitledSpinner mainSpinner;
036    private TitledSpinner accyPktSpinner;
037    private JRadioButton meter;
038    private JRadioButton setup;
039    private TitledSpinner preambleSpinner;
040            
041    protected String flagTitleStrings[] = new String[] {
042        Bundle.getMessage("UserFlags"),
043        Bundle.getMessage("OperationsFlags")
044    };
045
046    protected String flagStrings[][] = new String[][] {
047        // User
048        {Bundle.getMessage("Reserved"),
049            Bundle.getMessage("PermitSteal"),
050            Bundle.getMessage("PermitShare"),
051            Bundle.getMessage("Reserved"),
052            Bundle.getMessage("MapEvents"),
053            Bundle.getMessage("StopOnTimeout"),
054            Bundle.getMessage("StartOfDay"),
055            Bundle.getMessage("AutoPower")},
056        // Ops
057        {Bundle.getMessage("Reserved"),
058            Bundle.getMessage("Reserved"),
059            Bundle.getMessage("Reserved"),
060            Bundle.getMessage("ZtcMode"),
061            Bundle.getMessage("AllStopTrackOff"),
062            Bundle.getMessage("BluelineMode"),
063            Bundle.getMessage("AckSensitivity"),
064            Bundle.getMessage("Reserved")
065        }};
066
067    protected String flagTtStrings[][] = new String[][] {
068        // User
069        {Bundle.getMessage("ReservedTt"),
070            Bundle.getMessage("PermitStealTt"),
071            Bundle.getMessage("PermitShareTt"),
072            Bundle.getMessage("ReservedTt"),
073            Bundle.getMessage("MapEventsTt"),
074            Bundle.getMessage("StopOnTimeoutTt"),
075            Bundle.getMessage("StartOfDayTt"),
076            Bundle.getMessage("AutoPowerTt")},
077        // Ops
078        {Bundle.getMessage("ReservedTt"),
079            Bundle.getMessage("ReservedTt"),
080            Bundle.getMessage("ReservedTt"),
081            Bundle.getMessage("ZtcModeTt"),
082            Bundle.getMessage("AllStopTrackOffTt"),
083            Bundle.getMessage("BluelineModeTt"),
084            Bundle.getMessage("AckSensitivityTt"),
085            Bundle.getMessage("ReservedTt")
086        }};
087
088    protected PiSprog3EditNVPane(CbusNodeNVTableDataModel dataModel, CbusNode node) {
089        super(dataModel, node);
090    }
091    
092    /** {@inheritDoc} */
093    @Override
094    public AbstractEditNVPane getContent() {
095        
096        JTabbedPane tabbedPane = new JTabbedPane();
097        
098        JPanel cmdStaPane = new CmdStaPane();
099        JPanel dccPane = new DccPane();
100        JPanel cbusPane = new CbusPane();
101        tabbedPane.addTab(Bundle.getMessage("CmdSta"), cmdStaPane);
102        tabbedPane.addTab("DCC", dccPane);
103        tabbedPane.addTab("CBUS Diagnostics", cbusPane);
104        
105        JScrollPane scroll = new JScrollPane(tabbedPane);
106        add(scroll);
107        
108        return this;
109    }
110
111    /** {@inheritDoc} */
112    @Override
113    public void tableChanged(TableModelEvent e) {
114        if (e.getType() == TableModelEvent.UPDATE) {
115            int row = e.getFirstRow();
116            int nv = row + 1;
117            int value = getSelectValue8(nv);
118            log.debug("pisprog3 gui table changed NV: {} Value: {}", nv, value);
119            
120            switch (nv) {
121                case PiSprog3PaneProvider.USER_FLAGS:
122                    csFlags[0].setFlags(value);
123                    break;
124                    
125                case PiSprog3PaneProvider.OPERATIONS_FLAGS:
126                    csFlags[1].setFlags(value);
127                    break;
128                    
129                case PiSprog3PaneProvider.CMD_STATION_MODE:
130                    // Do nothing if nothing has changed, else causes an endless
131                    // round of gui update, table update, ...
132                    if (value != modeList.getSelectedIndex()) {
133                        modeList.setSelectedIndex(value);
134                    }
135                    break;
136                    
137                case PiSprog3PaneProvider.CURRENT_LIMIT:
138                    double mainLimit = (double)value/100;
139                    mainSpinner.setValue(mainLimit);
140                    break;
141                    
142                case PiSprog3PaneProvider.ACCY_PACKET_REPEAT_COUNT:
143                    accyPktSpinner.setValue(value);
144                    break;
145                    
146                case PiSprog3PaneProvider.MULTIMETER_MODE:
147                    meter.setSelected(value != 0);
148                    break;
149                    
150                case PiSprog3PaneProvider.SETUP:
151                    setup.setSelected(value != 0);
152                    break;
153                    
154                case PiSprog3PaneProvider.DCC_PREAMBLE:
155                    preambleSpinner.setValue(value);
156                    break;
157                    
158                case PiSprog3PaneProvider.INPUT_VOLTAGE:
159                case PiSprog3PaneProvider.TRACK_CURRENT:
160                    // These read-only NVs are not preented in the edit GUI as they can be displayed on meters
161                    break;
162                    
163                default:
164                    // Not used, or row was -1
165//                    log.debug("Update unknown NV {}", nv);
166                    
167            }
168        }
169    }
170    
171    /**
172     * Update the Flags
173     */
174    protected class UpdateFlags implements UpdateNV {
175        
176        /** {@inheritDoc} */
177        @Override
178        public void setNewVal(int index) {
179            int flags = csFlags[index].getFlags();
180            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
181            _dataModel.setValueAt(flags, csFlags[index].getNv() - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
182        }
183    }
184        
185    /**
186     * Update the multi meter events setting
187     */
188    protected class UpdateMeter implements UpdateNV {
189        
190        /** {@inheritDoc} */
191        @Override
192        public void setNewVal(int index) {
193            int mode = meter.isSelected() ? 1 : 0;
194            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
195            _dataModel.setValueAt(mode, index - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
196        }
197    }
198    
199    /**
200     * Update a current Limit
201     */
202    protected class UpdateCurrentLimit implements UpdateNV {
203        
204        /** {@inheritDoc} */
205        @Override
206        public void setNewVal(int index) {
207            int limit;
208            double fLimit;
209            fLimit = mainSpinner.getDoubleValue();
210            // Limit to 10mA precision
211            limit = (int)(fLimit*100 + 0.5);
212            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
213            _dataModel.setValueAt(limit, index - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
214        }
215    }
216    
217    /**
218     * Update the number of times a DCC accessory packet is repeated
219     */
220    protected class UpdateAccyCount implements UpdateNV {
221        
222        /** {@inheritDoc} */
223        @Override
224        public void setNewVal(int index) {
225            int cnt = accyPktSpinner.getIntegerValue();
226            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
227            _dataModel.setValueAt(cnt, index - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
228        }
229    }
230
231    /**
232     * Update the number of DCC packet preamble bits
233     */
234    protected class UpdatePreamble implements UpdateNV {
235        
236        /** {@inheritDoc} */
237        @Override
238        public void setNewVal(int index) {
239            int pre = preambleSpinner.getIntegerValue();
240            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
241            _dataModel.setValueAt(pre, index - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
242        }
243    }
244    
245    /**
246     * Update the command station power mode
247     */
248    protected class UpdatePowerMode implements UpdateNV {
249        
250        /** {@inheritDoc} */
251        @Override
252        public void setNewVal(int index) {
253            log.debug("UpdatePowerMode.setNewVal()");
254            int mode = modeList.getSelectedIndex();
255            // Note that changing the data model will result in tableChanged() being called, which can manipulate the buttons, etc
256            _dataModel.setValueAt(mode, index - 1, CbusNodeNVTableDataModel.NV_SELECT_COLUMN);
257        }
258    }
259    
260    public class CmdStaPane extends JPanel {
261
262        /**
263         * Panel to display command station NVs
264         */
265        public CmdStaPane() {
266            super();
267
268            JPanel gridPane = new JPanel(new GridBagLayout());
269            JPanel [] flagPane = new JPanel[2];
270            GridBagConstraints c = new GridBagConstraints();
271            c.fill = GridBagConstraints.HORIZONTAL;
272
273            for (int i = 0; i < 2; i++) {
274                csFlags[i] = new CmdStaFlags(i, PiSprog3PaneProvider.USER_FLAGS + i, flagTitleStrings[i], flagStrings[i], flagTtStrings[i], flagUpdateFn);
275                csFlags[i].setFlags(getSelectValue8(PiSprog3PaneProvider.USER_FLAGS + i));
276                flagPane[i] = csFlags[i].getContents();
277            }
278            
279            String modeStrings [] = new String[] {Bundle.getMessage("ProgMode"),
280                Bundle.getMessage("CmdMode")
281            };
282            
283            c.weightx = 1;
284            c.weighty = 1;
285            c.gridx = 0;
286            c.gridy = 0;
287
288            // x = 1
289            c.gridwidth = 3;
290            modeList = new JComboBox<>(modeStrings);
291            modeList.setSelectedIndex(getSelectValue8(PiSprog3PaneProvider.CMD_STATION_MODE));
292            modeList.addActionListener((ActionEvent e) -> {
293                modeActionListener(e);
294            });
295            gridPane.add(modeList, c);
296            c.gridwidth = 1;
297            c.gridy++;
298            
299            meter = new JRadioButton(Bundle.getMessage("Multimeter"));
300            meter.setSelected(false);
301            meter.setToolTipText(Bundle.getMessage("MultimeterTt"));
302            meter.addActionListener((ActionEvent e) -> {
303                meterActionListener(e);
304            });
305            gridPane.add(meter, c);
306            c.gridy++;
307            
308            mainSpinner = new TitledSpinner(Bundle.getMessage("MainLimit"), PiSprog3PaneProvider.CURRENT_LIMIT, currentLimitUpdateFn);
309            mainSpinner.setToolTip(Bundle.getMessage("MainLimitTt"));
310            mainSpinner.init(getSelectValue8(PiSprog3PaneProvider.CURRENT_LIMIT, 100)/100.0, 1.0, 2.5, 0.01);
311            gridPane.add(mainSpinner, c);
312            c.gridy++;
313            c.gridx = 0;
314            
315            gridPane.add(flagPane[USER_FLAGS], c);
316            c.gridx++;
317
318            gridPane.add(flagPane[OPS_FLAGS], c);
319            c.gridx++;
320
321            add(gridPane);
322        }
323        
324        /**
325         * Action listener for the command station power mode
326         * 
327         * @param e the action event
328         */
329        protected void modeActionListener(ActionEvent e) {
330            log.debug("modeActionListener()");
331            modeUpdateFn.setNewVal(PiSprog3PaneProvider.CMD_STATION_MODE);
332        }
333        
334        /**
335         * Action listener for multimeter nmode selection
336         * 
337         * @param e the action event
338         */
339        protected void meterActionListener(ActionEvent e) {
340            meterUpdateFn.setNewVal(PiSprog3PaneProvider.MULTIMETER_MODE);
341        }
342    }
343    
344    /**
345     * Panel to display DCC related NVs
346     */
347    public class DccPane extends JPanel {
348        public DccPane() {
349            super();
350
351            JPanel gridPane = new JPanel(new GridBagLayout());
352            GridBagConstraints c = new GridBagConstraints();
353            c.fill = GridBagConstraints.HORIZONTAL;
354
355            c.weightx = 1;
356            c.weighty = 1;
357            c.gridx = 0;
358            c.gridy = 0;
359            
360            accyPktSpinner = new TitledSpinner(Bundle.getMessage("AccyPktCnt"), PiSprog3PaneProvider.ACCY_PACKET_REPEAT_COUNT, accyPktUpdateFn);
361            accyPktSpinner.setToolTip(Bundle.getMessage("AccyPktCntTt"));
362            accyPktSpinner.init(getSelectValue8(PiSprog3PaneProvider.ACCY_PACKET_REPEAT_COUNT, 1), 1, 7, 1);
363            gridPane.add(accyPktSpinner, c);
364            c.gridy++;
365            
366            preambleSpinner = new TitledSpinner(Bundle.getMessage("DccPreambles"), PiSprog3PaneProvider.DCC_PREAMBLE, preambleUpdateFn);
367            preambleSpinner.setToolTip(Bundle.getMessage("DccPreamblesTt"));
368            preambleSpinner.init(getSelectValue8(PiSprog3PaneProvider.DCC_PREAMBLE, 14), 14, 32, 1);
369            gridPane.add(preambleSpinner, c);
370                    
371            add(gridPane);
372        }
373    }
374    
375    /**
376     * Panel to display CBUS operation related NVs
377     */
378    public class CbusPane extends JPanel {
379        public CbusPane() {
380            super();
381
382            JPanel gridPane = new JPanel(new GridBagLayout());
383            GridBagConstraints c = new GridBagConstraints();
384            c.fill = GridBagConstraints.HORIZONTAL;
385
386            c.weightx = 1;
387            c.weighty = 1;
388            c.gridx = 0;
389            c.gridy = 0;
390
391            setup = new JRadioButton("SetupMode");
392            setup.setSelected(false);
393            setup.setToolTipText(Bundle.getMessage("SetupModeTt"));
394            gridPane.add(setup, c);
395            c.gridy++;
396            
397            add(gridPane);
398        }
399    }
400    
401    private final static Logger log = LoggerFactory.getLogger(PiSprog3EditNVPane.class);
402
403}