001package jmri.jmrit.dualdecoder; 002 003import java.awt.GridLayout; 004import java.awt.event.ActionEvent; 005import java.awt.event.ActionListener; 006import javax.swing.BoxLayout; 007import javax.swing.ButtonGroup; 008import javax.swing.JButton; 009import javax.swing.JLabel; 010import javax.swing.JPanel; 011import javax.swing.JRadioButton; 012import javax.swing.JSeparator; 013import javax.swing.JToggleButton; 014import jmri.ProgListener; 015import jmri.Programmer; 016import org.slf4j.Logger; 017import org.slf4j.LoggerFactory; 018 019/** 020 * Pane for selecting an active decoder from multiple ones in a loco 021 * 022 * @author Bob Jacobsen Copyright (C) 2003 023 */ 024public class DualDecoderSelectPane extends javax.swing.JPanel implements jmri.ProgListener { 025 026 boolean scanning = false; 027 int next = 0; 028 029 final int NENTRIES = 8; 030 031 JLabel[] labels = new JLabel[NENTRIES]; 032 JRadioButton[] buttons = new JRadioButton[NENTRIES]; 033 034 JLabel status = new JLabel(Bundle.getMessage("ButtonIdle")); // is in jmrit.Bundle 035 JToggleButton searchButton = new JToggleButton(Bundle.getMessage("ButtonSearch")); 036 jmri.jmrit.progsupport.ProgModePane modePane = new jmri.jmrit.progsupport.ProgModePane(BoxLayout.Y_AXIS); 037 038 public DualDecoderSelectPane() { 039 // general GUI config 040 setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); 041 ButtonGroup g = new ButtonGroup(); 042 JPanel pane1 = new JPanel(); 043 pane1.setLayout(new GridLayout(NENTRIES, 1)); 044 for (int i = 0; i < NENTRIES; i++) { 045 JPanel p = new JPanel(); 046 String name = Bundle.getMessage("IDnumber", i); 047 if (i == NENTRIES - 1) { 048 name = Bundle.getMessage("Legacy"); 049 } 050 p.add(labels[i] = new JLabel(name)); 051 JRadioButton b = new JRadioButton(); 052 buttons[i] = b; 053 b.setActionCommand("" + i); 054 b.addActionListener(new ActionListener() { 055 @Override 056 public void actionPerformed(ActionEvent e) { 057 select(e.getActionCommand()); 058 } 059 }); 060 p.add(b); 061 g.add(b); 062 pane1.add(p); 063 } 064 add(pane1); 065 add(new JSeparator(JSeparator.HORIZONTAL)); 066 067 JPanel pane2 = new JPanel(); 068 JButton t; 069 pane2.add(searchButton); 070 searchButton.addActionListener(new ActionListener() { 071 @Override 072 public void actionPerformed(ActionEvent e) { 073 search(); 074 } 075 }); 076 pane2.add(t = new JButton(Bundle.getMessage("ButtonReset"))); 077 t.addActionListener(new ActionListener() { 078 @Override 079 public void actionPerformed(ActionEvent e) { 080 reset(); 081 } 082 }); 083 add(pane2); 084 085 JPanel pane3 = new JPanel(); 086 pane3.add(t = new JButton(Bundle.getMessage("InitDH163"))); 087 t.setToolTipText(Bundle.getMessage("InitDH163Tooltip")); 088 t.addActionListener(new ActionListener() { 089 @Override 090 public void actionPerformed(ActionEvent e) { 091 doInit(); 092 } 093 }); 094 add(pane3); 095 add(new JSeparator(JSeparator.HORIZONTAL)); 096 097 JPanel pane4 = new JPanel(); 098 pane4.add(status); 099 add(pane4); 100 add(new JSeparator(JSeparator.HORIZONTAL)); 101 102 add(modePane); 103 } 104 105 public void dispose() { 106 modePane.dispose(); 107 buttons = null; 108 labels = null; 109 status = null; 110 searchButton = null; 111 } 112 113 void reset() { 114 for (int i = 0; i < NENTRIES; i++) { 115 labels[i].setEnabled(true); 116 buttons[i].setSelected(false); 117 buttons[i].setEnabled(true); 118 } 119 } 120 121 void search() { 122 mode = SEARCH; 123 reset(); 124 searchButton.setSelected(true); 125 scanning = true; 126 next = 0; 127 select("0"); 128 } 129 130 void select(String number) { 131 mode = SEARCH; 132 next = Integer.parseInt(number); 133 // write that CV value 134 state = WROTECV15; 135 writeCV15(next); 136 } 137 138 void writeCV15(int value) { 139 writeCV("15", value); 140 } 141 142 void writeCV16(int value) { 143 writeCV("16", value); 144 } 145 146 void writeCV(String cv, int value) { 147 Programmer p = modePane.getProgrammer(); 148 if (p == null) { 149 state = IDLE; 150 status.setText(Bundle.getMessage("NoProgrammerConnected")); 151 } else { 152 try { 153 status.setText(Bundle.getMessage("StateWriting")); 154 p.writeCV(cv, value, this); 155 } catch (jmri.ProgrammerException ex) { 156 state = IDLE; 157 status.setText("" + ex); 158 } 159 } 160 } 161 162 void readCV16() { 163 Programmer p = modePane.getProgrammer(); 164 if (p == null) { 165 state = IDLE; 166 status.setText(Bundle.getMessage("NoProgrammerConnected")); 167 } else { 168 try { 169 status.setText(Bundle.getMessage("StateReading")); 170 state = READCV16; 171 p.readCV("16", this); 172 } catch (jmri.ProgrammerException ex) { 173 state = IDLE; 174 status.setText("" + ex); 175 } 176 } 177 } 178 179 // modes 180 final int SEARCH = 10; 181 final int INIT = 20; 182 int mode = SEARCH; 183 184 // search, select operation states 185 final int IDLE = 0; 186 final int WROTECV15 = 1; 187 final int READCV16 = 2; 188 final int FIRSTCV16 = 11; 189 final int FIRSTCV15 = 12; 190 final int SECONDCV16 = 13; 191 int state = IDLE; 192 193 @Override 194 public void programmingOpReply(int value, int retcode) { 195 switch (mode) { 196 case SEARCH: 197 searchReply(value, retcode); 198 break; 199 case INIT: 200 initReply(value, retcode); 201 break; 202 default: 203 log.warn("unexpected mode: {}", mode); 204 break; 205 } 206 } 207 208 void searchReply(int value, int retcode) { 209 switch (state) { 210 case IDLE: 211 default: 212 // shouldn't happen, reset and ignore 213 log.warn("Unexpected search programming reply: {} {}", value, retcode); 214 state = IDLE; 215 break; 216 case WROTECV15: 217 //confirm OK 218 readCV16(); 219 break; 220 case READCV16: 221 // was it OK? 222 String result = Bundle.getMessage("ButtonOK"); 223 if (retcode != ProgListener.OK) { 224 log.debug("Readback error: {} {}", retcode, value); 225 labels[next].setEnabled(false); 226 buttons[next].setEnabled(false); 227 result = "Could not confirm: " + modePane.getProgrammer().decodeErrorCode(retcode); 228 } else if (value != next) { 229 log.debug("Readback error: {} {}", retcode, value); 230 if (scanning) { 231 labels[next].setEnabled(false); 232 buttons[next].setEnabled(false); 233 } 234 result = Bundle.getMessage("UnexpectedID_Read", value); 235 } 236 // go on to next? 237 if (scanning) { 238 next++; 239 if (next >= NENTRIES) { 240 state = IDLE; 241 next = 0; 242 scanning = false; 243 status.setText(Bundle.getMessage("StateIdle")); 244 searchButton.setSelected(false); 245 break; 246 } 247 select("" + next); 248 break; 249 } else { 250 status.setText(result); 251 break; 252 } 253 } 254 } 255 256 /** 257 * Start process of initializing a Digitrax and legacy decoder. Operations 258 * are: 259 * <ol> 260 * <li>Write 1 to CV16, which will write both decoders 261 * <li>Write 7 to CV15, which will turn off Digitrax 262 * <li>Write 7 to CV16, which will be stored in the legacy decoder only 263 * </ol> 264 */ 265 void doInit() { 266 mode = INIT; 267 state = FIRSTCV16; 268 writeCV16(1); 269 } 270 271 void initReply(int value, int retcode) { 272 switch (state) { 273 case IDLE: 274 default: 275 // shouldn't happen, reset and ignore 276 log.warn("Unexpected init programming reply: {} {}", value, retcode); 277 state = IDLE; 278 break; 279 case FIRSTCV16: 280 state = FIRSTCV15; 281 if (retcode != ProgListener.OK) { 282 log.debug("Readback error: {} {}", retcode, value); 283 status.setText(Bundle.getMessage("WriteCVFailed", 15, 7)); 284 state = IDLE; 285 } else { // is OK 286 writeCV15(7); 287 } 288 break; 289 case FIRSTCV15: 290 state = SECONDCV16; 291 if (retcode != ProgListener.OK) { 292 log.debug("Readback error: {} {}", retcode, value); 293 status.setText(Bundle.getMessage("WriteCVFailed", 16, 7)); 294 state = IDLE; 295 } else { // is OK 296 writeCV16(7); 297 } 298 break; 299 case SECONDCV16: 300 if (retcode != ProgListener.OK) { 301 log.debug("Readback error: {} {}", retcode, value); 302 status.setText(Bundle.getMessage("WriteCVFailed", 16, 1)); 303 state = IDLE; 304 } else { // is OK 305 state = IDLE; 306 status.setText(Bundle.getMessage("StateInitialized")); 307 } 308 break; 309 } 310 } 311 312 private final static Logger log = LoggerFactory.getLogger(DualDecoderSelectPane.class); 313 314}