001package jmri.jmrit.sensorgroup; 002 003import java.awt.BorderLayout; 004import java.util.ArrayList; 005import java.util.List; 006 007import javax.swing.*; 008import javax.swing.table.TableColumn; 009import javax.swing.table.TableColumnModel; 010import javax.swing.table.TableRowSorter; 011 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 static final String NAME_PREFIX = "SENSOR GROUP:"; // should be upper case 033 private static final String NAME_DIVIDER = ":"; 034 public static final String logixSysName; 035 public static final String logixUserName = "System Logix"; 036 public static final String ConditionalSystemPrefix; 037 private static final String CONDITIONAL_USER_PREFIX = "Sensor Group "; 038 private int rowHeight; 039 040 static { 041 String logixPrefix = InstanceManager.getDefault(LogixManager.class).getSystemNamePrefix(); 042 logixSysName = logixPrefix + ":SYS"; 043 ConditionalSystemPrefix = logixSysName + "_SGC_"; 044 } 045 046 private SensorTableModel _sensorModel; 047 private JScrollPane _sensorScrollPane; 048 private JTextField _nameField; 049 private 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( e -> viewPressed()); 113 p4.add(viewButton); 114 JButton addButton = new JButton(Bundle.getMessage("ButtonMakeGroup")); 115 addButton.addActionListener( e -> addPressed()); 116 p4.add(addButton); 117 JButton undoButton = new JButton(Bundle.getMessage("ButtonUndoGroup")); 118 undoButton.addActionListener( e -> undoGroupPressed()); 119 p4.add(undoButton); 120 getContentPane().add(p4); 121 122 JPanel p5 = new JPanel(); 123 124 DefaultListModel<String> groupModel = new DefaultListModel<>(); 125 // Look for Sensor group in Route table 126 RouteManager rm = InstanceManager.getDefault(jmri.RouteManager.class); 127 // List<String> routeList = rm.getSystemNameList(); 128 int i = 0; 129 for (NamedBean obj : rm.getNamedBeanSet()) { 130 String name = obj.getSystemName(); 131 if (name.startsWith(NAME_PREFIX)) { 132 name = name.substring(NAME_PREFIX.length()); 133 String group = name.substring(0, name.indexOf(NAME_DIVIDER)); 134 String prefix = NAME_PREFIX + group + NAME_DIVIDER; 135 do { 136 i++; 137 if (i >= rm.getNamedBeanSet().size()) { 138 break; 139 } 140 name = obj.getSystemName(); 141 } while (name.startsWith(prefix)); 142 groupModel.addElement(group); 143 } 144 i++; 145 } 146 // Look for Sensor group in Logix 147 Logix logix = getSystemLogix(); 148 for (i = 0; i < logix.getNumConditionals(); i++) { 149 String name = logix.getConditionalByNumberOrder(i); 150 if ( name == null ) { 151 log.error("Could not locate Conditional {} for Logix {}", i, logix); 152 continue; 153 } 154 Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(name); 155 String uname = null; 156 if (c !=null) { 157 uname = c.getUserName(); 158 } 159 if (uname != null) { 160 groupModel.addElement(uname.substring(CONDITIONAL_USER_PREFIX.length())); 161 } 162 } 163 _sensorGroupList = new JList<>(groupModel); 164 _sensorGroupList.setPrototypeCellValue(CONDITIONAL_USER_PREFIX + "XXXXXXXXXX"); 165 _sensorGroupList.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); 166 _sensorGroupList.setVisibleRowCount(5); 167 JScrollPane scrollPane = new JScrollPane(_sensorGroupList); 168 p5.add(scrollPane); 169 p5.add(Box.createHorizontalStrut(10)); 170 JButton doneButton = new JButton(Bundle.getMessage("ButtonDone")); 171 doneButton.addActionListener( e -> donePressed()); 172 p5.add(doneButton); 173 getContentPane().add(p5); 174 175 // pack to cause display 176 pack(); 177 } 178 179 private void addPressed() { 180 deleteGroup(false); 181 String group = _nameField.getText(); 182 if (group == null || group.length() == 0) { 183 JmriJOptionPane.showMessageDialog(this, 184 Bundle.getMessage("MessageError1"), Bundle.getMessage("ErrorTitle"), 185 JmriJOptionPane.ERROR_MESSAGE); 186 return; 187 } 188 Logix logix = getSystemLogix(); 189 logix.deActivateLogix(); 190 String cSystemName = ConditionalSystemPrefix + group; 191 String cUserName = CONDITIONAL_USER_PREFIX + group; 192 // add new Conditional 193 ArrayList<ConditionalVariable> variableList = new ArrayList<>(); 194 ArrayList<ConditionalAction> actionList = new ArrayList<>(); 195 int count = 0; 196 for (int i = 0; i < _sensorModel.getRowCount(); i++) { 197 if (Boolean.TRUE.equals( _sensorModel.getValueAt(i, BeanTableModel.INCLUDE_COLUMN))) { 198 String sensor = (String) _sensorModel.getValueAt(i, BeanTableModel.UNAME_COLUMN); 199 if (sensor == null || sensor.length() == 0) { 200 sensor = (String) _sensorModel.getValueAt(i, BeanTableModel.SNAME_COLUMN); 201 } 202 variableList.add(new ConditionalVariable(false, Conditional.Operator.OR, 203 Conditional.Type.SENSOR_ACTIVE, sensor, true)); 204 actionList.add(new DefaultConditionalAction(Conditional.ACTION_OPTION_ON_CHANGE_TO_TRUE, 205 Conditional.Action.SET_SENSOR, sensor, 206 Sensor.INACTIVE, "")); 207 count++; 208 } 209 } 210 if (count < 2) { 211 JmriJOptionPane.showMessageDialog(this, 212 Bundle.getMessage("MessageError2"), Bundle.getMessage("ErrorTitle"), 213 JmriJOptionPane.ERROR_MESSAGE); 214 } 215 Conditional c = new SensorGroupConditional(cSystemName, cUserName); 216 c.setStateVariables(variableList); 217 c.setLogicType(Conditional.AntecedentOperator.ALL_OR, ""); 218 c.setAction(actionList); 219 logix.addConditional(cSystemName, 0); // Update the Logix Conditional names list 220 logix.addConditional(cSystemName, c); // Update the Logix Conditional hash map 221 logix.setEnabled(true); 222 logix.activateLogix(); 223 ((DefaultListModel<String>) _sensorGroupList.getModel()).addElement( 224 cUserName.substring(CONDITIONAL_USER_PREFIX.length())); 225 clear(); 226 } 227 228 private void viewPressed() { 229 for (int i = 0; i < _sensorModel.getRowCount(); i++) { 230 _sensorModel.setValueAt(Boolean.FALSE, i, BeanTableModel.INCLUDE_COLUMN); 231 } 232 // look for name in List panel 233 String group = _sensorGroupList.getSelectedValue(); 234 if (group == null) { // not there, look in text field 235 group = _nameField.getText(); 236 } 237 _nameField.setText(group); 238 // Look for Sensor group in Route table 239 RouteManager rm = InstanceManager.getDefault(jmri.RouteManager.class); 240 String prefix = (NAME_PREFIX + group + NAME_DIVIDER); 241 boolean isRoute = false; 242 int setRow = 0; 243 for (Route r : rm.getNamedBeanSet()) { 244 String name = r.getSystemName(); 245 if (name.startsWith(prefix)) { 246 isRoute = true; 247 String sensor = name.substring(prefix.length()); 248 // find and check that sensor 249 for (int j = _sensorModel.getRowCount() - 1; j >= 0; j--) { 250 if (_sensorModel.getValueAt(j, BeanTableModel.SNAME_COLUMN).equals(sensor)) { 251 _sensorModel.setValueAt(Boolean.TRUE, j, BeanTableModel.INCLUDE_COLUMN); 252 setRow = j; 253 } 254 } 255 } 256 } 257 258 // look for Sensor group in SYSTEM Logix 259 if (!isRoute) { 260 Logix logix = getSystemLogix(); 261 String cSystemName = (ConditionalSystemPrefix + group); 262 String cUserName = CONDITIONAL_USER_PREFIX + group; 263 for (int i = 0; i < logix.getNumConditionals(); i++) { 264 String name = logix.getConditionalByNumberOrder(i); 265 if (cSystemName.equalsIgnoreCase(name) || cUserName.equals(name)) { // Ignore case for compatibility 266 Conditional c = InstanceManager.getDefault(jmri.ConditionalManager.class).getBySystemName(name); 267 if (c == null) { 268 log.error("Conditional \"{}\" expected but NOT found in Logix {}", name, logix.getSystemName()); 269 } else { 270 List<ConditionalVariable> variableList = c.getCopyOfStateVariables(); 271 for (int k = 0; k < variableList.size(); k++) { 272 String sensor = variableList.get(k).getName(); 273 if (sensor != null) { 274 for (int j = _sensorModel.getRowCount() - 1; j >= 0; j--) { 275 if (sensor.equals(_sensorModel.getValueAt(j, BeanTableModel.UNAME_COLUMN)) 276 || sensor.equals(_sensorModel.getValueAt(j, BeanTableModel.SNAME_COLUMN))) { 277 _sensorModel.setValueAt(Boolean.TRUE, j, BeanTableModel.INCLUDE_COLUMN); 278 setRow = j; 279 } 280 } 281 } 282 } 283 } 284 } 285 } 286 } 287 _sensorModel.fireTableDataChanged(); 288 setRow -= 9; 289 if (setRow < 0) { 290 setRow = 0; 291 } 292 _sensorScrollPane.getVerticalScrollBar().setValue(setRow * rowHeight); 293 } 294 295 private Logix getSystemLogix() { 296 Logix logix = InstanceManager.getDefault(LogixManager.class).getBySystemName(logixSysName); 297 if (logix == null) { 298 logix = InstanceManager.getDefault(LogixManager.class).createNewLogix(logixSysName, logixUserName); 299 } 300 return logix; 301 } 302 303 private void clear() { 304 _sensorGroupList.getSelectionModel().clearSelection(); 305 _nameField.setText(""); 306 for (int i = 0; i < _sensorModel.getRowCount(); i++) { 307 _sensorModel.setValueAt(Boolean.FALSE, i, BeanTableModel.INCLUDE_COLUMN); 308 } 309 _sensorModel.fireTableDataChanged(); 310 } 311 312 private void donePressed() { 313 _sensorModel.dispose(); 314 dispose(); 315 } 316 317 private void deleteGroup(boolean showMsg) { 318 String group = _nameField.getText(); 319 320 if (group == null || group.isEmpty()) { 321 if (showMsg) { 322 JmriJOptionPane.showMessageDialog(this, 323 Bundle.getMessage("MessageError3"), Bundle.getMessage("ErrorTitle"), 324 JmriJOptionPane.ERROR_MESSAGE); 325 } 326 return; 327 } 328 329 // remove the old routes 330 String prefix = (NAME_PREFIX + group + NAME_DIVIDER); 331 RouteManager rm = InstanceManager.getDefault(RouteManager.class); 332 for (Route r : rm.getNamedBeanSet()) { 333 String name = r.getSystemName(); 334 if (name.startsWith(prefix)) { 335 // OK, kill this one 336 r.deActivateRoute(); 337 rm.deleteRoute(r); 338 } 339 } 340 341 // remove Logix IX:SYS conditional 342 // Due to a change at 4.17.2, the system names are no longer forced to upper case. 343 // Older SYS conditionals will have an upper case name, so the user name is an alternate name. 344 Logix logix = getSystemLogix(); 345 String cSystemName = ConditionalSystemPrefix + group; 346 String cUserName = CONDITIONAL_USER_PREFIX + group; 347 348 for (int i = 0; i < logix.getNumConditionals(); i++) { 349 String cdlName = logix.getConditionalByNumberOrder(i); 350 Conditional cdl = logix.getConditional(cdlName); 351 if (cdl != null) { 352 if (cSystemName.equals(cdl.getSystemName()) || cUserName.equals(cdl.getUserName())) { 353 String[] msgs = logix.deleteConditional(cdlName); 354 if (msgs == null) { 355 break; 356 } 357 358 // Conditional delete failed or was vetoed 359 if (showMsg) { 360 JmriJOptionPane.showMessageDialog(this, 361 Bundle.getMessage("MessageError41") + " " + msgs[0] + " (" + msgs[1] + ") " 362 + Bundle.getMessage("MessageError42") + " " + msgs[2] + " (" + msgs[3] + "), " 363 + Bundle.getMessage("MessageError43") + " " + msgs[4] + " (" + msgs[5] + "). " 364 + Bundle.getMessage("MessageError44"), 365 Bundle.getMessage("ErrorTitle"), JmriJOptionPane.ERROR_MESSAGE); 366 } 367 return; 368 } 369 } else { 370 log.error("Conditional \"{}\" expected but NOT found in Logix {}", cdlName, logix.getSystemName()); 371 return; 372 } 373 } 374 375 // remove from list 376 DefaultListModel<String> model = (DefaultListModel<String>) _sensorGroupList.getModel(); 377 model.removeElement(group); 378 } 379 380 private void undoGroupPressed() { 381 deleteGroup(true); 382 clear(); 383 } 384 385 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(SensorGroupFrame.class); 386}