Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RAD-5573 Fix schedule object using incorrect time format #101

Merged
merged 1 commit into from
Jul 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
74 changes: 56 additions & 18 deletions src/main/java/com/serotonin/bacnet4j/obj/ScheduleObject.java
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -355,23 +344,64 @@ 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);
}
}

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<TimeValue> 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<TimeValue> 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);
Expand All @@ -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();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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<TimeValue>()), //
new DailySchedule(new SequenceOf<TimeValue>()));
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)
Expand All @@ -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;
}
}