Skip to content
This repository was archived by the owner on Sep 1, 2024. It is now read-only.

Commit 13648e2

Browse files
committed
Migrated to timeline 0.7.0
Updated scheduler to properly restore activities to be scheduled
1 parent 683f45f commit 13648e2

6 files changed

Lines changed: 153 additions & 16 deletions

File tree

eu.dariolucia.reatmetric.driver.httpserver/src/main/resources/ext/rtmt-mimics.js

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -488,7 +488,6 @@ class TextNodeApplier extends Runnable {
488488
}
489489

490490
class BlinkNodeProcessor extends SvgAttributeProcessor {
491-
// TODO: cache the BlinkNodeApplier operation to avoid the search of the animate node?
492491
constructor(svgElement, name, value, objPropertyNames) {
493492
super(svgElement, name, value, objPropertyNames);
494493
}
@@ -540,7 +539,6 @@ class BlinkNodeApplier extends Runnable {
540539
}
541540

542541
class RotateNodeProcessor extends SvgAttributeProcessor {
543-
// TODO: cache the BlinkNodeApplier operation to avoid the search of the animate node?
544542
constructor(svgElement, name, value, objPropertyNames) {
545543
super(svgElement, name, value, objPropertyNames);
546544
}

eu.dariolucia.reatmetric.scheduler/Documentation.adoc

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,7 +128,12 @@ A commented example of such file is presented below.
128128

129129
[source,xml]
130130
----
131-
<ns1:scheduler xmlns:ns1="http://dariolucia.eu/reatmetric/scheduler" scheduler-enabled="true">
131+
<ns1:scheduler xmlns:ns1="http://dariolucia.eu/reatmetric/scheduler" scheduler-enabled="true"
132+
run-past-scheduled-activities="false">
133+
<!-- scheduler-enabled: status of the scheduler when the system starts, default is true -->
134+
<!-- run-past-scheduled-activities: if true, activities in status SCHEDULED in the past are restored and
135+
immediately executed; if false, scheduled activities in the past are marked as aborted. Future scheduled
136+
activities are restored and configured ready for execution at the scheduled time -->
132137
<!-- Define a new bot with name 'Bot Test 1', which is enabled by default and will execute the configured
133138
actions for the state upon initialisation -->
134139
<bot-definition name="Bot Test 1" execute-on-init="false" enabled="true">

eu.dariolucia.reatmetric.scheduler/src/main/java/eu/dariolucia/reatmetric/scheduler/Scheduler.java

Lines changed: 132 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -245,8 +245,8 @@ private void initFromArchive() {
245245
// Transition the RUNNING and WAITING activities in:
246246
// RUNNING -> UNKNOWN
247247
// WAITING -> ABORTED
248-
// TODO: transition activities that are SCHEDULED and scheduled time is in the past --> ABORTED
249-
// See method restoreActivitiesFromList
248+
// Transition activities that are SCHEDULED and scheduled time is in the past --> ABORTED
249+
// See method restoreActivitiesFromList
250250
try {
251251
// This part retrieves everything but not the event-based scheduled activities, for which the generation time is set
252252
// to EPOCH.
@@ -257,7 +257,7 @@ private void initFromArchive() {
257257

258258
restoreActivitiesFromList(scheduledItems);
259259

260-
// Now it is the time of the event-based activities
260+
// Now it is the time of the event-based activities, start and end time set to EPOCH
261261
scheduledItems = archive.retrieve(Instant.EPOCH,
262262
new ScheduledActivityDataFilter(null, null, null, null,
263263
Collections.singletonList(SchedulingState.SCHEDULED), null),
@@ -271,14 +271,97 @@ private void initFromArchive() {
271271
}
272272

273273
private void restoreActivitiesFromList(List<ScheduledActivityData> scheduledItems) throws SchedulingException {
274+
Instant now = Instant.now();
275+
// Partition: first schedule all the AbsoluteTimeSchedulingTrigger and the others are in another group
276+
List<ScheduledActivityData> absoluteScheduled = scheduledItems.stream().filter(i -> i.getTrigger() instanceof AbsoluteTimeSchedulingTrigger).collect(Collectors.toList());
277+
List<ScheduledActivityData> relativeScheduled = scheduledItems.stream().filter(i -> i.getTrigger() instanceof RelativeTimeSchedulingTrigger).collect(Collectors.toList());
278+
List<ScheduledActivityData> otherScheduled = scheduledItems.stream().filter(i -> !(i.getTrigger() instanceof AbsoluteTimeSchedulingTrigger) &&
279+
!(i.getTrigger() instanceof RelativeTimeSchedulingTrigger)).collect(Collectors.toList());
280+
// Sort the relative-scheduled items
281+
relativeScheduled = buildSortedRelativeScheduled(relativeScheduled);
282+
// Restore items
283+
restoreActivities(absoluteScheduled, now);
284+
restoreActivities(otherScheduled, now);
285+
restoreActivities(relativeScheduled, now);
286+
}
287+
288+
/**
289+
* The idea of this method is to build a list of scheduled activities, which would be able to resolve their predecessors
290+
* as they will be added to the scheduler. Therefore, the list will first have the activities without predecessors
291+
* (or with predecessors of other types). And only then, the activities with predecessors also of type relative-time,
292+
* in the correct order.
293+
* @param relativeScheduled the list to order
294+
* @return the ordered list
295+
*/
296+
private List<ScheduledActivityData> buildSortedRelativeScheduled(List<ScheduledActivityData> relativeScheduled) {
297+
List<ScheduledActivityData> relativeScheduledToReturn = new ArrayList<>(relativeScheduled.size());
298+
Queue<ScheduledActivityData> toProcess = new LinkedList<>(relativeScheduled);
299+
int pushBackIterations = 0;
300+
while(!toProcess.isEmpty()) {
301+
// Get the top
302+
ScheduledActivityData d = toProcess.remove();
303+
RelativeTimeSchedulingTrigger trigger = (RelativeTimeSchedulingTrigger) d.getTrigger();
304+
// Get the referenced activities
305+
boolean backInQueue = false;
306+
for(String pred : trigger.getPredecessors()) {
307+
// Look for predecessor in the queue: if it is there, then put the item back in the queue
308+
for(ScheduledActivityData sad : toProcess) {
309+
if(sad.getExternalId().equals(pred)) {
310+
toProcess.add(d);
311+
backInQueue = true;
312+
++pushBackIterations;
313+
break;
314+
}
315+
}
316+
if(backInQueue) {
317+
break;
318+
}
319+
}
320+
if(!backInQueue) {
321+
relativeScheduledToReturn.add(d);
322+
pushBackIterations = 0;
323+
}
324+
if(pushBackIterations > toProcess.size() + 1) {
325+
LOG.warning("Cycle detected when restoring scheduled items with relative time");
326+
// Cycle, stop and copy all remaining data in the list to return.
327+
// It should not happen.
328+
relativeScheduledToReturn.addAll(toProcess);
329+
break;
330+
}
331+
}
332+
return relativeScheduledToReturn;
333+
}
334+
335+
private void restoreActivities(List<ScheduledActivityData> scheduledItems, Instant now) throws SchedulingException {
274336
for (ScheduledActivityData item : scheduledItems) {
275337
if (item.getState() == SchedulingState.SCHEDULED) {
276-
// Create ScheduledTask
277-
ScheduledTask st = new ScheduledTask(this, timer, dispatcher, item);
278-
id2scheduledTask.put(st.getId(), st);
279-
// Prepare execution event depending on trigger (absolute, relative, event)
280-
st.armTrigger();
338+
if(isToBeScheduled(item, now)) {
339+
LOG.info("Restoring scheduled activity: " + item);
340+
// Create ScheduledTask
341+
ScheduledTask st = new ScheduledTask(this, timer, dispatcher, item);
342+
id2scheduledTask.put(st.getId(), st);
343+
// Prepare execution event depending on trigger (absolute, relative, event)
344+
st.armTrigger();
345+
} else {
346+
LOG.warning("Scheduled activity not restored (ABORTED): " + item);
347+
// ABORTED, it will never start
348+
storeAndDistribute(new ScheduledActivityData(item.getInternalId(),
349+
item.getGenerationTime(),
350+
item.getRequest(),
351+
item.getActivityOccurrence(),
352+
item.getResources(),
353+
item.getSource(),
354+
item.getExternalId(),
355+
item.getTrigger(),
356+
item.getLatestInvocationTime(),
357+
item.getStartTime(),
358+
item.getDuration(),
359+
item.getConflictStrategy(),
360+
SchedulingState.ABORTED,
361+
item.getExtension()));
362+
}
281363
} else if (item.getState() == SchedulingState.RUNNING) {
364+
LOG.warning("Scheduled running activity restored with status UNKNOWN: " + item);
282365
storeAndDistribute(new ScheduledActivityData(item.getInternalId(),
283366
item.getGenerationTime(),
284367
item.getRequest(),
@@ -294,6 +377,7 @@ private void restoreActivitiesFromList(List<ScheduledActivityData> scheduledItem
294377
SchedulingState.UNKNOWN,
295378
item.getExtension()));
296379
} else if (item.getState() == SchedulingState.WAITING) {
380+
LOG.warning("Scheduled waiting activity restored with status ABORTED: " + item);
297381
storeAndDistribute(new ScheduledActivityData(item.getInternalId(),
298382
item.getGenerationTime(),
299383
item.getRequest(),
@@ -312,6 +396,45 @@ private void restoreActivitiesFromList(List<ScheduledActivityData> scheduledItem
312396
}
313397
}
314398

399+
private boolean isToBeScheduled(ScheduledActivityData item, Instant now) {
400+
if(this.configuration.isRunPastScheduledActivities()) {
401+
return true;
402+
} else if(item.getTrigger() instanceof EventBasedSchedulingTrigger) {
403+
// Always schedule
404+
return true;
405+
} else if(item.getTrigger() instanceof AbsoluteTimeSchedulingTrigger) {
406+
Instant expectedStart = ((AbsoluteTimeSchedulingTrigger) item.getTrigger()).getReleaseTime();
407+
return expectedStart.equals(now) || expectedStart.isAfter(now);
408+
} else if(item.getTrigger() instanceof NowSchedulingTrigger) {
409+
// NowSchedulingTrigger are inaccurate, they were supposed to start right away, so at this stage do not restore
410+
return false;
411+
} else if(item.getTrigger() instanceof RelativeTimeSchedulingTrigger) {
412+
RelativeTimeSchedulingTrigger trigger = (RelativeTimeSchedulingTrigger) item.getTrigger();
413+
// Check that predecessors are all in the schedule and with right state
414+
for(String extId : trigger.getPredecessors()) {
415+
boolean found = false;
416+
for(ScheduledTask st : id2scheduledTask.values()) {
417+
if(Objects.equals(st.getCurrentData().getExternalId(), extId)) {
418+
found = true;
419+
// If the status is SCHEDULED --> OK, else return false
420+
if(st.getCurrentData().getState() != SchedulingState.SCHEDULED) {
421+
return false;
422+
}
423+
break;
424+
}
425+
}
426+
if(!found) {
427+
// Well, in the past or non-existing --> do not schedule
428+
return false;
429+
}
430+
}
431+
return true;
432+
} else {
433+
// ???
434+
return false;
435+
}
436+
}
437+
315438
@Override
316439
public void subscribe(ISchedulerSubscriber subscriber) {
317440
dispatcher.submit(() -> {
@@ -718,7 +841,7 @@ void removeTask(IUniqueId scheduledId) {
718841
// remove from the current internal set
719842
ScheduledTask st = id2scheduledTask.remove(scheduledId);
720843
if (st != null) {
721-
LOG.info(String.format("Removing scheduled task %s (%d)", st.getRequest().getRequest().getPath().asString(), st.getId().asLong()));
844+
LOG.info(String.format("Removing scheduled task %s (%s)", st.getRequest().getRequest().getPath().asString(), st.getRequest().getExternalId()));
722845
st.abortTask();
723846
// Update or remove in the archive
724847
ScheduledActivityData sad = st.getCurrentData();

eu.dariolucia.reatmetric.scheduler/src/main/java/eu/dariolucia/reatmetric/scheduler/definition/SchedulerConfiguration.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,9 @@ public static SchedulerConfiguration load(InputStream is) throws IOException {
4343
@XmlAttribute(name = "scheduler-enabled")
4444
private boolean schedulerEnabled = true;
4545

46+
@XmlAttribute(name = "run-past-scheduled-activities")
47+
private boolean runPastScheduledActivities = false;
48+
4649
@XmlElement(name = "bot-definition")
4750
private List<BotProcessingDefinition> bots = new LinkedList<>();
4851

@@ -61,4 +64,12 @@ public boolean isSchedulerEnabled() {
6164
public void setSchedulerEnabled(boolean schedulerEnabled) {
6265
this.schedulerEnabled = schedulerEnabled;
6366
}
67+
68+
public boolean isRunPastScheduledActivities() {
69+
return runPastScheduledActivities;
70+
}
71+
72+
public void setRunPastScheduledActivities(boolean runPastScheduledActivities) {
73+
this.runPastScheduledActivities = runPastScheduledActivities;
74+
}
6475
}

eu.dariolucia.reatmetric.ui/src/main/java/eu/dariolucia/reatmetric/ui/controller/SchedulerViewController.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -382,10 +382,10 @@ private void initializeColorMaps() {
382382
}
383383

384384
this.ganttChart.setBackgroundColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_BGCOLOR_PREFIX)));
385-
this.ganttChart.setPanelBackgroundColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_PANEL_BGCOLOR_PREFIX)));
385+
this.ganttChart.setPanelBackground(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_PANEL_BGCOLOR_PREFIX)));
386386
this.ganttChart.setPanelForegroundColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_PANEL_FGCOLOR_PREFIX)));
387387
this.ganttChart.setPanelBorderColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_PANEL_BORDERCOLOR_PREFIX)));
388-
this.ganttChart.setHeaderBackgroundColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_HEADER_BGCOLOR_PREFIX)));
388+
this.ganttChart.setHeaderBackground(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_HEADER_BGCOLOR_PREFIX)));
389389
this.ganttChart.setHeaderForegroundColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_HEADER_FGCOLOR_PREFIX)));
390390
this.ganttChart.setHeaderBorderColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_HEADER_BORDERCOLOR_PREFIX)));
391391
this.ganttChart.setSelectBorderColor(Color.valueOf(this.viewConfiguration.getProperty(TIMELINE_SELECTCOLOR_PREFIX)));
@@ -692,7 +692,7 @@ private void addToActivityList(ScheduledActivityOccurrenceDataWrapper wrapper) {
692692
data.startTimeProperty().bind(wrapper.startTimeProperty());
693693
data.expectedDurationProperty().bind(wrapper.durationProperty().map(Duration::toSeconds));
694694
data.actualDurationProperty().bind(wrapper.actualDurationProperty().map(Duration::getSeconds));
695-
data.taskBackgroundColorProperty().bind(wrapper.stateProperty().map(this::stateToBgColor));
695+
data.taskBackgroundProperty().bind(wrapper.stateProperty().map(this::stateToBgColor));
696696
data.taskTextColorProperty().bind(wrapper.stateProperty().map(this::stateToTextColor));
697697
// Add data to series
698698
series.getItems().add(data);

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@
6868
<groovy.version>2.5.13</groovy.version>
6969
<jython.version>2.7.2</jython.version>
7070
<ccsds.version>1.0.6</ccsds.version>
71-
<timeline.version>0.6.0</timeline.version>
71+
<timeline.version>0.7.0</timeline.version>
7272
<controlsfx.version>11.0.1</controlsfx.version>
7373
<usb4java.version>1.3.0</usb4java.version>
7474
<jserialcomm.version>2.8.0</jserialcomm.version>

0 commit comments

Comments
 (0)