001package jmri.implementation; 002 003import java.beans.PropertyChangeEvent; 004import java.beans.PropertyChangeListener; 005import java.util.ArrayList; 006import java.util.Collections; 007import java.util.Date; 008import java.util.List; 009import java.util.function.Predicate; 010import javax.annotation.Nonnull; 011import javax.swing.Timer; 012import jmri.*; 013import org.slf4j.Logger; 014import org.slf4j.LoggerFactory; 015 016/** 017 * Each LightControl object is linked to a specific Light, and provides one of 018 * the controls available for switching the Light ON/OFF in response to time or 019 * events occurring on the layout. 020 * <p> 021 * Each LightControl holds the information for one control of the parent Light. 022 * <p> 023 * Each Light may have as many controls as desired by the user. It is the user's 024 * responsibility to ensure that the various control mechanisms do not conflict 025 * with one another. 026 * <p> 027 * Available control types are those defined in the Light.java interface. 028 * Control types: SENSOR_CONTROL FAST_CLOCK_CONTROL TURNOUT_STATUS_CONTROL 029 * TIMED_ON_CONTROL TWO_SENSOR_CONTROL 030 * 031 * <hr> 032 * This file is part of JMRI. 033 * <p> 034 * JMRI is free software; you can redistribute it and/or modify it under the 035 * terms of version 2 of the GNU General Public License as published by the Free 036 * Software Foundation. See the "COPYING" file for a copy of this license. 037 * <p> 038 * JMRI is distributed in the hope that it will be useful, but WITHOUT ANY 039 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 040 * A PARTICULAR PURPOSE. See the GNU General Public License for more details. 041 * 042 * @author Dave Duchamp Copyright (C) 2010 043 */ 044public class DefaultLightControl implements LightControl { 045 046 /** 047 * Main constructor methods 048 */ 049 public DefaultLightControl() { 050 } 051 052 public DefaultLightControl(jmri.Light l) { 053 _parentLight = l; 054 } 055 056 // instance variables - saved with Light in configuration file 057 private int _controlType = Light.NO_CONTROL; // control type 058 private String _controlSensorName = ""; // controlling Sensor if SENSOR_CONTROL 059 protected int _controlSensorSense = Sensor.ACTIVE; // sense of Sensor for Light ON 060 private int _fastClockOnHour = 0; // on Hour if FAST_CLOCK_CONTROL 061 private int _fastClockOnMin = 0; // on Minute if FAST_CLOCK_CONTROL 062 private int _fastClockOffHour = 0; // off Hour if FAST_CLOCK_CONTROL 063 private int _fastClockOffMin = 0; // off Minute if FAST_CLOCK_CONTROL 064 private String _controlTurnoutName = ""; // turnout whose status is shown if TURNOUT_STATUS_CONTROL 065 private int _turnoutState = Turnout.CLOSED; // turnout state corresponding to this Light ON 066 private String _timedSensorName = ""; // trigger Sensor if TIMED_ON_CONTROL 067 protected int _timeOnDuration = 0; // duration (milliseconds) if TIMED_ON_CONTROL 068 private String _controlSensor2Name = ""; // second controlling sensor if TWO_SENSOR_CONTROL 069 070 /** 071 * Create a New LightControl from existing, 072 * for use when editing a LightControl 073 * 074 * @param lc the LightControl to be copied 075 */ 076 public DefaultLightControl(@Nonnull LightControl lc) { 077 this._controlType = lc.getControlType(); 078 this._controlSensorName = lc.getControlSensorName(); 079 this._controlSensorSense = lc.getControlSensorSense(); 080 this._fastClockOnHour = lc.getFastClockOnHour(); 081 this._fastClockOnMin = lc.getFastClockOnMin(); 082 this._fastClockOffHour = lc.getFastClockOffHour(); 083 this._fastClockOffMin = lc.getFastClockOffMin(); 084 this._controlTurnoutName = lc.getControlTurnoutName(); 085 this._turnoutState = lc.getControlTurnoutState(); 086 this._timedSensorName = lc.getTimedSensorName(); 087 this._timeOnDuration = lc.getTimedOnDuration(); 088 this._controlSensor2Name = lc.getControlSensor2Name(); 089 } 090 091 /** 092 * Test if a LightControl is equal to this one 093 * 094 * @param o the LightControl object to be checked 095 * @return True if the LightControl is equal, else false 096 */ 097 @Override 098 public boolean equals(Object o) { 099 if (!(o instanceof LightControl)) { 100 return false; 101 } 102 LightControl that = (LightControl) o; 103 if (that.getControlType() != this._controlType) return false; 104 boolean _shouldReturn = true; 105 switch(_controlType) { 106 case Light.NO_CONTROL : 107 break; 108 case Light.SENSOR_CONTROL : 109 if ((! that.getControlSensorName().equals(this._controlSensorName)) || 110 ( that.getControlSensorSense() != this._controlSensorSense)) _shouldReturn = false; 111 break; 112 case Light.FAST_CLOCK_CONTROL : 113 if ((that.getFastClockOffCombined() != this.getFastClockOffCombined()) || 114 (that.getFastClockOnCombined() != this.getFastClockOnCombined())) _shouldReturn = false; 115 break; 116 case Light.TURNOUT_STATUS_CONTROL : 117 if ((! that.getControlTurnoutName().equals(this._controlTurnoutName)) || 118 (that.getControlTurnoutState() != this._turnoutState)) _shouldReturn = false; 119 break; 120 case Light.TIMED_ON_CONTROL : 121 if ((! that.getTimedSensorName().equals(this._timedSensorName)) || 122 (that.getTimedOnDuration() != this._timeOnDuration)) _shouldReturn = false; 123 break; 124 case Light.TWO_SENSOR_CONTROL : 125 if ((! that.getControlSensorName().equals(this._controlSensorName)) || 126 (that.getControlSensorSense() != this._controlSensorSense) || 127 (! that.getControlSensor2Name().equals(this._controlSensor2Name))) _shouldReturn = false; 128 break; 129 default: 130 // unexpected _controlType value 131 jmri.util.LoggingUtil.warnOnce(log, "Unexpected _controlType = {}", _controlType); 132 } 133 return _shouldReturn; 134 } 135 136 /** {@inheritDoc} */ 137 @Override 138 public int hashCode() { 139 // matches with equals() by contract 140 return _controlType; 141 } 142 143 /** {@inheritDoc} */ 144 @Override 145 public int getControlType() { 146 return _controlType; 147 } 148 149 /** {@inheritDoc} */ 150 @Override 151 public void setControlType(int type) { 152 _controlType = type; 153 } 154 155 /** {@inheritDoc} */ 156 @Override 157 public void setControlSensorName(String sensorName) { 158 _controlSensorName = sensorName; 159 } 160 161 /** {@inheritDoc} */ 162 @Override 163 public int getControlSensorSense() { 164 return _controlSensorSense; 165 } 166 167 /** {@inheritDoc} */ 168 @Override 169 public String getControlSensorName() { 170 if (_namedControlSensor != null) { 171 return _namedControlSensor.getName(); 172 } 173 return _controlSensorName; 174 } 175 176 /** {@inheritDoc} */ 177 @Override 178 public void setControlSensorSense(int sense) { 179 if ( sense != Sensor.ACTIVE && sense != Sensor.INACTIVE ) { 180 log.error("Incorrect Sensor State Set"); 181 } else { 182 _controlSensorSense = sense; 183 } 184 } 185 186 /** {@inheritDoc} */ 187 @Override 188 public int getFastClockOnHour() { 189 return _fastClockOnHour; 190 } 191 192 /** {@inheritDoc} */ 193 @Override 194 public int getFastClockOnMin() { 195 return _fastClockOnMin; 196 } 197 198 /** {@inheritDoc} */ 199 @Override 200 public int getFastClockOnCombined() { 201 return _fastClockOnHour*60+_fastClockOnMin; 202 } 203 204 /** {@inheritDoc} */ 205 @Override 206 public int getFastClockOffHour() { 207 return _fastClockOffHour; 208 } 209 210 /** {@inheritDoc} */ 211 @Override 212 public int getFastClockOffMin() { 213 return _fastClockOffMin; 214 } 215 216 /** {@inheritDoc} */ 217 @Override 218 public int getFastClockOffCombined() { 219 return _fastClockOffHour*60+_fastClockOffMin; 220 } 221 222 /** {@inheritDoc} */ 223 @Override 224 public void setFastClockControlSchedule(int onHour, int onMin, int offHour, int offMin) { 225 _fastClockOnHour = onHour; 226 _fastClockOnMin = onMin; 227 _fastClockOffHour = offHour; 228 _fastClockOffMin = offMin; 229 } 230 231 /** {@inheritDoc} */ 232 @Override 233 public String getControlTurnoutName() { 234 return _controlTurnoutName; 235 } 236 237 /** {@inheritDoc} */ 238 @Override 239 public void setControlTurnout(String turnoutName) { 240 _controlTurnoutName = turnoutName; 241 } 242 243 /** {@inheritDoc} */ 244 @Override 245 public int getControlTurnoutState() { 246 return _turnoutState; 247 } 248 249 /** {@inheritDoc} */ 250 @Override 251 public void setControlTurnoutState(int state) { 252 if ( state != Turnout.CLOSED && state != Turnout.THROWN ) { 253 log.error("Incorrect Turnout State Set"); 254 } else { 255 _turnoutState = state; 256 } 257 } 258 259 /** {@inheritDoc} */ 260 @Override 261 public String getTimedSensorName() { 262 return _timedSensorName; 263 } 264 265 /** {@inheritDoc} */ 266 @Override 267 public String getControlTimedOnSensorName() { 268 if (_namedTimedControlSensor != null) { 269 return _namedTimedControlSensor.getName(); 270 } 271 return _timedSensorName; 272 } 273 274 /** {@inheritDoc} */ 275 @Override 276 public void setControlTimedOnSensorName(String sensorName) { 277 _timedSensorName = sensorName; 278 } 279 280 /** {@inheritDoc} */ 281 @Override 282 public int getTimedOnDuration() { 283 return _timeOnDuration; 284 } 285 286 /** {@inheritDoc} */ 287 @Override 288 public void setTimedOnDuration(int duration) { 289 _timeOnDuration = duration; 290 } 291 292 /** {@inheritDoc} */ 293 @Override 294 public String getControlSensor2Name() { 295 if (_namedControlSensor2 != null) { 296 return _namedControlSensor2.getName(); 297 } 298 return _controlSensor2Name; 299 } 300 301 /** {@inheritDoc} */ 302 @Override 303 public void setControlSensor2Name(String sensorName) { 304 _controlSensor2Name = sensorName; 305 } 306 307 /** {@inheritDoc} */ 308 @Override 309 public void setParentLight(Light l) { 310 _parentLight = l; 311 } 312 313 // operational instance variables - not saved between runs 314 private Light _parentLight = null; // Light that is being controlled 315 private boolean _active = false; 316 private NamedBeanHandle<Sensor> _namedControlSensor = null; 317 private PropertyChangeListener _sensorListener = null; 318 private NamedBeanHandle<Sensor> _namedControlSensor2 = null; 319 private PropertyChangeListener _sensor2Listener = null; 320 private PropertyChangeListener _timebaseListener = null; 321 private Timebase _clock = null; 322 private Turnout _controlTurnout = null; 323 private PropertyChangeListener _turnoutListener = null; 324 private NamedBeanHandle<Sensor> _namedTimedControlSensor = null; 325 private PropertyChangeListener _timedSensorListener = null; 326 private Timer _timedControlTimer = null; 327 private java.awt.event.ActionListener _timedControlListener = null; 328 private int _timeNow; 329 private PropertyChangeListener _parentLightListener = null; 330 private final jmri.NamedBeanHandleManager nbhm = InstanceManager.getDefault(jmri.NamedBeanHandleManager.class); 331 332 /** {@inheritDoc} */ 333 @Override 334 public String getDescriptionText(String lightName){ 335 StringBuilder name = new StringBuilder(jmri.jmrit.beantable.LightTableAction.lightControlTitle); 336 name.append(" "); 337 name.append(lightName); 338 name.append(" "); 339 name.append(jmri.jmrit.beantable.LightTableAction.getDescriptionText(this, getControlType())); 340 return name.toString(); 341 } 342 343 /** {@inheritDoc} */ 344 @Override 345 public void activateLightControl() { 346 // skip if Light Control is already active 347 if (_active) { 348 return; 349 } 350 351 if (_parentLight == null){ 352 log.error("No Parent Light when activating LightControl"); 353 return; 354 } 355 356 // register LightControl with Parent Light to indicate Control 357 // in use if user attempts to delete light 358 _parentLight.addPropertyChangeListener( 359 _parentLightListener = (PropertyChangeEvent e) -> { 360 },_parentLight.toString(), getDescriptionText("") ); 361 362 // activate according to control type 363 switch (_controlType) { 364 case Light.SENSOR_CONTROL: 365 _namedControlSensor = null; 366 if (!_controlSensorName.isEmpty()) { 367 Sensor sen = InstanceManager.sensorManagerInstance(). 368 provideSensor(_controlSensorName); 369 _namedControlSensor = nbhm.getNamedBeanHandle(_controlSensorName, sen); 370 } 371 if (_namedControlSensor != null) { 372 // if sensor state is currently known, set light accordingly 373 oneSensorChanged( _namedControlSensor.getBean().getKnownState() ); 374 // listen for change in sensor state 375 _namedControlSensor.getBean().addPropertyChangeListener( 376 _sensorListener = (PropertyChangeEvent e) -> { 377 if (e.getPropertyName().equals("KnownState")) { 378 oneSensorChanged( (int) e.getNewValue() ); 379 } 380 }, _controlSensorName, getDescriptionText(_parentLight.getDisplayName())); 381 _active = true; 382 } else { 383 // control sensor does not exist 384 log.error("Light {} is linked to a Sensor that does not exist: {}", 385 _parentLight.getSystemName(), _controlSensorName); 386 } 387 break; 388 case Light.FAST_CLOCK_CONTROL: 389 if (areFollowerTimesFaulty(_parentLight.getLightControlList())){ 390 log.error("Light has multiple actions for the same time in {}", 391 getDescriptionText(_parentLight.getDisplayName())); 392 } 393 if (_clock == null) { 394 _clock = InstanceManager.getDefault(jmri.Timebase.class); 395 } 396 // initialize light based on current fast time 397 updateClockControlLightFollower(); 398 // set up to listen for time changes on a minute basis 399 _clock.addMinuteChangeListener( 400 _timebaseListener = (PropertyChangeEvent e) -> { 401 updateClockControlLightFollower(); 402 }); 403 _active = true; 404 break; 405 case Light.TURNOUT_STATUS_CONTROL: 406 try { 407 _controlTurnout = InstanceManager.turnoutManagerInstance(). 408 provideTurnout(_controlTurnoutName); 409 } catch (IllegalArgumentException e) { 410 // control turnout does not exist 411 log.error("Light {} is linked to a Turnout that does not exist: {}", _parentLight.getSystemName(), _controlSensorName); 412 return; 413 } 414 // set light based on current turnout state if known 415 oneTurnoutChanged( _controlTurnout.getKnownState() ); 416 // listen for change in turnout state 417 _controlTurnout.addPropertyChangeListener( 418 _turnoutListener = (PropertyChangeEvent e) -> { 419 if (e.getPropertyName().equals("KnownState")) { 420 oneTurnoutChanged( (int) e.getNewValue() ); 421 } 422 }, _controlTurnoutName, getDescriptionText(_parentLight.getDisplayName())); 423 _active = true; 424 break; 425 case Light.TIMED_ON_CONTROL: 426 if (!_timedSensorName.isEmpty()) { 427 Sensor sen = InstanceManager.sensorManagerInstance(). 428 provideSensor(_timedSensorName); 429 _namedTimedControlSensor = nbhm.getNamedBeanHandle(_timedSensorName, sen); 430 } 431 if (_namedTimedControlSensor != null) { 432 if (_parentLight.getEnabled()) { 433 // set initial state off 434 _parentLight.setState(Light.OFF); 435 } 436 437 addNamedTimedControlListener(); 438 // listen for change in timed control sensor state 439 _active = true; 440 } else { 441 // timed control sensor does not exist 442 log.error("Light {} is linked to a Sensor that does not exist: {}", _parentLight.getSystemName(), _timedSensorName); 443 } 444 break; 445 case Light.TWO_SENSOR_CONTROL: 446 _namedControlSensor = null; 447 _namedControlSensor2 = null; 448 if (!_controlSensorName.isEmpty()) { 449 Sensor sen = InstanceManager.sensorManagerInstance(). 450 provideSensor(_controlSensorName); 451 _namedControlSensor = nbhm.getNamedBeanHandle(_controlSensorName, sen); 452 } 453 if (!_controlSensor2Name.isEmpty()) { 454 Sensor sen = InstanceManager.sensorManagerInstance(). 455 provideSensor(_controlSensor2Name); 456 _namedControlSensor2 = nbhm.getNamedBeanHandle(_controlSensor2Name, sen); 457 } 458 if ((_namedControlSensor != null) && (_namedControlSensor2 != null)) { 459 // if sensor state is currently known, set light accordingly 460 twoSensorChanged(); 461 // listen for change in sensor states 462 _sensorListener = addTwoSensorListener(_namedControlSensor.getBean()); 463 _sensor2Listener = addTwoSensorListener(_namedControlSensor2.getBean()); 464 _active = true; 465 } else { 466 // at least one control sensor does not exist 467 log.error("Light {} with 2 Sensor Control is linked to a Sensor that does not exist.", _parentLight.getSystemName()); 468 } 469 break; 470 default: 471 log.error("Unexpected control type when activating Light: {}", _parentLight); 472 } 473 474 } 475 476 /** 477 * Property Change Listener for Two Sensor. 478 */ 479 private PropertyChangeListener addTwoSensorListener(Sensor sensor) { 480 PropertyChangeListener pcl; 481 sensor.addPropertyChangeListener( 482 pcl = (PropertyChangeEvent e) -> { 483 if (e.getPropertyName().equals("KnownState")) { 484 twoSensorChanged(); 485 } 486 }, sensor.getDisplayName(), getDescriptionText(_parentLight.getDisplayName())); 487 return pcl; 488 } 489 490 /** 491 * Add a Timed Control Listener to a Sensor. 492 * 493 */ 494 private void addNamedTimedControlListener(){ 495 _namedTimedControlSensor.getBean().addPropertyChangeListener( 496 _timedSensorListener = (PropertyChangeEvent e) -> { 497 if (e.getPropertyName().equals("KnownState") 498 && (int) e.getNewValue() == Sensor.ACTIVE 499 && _timedControlTimer == null 500 && _parentLight.getEnabled()) { 501 // Turn light on 502 _parentLight.setState(Light.ON); 503 // Create a timer if one does not exist 504 _timedControlListener = new TimeLight(); 505 _timedControlTimer = new Timer(_timeOnDuration, 506 _timedControlListener); 507 // Start the Timer to turn the light OFF 508 _timedControlTimer.start(); 509 } 510 }, 511 _timedSensorName, getDescriptionText(_parentLight.getDisplayName())); 512 } 513 514 /** 515 * Internal routine for handling sensor change or startup 516 * for the 1 Sensor Control Type 517 */ 518 private void oneSensorChanged(int newSensorState){ 519 if (!_parentLight.getEnabled()) { 520 return; // ignore property change if user disabled Light 521 } 522 if (newSensorState == Sensor.ACTIVE) { 523 if (_controlSensorSense == Sensor.ACTIVE) { 524 // Turn light on 525 _parentLight.setState(Light.ON); 526 } else { 527 // Turn light off 528 _parentLight.setState(Light.OFF); 529 } 530 } else if (newSensorState == Sensor.INACTIVE) { 531 if (_controlSensorSense == Sensor.INACTIVE) { 532 // Turn light on 533 _parentLight.setState(Light.ON); 534 } else { 535 // Turn light off 536 _parentLight.setState(Light.OFF); 537 } 538 } 539 } 540 541 /** 542 * Internal routine for handling Turnout change or startup 543 * for the TURNOUT_STATUS_CONTROL Control Type 544 */ 545 private void oneTurnoutChanged(int newTurnoutState){ 546 if (!_parentLight.getEnabled()) { 547 return; // ignore property change if user disabled light 548 } 549 if (newTurnoutState == Turnout.CLOSED) { 550 if (_turnoutState == Turnout.CLOSED) { 551 // Turn light on 552 _parentLight.setState(Light.ON); 553 } else { 554 // Turn light off 555 _parentLight.setState(Light.OFF); 556 } 557 } else if (newTurnoutState == Turnout.THROWN) { 558 if (_turnoutState == Turnout.THROWN) { 559 // Turn light on 560 _parentLight.setState(Light.ON); 561 } else { 562 // Turn light off 563 _parentLight.setState(Light.OFF); 564 } 565 } 566 } 567 568 /** 569 * Internal routine for handling sensor changes 570 * for the 2 Sensor Control Type 571 */ 572 protected void twoSensorChanged() { 573 if (!_parentLight.getEnabled()) { 574 return; // ignore property change if user disabled Light 575 } 576 int kState = _namedControlSensor.getBean().getKnownState(); 577 int kState2 = _namedControlSensor2.getBean().getKnownState(); 578 if (_controlSensorSense == Sensor.ACTIVE) { 579 if ((kState == Sensor.ACTIVE) || (kState2 == Sensor.ACTIVE)) { 580 // Turn light on 581 _parentLight.setState(Light.ON); 582 } else { 583 // Turn light off 584 _parentLight.setState(Light.OFF); 585 } 586 } else if (_controlSensorSense == Sensor.INACTIVE) { 587 if ((kState == Sensor.INACTIVE) || (kState2 == Sensor.INACTIVE)) { 588 // Turn light on 589 _parentLight.setState(Light.ON); 590 } else { 591 // Turn light off 592 _parentLight.setState(Light.OFF); 593 } 594 } 595 } 596 597 /** 598 * Internal routine for seeing if we have the latest time to control the FastClock Follower. 599 * <p> 600 * Takes previous day times 601 * 602 * @return True if we have the most recent time ( either on or off ), otherwise False. 603 */ 604 private boolean isMasterFastClockFollower(){ 605 List<Integer> otherControlTimes= new ArrayList<>(); 606 List<Integer> thisControlTimes= new ArrayList<>(); 607 608 // put all other times in a single List to compare 609 _parentLight.getLightControlList().forEach((otherLc) -> { 610 if (otherLc!=this && otherLc.getControlType()==Light.FAST_CLOCK_CONTROL) { 611 // by adding 1440 mins to the today times, we can check yesterday in the same list. 612 otherControlTimes.add( otherLc.getFastClockOnCombined() ); // yesterdayOnTime 613 otherControlTimes.add( otherLc.getFastClockOffCombined() ); // yesterdayOffTime 614 otherControlTimes.add( otherLc.getFastClockOnCombined()+1440 ); // todayOnTime 615 otherControlTimes.add( otherLc.getFastClockOffCombined()+1440 ); // todayOffTime 616 } 617 }); 618 // log.debug("{} other control times in list {}",otherControlTimes.size(),otherControlTimes); 619 620 thisControlTimes.add( getFastClockOnCombined() ); // yesterdayOnTime 621 thisControlTimes.add( getFastClockOffCombined() ); // yesterdayOffTime 622 thisControlTimes.add( getFastClockOnCombined()+1440 ); // todayOnTime 623 thisControlTimes.add( getFastClockOffCombined()+1440 ); // todayOffTime 624 625 otherControlTimes.removeIf( e -> ( e > ( _timeNow +1440 ) )); // remove future times 626 thisControlTimes.removeIf( e -> ( e > ( _timeNow +1440 ) )); // remove future times 627 628 if (otherControlTimes.isEmpty()){ 629 return true; 630 } 631 return Collections.max(thisControlTimes) >= Collections.max(otherControlTimes); 632 } 633 634 /** {@inheritDoc} */ 635 @Override 636 public boolean onOffTimesFaulty() { 637 return (getFastClockOnCombined()==getFastClockOffCombined()); 638 } 639 640 /** 641 * @param time Combined hours / mins to check against. 642 */ 643 private Predicate<LightControl> isFastClockEqual(int time) { 644 return p -> ( !(p==this) && ( 645 p.getFastClockOnCombined() == time || p.getFastClockOffCombined() == time ) ); 646 } 647 648 /** {@inheritDoc} */ 649 @Override 650 public boolean areFollowerTimesFaulty( List<LightControl> compareList ) { 651 if (onOffTimesFaulty()){ 652 return true; 653 } 654 return (compareList.stream().anyMatch(isFastClockEqual(getFastClockOnCombined())) || 655 compareList.stream().anyMatch(isFastClockEqual(getFastClockOffCombined()))); 656 } 657 658 /** 659 * Updates the local int of the FastClock Time 660 */ 661 @SuppressWarnings("deprecation") // Date.getTime 662 private void setTheTime(){ 663 Date now = _clock.getTime(); 664 _timeNow = now.getHours() * 60 + now.getMinutes(); 665 } 666 667 /** 668 * Updates the status of a Light under FAST_CLOCK_CONTROL. This method is 669 * called every FastClock minute. 670 */ 671 private void updateClockControlLightFollower() { 672 if (!_parentLight.getEnabled()) { 673 return; // ignore property change if user disabled Light 674 } 675 if (_clock != null) { 676 setTheTime(); 677 // log.debug("updateClockControl, now is {} master {}",_timeNow,isMasterFastClockFollower()); 678 if (!isMasterFastClockFollower()){ 679 return; 680 } 681 int state = _parentLight.getState(); 682 if (getFastClockOnCombined() <= getFastClockOffCombined()) { 683 // on and off the same day 684 if ((_timeNow < getFastClockOnCombined()) || (_timeNow >= getFastClockOffCombined())) { 685 // Light should be OFF 686 if (state == Light.ON) { 687 logTimeChanges("OFF"); 688 _parentLight.setState(Light.OFF); 689 } 690 } else { 691 // Light should be ON 692 if (state == Light.OFF) { 693 logTimeChanges("ON"); 694 _parentLight.setState(Light.ON); 695 } 696 } 697 } else { 698 // on and off - different days 699 if ((_timeNow >= getFastClockOnCombined()) || (_timeNow < getFastClockOffCombined())) { 700 // Light should be ON 701 if (state == Light.OFF) { 702 logTimeChanges("ON"); 703 _parentLight.setState(Light.ON); 704 } 705 } else { 706 // Light should be OFF 707 if (state == Light.ON) { 708 logTimeChanges("OFF"); 709 _parentLight.setState(Light.OFF); 710 } 711 } 712 } 713 } 714 } 715 716 /** 717 * Outputs Time and Light Change info to log file. 718 * eg Output "DEBUG - 11:05 Setting Light My Light 2751 OFF" 719 */ 720 private void logTimeChanges(String onOrOff){ 721 log.debug("{}:{} Setting Light {} {}", 722 (_timeNow/60),String.format("%02d", (_timeNow % 60)), 723 _parentLight.getDisplayName(),onOrOff); 724 } 725 726 /** {@inheritDoc} */ 727 @Override 728 public void deactivateLightControl() { 729 // skip if Light Control is not active 730 if (_active) { 731 _parentLight.removePropertyChangeListener(_parentLightListener); 732 if (_sensorListener != null) { 733 _namedControlSensor.getBean().removePropertyChangeListener(_sensorListener); 734 _sensorListener = null; 735 } 736 if ((_clock != null) && (_timebaseListener != null)) { 737 _clock.removeMinuteChangeListener(_timebaseListener); 738 _timebaseListener = null; 739 } 740 if (_turnoutListener != null) { 741 _controlTurnout.removePropertyChangeListener(_turnoutListener); 742 _turnoutListener = null; 743 } 744 if (_timedSensorListener != null) { 745 _namedTimedControlSensor.getBean().removePropertyChangeListener(_timedSensorListener); 746 _timedSensorListener = null; 747 } 748 if (_timedControlListener != null && _timedControlTimer != null) { 749 _timedControlTimer.removeActionListener(_timedControlListener); 750 _timedControlListener = null; 751 } 752 if (_timedControlTimer != null) { 753 _timedControlTimer.stop(); 754 _timedControlTimer = null; 755 } 756 if (_sensor2Listener != null) { 757 _namedControlSensor2.getBean().removePropertyChangeListener(_sensor2Listener); 758 _sensor2Listener = null; 759 } 760 _active = false; 761 } 762 } 763 764 /** 765 * Class for defining ActionListener for TIMED_ON_CONTROL 766 */ 767 private class TimeLight implements java.awt.event.ActionListener { 768 769 @Override 770 public void actionPerformed(java.awt.event.ActionEvent event) { 771 // Turn Light OFF 772 _parentLight.setState(Light.OFF); 773 // Turn Timer OFF 774 if (_timedControlTimer != null ) { 775 _timedControlTimer.stop(); 776 } 777 _timedControlTimer = null; 778 } 779 } 780 781 private final static Logger log = LoggerFactory.getLogger(DefaultLightControl.class); 782}