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