001package jmri.jmrit.sensorgroup;
002
003import java.awt.BorderLayout;
004import java.awt.event.ActionEvent;
005import java.awt.event.ActionListener;
006import java.util.ArrayList;
007import java.util.List;
008import javax.swing.*;
009import javax.swing.table.TableColumn;
010import javax.swing.table.TableColumnModel;
011import javax.swing.table.TableRowSorter;
012import jmri.*;
013import jmri.implementation.DefaultConditionalAction;
014import jmri.implementation.SensorGroupConditional;
015import jmri.swing.RowSorterUtil;
016import jmri.util.swing.JmriJOptionPane;
017
018/**
019 * User interface for creating and editing sensor groups.
020 * <p>
021 * Sensor groups are implemented by (groups) of Routes, not by any other object.
022 *
023 * @author Bob Jacobsen Copyright (C) 2007
024 * @author Pete Cressman Copyright (C) 2009
025 */
026public class SensorGroupFrame extends jmri.util.JmriJFrame {
027
028    public SensorGroupFrame() {
029        super();
030    }
031
032    private final static String namePrefix = "SENSOR GROUP:";  // should be upper case
033    private final static String nameDivider = ":";
034    public final static String logixSysName;
035    public final static String logixUserName = "System Logix";
036    public final static String ConditionalSystemPrefix;
037    private final static String ConditionalUserPrefix = "Sensor Group ";
038    private int rowHeight;
039
040    static {
041        String logixPrefix = InstanceManager.getDefault(jmri.LogixManager.class).getSystemNamePrefix();
042        logixSysName = logixPrefix + ":SYS";
043        ConditionalSystemPrefix = logixSysName + "_SGC_";
044    }
045
046    SensorTableModel _sensorModel;
047    JScrollPane _sensorScrollPane;
048    JTextField _nameField;
049    JList<String> _sensorGroupList;
050
051    @Override
052    public void initComponents() {
053        addHelpMenu("package.jmri.jmrit.sensorgroup.SensorGroupFrame", true);
054
055        setTitle(Bundle.getMessage("Title"));
056        getContentPane().setLayout(new BoxLayout(getContentPane(), BoxLayout.Y_AXIS));
057
058        // add the sensor table
059        JPanel p2xs = new JPanel();
060
061        JPanel p21s = new JPanel();
062        p21s.setLayout(new BoxLayout(p21s, BoxLayout.Y_AXIS));
063        p21s.add(new JLabel(Bundle.getMessage("SensorTableLabel1")));
064        p21s.add(new JLabel(Bundle.getMessage("SensorTableLabel2")));
065        p21s.add(new JLabel(Bundle.getMessage("SensorTableLabel3")));
066        p21s.add(new JLabel(Bundle.getMessage("SensorTableLabel4")));
067        p2xs.add(p21s);
068        _sensorModel = new SensorTableModel();
069        JTable sensorTable = new JTable(_sensorModel);
070
071        TableRowSorter<SensorTableModel> sorter = new TableRowSorter<>(_sensorModel);
072        sorter.setComparator(SensorTableModel.SNAME_COLUMN, new jmri.util.AlphanumComparator());
073        sorter.setComparator(SensorTableModel.UNAME_COLUMN, new jmri.util.AlphanumComparator());
074        RowSorterUtil.setSortOrder(sorter, SensorTableModel.SNAME_COLUMN, SortOrder.ASCENDING);
075        sensorTable.setRowSorter(sorter);
076
077        sensorTable.setRowSelectionAllowed(false);
078        sensorTable.setPreferredScrollableViewportSize(new java.awt.Dimension(450, 200));
079        TableColumnModel sensorColumnModel = sensorTable.getColumnModel();
080        TableColumn includeColumnS = sensorColumnModel.
081                getColumn(SensorTableModel.INCLUDE_COLUMN);
082        includeColumnS.setResizable(false);
083        includeColumnS.setMinWidth(50);
084        includeColumnS.setMaxWidth(60);
085        TableColumn sNameColumnS = sensorColumnModel.
086                getColumn(SensorTableModel.SNAME_COLUMN);
087        sNameColumnS.setResizable(true);
088        sNameColumnS.setMinWidth(75);
089        sNameColumnS.setPreferredWidth(95);
090        TableColumn uNameColumnS = sensorColumnModel.
091                getColumn(SensorTableModel.UNAME_COLUMN);
092        uNameColumnS.setResizable(true);
093        uNameColumnS.setMinWidth(210);
094        uNameColumnS.setPreferredWidth(260);
095
096        rowHeight = sensorTable.getRowHeight();
097        _sensorScrollPane = new JScrollPane(sensorTable);
098        p2xs.add(_sensorScrollPane, BorderLayout.CENTER);
099        getContentPane().add(p2xs);
100        p2xs.setVisible(true);
101
102        // add name field
103        JPanel p3 = new JPanel();
104        p3.add(new JLabel(Bundle.getMessage("GroupName")));
105        _nameField = new JTextField(20);
106        p3.add(_nameField);
107        getContentPane().add(p3);
108
109        // button
110        JPanel p4 = new JPanel();
111        JButton viewButton = new JButton(Bundle.getMessage("ButtonViewGroup"));
112        viewButton.addActionListener(new ActionListener() {
113            @Override
114            public void actionPerformed(ActionEvent e) {
115                viewPressed();
116            }
117        });
118        p4.add(viewButton);
119        JButton addButton = new JButton(Bundle.getMessage("ButtonMakeGroup"));
120        addButton.addActionListener(new ActionListener() {
121            @Override
122            public void actionPerformed(ActionEvent e) {
123                addPressed();
124            }
125        });
126        p4.add(addButton);
127        JButton undoButton = new JButton(Bundle.getMessage("ButtonUndoGroup"));
128        undoButton.addActionListener(new ActionListener() {
129            @Override
130            public void actionPerformed(ActionEvent e) {
131                undoGroupPressed();
132            }
133        });
134        p4.add(undoButton);
135        getContentPane().add(p4);
136
137        JPanel p5 = new JPanel();
138
139        DefaultListModel<String> groupModel = new DefaultListModel<>();
140        // Look for Sensor group in Route table
141        RouteManager rm = InstanceManager.getDefault(jmri.RouteManager.class);
142        // List<String> routeList = rm.getSystemNameList();
143        int i = 0;
144        for (NamedBean obj : rm.getNamedBeanSet()) {
145            String name = obj.getSystemName();
146            if (name.startsWith(namePrefix)) {
147                name = name.substring(namePrefix.length());
148                String group = name.substring(0, name.indexOf(nameDivider));
149                String prefix = namePrefix + group + nameDivider;
150                do {
151                    i++;
152                    if (i >= rm.getNamedBeanSet().size()) {
153                        break;
154                    }
155                    name = obj.getSystemName();
156                } while (name.startsWith(prefix));
157                groupModel.addElement(group);
158            }
159            i++;
160        }
161        // Look for Sensor group in Logix
162        Logix logix = getSystemLogix();
163        for (i = 0; i < logix.getNumConditionals(); i++) {
164            String name = logix.getConditionalByNumberOrder(i);
165            Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(name);
166            String uname = null;
167            if (c !=null) {
168                uname = c.getUserName();
169            }
170            if (uname != null) {
171                groupModel.addElement(uname.substring(ConditionalUserPrefix.length()));
172            }
173        }
174        _sensorGroupList = new JList<>(groupModel);
175        _sensorGroupList.setPrototypeCellValue(ConditionalUserPrefix + "XXXXXXXXXX");
176        _sensorGroupList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION);
177        _sensorGroupList.setVisibleRowCount(5);
178        JScrollPane scrollPane = new JScrollPane(_sensorGroupList);
179        p5.add(scrollPane);
180        p5.add(Box.createHorizontalStrut(10));
181        JButton doneButton = new JButton(Bundle.getMessage("ButtonDone"));
182        doneButton.addActionListener(new ActionListener() {
183            @Override
184            public void actionPerformed(ActionEvent e) {
185                donePressed(e);
186            }
187        });
188        p5.add(doneButton);
189        getContentPane().add(p5);
190
191        // pack to cause display
192        pack();
193    }
194
195    void addPressed() {
196        deleteGroup(false);
197        String group = _nameField.getText();
198        if (group == null || group.length() == 0) {
199            JmriJOptionPane.showMessageDialog(this,
200                    Bundle.getMessage("MessageError1"), Bundle.getMessage("ErrorTitle"),
201                    JmriJOptionPane.ERROR_MESSAGE);
202            return;
203        }
204        Logix logix = getSystemLogix();
205        logix.deActivateLogix();
206        String cSystemName = ConditionalSystemPrefix + group;
207        String cUserName = ConditionalUserPrefix + group;
208        // add new Conditional
209        ArrayList<ConditionalVariable> variableList = new ArrayList<>();
210        ArrayList<ConditionalAction> actionList = new ArrayList<>();
211        int count = 0;
212        for (int i = 0; i < _sensorModel.getRowCount(); i++) {
213            if ((Boolean) _sensorModel.getValueAt(i, BeanTableModel.INCLUDE_COLUMN)) {
214                String sensor = (String) _sensorModel.getValueAt(i, BeanTableModel.UNAME_COLUMN);
215                if (sensor == null || sensor.length() == 0) {
216                    sensor = (String) _sensorModel.getValueAt(i, BeanTableModel.SNAME_COLUMN);
217                }
218                variableList.add(new ConditionalVariable(false, Conditional.Operator.OR,
219                        Conditional.Type.SENSOR_ACTIVE, sensor, true));
220                actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE,
221                        Conditional.Action.SET_SENSOR, sensor,
222                        Sensor.INACTIVE, ""));
223                count++;
224            }
225        }
226        if (count < 2) {
227            JmriJOptionPane.showMessageDialog(this,
228                    Bundle.getMessage("MessageError2"), Bundle.getMessage("ErrorTitle"),
229                    JmriJOptionPane.ERROR_MESSAGE);
230        }
231        Conditional c = new SensorGroupConditional(cSystemName, cUserName);
232        c.setStateVariables(variableList);
233        c.setLogicType(Conditional.AntecedentOperator.ALL_OR, "");
234        c.setAction(actionList);
235        logix.addConditional(cSystemName, 0);       // Update the Logix Conditional names list
236        logix.addConditional(cSystemName, c);       // Update the Logix Conditional hash map
237        logix.setEnabled(true);
238        logix.activateLogix();
239        ((DefaultListModel<String>) _sensorGroupList.getModel()).addElement(
240                cUserName.substring(ConditionalUserPrefix.length()));
241        clear();
242    }
243
244    void viewPressed() {
245        for (int i = 0; i < _sensorModel.getRowCount(); i++) {
246            _sensorModel.setValueAt(Boolean.FALSE, i, BeanTableModel.INCLUDE_COLUMN);
247        }
248        // look for name in List panel
249        String group = _sensorGroupList.getSelectedValue();
250        if (group == null) { // not there, look in text field
251            group = _nameField.getText();
252        }
253        _nameField.setText(group);
254        // Look for Sensor group in Route table
255        RouteManager rm = InstanceManager.getDefault(jmri.RouteManager.class);
256        String prefix = (namePrefix + group + nameDivider);
257        boolean isRoute = false;
258        int setRow = 0;
259        for (Route r : rm.getNamedBeanSet()) {
260            String name = r.getSystemName();
261            if (name.startsWith(prefix)) {
262                isRoute = true;
263                String sensor = name.substring(prefix.length());
264                // find and check that sensor
265                for (int j = _sensorModel.getRowCount() - 1; j >= 0; j--) {
266                    if (_sensorModel.getValueAt(j, BeanTableModel.SNAME_COLUMN).equals(sensor)) {
267                        _sensorModel.setValueAt(Boolean.TRUE, j, BeanTableModel.INCLUDE_COLUMN);
268                        setRow = j;
269                    }
270                }
271            }
272        }
273
274        // look for  Sensor group in SYSTEM Logix
275        if (!isRoute) {
276            Logix logix = getSystemLogix();
277            String cSystemName = (ConditionalSystemPrefix + group);
278            String cUserName = ConditionalUserPrefix + group;
279            for (int i = 0; i < logix.getNumConditionals(); i++) {
280                String name = logix.getConditionalByNumberOrder(i);
281                if (cSystemName.equalsIgnoreCase(name) || cUserName.equals(name)) {     // Ignore case for compatibility
282                    Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(name);
283                    if (c == null) {
284                        log.error("Conditional \"{}\" expected but NOT found in Logix {}", name, logix.getSystemName());
285                    } else {
286                        List<ConditionalVariable> variableList = c.getCopyOfStateVariables();
287                        for (int k = 0; k < variableList.size(); k++) {
288                            String sensor = variableList.get(k).getName();
289                            if (sensor != null) {
290                                for (int j = _sensorModel.getRowCount() - 1; j >= 0; j--) {
291                                    if (sensor.equals(_sensorModel.getValueAt(j, BeanTableModel.UNAME_COLUMN))
292                                            || sensor.equals(_sensorModel.getValueAt(j, BeanTableModel.SNAME_COLUMN))) {
293                                        _sensorModel.setValueAt(Boolean.TRUE, j, BeanTableModel.INCLUDE_COLUMN);
294                                        setRow = j;
295                                    }
296                                }
297                            }
298                        }
299                    }
300                }
301            }
302        }
303        _sensorModel.fireTableDataChanged();
304        setRow -= 9;
305        if (setRow < 0) {
306            setRow = 0;
307        }
308        _sensorScrollPane.getVerticalScrollBar().setValue(setRow * rowHeight);
309    }
310
311    Logix getSystemLogix() {
312        Logix logix = InstanceManager.getDefault(jmri.LogixManager.class).getBySystemName(logixSysName);
313        if (logix == null) {
314            logix = InstanceManager.getDefault(jmri.LogixManager.class).createNewLogix(logixSysName, logixUserName);
315        }
316        return logix;
317    }
318
319    void clear() {
320        _sensorGroupList.getSelectionModel().clearSelection();
321        _nameField.setText("");
322        for (int i = 0; i < _sensorModel.getRowCount(); i++) {
323            _sensorModel.setValueAt(Boolean.FALSE, i, BeanTableModel.INCLUDE_COLUMN);
324        }
325        _sensorModel.fireTableDataChanged();
326    }
327
328    void donePressed(ActionEvent e) {
329        _sensorModel.dispose();
330        dispose();
331    }
332
333    void deleteGroup(boolean showMsg) {
334        String group = _nameField.getText();
335
336        if (group == null || group.isEmpty()) {
337            if (showMsg) {
338                JmriJOptionPane.showMessageDialog(this,
339                        Bundle.getMessage("MessageError3"), Bundle.getMessage("ErrorTitle"),
340                        JmriJOptionPane.ERROR_MESSAGE);
341            }
342            return;
343        }
344
345        // remove the old routes
346        String prefix = (namePrefix + group + nameDivider);
347        RouteManager rm = InstanceManager.getDefault(jmri.RouteManager.class);
348        for (Route r : rm.getNamedBeanSet()) {
349            String name = r.getSystemName();
350            if (name.startsWith(prefix)) {
351                // OK, kill this one
352                r.deActivateRoute();
353                rm.deleteRoute(r);
354            }
355        }
356
357        // remove Logix IX:SYS conditional
358        // Due to a change at 4.17.2, the system names are no longer forced to upper case.
359        // Older SYS conditionals will have an upper case name, so the user name is an alternate name.
360        Logix logix = getSystemLogix();
361        String cSystemName = ConditionalSystemPrefix + group;
362        String cUserName = ConditionalUserPrefix + group;
363
364        for (int i = 0; i < logix.getNumConditionals(); i++) {
365            String cdlName = logix.getConditionalByNumberOrder(i);
366            Conditional cdl = logix.getConditional(cdlName);
367            if (cdl != null) {
368                if (cSystemName.equals(cdl.getSystemName()) || cUserName.equals(cdl.getUserName())) {
369                    String[] msgs = logix.deleteConditional(cdlName);
370                    if (msgs == null) {
371                        break;
372                    }
373
374                    // Conditional delete failed or was vetoed
375                    if (showMsg) {
376                        JmriJOptionPane.showMessageDialog(this,
377                                Bundle.getMessage("MessageError41") + " " + msgs[0] + " (" + msgs[1] + ") "
378                                + Bundle.getMessage("MessageError42") + " " + msgs[2] + " (" + msgs[3] + "), "
379                                + Bundle.getMessage("MessageError43") + " " + msgs[4] + " (" + msgs[5] + "). "
380                                + Bundle.getMessage("MessageError44"),
381                                Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE);
382                    }
383                    return;
384                }
385            } else {
386                log.error("Conditional \"{}\" expected but NOT found in Logix {}", cdlName, logix.getSystemName());
387                return;
388            }
389        }
390
391        // remove from list
392        DefaultListModel<String> model = (DefaultListModel<String>) _sensorGroupList.getModel();
393        model.removeElement(group);
394    }
395
396    void undoGroupPressed() {
397        deleteGroup(true);
398        clear();
399    }
400
401    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SensorGroupFrame.class);
402}