001package jmri.jmrix.loconet.locogen; 002 003import java.awt.GridLayout; 004import javax.swing.Box; 005import javax.swing.BoxLayout; 006import javax.swing.JCheckBox; 007import javax.swing.JLabel; 008import javax.swing.JPanel; 009import javax.swing.JSeparator; 010import javax.swing.JTextField; 011import javax.swing.JToggleButton; 012import jmri.jmrix.loconet.LocoNetListener; 013import jmri.jmrix.loconet.LocoNetMessage; 014import jmri.jmrix.loconet.LocoNetSystemConnectionMemo; 015import jmri.util.StringUtil; 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * User interface for sending LocoNet messages to exercise the system. 021 * <p> 022 * When sending a sequence of operations: 023 * <ul> 024 * <li>Send the next message 025 * <li>Wait until you hear the echo, then start a timer 026 * <li>When the timer trips, repeat if buttons still down. 027 * </ul> 028 * @see jmri.jmrix.can.swing.send.CanSendPane 029 * 030 * @author Bob Jacobsen Copyright (C) 2001, 2002, 2010 031 */ 032public class LocoGenPanel extends jmri.jmrix.loconet.swing.LnPanel 033 implements LocoNetListener { 034 035 // member declarations 036 javax.swing.JLabel jLabel1 = new javax.swing.JLabel(); 037 javax.swing.JButton sendButton = new javax.swing.JButton(); 038 javax.swing.JTextField packetTextField = new javax.swing.JTextField(12); 039 040 public LocoGenPanel() { 041 super(); 042 } 043 044 // internal members to hold sequence widgets 045 static final int MAXSEQUENCE = 4; 046 JTextField mPacketField[] = new JTextField[MAXSEQUENCE]; 047 JCheckBox mUseField[] = new JCheckBox[MAXSEQUENCE]; 048 JTextField mDelayField[] = new JTextField[MAXSEQUENCE]; 049 JToggleButton mRunButton = new JToggleButton(Bundle.getMessage("ButtonRun")); 050 051 /** 052 * {@inheritDoc} 053 */ 054 @Override 055 public String getHelpTarget() { 056 return "package.jmri.jmrix.loconet.locogen.LocoGenFrame"; // NOI18N 057 } 058 059 /** 060 * {@inheritDoc} 061 */ 062 @Override 063 public String getTitle() { 064 return getTitle(Bundle.getMessage("MenuItemSendPacket")); 065 } 066 067 /** 068 * {@inheritDoc} 069 */ 070 @Override 071 public void initComponents() { 072 073 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 074 075 // handle single-packet part 076 add(new JLabel(Bundle.getMessage("LabelSendOne"))); 077 { 078 JPanel pane1 = new JPanel(); 079 pane1.setLayout(new BoxLayout(pane1, BoxLayout.Y_AXIS)); 080 081 jLabel1.setText(Bundle.getMessage("MakeLabel", Bundle.getMessage("PacketLabel"))); 082 jLabel1.setVisible(true); 083 084 sendButton.setText(Bundle.getMessage("ButtonSend")); 085 sendButton.setVisible(true); 086 sendButton.setToolTipText(Bundle.getMessage("TooltipSendPacket")); 087 088 packetTextField.setToolTipText(Bundle.getMessage("EnterHexToolTip")); 089 090 pane1.add(jLabel1); 091 pane1.add(packetTextField); 092 pane1.add(sendButton); 093 pane1.add(Box.createVerticalGlue()); 094 095 sendButton.addActionListener(this::sendButtonActionPerformed); 096 097 add(pane1); 098 } 099 100 add(new JSeparator()); 101 102 // Configure the sequence 103 add(new JLabel(Bundle.getMessage("SendSeqTitle"))); 104 JPanel pane2 = new JPanel(); 105 pane2.setLayout(new GridLayout(MAXSEQUENCE + 2, 4)); 106 pane2.add(new JLabel("")); 107 pane2.add(new JLabel(Bundle.getMessage("ButtonSend"))); 108 pane2.add(new JLabel(Bundle.getMessage("PacketLabel"))); 109 pane2.add(new JLabel(Bundle.getMessage("WaitLabel"))); 110 for (int i = 0; i < MAXSEQUENCE; i++) { 111 pane2.add(new JLabel(Integer.toString(i + 1))); 112 mUseField[i] = new JCheckBox(); 113 mPacketField[i] = new JTextField(10); 114 mDelayField[i] = new JTextField(10); 115 pane2.add(mUseField[i]); 116 pane2.add(mPacketField[i]); 117 pane2.add(mDelayField[i]); 118 } 119 pane2.add(mRunButton); // starts a new row in layout 120 add(pane2); 121 122 mRunButton.addActionListener(this::runButtonActionPerformed); 123 } 124 125 /** 126 * {@inheritDoc} 127 */ 128 @Override 129 public void initComponents(LocoNetSystemConnectionMemo memo) { 130 super.initComponents(memo); 131 132 memo.getLnTrafficController().addLocoNetListener(~0, this); 133 } 134 135 public void sendButtonActionPerformed(java.awt.event.ActionEvent e) { 136 String input = packetTextField.getText(); 137 // TODO check input + feedback on error. Too easy to cause NPE 138 memo.getLnTrafficController().sendLocoNetMessage(createPacket(input)); 139 } 140 141 // control sequence operation 142 int mNextSequenceElement = 0; 143 LocoNetMessage mNextEcho = null; 144 javax.swing.Timer timer = null; 145 146 /** 147 * Internal routine to handle timer starts and restarts 148 * 149 * @param delay in mSec 150 */ 151 protected void restartTimer(int delay) { 152 if (timer == null) { 153 timer = new javax.swing.Timer(delay, new java.awt.event.ActionListener() { 154 @Override 155 public void actionPerformed(java.awt.event.ActionEvent e) { 156 sendNextItem(); 157 } 158 }); 159 } 160 timer.stop(); 161 timer.setInitialDelay(delay); 162 timer.setRepeats(false); 163 timer.start(); 164 } 165 166 /** 167 * Run button pressed down, start the sequence operation. 168 * 169 * @param e a {@link java.awt.event.ActionEvent} to be triggered 170 */ 171 public void runButtonActionPerformed(java.awt.event.ActionEvent e) { 172 if (!mRunButton.isSelected()) { 173 return; 174 } 175 // make sure at least one is checked 176 boolean ok = false; 177 for (int i = 0; i < MAXSEQUENCE; i++) { 178 if (mUseField[i].isSelected()) { 179 ok = true; 180 } 181 } 182 if (!ok) { 183 mRunButton.setSelected(false); 184 return; 185 } 186 // start the operation 187 mNextSequenceElement = 0; 188 sendNextItem(); 189 } 190 191 /** 192 * {@inheritDoc} 193 */ 194 @Override 195 public void message(LocoNetMessage m) { 196 log.debug("message"); // NOI18N 197 // are we running? 198 if (!mRunButton.isSelected()) { 199 return; 200 } 201 // yes, is this what we're looking for 202 if (!(mNextEcho.equals(m))) { 203 return; 204 } 205 // yes, we got it, do the next 206 startSequenceDelay(); 207 } 208 209 /** 210 * Echo has been heard, start delay for next packet 211 */ 212 void startSequenceDelay() { 213 log.debug("startSequenceDelay"); // NOI18N 214 // at the start, mNextSequenceElement contains index we're 215 // working on 216 int delay = Integer.parseInt(mDelayField[mNextSequenceElement].getText()); 217 // increment to next line at completion 218 mNextSequenceElement++; 219 // start timer 220 restartTimer(delay); 221 } 222 223 /** 224 * Send next item; may be used for the first item or when a delay has 225 * elapsed. 226 */ 227 void sendNextItem() { 228 log.debug("sendNextItem"); // NOI18N 229 // check if still running 230 if (!mRunButton.isSelected()) { 231 return; 232 } 233 // have we run off the end? 234 if (mNextSequenceElement >= MAXSEQUENCE) { 235 // past the end, go back 236 mNextSequenceElement = 0; 237 } 238 // is this one enabled? 239 if (mUseField[mNextSequenceElement].isSelected()) { 240 // make the packet 241 LocoNetMessage m = createPacket(mPacketField[mNextSequenceElement].getText()); 242 // send it 243 mNextEcho = m; 244 memo.getLnTrafficController().sendLocoNetMessage(m); 245 } else { 246 // ask for the next one 247 mNextSequenceElement++; 248 sendNextItem(); 249 } 250 } 251 252 /** 253 * Create a well-formed LocoNet packet from a String. 254 * <p> 255 * Well-formed generally means a space-separated string of hex values of 256 * two characters each, as defined in 257 * {@link jmri.util.StringUtil#bytesFromHexString(String s)} . 258 * 259 * @param s a string containing raw hex data of good form 260 * @return The packet, with contents filled-in 261 */ 262 LocoNetMessage createPacket(String s) { 263 // gather bytes in result 264 byte b[] = StringUtil.bytesFromHexString(s); 265 if (b.length == 0) { 266 return null; // no such thing as a zero-length message 267 } 268 LocoNetMessage m = new LocoNetMessage(b.length); 269 for (int i = 0; i < b.length; i++) { 270 m.setElement(i, b[i]); 271 } 272 return m; 273 } 274 275 /** 276 * When the window closes, stop any sequences running 277 */ 278 @Override 279 public void dispose() { 280 mRunButton.setSelected(false); 281 super.dispose(); 282 } 283 284 // initialize logging 285 private final static Logger log = LoggerFactory.getLogger(LocoGenPanel.class); 286 287}