001package jmri.jmrix.can.cbus.swing.nodeconfig; 002 003import java.awt.BorderLayout; 004import java.awt.Color; 005import java.awt.GridLayout; 006import java.awt.event.ActionListener; 007import javax.swing.*; 008import javax.swing.event.ChangeEvent; 009 010import jmri.jmrix.can.cbus.node.CbusNode; 011import jmri.jmrix.can.cbus.node.CbusNodeTimerManager; 012import jmri.util.ThreadingUtil; 013import jmri.util.swing.JmriJOptionPane; 014import jmri.util.swing.JSpinnerUtil; 015 016/** 017 * 018 * @author Steve Young Copyright (C) 2019 019 */ 020public class CbusNodeSetupPane extends CbusNodeConfigTab { 021 022 private transient ActionListener setNameListener; 023 private transient ActionListener removeListener; 024 private transient ActionListener setCanIdListener; 025 private transient ActionListener selfEnumerateListener; 026 private transient ActionListener clearAllEventsListener; 027 private jmri.util.swing.BusyDialog busy_dialog; 028 029 private JTextField textFieldName; 030 031 /** 032 * Create a new instance of CbusNodeSetupPane. 033 * @param main the main NodeConfigToolPane this is a pane of. 034 */ 035 protected CbusNodeSetupPane( NodeConfigToolPane main ) { 036 super(main); 037 getInitPane(); 038 } 039 040 /** 041 * {@inheritDoc} 042 */ 043 @Override 044 public String getTitle(){ 045 return "Node Setup"; 046 } 047 048 /** 049 * {@inheritDoc} 050 */ 051 @Override 052 public void changedNode(CbusNode newNode){ 053 054 textFieldName.setText( nodeOfInterest.getUserName() ); 055 validate(); 056 repaint(); 057 058 } 059 060 private void getInitPane() { 061 062 JPanel evPane = new JPanel(); 063 evPane.setLayout(new BoxLayout(evPane, BoxLayout.Y_AXIS)); 064 065 initListeners(); 066 067 JPanel nodeEventsPanel = new JPanel(); 068 nodeEventsPanel.setBorder(BorderFactory.createTitledBorder( 069 BorderFactory.createEtchedBorder(), Bundle.getMessage("EventCol"))); 070 JButton clearAllEventsButton = new JButton("Clear All Events"); 071 clearAllEventsButton.addActionListener(clearAllEventsListener); 072 nodeEventsPanel.add(clearAllEventsButton); 073 074 evPane.add(getNamePanel()); 075 evPane.add(getCanIdPanel()); 076 evPane.add(nodeEventsPanel); 077 evPane.add(getRemovePanel()); 078 079 JScrollPane eventScroll = new JScrollPane(evPane); 080 081 add(eventScroll, BorderLayout.CENTER); 082 083 } 084 085 private JPanel getNamePanel() { 086 087 JPanel namePanel = new JPanel(); 088 namePanel.setBorder(BorderFactory.createTitledBorder( 089 BorderFactory.createEtchedBorder(), ("JMRI Node User Name" ) ) ); 090 JButton setNameButton = new JButton("Set Module User Name"); 091 textFieldName = new JTextField(20); 092 093 namePanel.add(textFieldName); 094 namePanel.add(setNameButton); 095 setNameButton.addActionListener(setNameListener); 096 return namePanel; 097 } 098 099 private JPanel getCanIdPanel() { 100 101 JPanel canIdPanel = new JPanel(); 102 canIdPanel.setBorder(BorderFactory.createTitledBorder( 103 BorderFactory.createEtchedBorder(), ( "CAN ID"))); 104 JButton selfCanEnumerateButton = new JButton("CAN ID Self Enumeration"); 105 selfCanEnumerateButton.addActionListener(selfEnumerateListener); 106 JButton setCanIdButton = new JButton("Force set CAN ID"); 107 setCanIdButton.addActionListener(setCanIdListener); 108 canIdPanel.add(selfCanEnumerateButton); 109 canIdPanel.add(setCanIdButton); 110 111 return canIdPanel; 112 } 113 114 private JPanel getRemovePanel() { 115 JPanel removePanel = new JPanel(); 116 removePanel.setBorder(BorderFactory.createTitledBorder( 117 BorderFactory.createEtchedBorder(), ("Node Manager"))); 118 JButton removeNodeButton = new JButton("Remove from Table"); 119 removePanel.add(removeNodeButton); 120 removeNodeButton.addActionListener(removeListener); 121 return removePanel; 122 } 123 124 private void initListeners() { 125 126 setNameListener = ae -> { 127 nodeOfInterest.setUserName(textFieldName.getText()); 128 changedNode(nodeOfInterest); 129 }; 130 131 removeListener = ae -> { 132 JCheckBox checkbox = new JCheckBox(("Remove node xml File")); 133 int oldRow = Math.max(0, getNodeRow()-1); 134 int option = JmriJOptionPane.showConfirmDialog(this, 135 new Object[]{("Remove Node from Manager?"), checkbox}, 136 "Please Confirm", 137 JmriJOptionPane.OK_CANCEL_OPTION); 138 if ( option == JmriJOptionPane.OK_OPTION ) { 139 getMainPane().getNodeModel(). 140 removeRow( getMainPane().getNodeModel().getNodeRowFromNodeNum(nodeOfInterest.getNodeNumber()) 141 ,checkbox.isSelected() ); 142 if (getMainPane().nodeTable.getRowCount() > 0 ) { 143 getMainPane().nodeTable.getSelectionModel().setSelectionInterval(oldRow,oldRow); 144 getMainPane().tabbedPane.setSelectedIndex(0); 145 } 146 } 147 }; 148 149 selfEnumerateListener = ae -> { 150 // start busy 151 busy_dialog = new jmri.util.swing.BusyDialog(null, "CAN ID", false); 152 busy_dialog.start(); 153 // CbusNode will pick the outgoing message up, start timer and show dialogue on error / timeout 154 nodeOfInterest.send.eNUM(nodeOfInterest.getNodeNumber()); 155 // cancel the busy 156 ThreadingUtil.runOnGUIDelayed(() -> { 157 changedNode(nodeOfInterest); // refresh pane with new CAN ID 158 busy_dialog.finish(); 159 busy_dialog=null; 160 },CbusNodeTimerManager.SINGLE_MESSAGE_TIMEOUT_TIME ); 161 }; 162 163 setCanIdListener = ae -> { 164 newCanIdDialogue(); 165 }; 166 167 clearAllEventsListener = ae -> { 168 int option = JmriJOptionPane.showConfirmDialog(this, 169 "Delete All Events from Node?", 170 "Please Confirm", 171 JmriJOptionPane.OK_CANCEL_OPTION); 172 if ( option == JmriJOptionPane.OK_OPTION ) { 173 174 // check for existing nodes in learn mode 175 if ( getMainPane().getNodeModel().getAnyNodeInLearnMode() > -1 ) { 176 log.warn("Cancelling action, node {} is already in learn mode",getMainPane().getNodeModel().getAnyNodeInLearnMode()); 177 return; 178 } 179 180 // start busy 181 busy_dialog = new jmri.util.swing.BusyDialog(null, "Clear All Events", false); 182 busy_dialog.start(); 183 184 // node enter learn mode 185 nodeOfInterest.send.nodeEnterLearnEvMode( nodeOfInterest.getNodeNumber() ); // no response expected but we add a mini delay for other traffic 186 187 ThreadingUtil.runOnLayoutDelayed( () -> { 188 nodeOfInterest.send.nNCLR(nodeOfInterest.getNodeNumber());// no response expected 189 }, 150 ); 190 191 ThreadingUtil.runOnLayoutDelayed(() -> { 192 // node exit learn mode 193 nodeOfInterest.send.nodeExitLearnEvMode( nodeOfInterest.getNodeNumber() ); // no response expected 194 }, CbusNodeTimerManager.SINGLE_MESSAGE_TIMEOUT_TIME ); 195 196 ThreadingUtil.runOnGUIDelayed(() -> { 197 198 // stop 199 busy_dialog.finish(); 200 busy_dialog=null; 201 202 // query new num events which should be 0 203 // RQEVN 204 nodeOfInterest.send.rQEVN( nodeOfInterest.getNodeNumber() ); 205 206 }, ( CbusNodeTimerManager.SINGLE_MESSAGE_TIMEOUT_TIME + 150 ) ); 207 208 } 209 }; 210 211 212 } 213 214 private boolean CANID_DIALOGUE_OPEN = false; 215 private JFormattedTextField rqfield; 216 private JLabel rqNNspinnerlabel; 217 218 private void newCanIdDialogue() { 219 220 if (CANID_DIALOGUE_OPEN) { 221 return; 222 } 223 224 log.debug("allocating new can id"); 225 226 CANID_DIALOGUE_OPEN=true; 227 228 JPanel rqNNpane = new JPanel(); 229 JPanel bottomrqNNpane = new JPanel(); 230 String spinnerlabel=""; 231 rqNNspinnerlabel = new JLabel(spinnerlabel); 232 233 bottomrqNNpane.setLayout(new GridLayout(2, 1)); 234 rqNNpane.setLayout(new BorderLayout()); 235 236 String popuplabel; 237 popuplabel=("Please Select a new CAN ID"); 238 239 // forces a value between 1-99 240 JSpinner rqnnSpinner = new JSpinner( 241 new SpinnerNumberModel(Math.min(99,(Math.max(1,nodeOfInterest.getNodeCanId()))), 1, 99, 1)); 242 JSpinnerUtil.setCommitsOnValidEdit(rqnnSpinner, true); 243 JComponent rqcomp = rqnnSpinner.getEditor(); 244 rqfield = (JFormattedTextField) rqcomp.getComponent(0); 245 rqfield.setBackground(Color.white); 246 rqnnSpinner.addChangeListener((ChangeEvent e) -> { 247 int newval = (Integer) rqnnSpinner.getValue(); 248 log.debug("new canid selected value {}",newval); 249 updateSpinnerFeedback(newval); 250 }); 251 252 bottomrqNNpane.add(rqNNspinnerlabel); 253 bottomrqNNpane.add(rqnnSpinner); 254 255 rqNNpane.add(bottomrqNNpane, BorderLayout.CENTER); 256 257 // forces a value between 1-99 258 updateSpinnerFeedback( Math.min(99,(Math.max(1,nodeOfInterest.getNodeCanId()))) ); 259 260 int option = JmriJOptionPane.showConfirmDialog(this, 261 rqNNpane, 262 popuplabel, 263 JmriJOptionPane.OK_CANCEL_OPTION); 264 if ( option == JmriJOptionPane.CANCEL_OPTION || option == JmriJOptionPane.CLOSED_OPTION ) { 265 CANID_DIALOGUE_OPEN=false; 266 } else if ( option == JmriJOptionPane.OK_OPTION ) { 267 int newval = (Integer) rqnnSpinner.getValue(); 268 // baseNodeNum = newval; 269 270 busy_dialog = new jmri.util.swing.BusyDialog(null, "CAN ID", false); 271 busy_dialog.start(); 272 // CbusNode will pick the outgoing message up, start timer and show dialogue on error / timeout 273 274 nodeOfInterest.send.cANID(nodeOfInterest.getNodeNumber(), newval); 275 276 // cancel the busy 277 ThreadingUtil.runOnGUIDelayed(() -> { 278 changedNode(nodeOfInterest); // refresh pane with new CAN ID 279 busy_dialog.finish(); 280 busy_dialog=null; 281 CANID_DIALOGUE_OPEN=false; 282 283 },CbusNodeTimerManager.SINGLE_MESSAGE_TIMEOUT_TIME ); 284 } 285 } 286 287 private void updateSpinnerFeedback( int newval ) { 288 if ( getMainPane().getNodeModel().getNodeNameFromCanId(newval).isEmpty() ) { 289 rqfield.setBackground(Color.white); 290 rqNNspinnerlabel.setText(""); 291 } 292 else { 293 rqfield.setBackground(Color.yellow); 294 rqNNspinnerlabel.setText("In Use by " + getMainPane().getNodeModel().getNodeNameFromCanId(newval) ); 295 } 296 } 297 298 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(CbusNodeSetupPane.class); 299 300}