001package jmri.jmrit.logix; 002 003import jmri.InstanceManager; 004import jmri.JmriException; 005import jmri.NamedBean; 006import jmri.NamedBeanHandle; 007import jmri.NamedBeanHandleManager; 008import jmri.Sensor; 009import jmri.SpeedStepMode; 010 011import java.text.NumberFormat; 012 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Oct 2020 - change formats to allow I18N of parameters 018 * @author Pete Cressman Copyright (C) 2009, 2020 019 */ 020public class ThrottleSetting { 021 022 static final int CMD_SPEED = 1; 023 static final int CMD_SPEEDSTEP = 2; 024 static final int CMD_FORWARD = 3; 025 static final int CMD_FTN = 4; 026 static final int CMD_LATCH = 5; 027 static final int CMD_NOOP = 6; 028 static final int CMD_SET_SENSOR = 7; 029 static final int CMD_WAIT_SENSOR = 8; 030 static final int CMD_RUN_WARRANT = 9; 031 032 public enum Command { 033 SPEED(CMD_SPEED, true, "speed"), 034 FORWARD(CMD_FORWARD, true, "forward"), 035 FKEY(CMD_FTN, true, "setFunction"), 036 LATCHF(CMD_LATCH, true, "setKeyMomentary"), 037 SET_SENSOR(CMD_SET_SENSOR, false, "SetSensor"), 038 WAIT_SENSOR(CMD_WAIT_SENSOR, false, "WaitSensor"), 039 RUN_WARRANT(CMD_RUN_WARRANT, false, "runWarrant"), 040 NOOP(CMD_NOOP, true, "NoOp"), 041 SPEEDSTEP(CMD_SPEEDSTEP, true, "speedstep"); 042 043 int _command; 044 boolean _hasBlock; // when bean is an OBlock. 045 String _bundleKey; // key to get command display name 046 047 Command(int cmd, boolean hasBlock, String bundleName) { 048 _command = cmd; 049 _hasBlock = hasBlock; 050 _bundleKey = bundleName; 051 } 052 053 public int getIntId() { 054 return _command; 055 } 056 057 boolean hasBlockName() { 058 return _hasBlock; 059 } 060 061 @Override 062 public String toString() { 063 return Bundle.getMessage(_bundleKey); 064 } 065 } 066 067 static final int VALUE_FLOAT = 1; 068 static final int VALUE_NOOP = 2; 069 static final int VALUE_INT = 3; 070 static final int VALUE_STEP = 4; 071 static final int VALUE_TRUE = 10; 072 static final int VALUE_FALSE = 11; 073 static final int VALUE_ON = 20; 074 static final int VALUE_OFF = 21; 075 static final int VALUE_ACTIVE = 30; 076 static final int VALUE_INACTIVE = 31; 077 078 static final int IS_TRUE_FALSE = 1; 079 static final int IS_ON_OFF = 2; 080 static final int IS_SENSOR_STATE = 3; 081 082 public enum ValueType { 083 VAL_FLOAT(VALUE_FLOAT, "NumData"), 084 VAL_NOOP(VALUE_NOOP, "Mark"), 085 VAL_INT(VALUE_INT, "NumData"), 086 VAL_STEP(VALUE_STEP, "speedstep"), 087 VAL_TRUE(VALUE_TRUE, "StateTrue"), 088 VAL_FALSE(VALUE_FALSE, "StateFalse"), 089 VAL_ON(VALUE_ON, "StateOn"), 090 VAL_OFF(VALUE_OFF, "StateOff"), 091 VAL_ACTIVE(VALUE_ACTIVE, "SensorStateActive"), 092 VAL_INACTIVE(VALUE_INACTIVE, "SensorStateInactive"); 093 094 int _valueId; // state id 095 String _bundleKey; // key to get state display name 096 097 ValueType(int id, String bundleName) { 098 _valueId = id; 099 _bundleKey = bundleName; 100 } 101 102 public int getIntId() { 103 return _valueId; 104 } 105 106 @Override 107 public String toString() { 108 return Bundle.getMessage(_bundleKey); 109 } 110 } 111 112 public static class CommandValue { 113 ValueType _type; 114 SpeedStepMode _stepMode; 115 float _floatValue; 116 NumberFormat formatter = NumberFormat.getNumberInstance(); 117 NumberFormat intFormatter = NumberFormat.getIntegerInstance(); 118 119 public CommandValue(ValueType t, SpeedStepMode s, float f) { 120 _type = t; 121 _stepMode = s; 122 _floatValue = f; 123 } 124 125 @Override 126 protected CommandValue clone() { 127 return new CommandValue(_type, _stepMode, _floatValue); 128 } 129 130 public ValueType getType() { 131 return _type; 132 } 133 134 public SpeedStepMode getMode() { 135 return _stepMode; 136 } 137 138 void setFloat(float f) { 139 _floatValue = f; 140 } 141 142 public float getFloat() { 143 return _floatValue; 144 } 145 146 public String showValue() { 147 if (_type == ValueType.VAL_FLOAT) { 148 return formatter.format(_floatValue); 149 } else if (_type == ValueType.VAL_INT) { 150 return intFormatter.format(_floatValue); 151 } else if (_type == ValueType.VAL_STEP) { 152 return _stepMode.name; 153 } else { 154 return _type.toString(); 155 } 156 } 157 158 @Override 159 public String toString() { 160 return "CommandValue type "+_type.getIntId(); 161 } 162 } 163 164 public static Command getCommandTypeFromInt(int typeInt) { 165 for (Command type : Command.values()) { 166 if (type.getIntId() == typeInt) { 167 return type; 168 } 169 } 170 throw new IllegalArgumentException(typeInt + " Command type ID is unknown"); 171 } 172 173 public static ValueType getValueTypeFromInt(int typeInt) { 174 for (ValueType type : ValueType.values()) { 175 if (type.getIntId() == typeInt) { 176 return type; 177 } 178 } 179 throw new IllegalArgumentException(typeInt + " ValueType ID is unknown"); 180 } 181 182 //====================== THrottleSteeing Class ==================== 183 private long _time; 184 private Command _command; 185 private int _keyNum; // function key number 186 private CommandValue _value; 187 // _namedHandle may be of 3 different types 188 private NamedBeanHandle<? extends NamedBean> _namedHandle = null; 189 private float _trackSpeed; // track speed of the train (millimeters per second) 190 191 public ThrottleSetting() { 192 _keyNum = -1; 193 } 194 195 public ThrottleSetting(long time, Command command, int key, ValueType vType, SpeedStepMode ss, float f, String beanName) { 196 _time = time; 197 _command = command; 198 _keyNum = key; 199 setValue(vType, ss, f); 200 setNamedBean(command, beanName); 201 _trackSpeed = 0.0f; 202 } 203 204 public ThrottleSetting(long time, Command command, int key, ValueType vType, SpeedStepMode ss, float f, String beanName, float trkSpd) { 205 _time = time; 206 _command = command; 207 _keyNum = key; 208 setValue(vType, ss, f); 209 setNamedBean(command, beanName); 210 _trackSpeed = trkSpd; 211 } 212 213 // pre 4.21.3 214 public ThrottleSetting(long time, String cmdStr, String value, String beanName) { 215 _time = time; 216 setCommand(cmdStr); 217 setValue(value); // must follow setCommand() 218 setNamedBean(_command, beanName); 219 _trackSpeed = 0.0f; 220 } 221 222 // pre 4.21.3 223 public ThrottleSetting(long time, String cmdStr, String value, String beanName, float trkSpd) { 224 _time = time; 225 setCommand(cmdStr); 226 setValue(value); 227 setNamedBean(_command, beanName); 228 _trackSpeed = trkSpd; 229 } 230 231 public ThrottleSetting(long time, Command command, int key, String value, String beanName, float trkSpd) { 232 _time = time; 233 _command = command; 234 _keyNum = key; 235 setValue(value); // must follow setCommand() 236 _namedHandle = null; 237 _trackSpeed = trkSpd; 238 } 239 240 public ThrottleSetting(ThrottleSetting ts) { 241 _time = ts.getTime(); 242 _command = ts.getCommand(); 243 _keyNum = ts.getKeyNum(); 244 setValue(ts.getValue()); 245 _namedHandle = ts.getNamedBeanHandle(); 246 _trackSpeed = ts.getTrackSpeed(); 247 } 248 249 /** 250 * Convert old format. (former Strings for Command enums) 251 * @param cmdStr old style description string 252 * @return enum Command 253 * @throws JmriException in case of a non-integer Function or Fn lock/latch value 254 */ 255 private Command getCommandFromString(String cmdStr) throws JmriException { 256 Command command; 257 String cmd = cmdStr.trim().toUpperCase(); 258 if ("SPEED".equals(cmd) || Bundle.getMessage("speed").toUpperCase().equals(cmd)) { 259 command = Command.SPEED; 260 _keyNum = -1; 261 } else if ("SPEEDSTEP".equals(cmd) || Bundle.getMessage("speedstep").toUpperCase().equals(cmd)) { 262 command = Command.SPEEDSTEP; 263 _keyNum = -1; 264 } else if ("FORWARD".equals(cmd) || Bundle.getMessage("forward").toUpperCase().equals(cmd)) { 265 command = Command.FORWARD; 266 _keyNum = -1; 267 } else if (cmd.startsWith("F") || Bundle.getMessage("setFunction").toUpperCase().equals(cmd)) { 268 command = Command.FKEY; 269 try { 270 _keyNum = Integer.parseInt(cmd.substring(1)); 271 } catch (NumberFormatException nfe) { 272 throw new JmriException(Bundle.getMessage("badFunctionNum"), nfe); 273 } 274 } else if (cmd.startsWith("LOCKF") || Bundle.getMessage("setKeyMomentary").toUpperCase().equals(cmd)) { 275 command = Command.LATCHF; 276 try { 277 _keyNum = Integer.parseInt(cmd.substring(5)); 278 } catch (NumberFormatException nfe) { 279 throw new JmriException(Bundle.getMessage("badLockFNum"), nfe); 280 } 281 } else if ("NOOP".equals(cmd) || Bundle.getMessage("NoOp").toUpperCase().equals(cmd)) { 282 command = Command.NOOP; 283 _keyNum = -1; 284 } else if ("SENSOR".equals(cmd) || "SET SENSOR".equals(cmd) || "SET".equals(cmd) 285 || Bundle.getMessage("SetSensor").toUpperCase().equals(cmd)) { 286 command = Command.SET_SENSOR; 287 _keyNum = -1; 288 } else if ("WAIT SENSOR".equals(cmd) || "WAIT".equals(cmd) 289 || Bundle.getMessage("WaitSensor").toUpperCase().equals(cmd)) { 290 command = Command.WAIT_SENSOR; 291 _keyNum = -1; 292 } else if ("RUN WARRANT".equals(cmd) || Bundle.getMessage("runWarrant").toUpperCase().equals(cmd)) { 293 command = Command.RUN_WARRANT; 294 _keyNum = -1; 295 } else { 296 throw new jmri.JmriException(Bundle.getMessage("badCommand", cmdStr)); 297 } 298 return command; 299 } 300 301 static protected CommandValue getValueFromString(Command command, String valueStr) throws JmriException { 302 if (command == null) { 303 throw new jmri.JmriException(Bundle.getMessage("badCommand", "Command missing "+valueStr)); 304 } 305 ValueType type; 306 SpeedStepMode mode = SpeedStepMode.UNKNOWN; 307 float speed = 0.0f; 308 String val = valueStr.trim().toUpperCase(); 309 if ("ON".equals(val) || Bundle.getMessage("StateOn").toUpperCase().equals(val)) { 310 switch (command) { 311 case FKEY: 312 case LATCHF: 313 type = ValueType.VAL_ON; 314 break; 315 default: 316 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 317 } 318 } else if ("OFF".equals(val) || Bundle.getMessage("StateOff").toUpperCase().equals(val)) { 319 switch (command) { 320 case FKEY: 321 case LATCHF: 322 type = ValueType.VAL_OFF; 323 break; 324 default: 325 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 326 } 327 } else if ("TRUE".equals(val) || Bundle.getMessage("StateTrue").toUpperCase().equals(val)) { 328 switch (command) { 329 case FORWARD: 330 type = ValueType.VAL_TRUE; 331 break; 332 case FKEY: 333 case LATCHF: 334 type = ValueType.VAL_ON; 335 break; 336 default: 337 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 338 } 339 } else if ("FALSE".equals(val) || Bundle.getMessage("StateFalse").toUpperCase().equals(val)) { 340 switch (command) { 341 case FORWARD: 342 type = ValueType.VAL_FALSE; 343 break; 344 case FKEY: 345 case LATCHF: 346 type = ValueType.VAL_OFF; 347 break; 348 default: 349 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 350 } 351 } else if ("ACTIVE".equals(val) || Bundle.getMessage("SensorStateActive").toUpperCase().equals(val)) { 352 switch (command) { 353 case SET_SENSOR: 354 case WAIT_SENSOR: 355 type = ValueType.VAL_ACTIVE; 356 break; 357 default: 358 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 359 } 360 } else if ("INACTIVE".equals(val) || Bundle.getMessage("SensorStateInactive").toUpperCase().equals(val)) { 361 switch (command) { 362 case SET_SENSOR: 363 case WAIT_SENSOR: 364 type = ValueType.VAL_INACTIVE; 365 break; 366 default: 367 throw new jmri.JmriException(Bundle.getMessage("badValue", valueStr, command)); 368 } 369 } else { 370 try { 371 switch (command) { 372 case SPEED: 373 speed = Float.parseFloat(valueStr.replace(',', '.')); 374 type = ValueType.VAL_FLOAT; 375 break; 376 case NOOP: 377 type = ValueType.VAL_NOOP; 378 break; 379 case RUN_WARRANT: 380 speed = Float.parseFloat(valueStr.replace(',', '.')); 381 type = ValueType.VAL_INT; 382 break; 383 case SPEEDSTEP: 384 mode = SpeedStepMode.getByName(val); 385 type = ValueType.VAL_STEP; 386 break; 387 default: 388 throw new JmriException(Bundle.getMessage("badValue", valueStr, command)); 389 } 390 } catch (IllegalArgumentException | NullPointerException ex) { // NumberFormatException is sublass of iae 391 throw new JmriException(Bundle.getMessage("badValue", valueStr, command), ex); 392 } 393 } 394 return new CommandValue(type, mode, speed); 395 } 396 397 /** 398 * Time is an object so that a "synch to block entry" notation can be used 399 * rather than elapsed time. 400 * 401 * @param time the time in some unit 402 */ 403 public void setTime(long time) { 404 _time = time; 405 } 406 407 public long getTime() { 408 return _time; 409 } 410 411 public void setCommand(String cmdStr) { 412 try { 413 _command = getCommandFromString(cmdStr); 414 } catch (JmriException je) { 415 log.error("Cannot set command from string \"{}\" {}", cmdStr, je.toString()); 416 } 417 } 418 419 public void setCommand(Command command) { 420 _command = command; 421 } 422 423 public Command getCommand() { 424 return _command; 425 } 426 427 public void setKeyNum(int key) { 428 _keyNum = key; 429 } 430 431 public int getKeyNum() { 432 return _keyNum; 433 } 434 435 public void setValue(String valueStr) { 436 try { 437 _value = getValueFromString(_command, valueStr); 438 } catch (JmriException je) { 439 log.error("Cannot set value for command {}. {}", 440 (_command!=null?_command.toString():"null"), je.toString()); 441 } 442 } 443 444 public void setValue(CommandValue value) { 445 _value = value.clone(); 446 } 447 448 public void setValue(ValueType t, SpeedStepMode s, float f) { 449 _value = new CommandValue(t, s, f); 450 } 451 452 public CommandValue getValue() { 453 return _value; 454 } 455 456 public void setTrackSpeed(float s) { 457 _trackSpeed = s; 458 } 459 460 public float getTrackSpeed() { 461 return _trackSpeed; 462 } 463 464 // _namedHandle may be of 3 different types 465 public String setNamedBean(Command cmd, String name) { 466 if (log.isDebugEnabled()) { 467 log.debug("setNamedBean({}, {})", cmd, name); 468 } 469 String msg = WarrantFrame.checkBeanName(cmd, name); 470 if (msg != null) { 471 _namedHandle = null; 472 return msg; 473 } 474 try { 475 if (cmd.equals(Command.SET_SENSOR) || cmd.equals(Command.WAIT_SENSOR)) { 476 Sensor s = InstanceManager.sensorManagerInstance().provideSensor(name); 477 _namedHandle = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, s); 478 } else if (cmd.equals(Command.RUN_WARRANT)) { 479 Warrant w = InstanceManager.getDefault(jmri.jmrit.logix.WarrantManager.class).provideWarrant(name); 480 _namedHandle = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, w); 481 } else { 482 OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(name); 483 if (b != null) { 484 _namedHandle = InstanceManager.getDefault(NamedBeanHandleManager.class).getNamedBeanHandle(name, b); 485 } 486 } 487 } catch (IllegalArgumentException iae) { 488 return Bundle.getMessage("badCommand", cmd+iae.toString()); 489 } 490 return null; 491 } 492 493 // _namedHandle may be of 3 different types 494 public <T extends NamedBean> void setNamedBeanHandle(NamedBeanHandle<T> bh) { 495 _namedHandle = bh; 496 } 497 498 // _namedHandle may be of 3 different types 499 public NamedBeanHandle<? extends NamedBean> getNamedBeanHandle() { 500 return _namedHandle; 501 } 502 503 public NamedBean getBean() { 504 if (_namedHandle == null) { 505 return null; 506 } 507 return _namedHandle.getBean(); 508 } 509 510 public String getBeanDisplayName() { 511 if (_namedHandle == null) { 512 return null; 513 } 514 return _namedHandle.getBean().getDisplayName(); 515 } 516 517 public String getBeanSystemName() { 518 if (_namedHandle == null) { 519 return null; 520 } 521 return _namedHandle.getBean().getSystemName(); 522 } 523 524 @Override 525 public String toString() { 526 return "ThrottleSetting: wait " + _time + "ms then " + _command.toString() 527 + " with value " + _value.showValue() + " for bean \"" + getBeanDisplayName() 528 + "\" at trackSpeed " + getTrackSpeed() + "\""; 529 } 530 531 private static final Logger log = LoggerFactory.getLogger(ThrottleSetting.class); 532}