diff --git a/README.md b/README.md index 68c883b0..01cd9952 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,7 @@ Releases *Version 6.0.1* - Allow NULL values for daily schedule, exception schedule and schedule default - Fix scheduling issues when TimeValue sequences are not in chronological order +- Fix schedule object using incorrect time format to trigger next update *Version 6.0.0* - fix DeviceObjectTest.timeSynchronization test to pass diff --git a/src/main/java/com/serotonin/bacnet4j/obj/ScheduleObject.java b/src/main/java/com/serotonin/bacnet4j/obj/ScheduleObject.java index 213ec1ac..53cb5be2 100644 --- a/src/main/java/com/serotonin/bacnet4j/obj/ScheduleObject.java +++ b/src/main/java/com/serotonin/bacnet4j/obj/ScheduleObject.java @@ -331,18 +331,7 @@ private void updatePresentValue(final DateTime now) { nextCheck = nextDay(now); } else { // Find the schedule entry in effect at this time. - TimeValue currentTv = null; - int tvIndex = schedule.getCount(); - for (; tvIndex > 0; tvIndex--) { - final TimeValue tv = schedule.getBase1(tvIndex); - - if (!tv.getTime().after(now.getTime())) { - // Find time value entry that should be used - if (currentTv == null || tv.getTime().after(currentTv.getTime())) { - currentTv = tv; - } - } - } + TimeValue currentTv = getCurrentTimeValue(schedule, now); // Determine the new present value. // Our interpretation for ANSI/ASHRAE Standard 135-2016, 12.24.4 Present_Value @@ -355,11 +344,10 @@ private void updatePresentValue(final DateTime now) { else newValue = currentTv.getValue(); - // Determine the next time this method should run. - if (tvIndex < schedule.getCount()) { - final TimeValue nextTv = schedule.getBase1(tvIndex + 1); + final TimeValue nextTv = getNextTimeValue(currentTv, schedule, now); + if (nextTv != null) nextCheck = timeOf(now.getDate(), nextTv); - } else + else nextCheck = nextDay(now); } } @@ -367,11 +355,53 @@ private void updatePresentValue(final DateTime now) { writePropertyInternal(PropertyIdentifier.presentValue, newValue); final java.util.Date nextRuntime = new java.util.Date(nextCheck); - presentValueRefersher = getLocalDevice().schedule(() -> updatePresentValue(), nextRuntime.getTime(), - TimeUnit.MILLISECONDS); + long delay = nextRuntime.getTime() - now.getGC().getTimeInMillis(); + presentValueRefersher = getLocalDevice().schedule(this::updatePresentValue, delay, TimeUnit.MILLISECONDS); LOG.debug("Timer scheduled to run at {}", nextRuntime); } + private static TimeValue getCurrentTimeValue(SequenceOf schedule, DateTime now) { + TimeValue currentTv = null; + int tvIndex = schedule.getCount(); + for (; tvIndex > 0; tvIndex--) { + final TimeValue tv = schedule.getBase1(tvIndex); + if (!tv.getTime().after(now.getTime())) { + // Find time value entry that should be used + if (currentTv == null || tv.getTime().after(currentTv.getTime())) { + currentTv = tv; + } + } + } + return currentTv; + } + + private static TimeValue getNextTimeValue(TimeValue currentTv, SequenceOf schedule, DateTime now) { + TimeValue nextTv = null; + if (currentTv != null) nextTv = new TimeValue(currentTv.getTime(), currentTv.getValue()); + + int tvIndex = schedule.getCount(); + for (; tvIndex > 0; tvIndex--) { + final TimeValue tv = schedule.getBase1(tvIndex); + if (nextTv == null && isBeginningOfDay(now)){ + nextTv = tv; + } + if (nextTv != null && currentTv != null) { + if (tv.getTime().after(nextTv.getTime()) && tv.getTime().after(currentTv.getTime())) { + nextTv = tv; + } + } else if (nextTv != null) { + if (tv.getTime().before(nextTv.getTime())) { + nextTv = tv; + } + } + } + + if (nextTv != null && currentTv != null && nextTv.getTime().equals(currentTv.getTime())) { + return null; + } + return nextTv; + } + private static long nextDay(final DateTime now) { final GregorianCalendar gc = now.getGC(); gc.add(Calendar.DATE, 1); @@ -382,6 +412,14 @@ private static long nextDay(final DateTime now) { return gc.getTimeInMillis(); } + private static boolean isBeginningOfDay(final DateTime now) { + final GregorianCalendar gc = now.getGC(); + return gc.get(Calendar.HOUR_OF_DAY) == 0 && + gc.get(Calendar.MINUTE) == 0 && + gc.get(Calendar.SECOND) == 0 && + gc.get(Calendar.MILLISECOND) == 0; + } + private static long timeOf(final Date date, final TimeValue tv) { final DateTime dt = new DateTime(date, tv.getTime()); return dt.getGC().getTimeInMillis(); diff --git a/src/test/java/com/serotonin/bacnet4j/obj/ScheduleObjectTest.java b/src/test/java/com/serotonin/bacnet4j/obj/ScheduleObjectTest.java index 632e23c8..30eb7c95 100644 --- a/src/test/java/com/serotonin/bacnet4j/obj/ScheduleObjectTest.java +++ b/src/test/java/com/serotonin/bacnet4j/obj/ScheduleObjectTest.java @@ -142,7 +142,6 @@ private void testTime(final ScheduleObject so, final AnalogValueObject av0, fina final java.time.Month month, final int day, final int hour, final int min, final Primitive scheduledValue) throws Exception { clock.set(2115, month, day, hour, min, 0); - so.updatePresentValue(); Thread.sleep(100); // Let the requests be received. if (scheduledValue.getClass().equals(Null.class)) { @@ -415,8 +414,8 @@ private ScheduleObject createScheduleObject(AnalogValueObject av0, AnalogValueOb new TimeValue(new Time(20, 0, 0, 0), new Real(17)))), // new DailySchedule(new SequenceOf<>(new TimeValue(new Time(9, 0, 0, 0), new Real(18)), new TimeValue(new Time(21, 30, 0, 0), new Real(19)))), // - new DailySchedule(new SequenceOf()), // - new DailySchedule(new SequenceOf())); + new DailySchedule(new SequenceOf<>()), // + new DailySchedule(new SequenceOf<>())); // Every Friday, November to February, The Wednesday during the 4th week of each month - 8:00 and 22:00 (Priority:10) // Every Friday, November to February, The Wednesday during the 4th week of each month - 13:00 and 14:00 (Priority:7) @@ -441,8 +440,7 @@ private ScheduleObject createScheduleObject(AnalogValueObject av0, AnalogValueOb new DeviceObjectPropertyReference(av1.getId(), PropertyIdentifier.presentValue, null, null) // ); - final ScheduleObject so = new ScheduleObject(d1, 0, "sch0", effectivePeriod, weeklySchedule, exceptionSchedule, + return new ScheduleObject(d1, 0, "sch0", effectivePeriod, weeklySchedule, exceptionSchedule, scheduleDefault, listOfObjectPropertyReferences, 12, false); - return so; } }