001package jmri; 002 003import java.util.Date; 004import java.util.ResourceBundle; 005import java.text.MessageFormat; 006 007import javax.annotation.CheckForNull; 008import javax.annotation.Nonnull; 009 010import jmri.Conditional.Operator; 011import jmri.jmrit.beantable.LogixTableAction; 012import jmri.jmrit.logix.OBlock; 013import jmri.jmrit.logix.Warrant; 014import jmri.jmrit.logix.WarrantManager; 015 016/** 017 * The variable used in the antecedent (the 'if' part) of the Conditional. 018 * proposition. The states of ConditionalVariables and logic expression of the 019 * antecedent determine the state of the Conditional. 020 * <p> 021 * ConditionalVariable objects are fully mutable, so use the default equals() 022 * operator that checks for identical objects, not identical contents. 023 * 024 * This file is part of JMRI. 025 * <p> 026 * JMRI is free software; you can redistribute it and/or modify it under the 027 * terms of version 2 of the GNU General Public License as published by the Free 028 * Software Foundation. See the "COPYING" file for a copy of this license. 029 * <p> 030 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 031 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 032 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 033 * 034 * @author Pete Cressman Copyright (C) 2009 035 * @author Bob Jacobsen Copyright (C) 2016 036 */ 037public class ConditionalVariable { 038 039 static final ResourceBundle rbx = ResourceBundle.getBundle("jmri.jmrit.conditional.ConditionalBundle"); 040 041 public static final int NUM_COMPARE_OPERATIONS = 5; 042 public static final int LESS_THAN = 1; 043 public static final int LESS_THAN_OR_EQUAL = 2; 044 public static final int EQUAL = 3; 045 public static final int GREATER_THAN_OR_EQUAL = 4; 046 public static final int GREATER_THAN = 5; 047 048 private boolean _not = false; 049 // Not a variable attribute, but retained as an artifact of previous releases. This will be used 050 // as the default operator immediately to the left of this variable in the antecedent statement. 051 // It may be over written by the antecedent statement in the Conditional to which this variable 052 // belongs. 053 private Operator _opern = Operator.NONE; 054 private Conditional.Type _type = Conditional.Type.NONE; 055 private String _name = ""; 056 private String _dataString = ""; 057 private int _num1 = 0; 058 private int _num2 = 0; 059 private String _guiName = ""; // Contains the user name of the referenced conditional 060 private NamedBeanHandle<?> _namedBean = null; 061 //private NamedBeanHandle<Sensor> _namedSensorBean = null; 062 protected NamedBeanHandleManager nbhm = InstanceManager.getDefault(NamedBeanHandleManager.class); 063 // Name clarification: Formerly was named '_triggersCalculation' because it controlled whether 064 // a listener was installed for this device and thus trigger calculation of the Conditional. 065 // Now named '_triggersActions' because listeners are always installed for activated Logix 066 // Conditionals and this parameter nows controls whether, if its change of state changes the 067 // state of the conditional, should that also trigger the actions. 068 private boolean _triggersActions = true; 069 private int _state = NamedBean.UNKNOWN; // tri-state 070 071 /** 072 * Create a blank ConditionalVariable, to be filled in later. 073 */ 074 public ConditionalVariable() { 075 } 076 077 /** 078 * Create a ConditionalVariable with a set of given properties. 079 * @param not true if the ConditionalVariable should be negated 080 * @param opern the boolean operator for this ConditionalVariable 081 * @param type the type this ConditionalVariable operates on (Turnout, Sensor, ...) 082 * @param name the device name 083 * @param trigger true if actions should be performed if triggered 084 */ 085 public ConditionalVariable(boolean not, @Nonnull Operator opern, @Nonnull Conditional.Type type, 086 String name, boolean trigger) { 087 _not = not; 088 // setOpern does some checks of opern 089 _opern = opern; 090 _type = type; 091 _name = name; 092 _triggersActions = trigger; 093 _guiName = ""; 094 try { 095 Conditional.ItemType itemType = type.getItemType(); 096 switch (itemType) { 097 case SENSOR: 098 try { 099 Sensor sn = InstanceManager.sensorManagerInstance().provideSensor(_name); 100 _namedBean = nbhm.getNamedBeanHandle(_name, sn); 101 } catch (IllegalArgumentException e) { 102 log.error("invalid sensor name= \"{}\" in state variable", _name); 103 } 104 break; 105 case TURNOUT: 106 try { 107 Turnout tn = InstanceManager.turnoutManagerInstance().provideTurnout(_name); 108 _namedBean = nbhm.getNamedBeanHandle(_name, tn); 109 } catch (IllegalArgumentException e) { 110 log.error("invalid turnout name= \"{}\" in state variable", _name); 111 } 112 break; 113 case MEMORY: 114 try { 115 Memory my = InstanceManager.memoryManagerInstance().provideMemory(_name); 116 _namedBean = nbhm.getNamedBeanHandle(_name, my); 117 } catch (IllegalArgumentException e) { 118 log.error("invalid memory name= \"{}\" in state variable", _name); 119 } 120 break; 121 case LIGHT: 122 try { 123 Light l = InstanceManager.lightManagerInstance().provideLight(_name); 124 _namedBean = nbhm.getNamedBeanHandle(_name, l); 125 } catch (IllegalArgumentException e) { 126 log.error("invalid light name= \"{}\" in state variable", _name); 127 } 128 break; 129 case SIGNALHEAD: 130 SignalHead s = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(_name); 131 if (s == null) { 132 log.error("invalid signalhead name= \"{}\" in state variable", _name); 133 return; 134 } 135 _namedBean = nbhm.getNamedBeanHandle(_name, s); 136 break; 137 case SIGNALMAST: 138 try { 139 SignalMast sm = InstanceManager.getDefault(SignalMastManager.class).provideSignalMast(_name); 140 _namedBean = nbhm.getNamedBeanHandle(_name, sm); 141 } catch (IllegalArgumentException e) { 142 log.error("invalid signalmast name= \"{}\" in state variable", _name); 143 } 144 break; 145 case ENTRYEXIT: 146 NamedBean nb = InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getBySystemName(_name); 147 if (nb == null) { 148 log.error("invalid entry exit name= \"{}\" in state variable", _name); 149 return; 150 } 151 _namedBean = nbhm.getNamedBeanHandle(_name, nb); 152 break; 153 case CONDITIONAL: 154 Conditional c = InstanceManager.getDefault(ConditionalManager.class).getConditional(_name); 155 if (c == null) { 156 log.error("invalid conditional; name= \"{}\" in state variable", _name); 157 return; 158 } 159 _namedBean = nbhm.getNamedBeanHandle(_name, c); 160 break; 161 case WARRANT: 162 Warrant w = InstanceManager.getDefault(WarrantManager.class).getWarrant(_name); 163 if (w == null) { 164 log.error("invalid warrant name= \"{}\" in state variable", _name); 165 return; 166 } 167 _namedBean = nbhm.getNamedBeanHandle(_name, w); 168 break; 169 case OBLOCK: 170 OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(_name); 171 if (b == null) { 172 log.error("invalid block name= \"{}\" in state variable", _name); 173 return; 174 } 175 _namedBean = nbhm.getNamedBeanHandle(_name, b); 176 break; 177 178 default: 179 log.warn("Unexpected type in ConditionalVariable ctor: {} -> {}", _type, itemType); 180 break; 181 } 182 } catch (java.lang.NumberFormatException ex) { 183 //Can be Considered Normal where the logix is loaded prior to any other beans 184 } catch (IllegalArgumentException ex) { 185 log.warn("could not provide \"{}\" in constructor", _name); 186 _namedBean = null; 187 } 188 } 189 190 public boolean isNegated() { 191 return _not; 192 } 193 194 public void setNegation(boolean not) { 195 _not = not; 196 } 197 198 @Nonnull 199 public Operator getOpern() { 200 return _opern; 201 } 202 203 public final void setOpern(@Nonnull Operator opern) { 204 _opern = opern; 205 } 206 207 @Nonnull 208 public Conditional.Type getType() { 209 return _type; 210 } 211 212 public void setType(@Nonnull Conditional.Type type) { 213 _type = type; 214 } 215 216 public String getName() { 217 if (_namedBean != null) { 218 return _namedBean.getName(); 219 } 220 /* As we have a trigger for something using the variable, then hopefully 221 all the managers have been loaded and we can get the bean, which prevented 222 the bean from being loaded in the first place */ 223 setName(_name); 224 return _name; 225 } 226 227 public void setName(String name) { 228 _name = name; 229 NamedBean bean = null; 230 Conditional.ItemType itemType = _type.getItemType(); 231 232 try { 233 switch (itemType) { 234 case NONE: 235 break; 236 case CLOCK: 237 break; // no beans for these, at least that I know of 238 case SENSOR: 239 bean = InstanceManager.sensorManagerInstance().provideSensor(_name); 240 break; 241 case TURNOUT: 242 bean = InstanceManager.turnoutManagerInstance().provideTurnout(_name); 243 break; 244 case LIGHT: 245 bean = InstanceManager.lightManagerInstance().getLight(_name); 246 break; 247 case MEMORY: 248 bean = InstanceManager.memoryManagerInstance().provideMemory(_name); 249 break; 250 case SIGNALMAST: 251 bean = InstanceManager.getDefault(SignalMastManager.class).provideSignalMast(_name); 252 break; 253 case SIGNALHEAD: 254 bean = InstanceManager.getDefault(SignalHeadManager.class).getSignalHead(_name); 255 break; 256 case CONDITIONAL: 257 bean = InstanceManager.getDefault(ConditionalManager.class).getConditional(_name); 258 break; 259 case WARRANT: 260 bean = InstanceManager.getDefault(WarrantManager.class).getWarrant(_name); 261 break; 262 case OBLOCK: 263 bean = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(_name); 264 break; 265 case ENTRYEXIT: 266 bean = InstanceManager.getDefault(jmri.jmrit.entryexit.EntryExitPairs.class).getNamedBean(_name); 267 break; 268 default: 269 log.error("Type {} not set for {}", itemType, _name); 270 } 271 272 //Once all refactored, we should probably register an error if the bean is returned null. 273 if (bean != null) { 274 _namedBean = nbhm.getNamedBeanHandle(_name, bean); 275 } else { 276 log.debug("Did not have or create \"{}\" in setName. namedBean is unchanged", _name); 277 } 278 279 } catch (IllegalArgumentException ex) { 280 log.warn("Did not have or create \"{}\" in setName", _name); 281 _namedBean = null; 282 } 283 } 284 285 @CheckForNull 286 public NamedBeanHandle<?> getNamedBean() { 287 return _namedBean; 288 } 289 290 @CheckForNull 291 public NamedBean getBean() { 292 if (_namedBean != null) { 293 return _namedBean.getBean(); 294 } 295 setName(_name); //ReApply name as that will create namedBean, save replicating it here 296 if (_namedBean != null) { 297 return _namedBean.getBean(); 298 } 299 return null; 300 } 301 302 public String getDataString() { 303 if (_type.getItemType() == Conditional.ItemType.MEMORY 304 && _namedBeanData != null) { 305 return _namedBeanData.getName(); 306 } 307 return _dataString; 308 } 309 310 public void setDataString(@CheckForNull String data) { 311 _dataString = data; 312 if (data != null && !data.isEmpty() 313 && _type.getItemType() == Conditional.ItemType.MEMORY) { 314 NamedBean bean = InstanceManager.memoryManagerInstance().getMemory(data); 315 if (bean != null) { 316 _namedBeanData = nbhm.getNamedBeanHandle(data, bean); 317 } 318 } 319 } 320 321 private NamedBeanHandle<?> _namedBeanData = null; 322 323 @CheckForNull 324 public NamedBean getNamedBeanData() { 325 if (_namedBeanData != null) { 326 return _namedBeanData.getBean(); 327 } 328 return null; 329 } 330 331 /** 332 * Get Data 1. 333 * Used for Fast Clock Start time, 334 * and value for Memory expression type, e.g. LESS_THAN or GREATER_THAN 335 * @return value for Data 1. 336 */ 337 public int getNum1() { 338 return _num1; 339 } 340 341 /** 342 * Set Data 1. 343 * Used for Fast Clock Start time and Memoey expression operator value. 344 * @param num the value. 345 */ 346 public void setNum1(int num) { 347 _num1 = num; 348 } 349 350 /** 351 * Get Data 2. 352 * Used for Fast Clock Finish time. 353 * @return value for Data 2. 354 */ 355 public int getNum2() { 356 return _num2; 357 } 358 359 /** 360 * Set Data 2. 361 * Used for Fast Clock Finish time. 362 * @param num the value. 363 */ 364 public void setNum2(int num) { 365 _num2 = num; 366 } 367 368 /** 369 * @since 4.7.4 370 * @return the GUI name for the referenced conditional. 371 */ 372 public String getGuiName() { 373 return _guiName; 374 } 375 376 /** 377 * Set the GUI name for the conditional state variable. 378 * @since 4.7.4 379 * @param guiName The referenced Conditional user name. 380 */ 381 public void setGuiName(String guiName) { 382 _guiName = guiName; 383 } 384 385 386 /** 387 * If change of state of this object causes a change of state of the 388 * Conditional, should any actions be executed. 389 * 390 * @return true if actions should be performed if triggered 391 */ 392 public boolean doTriggerActions() { 393 return _triggersActions; 394 } 395 396 public void setTriggerActions(boolean trigger) { 397 _triggersActions = trigger; 398 } 399 400 public int getState() { 401 return _state; 402 } 403 404 public void setState(int state) { 405 _state = state; 406 } 407 408 public void setState(boolean state) { 409 _state = state ? Conditional.TRUE : Conditional.FALSE; 410 } 411 412 public String getTestTypeString() { 413 return _type.getTestTypeString(); 414 } 415 416 /** 417 * Provide a localized text for screen display of the logic operator. 418 * 419 * @return translated string (from jmri.NamedBeanBundle.properties) 420 */ 421 @Nonnull 422 public String getOpernString() { 423 switch (_opern) { 424 case AND: 425 return Bundle.getMessage("LogicAND"); // NOI18N 426 case NONE: 427 return ""; 428 case OR: 429 return Bundle.getMessage("LogicOR"); // NOI18N 430 default: 431 return ""; 432 } 433 } 434 435 /** 436 * Evaluates this State Variable. 437 * 438 * @return true if variable evaluates true, otherwise false. 439 */ 440 @SuppressWarnings("deprecation") // Date.getMinutes, Date.getHours 441 public boolean evaluate() { 442 boolean result = true; 443 // evaluate according to state variable type 444 Conditional.ItemType itemType = _type.getItemType(); 445 log.debug("evaluate: \"{}\" type= {} itemType= {}", getName(), _type, itemType); 446 switch (itemType) { 447 case SENSOR: 448 Sensor sn = (Sensor) getBean(); 449 if (sn == null) { 450 log.error("invalid sensor name= \"{}\" in state variable", getName()); 451 return false; 452 } 453 if (_type == Conditional.Type.SENSOR_ACTIVE) { 454 result = sn.getState() == Sensor.ACTIVE; 455 } else { 456 result = sn.getState() == Sensor.INACTIVE; 457 } 458 break; 459 case TURNOUT: 460 Turnout t = (Turnout) getBean(); 461 if (t == null) { 462 log.error("invalid turnout name= \"{}\" in state variable", getName()); 463 return false; 464 } 465 if (_type == Conditional.Type.TURNOUT_THROWN) { 466 result = t.getKnownState() == Turnout.THROWN; 467 } else { 468 result = t.getKnownState() == Turnout.CLOSED; 469 } 470 break; 471 case LIGHT: 472 Light lgt = (Light) getBean(); 473 if (lgt == null) { 474 log.error("invalid light name= \"{}\" in state variable", getName()); 475 return false; 476 } 477 if (_type == Conditional.Type.LIGHT_ON) { 478 result = lgt.getState() == Light.ON; 479 } else { 480 result = lgt.getState() == Light.OFF; 481 } 482 break; 483 case SIGNALMAST: 484 SignalMast f = (SignalMast) getBean(); 485 if (f == null) { 486 log.error("invalid signal mast name= \"{}\" in state variable", getName()); 487 return false; 488 } 489 switch (_type) { 490 case SIGNAL_MAST_LIT: 491 result = f.getLit(); 492 break; 493 case SIGNAL_MAST_HELD: 494 result = f.getHeld(); 495 break; 496 case SIGNAL_MAST_ASPECT_EQUALS: 497 String aspect = f.getAspect(); 498 if ( aspect == null) { 499 result = false; 500 } else { 501 result = aspect.equals(_dataString); 502 } 503 break; 504 default: 505 log.warn("unexpected type {} in ITEM_TYPE_SIGNALMAST", _type); 506 } 507 break; 508 case SIGNALHEAD: 509 SignalHead h = (SignalHead) getBean(); 510 if (h == null) { 511 log.error("invalid signal head name= \"{}\" in state variable", getName()); 512 return false; 513 } 514 switch (_type) { 515 case SIGNAL_HEAD_RED: 516 result = h.getAppearance() == SignalHead.RED; 517 break; 518 case SIGNAL_HEAD_YELLOW: 519 result = h.getAppearance() == SignalHead.YELLOW; 520 break; 521 case SIGNAL_HEAD_GREEN: 522 result = h.getAppearance() == SignalHead.GREEN; 523 break; 524 case SIGNAL_HEAD_DARK: 525 result = h.getAppearance() == SignalHead.DARK; 526 break; 527 case SIGNAL_HEAD_FLASHRED: 528 result = h.getAppearance() == SignalHead.FLASHRED; 529 break; 530 case SIGNAL_HEAD_FLASHYELLOW: 531 result = h.getAppearance() == SignalHead.FLASHYELLOW; 532 break; 533 case SIGNAL_HEAD_FLASHGREEN: 534 result = h.getAppearance() == SignalHead.FLASHGREEN; 535 break; 536 case SIGNAL_HEAD_LUNAR: 537 result = h.getAppearance() == SignalHead.LUNAR; 538 break; 539 case SIGNAL_HEAD_FLASHLUNAR: 540 result = h.getAppearance() == SignalHead.FLASHLUNAR; 541 break; 542 case SIGNAL_HEAD_LIT: 543 result = h.getLit(); 544 break; 545 case SIGNAL_HEAD_HELD: 546 result = h.getHeld(); 547 break; 548 default: 549 result = false; 550 } 551 break; 552 case MEMORY: 553 Memory m = (Memory) getBean(); 554 if (m == null) { 555 log.error("invalid memory name= \"{}\" in state variable", getName()); 556 return false; 557 } 558 String value1 = null; 559 String value2 = null; 560 if (m.getValue() != null) { 561 value1 = m.getValue().toString(); 562 } 563 boolean caseInsensitive = ((_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE) 564 || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)); 565 if ((_type == Conditional.Type.MEMORY_COMPARE) 566 || (_type == Conditional.Type.MEMORY_COMPARE_INSENSITIVE)) { 567 Memory m2; 568 if (_namedBeanData != null) { 569 m2 = (Memory) _namedBeanData.getBean(); 570 } else { 571 try { 572 m2 = InstanceManager.memoryManagerInstance().provideMemory(_dataString); 573 } catch (IllegalArgumentException ex) { 574 log.error("invalid data memory name= \"{}\" in state variable", _dataString); 575 return false; 576 } 577 } 578 if (m2.getValue() != null) { 579 value2 = m2.getValue().toString(); 580 } 581 } else { 582 value2 = _dataString; 583 } 584 result = compare(value1, value2, caseInsensitive); 585 break; 586 case CONDITIONAL: 587 Conditional c = InstanceManager.getDefault(ConditionalManager.class).getBySystemName(getName()); 588 if (c == null) { 589 c = InstanceManager.getDefault(ConditionalManager.class).getByUserName(getName()); 590 if (c == null) { 591 log.error("invalid conditional name= \"{}\" in state variable", getName()); 592 return false; 593 } 594 } 595 if (_type == Conditional.Type.CONDITIONAL_TRUE) { 596 result = c.getState() == Conditional.TRUE; 597 } else { 598 result = c.getState() == Conditional.FALSE; 599 } 600 break; 601 case WARRANT: 602 Warrant w = InstanceManager.getDefault(WarrantManager.class).getWarrant(getName()); 603 if (w == null) { 604 log.error("invalid Warrant name= \"{}\" in state variable", getName()); 605 return false; 606 } 607 switch (_type) { 608 case ROUTE_FREE: 609 result = w.routeIsFree(); 610 break; 611 case ROUTE_OCCUPIED: 612 result = w.routeIsOccupied(); 613 break; 614 case ROUTE_ALLOCATED: 615 result = w.isAllocated(); 616 break; 617 case ROUTE_SET: 618 result = w.hasRouteSet(); 619 break; 620 case TRAIN_RUNNING: 621 // not in either RUN or LEARN state 622 result = (w.getRunMode() != Warrant.MODE_NONE); 623 break; 624 default: 625 result = false; 626 } 627 break; 628 case CLOCK: 629 Timebase fastClock = InstanceManager.getDefault(Timebase.class); 630 Date currentTime = fastClock.getTime(); 631 int currentMinutes = (currentTime.getHours() * 60) + currentTime.getMinutes(); 632 int beginTime = fixMidnight(_num1); 633 int endTime = fixMidnight(_num2); 634 // check if current time is within range specified 635 if (beginTime <= endTime) { 636 // range is entirely within one day 637 result = (beginTime <= currentMinutes) && (currentMinutes <= endTime); 638 } else { 639 // range includes midnight 640 result = beginTime <= currentMinutes || currentMinutes <= endTime; 641 } 642 break; 643 case OBLOCK: 644 OBlock b = InstanceManager.getDefault(jmri.jmrit.logix.OBlockManager.class).getOBlock(getName()); 645 if (b == null) { 646 log.error("invalid OBlock name= \"{}\" in state variable", getName()); 647 return false; 648 } 649 result = b.statusIs(_dataString); 650 break; 651 case ENTRYEXIT: 652 NamedBean e = getBean(); 653 if ( e == null ) { 654 log.error("invalid EntryExit name= \"{}\" in state variable", getName()); 655 return false; 656 } 657 if (_type == Conditional.Type.ENTRYEXIT_ACTIVE) { 658 result = e.getState() == 0x02; 659 } else { 660 result = e.getState() == 0x04; 661 } 662 break; 663 default: 664 break; 665 } 666 // apply NOT if specified 667 if (_not) { 668 result = !result; 669 } 670 setState( result ); 671 return result; 672 } 673 674 /** 675 * Compare two values using the comparator set using the comparison 676 * instructions in {@link #setNum1(int)}. 677 * 678 * <strong>Note:</strong> {@link #getNum1()} must be one of {@link #LESS_THAN}, 679 * {@link #LESS_THAN_OR_EQUAL}, {@link #EQUAL}, 680 * {@link #GREATER_THAN_OR_EQUAL}, or {@link #GREATER_THAN}. 681 * 682 * @param value1 left side of the comparison 683 * @param value2 right side of the comparison 684 * @param caseInsensitive true if comparison should be case insensitive; 685 * false otherwise 686 * @return true if values compare per getNum1(); false otherwise 687 */ 688 boolean compare(@CheckForNull String value1, @CheckForNull String value2, boolean caseInsensitive) { 689 if (value1 == null) { 690 return value2 == null; 691 } else { 692 if (value2 == null) { 693 return false; 694 } 695 value1 = value1.trim(); 696 value2 = value2.trim(); 697 } 698 try { 699 int n1 = Integer.parseInt(value1); 700 try { 701 int n2 = Integer.parseInt(value2); 702 if (_num1 == 0) { // for former code 703 return n1 == n2; 704 } 705 log.debug("Compare numbers: n1= {} to n2= {}", n1, n2); 706 switch (_num1) // both are numbers 707 { 708 case LESS_THAN: 709 return (n1 < n2); 710 case LESS_THAN_OR_EQUAL: 711 return (n1 <= n2); 712 case EQUAL: 713 return (n1 == n2); 714 case GREATER_THAN_OR_EQUAL: 715 return (n1 >= n2); 716 case GREATER_THAN: 717 return (n1 > n2); 718 default: 719 log.error("Compare numbers: invalid compare case: {}", _num1); 720 return false; 721 } 722 } catch (NumberFormatException nfe) { 723 return false; // n1 is a number, n2 is not 724 } 725 } catch (NumberFormatException nfe) { 726 try { 727 Integer.parseInt(value2); 728 return false; // n1 is not a number, n2 is 729 } catch (NumberFormatException ex) { // OK neither a number 730 } 731 } 732 log.debug("Compare Strings: value1= {} to value2= {}", value1, value2); 733 int compare; 734 if (caseInsensitive) { 735 compare = value1.compareToIgnoreCase(value2); 736 } else { 737 compare = value1.compareTo(value2); 738 } 739 if (_num1 == 0) { // for former code 740 return compare == 0; 741 } 742 switch (_num1) { 743 case LESS_THAN: 744 if (compare < 0) { 745 return true; 746 } 747 break; 748 case LESS_THAN_OR_EQUAL: 749 if (compare <= 0) { 750 return true; 751 } 752 break; 753 case EQUAL: 754 if (compare == 0) { 755 return true; 756 } 757 break; 758 case GREATER_THAN_OR_EQUAL: 759 if (compare >= 0) { 760 return true; 761 } 762 break; 763 case GREATER_THAN: 764 if (compare > 0) { 765 return true; 766 } 767 break; 768 default: 769 // fall through 770 break; 771 } 772 return false; 773 } 774 775 public static int fixMidnight(int time) { 776 if (time > 24 * 60) { 777 return time - ( 24 * 60 ); 778 } 779 return time; 780 } 781 782 /** 783 * Convert Variable Type to Text String 784 * 785 * @param t the type 786 * @return the localized description 787 */ 788 @Nonnull 789 public static String getItemTypeString( @Nonnull Conditional.ItemType t) { 790 switch (t) { 791 case SENSOR: 792 return Bundle.getMessage("BeanNameSensor"); // NOI18N 793 case TURNOUT: 794 return Bundle.getMessage("BeanNameTurnout"); // NOI18N 795 case LIGHT: 796 return Bundle.getMessage("BeanNameLight"); // NOI18N 797 case SIGNALHEAD: 798 return Bundle.getMessage("BeanNameSignalHead"); // NOI18N 799 case SIGNALMAST: 800 return Bundle.getMessage("BeanNameSignalMast"); // NOI18N 801 case MEMORY: 802 return Bundle.getMessage("BeanNameMemory"); // NOI18N 803 case CONDITIONAL: 804 return Bundle.getMessage("BeanNameConditional"); // NOI18N 805 case WARRANT: 806 return Bundle.getMessage("BeanNameWarrant"); // NOI18N 807 case CLOCK: 808 return Bundle.getMessage("FastClock"); // NOI18N 809 case OBLOCK: 810 return Bundle.getMessage("BeanNameOBlock"); // NOI18N 811 case ENTRYEXIT: 812 return Bundle.getMessage("BeanNameEntryExit"); // NOI18N 813 default: 814 return ""; 815 } 816 } 817 818 @Nonnull 819 public static String getCompareOperationString(int index) { 820 switch (index) { 821 case LESS_THAN: 822 return rbx.getString("LessThan"); // NOI18N 823 case LESS_THAN_OR_EQUAL: 824 return rbx.getString("LessOrEqual"); // NOI18N 825 case 0: 826 case EQUAL: 827 return rbx.getString("Equal"); // NOI18N 828 case GREATER_THAN_OR_EQUAL: 829 return rbx.getString("GreaterOrEqual"); // NOI18N 830 case GREATER_THAN: 831 return rbx.getString("GreaterThan"); // NOI18N 832 default: 833 // fall through 834 break; 835 } 836 return ""; // NOI18N 837 } 838 839 @Nonnull 840 public static String getCompareSymbols(int index) { 841 switch (index) { 842 case LESS_THAN: 843 return "<"; // NOI18N 844 case LESS_THAN_OR_EQUAL: 845 return "<="; // NOI18N 846 case 0: 847 case EQUAL: 848 return "="; // NOI18N 849 case GREATER_THAN_OR_EQUAL: 850 return ">="; // NOI18N 851 case GREATER_THAN: 852 return ">"; // NOI18N 853 default: 854 break; 855 } 856 return ""; // NOI18N 857 } 858 859 /** 860 * Identify action Data from Text String. 861 * 862 * @param str the text to check 863 * @return the conditional action type or -1 if if string does not 864 * correspond to an action Data as defined in ConditionalAction 865 */ 866 @Nonnull 867 public static Conditional.Type stringToVariableTest(@Nonnull String str) { 868 if (str.equals(Bundle.getMessage("SignalHeadStateRed"))) { // NOI18N 869 return Conditional.Type.SIGNAL_HEAD_RED; 870 } else if (str.equals(Bundle.getMessage("SignalHeadStateYellow"))) { // NOI18N 871 return Conditional.Type.SIGNAL_HEAD_YELLOW; 872 } else if (str.equals(Bundle.getMessage("SignalHeadStateGreen"))) { // NOI18N 873 return Conditional.Type.SIGNAL_HEAD_GREEN; 874 } else if (str.equals(Bundle.getMessage("SignalHeadStateDark"))) { // NOI18N 875 return Conditional.Type.SIGNAL_HEAD_DARK; 876 } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingRed"))) { // NOI18N 877 return Conditional.Type.SIGNAL_HEAD_FLASHRED; 878 } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingYellow"))) { // NOI18N 879 return Conditional.Type.SIGNAL_HEAD_FLASHYELLOW; 880 } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingGreen"))) { // NOI18N 881 return Conditional.Type.SIGNAL_HEAD_FLASHGREEN; 882 } else if (str.equals(Bundle.getMessage("SignalHeadStateLunar"))) { // NOI18N 883 return Conditional.Type.SIGNAL_HEAD_LUNAR; 884 } else if (str.equals(Bundle.getMessage("SignalHeadStateFlashingLunar"))) { // NOI18N 885 return Conditional.Type.SIGNAL_HEAD_FLASHLUNAR; 886 } 887 // empty strings can occur frequently with types that have no integer data 888 if (str.length() > 0) { 889 log.warn("Unexpected parameter to stringToVariableTest({})", str); 890 } 891 return Conditional.Type.ERROR; 892 } 893 894 @Override 895 public String toString() { 896 String type = _type.getTestTypeString(); 897 Conditional.ItemType itemType = _type.getItemType(); 898 switch (itemType) { 899 case SENSOR: 900 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 901 Bundle.getMessage("BeanNameSensor"), getName(), type ); 902 case TURNOUT: 903 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 904 Bundle.getMessage("BeanNameTurnout"), getName(), type); 905 case LIGHT: 906 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 907 Bundle.getMessage("BeanNameLight"), getName(), type ); 908 case SIGNALHEAD: 909 if ((_type == Conditional.Type.SIGNAL_HEAD_LIT) 910 || (_type == Conditional.Type.SIGNAL_HEAD_HELD)) { 911 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 912 Bundle.getMessage("BeanNameSignalHead"), getName(), type ); 913 } else { 914 return MessageFormat.format(rbx.getString("SignalHeadStateDescrpt"), 915 Bundle.getMessage("BeanNameSignalHead"), getName(), type ); 916 } 917 case SIGNALMAST: 918 if ((_type == Conditional.Type.SIGNAL_MAST_LIT) 919 || (_type == Conditional.Type.SIGNAL_MAST_HELD)) { 920 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 921 Bundle.getMessage("BeanNameSignalMast"), getName(), type ); 922 } else { 923 return MessageFormat.format(rbx.getString("SignalMastStateDescrpt"), 924 Bundle.getMessage("BeanNameSignalMast"), getName(), _dataString ); 925 } 926 case MEMORY: 927 if ((_type == Conditional.Type.MEMORY_EQUALS) 928 || (_type == Conditional.Type.MEMORY_EQUALS_INSENSITIVE)) { 929 return MessageFormat.format(rbx.getString("MemoryValueDescrpt"), 930 Bundle.getMessage("BeanNameMemory"), getName(), 931 getCompareSymbols(_num1), _dataString); 932 } else { 933 return MessageFormat.format(rbx.getString("MemoryCompareDescrpt"), 934 Bundle.getMessage("BeanNameMemory"), getName(), 935 getCompareSymbols(_num1), _dataString); 936 } 937 case CONDITIONAL: 938 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 939 Bundle.getMessage("BeanNameConditional"), getGuiName(), type ); 940 case WARRANT: 941 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 942 rbx.getString("WarrantRoute"), getName(), type ); 943 case CLOCK: 944 return MessageFormat.format(rbx.getString("FastClockDescrpt"), 945 Bundle.getMessage("FastClock"), 946 LogixTableAction.formatTime(_num1 / 60, _num1 - ((_num1 / 60) * 60)), 947 LogixTableAction.formatTime(_num2 / 60, _num2 - ((_num2 / 60) * 60))); 948 case OBLOCK: 949 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 950 rbx.getString("OBlockStatus"), getName(), _dataString ); 951 case ENTRYEXIT: 952 NamedBean nb = getBean(); 953 return MessageFormat.format(rbx.getString("VarStateDescrpt"), 954 Bundle.getMessage("BeanNameEntryExit"), 955 nb == null ? "<NULL>" : nb.getUserName(), type); 956 case NONE: 957 return getName() + " type " + type; 958 default: 959 // fall through 960 break; 961 } 962 return super.toString(); 963 } 964 965 private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(ConditionalVariable.class); 966}