diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/plugin.xml b/plugins/org.eclipse.fordiac.ide.debug.ui/plugin.xml
index 3f5624ea58..e89bac3456 100644
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/plugin.xml
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/plugin.xml
@@ -111,7 +111,7 @@
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/Messages.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/Messages.java
index c6b1c7186f..76f5fe37e9 100644
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/Messages.java
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/Messages.java
@@ -16,7 +16,19 @@
@SuppressWarnings("squid:S3008") // tell sonar the java naming convention does not make sense for this class
public final class Messages extends NLS {
- private static final String BUNDLE_NAME = Messages.class.getPackageName() + ".messages"; //$NON-NLS-1$
+ private static final String BUNDLE_NAME = "org.eclipse.fordiac.ide.debug.ui.messages"; //$NON-NLS-1$
+ public static String DebugClockWidget_ClockIntervalTextLabel;
+ public static String DebugClockWidget_FixedClock;
+ public static String DebugClockWidget_IntervalClock;
+ public static String DebugClockWidget_InvalidInterval;
+ public static String DebugClockWidget_InvalidMonotonicClockValue;
+ public static String DebugClockWidget_InvalidRealtimeClockValue;
+ public static String DebugClockWidget_MonotonicClock;
+ public static String DebugClockWidget_MonotonicClockTextLabel;
+ public static String DebugClockWidget_RealtimeClock;
+ public static String DebugClockWidget_RealtimeClockTextLabel;
+ public static String DebugClockWidget_SystemClock;
+ public static String DebugClockWidget_Title;
public static String EvaluatorDebugFindAction_Text;
public static String EvaluatorDebugFindDialog_Title;
public static String EvaluatorVariableValueEditor_Exception;
@@ -31,18 +43,11 @@ public final class Messages extends NLS {
public static String MainLaunchConfigurationTab_ErrorUpdatingArguments;
public static String MainLaunchConfigurationTab_InvalidValueMessage;
public static String MainLaunchConfigurationTab_InvalidValueTitle;
-
- public static String FBLaunchConfigurationTab_ClockInterval;
- public static String FBLaunchConfigurationTab_DebugTime;
- public static String FBLaunchConfigurationTab_ERROR_InvalidDebugTime;
+ public static String FBDebugViewClockWidget_Apply;
+ public static String FBDebugViewClockWidget_InvalidValues;
public static String FBLaunchConfigurationTab_Event;
- public static String FBLaunchConfigurationTab_IncrementClockAfterEachEventBySpecifiedAmount;
public static String FBLaunchConfigurationTab_KeepDebuggerRunningWhenIdle;
public static String FBLaunchConfigurationTab_RepeatEvent;
-
- public static String FBLaunchConfigurationTab_UseFixedClockWithSpecifiedTime;
- public static String FBLaunchConfigurationTab_UseSystemClock;
-
static {
// initialize resource bundle
NLS.initializeMessages(BUNDLE_NAME, Messages.class);
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/fb/FBLaunchConfigurationTab.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/fb/FBLaunchConfigurationTab.java
index d9f78b813d..005a45674f 100644
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/fb/FBLaunchConfigurationTab.java
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/fb/FBLaunchConfigurationTab.java
@@ -17,6 +17,7 @@
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
+import java.util.Optional;
import java.util.stream.Stream;
import org.eclipse.core.resources.IFile;
@@ -29,6 +30,7 @@
import org.eclipse.fordiac.ide.debug.fb.FBLaunchConfigurationDelegate;
import org.eclipse.fordiac.ide.debug.ui.MainLaunchConfigurationTab;
import org.eclipse.fordiac.ide.debug.ui.Messages;
+import org.eclipse.fordiac.ide.debug.ui.widgets.DebugClockWidget;
import org.eclipse.fordiac.ide.model.eval.variable.Variable;
import org.eclipse.fordiac.ide.model.libraryElement.AdapterDeclaration;
import org.eclipse.fordiac.ide.model.libraryElement.AdapterFB;
@@ -47,32 +49,32 @@
import org.eclipse.jface.viewers.LabelProvider;
import org.eclipse.jface.viewers.StructuredSelection;
import org.eclipse.swt.SWT;
-import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Control;
import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
public class FBLaunchConfigurationTab extends MainLaunchConfigurationTab {
private ComboViewer eventCombo;
private Button repeatEventCheckbox;
private Button keepDebuggerRunningCheckbox;
- private Button systemTimeRadio;
- private Button incrementTimeRadio;
- private Button manualTimeRadio;
- private Text debugTimeText;
+ private final DebugClockWidget clockWidget = new DebugClockWidget(this::updateLaunchConfigurationDialog);
@Override
public void createControl(final Composite parent) {
super.createControl(parent);
- final Composite eventComponent = createEventComponent((Composite) getControl());
+ final Composite comp = (Composite) getControl();
+
+ final Composite eventComponent = createEventComponent(comp);
GridDataFactory.fillDefaults().grab(true, false).applyTo(eventComponent);
- final Composite argumentsComponent = createArgumentsComponent((Composite) getControl());
+ final Composite argumentsComponent = createArgumentsComponent(comp);
GridDataFactory.fillDefaults().grab(true, true).applyTo(argumentsComponent);
+
+ final Control clockComponent = clockWidget.createControl(comp);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(clockComponent);
}
@Override
@@ -90,9 +92,6 @@ protected Composite createOptionsComponent(final Composite parent) {
keepDebuggerRunningCheckbox.addSelectionListener(widgetSelectedAdapter(e -> updateLaunchConfigurationDialog()));
GridDataFactory.fillDefaults().applyTo(keepDebuggerRunningCheckbox);
- final Composite debugTimeComponent = createDebugTimeComponent(comp);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(debugTimeComponent);
-
return group;
}
@@ -114,47 +113,6 @@ protected Composite createEventComponent(final Composite parent) {
return group;
}
- protected Composite createDebugTimeComponent(final Composite parent) {
- final Group group = new Group(parent, SWT.BORDER);
- GridLayoutFactory.swtDefaults().applyTo(group);
- group.setText(Messages.FBLaunchConfigurationTab_DebugTime);
-
- final Composite radioComp = new Composite(group, SWT.NONE);
- GridLayoutFactory.swtDefaults().numColumns(1).applyTo(radioComp);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(radioComp);
-
- systemTimeRadio = new Button(radioComp, SWT.RADIO);
- systemTimeRadio.setText(Messages.FBLaunchConfigurationTab_UseSystemClock);
- systemTimeRadio.addListener(SWT.Selection, e -> updateLaunchConfigurationDialog());
- GridDataFactory.fillDefaults().applyTo(systemTimeRadio);
-
- incrementTimeRadio = new Button(radioComp, SWT.RADIO);
- incrementTimeRadio.setText(Messages.FBLaunchConfigurationTab_IncrementClockAfterEachEventBySpecifiedAmount);
- incrementTimeRadio.addListener(SWT.Selection, e -> updateLaunchConfigurationDialog());
- GridDataFactory.fillDefaults().applyTo(incrementTimeRadio);
-
- manualTimeRadio = new Button(radioComp, SWT.RADIO);
- manualTimeRadio.setText(Messages.FBLaunchConfigurationTab_UseFixedClockWithSpecifiedTime);
- manualTimeRadio.addListener(SWT.Selection, e -> updateLaunchConfigurationDialog());
- GridDataFactory.fillDefaults().applyTo(manualTimeRadio);
-
- final Composite c = new Composite(group, SWT.NONE);
- GridLayoutFactory.swtDefaults().numColumns(3).applyTo(c);
- GridDataFactory.fillDefaults().grab(true, false).applyTo(c);
-
- final Label clockIntervalLabel = new Label(c, SWT.NONE);
- clockIntervalLabel.setText(Messages.FBLaunchConfigurationTab_ClockInterval);
- debugTimeText = new Text(c, SWT.BORDER);
- debugTimeText.setText("0"); //$NON-NLS-1$
- debugTimeText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
- debugTimeText.addSegmentListener(e -> updateLaunchConfigurationDialog());
- final Label debugTimeLabel = new Label(c, SWT.NONE);
- debugTimeLabel.setText("ms"); //$NON-NLS-1$
- debugTimeLabel.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, false, false, 1, 1));
-
- return group;
- }
-
@Override
public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
super.setDefaults(configuration);
@@ -163,6 +121,8 @@ public void setDefaults(final ILaunchConfigurationWorkingCopy configuration) {
configuration.removeAttribute(FBLaunchConfigurationAttributes.KEEP_RUNNING_WHEN_IDLE);
configuration.removeAttribute(FBLaunchConfigurationAttributes.CLOCK_MODE);
configuration.removeAttribute(FBLaunchConfigurationAttributes.CLOCK_INTERVAL);
+ configuration.removeAttribute(FBLaunchConfigurationAttributes.CLOCK_REALTIME_OFFSET);
+ configuration.removeAttribute(FBLaunchConfigurationAttributes.CLOCK_MONOTONIC_OFFSET);
}
@Override
@@ -179,10 +139,12 @@ public void initializeFrom(final ILaunchConfiguration configuration) {
repeatEventCheckbox.setSelection(FBLaunchConfigurationAttributes.isRepeatEvent(configuration));
keepDebuggerRunningCheckbox
.setSelection(FBLaunchConfigurationAttributes.isKeepRunningWhenIdle(configuration));
- systemTimeRadio.setSelection(FBLaunchConfigurationAttributes.isSystem(configuration));
- incrementTimeRadio.setSelection(FBLaunchConfigurationAttributes.isIncrement(configuration));
- manualTimeRadio.setSelection(FBLaunchConfigurationAttributes.isManual(configuration));
- debugTimeText.setText(FBLaunchConfigurationAttributes.getClockIntervalText(configuration));
+ clockWidget.setSelectedClockMode(FBLaunchConfigurationAttributes.getClockMode(configuration));
+ clockWidget.setClockIntervalText(FBLaunchConfigurationAttributes.getClockIntervalText(configuration));
+ clockWidget
+ .setRealtimeClockText(FBLaunchConfigurationAttributes.getClockRealtimeOffsetText(configuration));
+ clockWidget.setMonotonicClockText(
+ FBLaunchConfigurationAttributes.getClockMonotonicOffsetText(configuration));
} catch (final CoreException e) {
// ignore
}
@@ -202,21 +164,16 @@ public void performApply(final ILaunchConfigurationWorkingCopy configuration) {
configuration.setAttribute(FBLaunchConfigurationAttributes.KEEP_RUNNING_WHEN_IDLE,
keepDebuggerRunningCheckbox.getSelection());
- if (systemTimeRadio.getSelection()) {
- configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_MODE, FBDebugClockMode.SYSTEM.toString());
- }
- if (incrementTimeRadio.getSelection()) {
- configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_MODE,
- FBDebugClockMode.INCREMENT.toString());
+ final FBDebugClockMode clockMode = clockWidget.getSelectedClockMode();
+ configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_MODE, clockMode.toString());
+ if (clockMode == FBDebugClockMode.INTERVAL) {
+ configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_INTERVAL, clockWidget.getClockIntervalText());
}
- if (manualTimeRadio.getSelection()) {
- configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_MODE, FBDebugClockMode.MANUAL.toString());
- }
- final String debugTime = debugTimeText.getText();
- if (!debugTime.isBlank()) {
- configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_INTERVAL, debugTime);
- } else {
- configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_INTERVAL, "0"); //$NON-NLS-1$
+ if (clockMode == FBDebugClockMode.INTERVAL || clockMode == FBDebugClockMode.FIXED) {
+ configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_REALTIME_OFFSET,
+ clockWidget.getRealtimeClockText());
+ configuration.setAttribute(FBLaunchConfigurationAttributes.CLOCK_MONOTONIC_OFFSET,
+ clockWidget.getMonotonicClockText());
}
}
@@ -279,17 +236,9 @@ protected boolean filterTargetResource(final IResource resource) throws CoreExce
@Override
public boolean isValid(final ILaunchConfiguration launchConfig) {
- setErrorMessage(null);
- try {
- final int debugTime = Integer.parseInt(debugTimeText.getText());
- if (debugTime < 0) {
- throw new NumberFormatException();
- }
- } catch (final NumberFormatException nfe) {
- setErrorMessage(Messages.FBLaunchConfigurationTab_ERROR_InvalidDebugTime);
- return false;
- }
- return true;
+ final Optional clockValid = clockWidget.validate();
+ setErrorMessage(clockValid.orElse(null));
+ return clockValid.isEmpty();
}
@SuppressWarnings("static-method") // subclasses may override
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/messages.properties b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/messages.properties
index ba12fbf116..3ed6cd9d83 100644
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/messages.properties
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/messages.properties
@@ -1,3 +1,15 @@
+DebugClockWidget_ClockIntervalTextLabel=T\#
+DebugClockWidget_FixedClock=Use fixed clock with specified value
+DebugClockWidget_IntervalClock=Increment clock on each event by
+DebugClockWidget_InvalidInterval=Invalid clock interval: {0}
+DebugClockWidget_InvalidMonotonicClockValue=Invalid monotonic clock value: {0}
+DebugClockWidget_InvalidRealtimeClockValue=Invalid real-time clock value: {0}
+DebugClockWidget_MonotonicClock=Monotonic Clock:
+DebugClockWidget_MonotonicClockTextLabel=T\#
+DebugClockWidget_RealtimeClock=Real-time Clock:
+DebugClockWidget_RealtimeClockTextLabel=DT\#
+DebugClockWidget_SystemClock=Use system clock
+DebugClockWidget_Title=Clock
EvaluatorDebugFindAction_Text=&Find...
EvaluatorDebugFindDialog_Title=Filter
EvaluatorVariableValueEditor_Exception=An exception occurred
@@ -12,13 +24,8 @@ MainLaunchConfigurationTab_ErrorInitializingArguments=Error initializing argumen
MainLaunchConfigurationTab_ErrorUpdatingArguments=Error updating arguments
MainLaunchConfigurationTab_InvalidValueMessage={0} is not a valid value for variable {1} with type {2}
MainLaunchConfigurationTab_InvalidValueTitle=Invalid Value
-
-FBLaunchConfigurationTab_ClockInterval=Clock interval:
-FBLaunchConfigurationTab_DebugTime=Debug Time
-FBLaunchConfigurationTab_ERROR_InvalidDebugTime=Invalid value for clock interval given. Must be number greater or equal to 0.
+FBDebugViewClockWidget_Apply=Apply
+FBDebugViewClockWidget_InvalidValues=Invalid Values
FBLaunchConfigurationTab_Event=Event
-FBLaunchConfigurationTab_IncrementClockAfterEachEventBySpecifiedAmount=Increment clock after each event by specified amount
FBLaunchConfigurationTab_KeepDebuggerRunningWhenIdle=Keep debugger running when idle
FBLaunchConfigurationTab_RepeatEvent=Repeat event
-FBLaunchConfigurationTab_UseFixedClockWithSpecifiedTime=Use fixed clock with specified time
-FBLaunchConfigurationTab_UseSystemClock=Use system clock
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/DebugTimeComposite.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/DebugTimeComposite.java
deleted file mode 100644
index c6ba9861b8..0000000000
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/DebugTimeComposite.java
+++ /dev/null
@@ -1,251 +0,0 @@
-/*******************************************************************************
- * Copyright (c) 2023 Primetals Technologies Austria GmbH
- *
- * This program and the accompanying materials are made available under the
- * terms of the Eclipse Public License 2.0 which is available at
- * http://www.eclipse.org/legal/epl-2.0.
- *
- * SPDX-License-Identifier: EPL-2.0
- *
- * Contributors:
- * Lukas Leimeister - initial API and implementation and/or initial documentation
- *******************************************************************************/
-package org.eclipse.fordiac.ide.debug.ui.view;
-
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.ZoneId;
-import java.time.temporal.ChronoUnit;
-
-import org.eclipse.fordiac.ide.debug.EvaluatorProcess;
-import org.eclipse.fordiac.ide.debug.fb.FBDebugClockMode;
-import org.eclipse.fordiac.ide.debug.fb.FBLaunchEventQueue;
-import org.eclipse.fordiac.ide.model.eval.AbstractEvaluator;
-import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluator;
-import org.eclipse.jface.layout.GridDataFactory;
-import org.eclipse.jface.layout.GridLayoutFactory;
-import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.SelectionListener;
-import org.eclipse.swt.layout.GridData;
-import org.eclipse.swt.widgets.Button;
-import org.eclipse.swt.widgets.Composite;
-import org.eclipse.swt.widgets.Group;
-import org.eclipse.swt.widgets.Label;
-import org.eclipse.swt.widgets.Text;
-
-public class DebugTimeComposite {
-
- private static final int NUM_COLUMNS = 1;
- private static final int NUM_BUTTONS = 3;
- private EvaluatorProcess evaluator;
- private FBLaunchEventQueue eventQueue;
- private final Group debugTimeGroup;
- private Button systemTimeRadio;
- private Button incrementTimeRadio;
- private Button manualTimeRadio;
- private Text debugTimeText;
-
- /**
- * Create an new edit part to control the debug time settings. This interface
- * contains three radio buttons for mode selection and one text/ lable to adjust
- * the time value. It is part of the debug view and only visible, when context
- * is activated.
- *
- * @param parent The parent Composite of the debug view.
- */
- public DebugTimeComposite(final Composite parent) {
- debugTimeGroup = createDebugTimeGroup(parent);
- GridLayoutFactory.fillDefaults().numColumns(NUM_COLUMNS).margins(0, 0).generateLayout(debugTimeGroup);
- GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).grab(true, false).applyTo(debugTimeGroup);
- setEditPartVisible(false);
- }
-
- protected Group createDebugTimeGroup(final Composite parent) {
- // this method creates the Test Recorder Interface Group
- final Group group = new Group(parent, SWT.NONE);
- group.setText("Set Debug Time"); //$NON-NLS-1$
-
- // create sleeptime selection
- final Composite debugTimeSelectionComposite = createDebugTimeModeSelectionComposite(group);
- GridLayoutFactory.fillDefaults().numColumns(NUM_BUTTONS).margins(0, 0)
- .generateLayout(debugTimeSelectionComposite);
- GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).grab(true, false)
- .applyTo(debugTimeSelectionComposite);
-
- // create sleeptime settings
- final Composite debugTimeValueComposite = createDebugTimeValueComposite(group);
- GridLayoutFactory.fillDefaults().numColumns(NUM_BUTTONS).margins(0, 0).generateLayout(debugTimeValueComposite);
- GridDataFactory.fillDefaults().align(SWT.CENTER, SWT.CENTER).grab(true, false).applyTo(debugTimeValueComposite);
-
- // set visability of the group
- setEditPartVisible(false);
- return group;
- }
-
- protected Composite createDebugTimeValueComposite(final Group group) {
- // create composite for sleeptime section
- final Composite composite = new Composite(group, SWT.NONE);
-
- // create elements fo composite
- final Button setButton = new Button(composite, SWT.PUSH);
- setButton.setText("Set"); //$NON-NLS-1$
- setButton.setLayoutData(new GridData(SWT.CENTER, SWT.FILL, true, true, 1, 1));
- debugTimeText = new Text(composite, SWT.BORDER);
- final Label debugTimeLable = new Label(composite, SWT.NONE);
-
- // customize elements of composite
- debugTimeText.setText(""); //$NON-NLS-1$
- debugTimeText.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false, 1, 1));
- debugTimeLable.setText("ms"); //$NON-NLS-1$
- debugTimeLable.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 1, 1));
-
- // customize elements of composite
- setButton.addSelectionListener(
- SelectionListener.widgetSelectedAdapter(e -> setDebugTime(debugTimeText.getText())));
- return composite;
- }
-
- protected Composite createDebugTimeModeSelectionComposite(final Group group) {
- // create composite for sleeptime section
- final Composite composite = new Composite(group, SWT.NONE);
-
- systemTimeRadio = new Button(composite, SWT.RADIO);
- systemTimeRadio.setText("System"); //$NON-NLS-1$
- incrementTimeRadio = new Button(composite, SWT.RADIO);
- incrementTimeRadio.setText("Increment"); //$NON-NLS-1$
- manualTimeRadio = new Button(composite, SWT.RADIO);
- manualTimeRadio.setText("Manual"); //$NON-NLS-1$
- return composite;
- }
-
- protected void setCheckbox(final String text) {
- // only the most recent selected box is checked
- switch (text) {
- case "System": //$NON-NLS-1$
- systemTimeRadio.setSelection(true);
- incrementTimeRadio.setSelection(false);
- manualTimeRadio.setSelection(false);
- break;
- case "Increment": //$NON-NLS-1$
- systemTimeRadio.setSelection(false);
- incrementTimeRadio.setSelection(true);
- manualTimeRadio.setSelection(false);
- break;
- case "Manual": //$NON-NLS-1$
- systemTimeRadio.setSelection(false);
- incrementTimeRadio.setSelection(false);
- manualTimeRadio.setSelection(true);
- break;
- default:
- break;
- }
- }
-
- private void setDebugTime(final String text) {
- // set button is pushed combined with one of the three selections
- if (manualTimeRadio.getSelection() && (text != null)) {
- manualSelection(text);
- }
- if (incrementTimeRadio.getSelection() && (text != null)) {
- incrementSelection(text);
- }
- if (systemTimeRadio.getSelection()) {
- systemSelection();
- }
- }
-
- private void systemSelection() {
- // time is set to default system time
- evaluator.getExecutor().setClock(AbstractEvaluator.MonotonicClock.UTC);
- if (eventQueue != null) {
- eventQueue.setEvaluatorProcess(evaluator);
- eventQueue.setDebugTimeValue(FBDebugClockMode.SYSTEM, Duration.ZERO);
- }
- }
-
- private void incrementSelection(final String text) {
- // time is incrementally increased every time an event is triggered
- try {
- final long value = Long.parseLong(text);
- final Duration sleepTime = Duration.of(value, ChronoUnit.MILLIS);
- if (eventQueue != null) {
- eventQueue.setEvaluatorProcess(evaluator);
- eventQueue.setDebugTimeValue(FBDebugClockMode.INCREMENT, sleepTime);
- }
- } catch (final NumberFormatException | java.lang.ArithmeticException e) {
- throw new IllegalStateException("Debug Time Value is not accepted!"); //$NON-NLS-1$
- }
- }
-
- private void manualSelection(final String text) {
- // time is fixed and set to user input
- try {
- final long value = Long.parseLong(text);
- final Duration manualTime = Duration.of(value, ChronoUnit.MILLIS);
- final Instant instant = Instant.ofEpochSecond(manualTime.getSeconds(), manualTime.getNano());
- if (eventQueue != null) {
- eventQueue.setEvaluatorProcess(evaluator);
- eventQueue.setDebugTimeValue(FBDebugClockMode.MANUAL, manualTime);
- }
- evaluator.getExecutor().setClock(Clock.fixed(instant, ZoneId.systemDefault()));
- } catch (final NumberFormatException | java.lang.ArithmeticException e) {
- throw new IllegalStateException("Debug Time Value is not accepted!"); //$NON-NLS-1$
- }
- }
-
- /**
- * Set the Edit Part visible or invisible.
- *
- * @param visiblestate The boolean state of the visibility
- */
- public void setEditPartVisible(final boolean visiblestate) {
- // set the visibility status of the group
- if (debugTimeGroup != null) {
- debugTimeGroup.setVisible(visiblestate);
- }
- }
-
- /**
- * Update the contents of the Edit Part.
- *
- * @param visiblestate The boolean state of the visibility
- */
- public void updateEditPartVisible() {
- // update elements of edit part
- final FBLaunchEventQueue newEventQueue = getEventQueue(evaluator);
- if (eventQueue != newEventQueue) {
- eventQueue = newEventQueue;
- }
- if (eventQueue != null) {
- systemTimeRadio.setSelection(eventQueue.isDebugTimeSystem());
- incrementTimeRadio.setSelection(eventQueue.isDebugTimeIncremental());
- manualTimeRadio.setSelection(eventQueue.isDebugTimeManual());
- debugTimeText.setText(String.valueOf(eventQueue.getDebugTimeValue().toMillis()));
- eventQueue.setEvaluatorProcess(evaluator);
- }
- }
-
- /**
- * Set the content of the DebugTimeEditPart.
- *
- * @param evaluatorprocess Evaluator Process which is currently active in View
- */
- public void setContent(final EvaluatorProcess evaluatorprocess) {
- evaluator = evaluatorprocess;
- final FBLaunchEventQueue newEventQueue = getEventQueue(evaluator);
- if (eventQueue != newEventQueue) {
- eventQueue = newEventQueue;
- }
- }
-
- private static FBLaunchEventQueue getEventQueue(final EvaluatorProcess evaluator) {
- if (evaluator != null) {
- final var queue = ((FBEvaluator>) evaluator.getEvaluator()).getEventQueue();
- if (queue instanceof final FBLaunchEventQueue fBLaunchEventQueue) {
- return fBLaunchEventQueue;
- }
- }
- return null;
- }
-}
\ No newline at end of file
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugView.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugView.java
index a637481ff3..7efd6e851c 100644
--- a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugView.java
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugView.java
@@ -115,17 +115,18 @@ public Rectangle getClientArea(final Rectangle rect) {
private GraphicalViewer viewer;
private ActionRegistry actionRegistry;
- private static final int NUM_COLUMNS = 1;
private KeyHandler sharedKeyHandler;
private RepeatEventAction repeatEventAction;
- private DebugTimeComposite debugTimeEditPart;
+ private final FBDebugViewClockWidget clockWidget = new FBDebugViewClockWidget();
@Override
public void createPartControl(final Composite parent) {
getSite().getWorkbenchWindow().getSelectionService().addSelectionListener(this);
- GridLayoutFactory.fillDefaults().numColumns(NUM_COLUMNS).margins(0, 0).generateLayout(parent);
+ GridLayoutFactory.fillDefaults().applyTo(parent);
createGraphicalViewer(parent);
- debugTimeEditPart = new DebugTimeComposite(parent);
+ final Composite clockControl = clockWidget.createControl(parent);
+ clockControl.setVisible(false);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(clockControl);
createToolBarEntries();
hookDebugListeners();
}
@@ -263,10 +264,10 @@ private void contextActivated(final ISelection selection) {
final Object source = structSel.getFirstElement();
if (source == null) {
setContents(null);
- debugTimeEditPart.setEditPartVisible(false);
+ clockWidget.getControl().setVisible(false);
} else {
final EvaluatorProcess evaluator = getFBEvaluatorDebugContext(source);
- debugTimeEditPart.setEditPartVisible(true);
+ clockWidget.getControl().setVisible(true);
if (!isViewerContent(evaluator)) {
setContents(evaluator);
}
@@ -277,8 +278,8 @@ private void contextActivated(final ISelection selection) {
private void setContents(final EvaluatorProcess evaluator) {
viewer.setContents(evaluator);
repeatEventAction.updateEvaluator(evaluator);
- debugTimeEditPart.setContent(evaluator);
- debugTimeEditPart.updateEditPartVisible();
+ clockWidget.setProcess(evaluator);
+ clockWidget.refresh(true);
setScrollPosition();
}
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugViewClockWidget.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugViewClockWidget.java
new file mode 100644
index 0000000000..a27e8ed2c5
--- /dev/null
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/view/FBDebugViewClockWidget.java
@@ -0,0 +1,154 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Martin Erich Jobst
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Martin Jobst - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.fordiac.ide.debug.ui.view;
+
+import java.time.Clock;
+import java.time.Duration;
+import java.util.Collection;
+import java.util.Optional;
+
+import org.eclipse.fordiac.ide.debug.EvaluatorProcess;
+import org.eclipse.fordiac.ide.debug.fb.FBDebugClockMode;
+import org.eclipse.fordiac.ide.debug.fb.FBLaunchConfigurationDelegate;
+import org.eclipse.fordiac.ide.debug.ui.Messages;
+import org.eclipse.fordiac.ide.debug.ui.widgets.DebugClockWidget;
+import org.eclipse.fordiac.ide.model.eval.AbstractEvaluator;
+import org.eclipse.fordiac.ide.model.eval.Evaluator;
+import org.eclipse.fordiac.ide.model.eval.EvaluatorMonitor;
+import org.eclipse.fordiac.ide.model.eval.EvaluatorMonitor.NullEvaluatorMonitor;
+import org.eclipse.fordiac.ide.model.eval.EvaluatorThreadPoolExecutor;
+import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluator;
+import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluatorCountingEventQueue;
+import org.eclipse.fordiac.ide.model.eval.variable.Variable;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.events.SelectionListener;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Display;
+
+public class FBDebugViewClockWidget extends DebugClockWidget {
+
+ private EvaluatorProcess process;
+ private boolean dirty;
+ private boolean refreshing;
+
+ private Button applyButton;
+
+ private final EvaluatorMonitor evaluatorMonitor = new NullEvaluatorMonitor() {
+
+ @Override
+ public void update(final Collection extends Variable>> variables, final Evaluator evaluator) {
+ if (process != null && evaluator == process.getEvaluator() && !refreshing) {
+ refreshing = true;
+ Display.getDefault().asyncExec(FBDebugViewClockWidget.this::refresh);
+ }
+ }
+
+ @Override
+ public void terminated(final EvaluatorThreadPoolExecutor executor) {
+ Display.getDefault().asyncExec(FBDebugViewClockWidget.this::refresh);
+ }
+ };
+
+ @Override
+ public Composite createControl(final Composite parent) {
+ final Composite composite = super.createControl(parent);
+ applyButton = new Button(composite, SWT.PUSH);
+ applyButton.setText(Messages.FBDebugViewClockWidget_Apply);
+ applyButton.setEnabled(false);
+ applyButton.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> performApply()));
+ GridDataFactory.swtDefaults().applyTo(applyButton);
+ return composite;
+ }
+
+ private void performApply() {
+ if (process == null || process.isTerminated()) {
+ return;
+ }
+ final Optional error = validate();
+ if (error.isPresent()) {
+ MessageDialog.openError(getControl().getShell(), Messages.FBDebugViewClockWidget_InvalidValues,
+ error.get());
+ return;
+ }
+ final FBDebugClockMode clockMode = getSelectedClockMode();
+ final Duration clockInterval = getClockInterval();
+ final FBEvaluatorCountingEventQueue eventQueue = getEventQueue();
+
+ process.getExecutor().setRealtimeClock(FBLaunchConfigurationDelegate.createClock(clockMode,
+ getRealtimeClockValue(), clockInterval, eventQueue));
+ process.getExecutor().setMonotonicClock(FBLaunchConfigurationDelegate.createClock(clockMode,
+ getMonotonicClockValue(), clockInterval, eventQueue));
+
+ setDirty(false);
+ }
+
+ public void refresh() {
+ refresh(false);
+ }
+
+ public void refresh(final boolean force) {
+ if (process != null && (force || !isDirty())) {
+ final Clock realtimeClock = process.getExecutor().getRealtimeClock();
+ final Clock monotonicClock = process.getExecutor().getMonotonicClock();
+ setRealtimeClockValue(realtimeClock.instant());
+ setMonotonicClockValue(monotonicClock.instant());
+ switch (monotonicClock) {
+ case final AbstractEvaluator.MonotonicClock unused -> setSelectedClockMode(FBDebugClockMode.SYSTEM);
+ case final AbstractEvaluator.IntervalClock intervalClock -> {
+ setClockInterval(intervalClock.getInterval());
+ setSelectedClockMode(FBDebugClockMode.INTERVAL);
+ }
+ default -> setSelectedClockMode(FBDebugClockMode.FIXED);
+ }
+ setDirty(false); // ensure not dirty
+ }
+ refreshing = false;
+ }
+
+ protected FBEvaluatorCountingEventQueue getEventQueue() {
+ return process != null && process.getEvaluator() instanceof final FBEvaluator> evaluator
+ && evaluator.getEventQueue() instanceof final FBEvaluatorCountingEventQueue queue ? queue : null;
+ }
+
+ @Override
+ protected void handleClockUpdated() {
+ setDirty(true);
+ super.handleClockUpdated();
+ }
+
+ public EvaluatorProcess getProcess() {
+ return process;
+ }
+
+ public void setProcess(final EvaluatorProcess process) {
+ if (this.process != null) {
+ this.process.getExecutor().removeMonitor(evaluatorMonitor);
+ }
+ this.process = process;
+ if (this.process != null) {
+ this.process.getExecutor().addMonitor(evaluatorMonitor);
+ }
+ }
+
+ public boolean isDirty() {
+ return dirty;
+ }
+
+ public void setDirty(final boolean dirty) {
+ this.dirty = dirty;
+ applyButton.setEnabled(dirty);
+ }
+}
diff --git a/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/widgets/DebugClockWidget.java b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/widgets/DebugClockWidget.java
new file mode 100644
index 0000000000..1f6a88f480
--- /dev/null
+++ b/plugins/org.eclipse.fordiac.ide.debug.ui/src/org/eclipse/fordiac/ide/debug/ui/widgets/DebugClockWidget.java
@@ -0,0 +1,291 @@
+/*******************************************************************************
+ * Copyright (c) 2025 Martin Erich Jobst
+ *
+ * This program and the accompanying materials are made available under the
+ * terms of the Eclipse Public License 2.0 which is available at
+ * http://www.eclipse.org/legal/epl-2.0.
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *
+ * Contributors:
+ * Martin Jobst - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+package org.eclipse.fordiac.ide.debug.ui.widgets;
+
+import static org.eclipse.swt.events.SelectionListener.widgetSelectedAdapter;
+
+import java.text.MessageFormat;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+import java.util.Optional;
+import java.util.stream.Stream;
+
+import org.eclipse.fordiac.ide.debug.fb.FBDebugClockMode;
+import org.eclipse.fordiac.ide.debug.ui.Messages;
+import org.eclipse.fordiac.ide.model.value.DateAndTimeValueConverter;
+import org.eclipse.fordiac.ide.model.value.TimeValueConverter;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
+import org.eclipse.swt.SWT;
+import org.eclipse.swt.widgets.Button;
+import org.eclipse.swt.widgets.Composite;
+import org.eclipse.swt.widgets.Group;
+import org.eclipse.swt.widgets.Label;
+import org.eclipse.swt.widgets.Text;
+
+public class DebugClockWidget {
+
+ private Group group;
+ private Button systemClockRadio;
+ private Button intervalClockRadio;
+ private Button fixedClockRadio;
+ private Text clockIntervalText;
+ private Text realtimeClockText;
+ private Text monotonicClockText;
+
+ private Runnable updateRunnable;
+
+ public DebugClockWidget() {
+ }
+
+ public DebugClockWidget(final Runnable updateRunnable) {
+ this.updateRunnable = updateRunnable;
+ }
+
+ public Composite createControl(final Composite parent) {
+ group = new Group(parent, SWT.BORDER);
+ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(group);
+ group.setText(Messages.DebugClockWidget_Title);
+
+ final Composite clockModes = new Composite(group, SWT.NONE);
+ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(clockModes);
+ GridDataFactory.fillDefaults().grab(true, false).span(2, 1).applyTo(clockModes);
+
+ systemClockRadio = new Button(clockModes, SWT.RADIO);
+ systemClockRadio.setText(Messages.DebugClockWidget_SystemClock);
+ systemClockRadio.addSelectionListener(widgetSelectedAdapter(e -> handleClockModeUpdated()));
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(systemClockRadio);
+
+ intervalClockRadio = new Button(clockModes, SWT.RADIO);
+ intervalClockRadio.setText(Messages.DebugClockWidget_IntervalClock);
+ intervalClockRadio.addSelectionListener(widgetSelectedAdapter(e -> handleClockModeUpdated()));
+ GridDataFactory.swtDefaults().applyTo(intervalClockRadio);
+
+ final Composite clockIntervalComposite = createClockIntervalComposite(clockModes);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(clockIntervalComposite);
+
+ fixedClockRadio = new Button(clockModes, SWT.RADIO);
+ fixedClockRadio.setText(Messages.DebugClockWidget_FixedClock);
+ fixedClockRadio.addSelectionListener(widgetSelectedAdapter(e -> handleClockModeUpdated()));
+ GridDataFactory.fillDefaults().span(2, 1).applyTo(fixedClockRadio);
+
+ final Label realtimeClockLabel = new Label(group, SWT.NONE);
+ realtimeClockLabel.setText(Messages.DebugClockWidget_RealtimeClock);
+ GridDataFactory.swtDefaults().applyTo(realtimeClockLabel);
+
+ final Composite realtimeClockTextComposite = createRealtimeClockTextComposite(group);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(realtimeClockTextComposite);
+
+ final Label monotonicClockLabel = new Label(group, SWT.NONE);
+ monotonicClockLabel.setText(Messages.DebugClockWidget_MonotonicClock);
+ GridDataFactory.swtDefaults().applyTo(monotonicClockLabel);
+
+ final Composite monotonicClockTextComposite = createMonotonicClockTextComposite(group);
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(monotonicClockTextComposite);
+
+ return group;
+ }
+
+ public Composite createClockIntervalComposite(final Composite parent) {
+ final Composite clockIntervalComposite = new Composite(parent, SWT.NONE);
+ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(clockIntervalComposite);
+
+ final Label clockIntervalTextLabel = new Label(clockIntervalComposite, SWT.NONE);
+ clockIntervalTextLabel.setText(Messages.DebugClockWidget_ClockIntervalTextLabel);
+ GridDataFactory.swtDefaults().applyTo(clockIntervalTextLabel);
+
+ clockIntervalText = new Text(clockIntervalComposite, SWT.BORDER);
+ clockIntervalText.setEditable(false);
+ clockIntervalText.addModifyListener(e -> handleClockUpdated());
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(clockIntervalText);
+ return clockIntervalComposite;
+ }
+
+ public Composite createRealtimeClockTextComposite(final Composite parent) {
+ final Composite realtimeClockComposite = new Composite(parent, SWT.NONE);
+ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(realtimeClockComposite);
+
+ final Label realtimeClockTextLabel = new Label(realtimeClockComposite, SWT.NONE);
+ realtimeClockTextLabel.setText(Messages.DebugClockWidget_RealtimeClockTextLabel);
+ GridDataFactory.swtDefaults().applyTo(realtimeClockTextLabel);
+
+ realtimeClockText = new Text(realtimeClockComposite, SWT.BORDER);
+ realtimeClockText.setEditable(false);
+ realtimeClockText.addModifyListener(e -> handleClockUpdated());
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(realtimeClockText);
+ return realtimeClockComposite;
+ }
+
+ public Composite createMonotonicClockTextComposite(final Composite parent) {
+ final Composite monotonicClockComposite = new Composite(parent, SWT.NONE);
+ GridLayoutFactory.swtDefaults().numColumns(2).applyTo(monotonicClockComposite);
+
+ final Label monotonicClockTextLabel = new Label(monotonicClockComposite, SWT.NONE);
+ monotonicClockTextLabel.setText(Messages.DebugClockWidget_MonotonicClockTextLabel);
+ GridDataFactory.swtDefaults().applyTo(monotonicClockTextLabel);
+
+ monotonicClockText = new Text(monotonicClockComposite, SWT.BORDER);
+ monotonicClockText.setEditable(false);
+ monotonicClockText.addModifyListener(e -> handleClockUpdated());
+ GridDataFactory.fillDefaults().grab(true, false).applyTo(monotonicClockText);
+ return monotonicClockComposite;
+ }
+
+ public FBDebugClockMode getSelectedClockMode() {
+ if (systemClockRadio.getSelection()) {
+ return FBDebugClockMode.SYSTEM;
+ }
+ if (fixedClockRadio.getSelection()) {
+ return FBDebugClockMode.FIXED;
+ }
+ if (intervalClockRadio.getSelection()) {
+ return FBDebugClockMode.INTERVAL;
+ }
+ return FBDebugClockMode.SYSTEM;
+ }
+
+ public void setSelectedClockMode(final FBDebugClockMode clockMode) {
+ final Button selected = switch (clockMode) {
+ case SYSTEM -> systemClockRadio;
+ case INTERVAL -> intervalClockRadio;
+ case FIXED -> fixedClockRadio;
+ case null, default -> null;
+ };
+ Stream.of(systemClockRadio, intervalClockRadio, fixedClockRadio)
+ .forEach(radio -> radio.setSelection(radio == selected));
+ clockIntervalText.setEditable(selected == intervalClockRadio);
+ realtimeClockText.setEditable(selected == intervalClockRadio || selected == fixedClockRadio);
+ monotonicClockText.setEditable(selected == intervalClockRadio || selected == fixedClockRadio);
+ }
+
+ protected void handleClockModeUpdated() {
+ clockIntervalText.setEditable(intervalClockRadio.getSelection());
+ realtimeClockText.setEditable(intervalClockRadio.getSelection() || fixedClockRadio.getSelection());
+ monotonicClockText.setEditable(intervalClockRadio.getSelection() || fixedClockRadio.getSelection());
+ handleClockUpdated();
+ }
+
+ protected void handleClockUpdated() {
+ if (updateRunnable != null) {
+ updateRunnable.run();
+ }
+ }
+
+ public Optional validate() {
+ final FBDebugClockMode clockMode = getSelectedClockMode();
+ if (clockMode == FBDebugClockMode.INTERVAL) {
+ try {
+ TimeValueConverter.INSTANCE.toValue(getClockIntervalText());
+ } catch (final IllegalArgumentException e) {
+ return Optional
+ .of(MessageFormat.format(Messages.DebugClockWidget_InvalidInterval, e.getLocalizedMessage()));
+ }
+ }
+ if (clockMode == FBDebugClockMode.INTERVAL || clockMode == FBDebugClockMode.FIXED) {
+ try {
+ DateAndTimeValueConverter.INSTANCE.toValue(getRealtimeClockText());
+ } catch (final IllegalArgumentException e) {
+ return Optional.of(MessageFormat.format(Messages.DebugClockWidget_InvalidRealtimeClockValue,
+ e.getLocalizedMessage()));
+ }
+ try {
+ TimeValueConverter.INSTANCE.toValue(getMonotonicClockText());
+ } catch (final IllegalArgumentException e) {
+ return Optional.of(MessageFormat.format(Messages.DebugClockWidget_InvalidMonotonicClockValue,
+ e.getLocalizedMessage()));
+ }
+ }
+ return Optional.empty();
+ }
+
+ public Group getControl() {
+ return group;
+ }
+
+ public void setControl(final Group control) {
+ this.group = control;
+ }
+
+ public Duration getClockInterval() {
+ try {
+ return TimeValueConverter.INSTANCE.toValue(getClockIntervalText());
+ } catch (final IllegalArgumentException e) {
+ return Duration.ZERO;
+ }
+ }
+
+ public void setClockInterval(final Duration value) {
+ setClockIntervalText(TimeValueConverter.INSTANCE.toString(value));
+ }
+
+ public String getClockIntervalText() {
+ return clockIntervalText.getText();
+ }
+
+ public void setClockIntervalText(final String value) {
+ clockIntervalText.setText(value);
+ }
+
+ public Instant getRealtimeClockValue() {
+ try {
+ return DateAndTimeValueConverter.INSTANCE.toValue(getRealtimeClockText()).toInstant(ZoneOffset.UTC);
+ } catch (final Exception e) {
+ return Instant.EPOCH;
+ }
+ }
+
+ public void setRealtimeClockValue(final Instant value) {
+ setRealtimeClockText(
+ DateAndTimeValueConverter.INSTANCE.toString(LocalDateTime.ofInstant(value, ZoneOffset.UTC)));
+ }
+
+ public String getRealtimeClockText() {
+ return realtimeClockText.getText();
+ }
+
+ public void setRealtimeClockText(final String value) {
+ realtimeClockText.setText(value);
+ }
+
+ public Instant getMonotonicClockValue() {
+ try {
+ final Duration value = TimeValueConverter.INSTANCE.toValue(getMonotonicClockText());
+ return Instant.ofEpochSecond(value.getSeconds(), value.getNano());
+ } catch (final Exception e) {
+ return Instant.EPOCH;
+ }
+ }
+
+ public void setMonotonicClockValue(final Instant value) {
+ setMonotonicClockText(
+ TimeValueConverter.INSTANCE.toString(Duration.ofSeconds(value.getEpochSecond(), value.getNano())));
+ }
+
+ public String getMonotonicClockText() {
+ return monotonicClockText.getText();
+ }
+
+ public void setMonotonicClockText(final String value) {
+ monotonicClockText.setText(value);
+ }
+
+ public Runnable getUpdateRunnable() {
+ return updateRunnable;
+ }
+
+ public void setUpdateRunnable(final Runnable updateRunnable) {
+ this.updateRunnable = updateRunnable;
+ }
+}
diff --git a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/CommonLaunchConfigurationDelegate.java b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/CommonLaunchConfigurationDelegate.java
index e6ba51486e..ab17aa89d8 100644
--- a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/CommonLaunchConfigurationDelegate.java
+++ b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/CommonLaunchConfigurationDelegate.java
@@ -12,6 +12,8 @@
*******************************************************************************/
package org.eclipse.fordiac.ide.debug;
+import java.time.Clock;
+
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
@@ -25,15 +27,25 @@
public abstract class CommonLaunchConfigurationDelegate extends LaunchConfigurationDelegate {
- @SuppressWarnings("static-method")
protected void launch(final Evaluator evaluator, final ILaunchConfiguration configuration, final String mode,
final ILaunch launch, final IResource resource, final IProgressMonitor monitor) throws CoreException {
+ launch(evaluator, null, null, configuration, mode, launch, resource, monitor);
+ }
+
+ @SuppressWarnings("static-method")
+ protected void launch(final Evaluator evaluator, final Clock realtimeClock, final Clock monotonicClock,
+ final ILaunchConfiguration configuration, final String mode, final ILaunch launch, final IResource resource,
+ final IProgressMonitor monitor) throws CoreException {
if (ILaunchManager.RUN_MODE.equals(mode)) {
final EvaluatorProcess process = new EvaluatorProcess(configuration.getName(), evaluator, launch);
+ process.getExecutor().setRealtimeClock(realtimeClock);
+ process.getExecutor().setMonotonicClock(monotonicClock);
process.start();
} else if (ILaunchManager.DEBUG_MODE.equals(mode)) {
final EvaluatorDebugTarget debugTarget = new EvaluatorDebugTarget(configuration.getName(), evaluator,
launch, resource);
+ debugTarget.getProcess().getExecutor().setRealtimeClock(realtimeClock);
+ debugTarget.getProcess().getExecutor().setMonotonicClock(monotonicClock);
if (LaunchConfigurationAttributes.isStopOnFirstLine(configuration)) {
debugTarget.getDebugger().setSuspendOnFirstLine(true);
}
diff --git a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBDebugClockMode.java b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBDebugClockMode.java
index c6145892ce..0b03167f23 100644
--- a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBDebugClockMode.java
+++ b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBDebugClockMode.java
@@ -12,19 +12,7 @@
*******************************************************************************/
package org.eclipse.fordiac.ide.debug.fb;
-import org.eclipse.fordiac.ide.ui.FordiacLogHelper;
-
public enum FBDebugClockMode {
- SYSTEM, INCREMENT, MANUAL;
-
- public static FBDebugClockMode fromString(final String val) {
- try {
- return valueOf(val);
- } catch (final IllegalArgumentException | NullPointerException ex) {
- FordiacLogHelper.logWarning("Could not convert clock mode from string: " + val, ex); //$NON-NLS-1$
- }
- return SYSTEM;
- }
-
+ SYSTEM, INTERVAL, FIXED;
}
diff --git a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationAttributes.java b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationAttributes.java
index f11952eed9..3689445cd0 100644
--- a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationAttributes.java
+++ b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationAttributes.java
@@ -13,18 +13,22 @@
package org.eclipse.fordiac.ide.debug.fb;
import java.time.Duration;
-import java.time.temporal.ChronoUnit;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.Objects;
import java.util.function.Predicate;
import java.util.stream.Stream;
import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.Status;
import org.eclipse.debug.core.ILaunchConfiguration;
import org.eclipse.fordiac.ide.debug.LaunchConfigurationAttributes;
import org.eclipse.fordiac.ide.model.libraryElement.AdapterDeclaration;
import org.eclipse.fordiac.ide.model.libraryElement.AdapterFB;
import org.eclipse.fordiac.ide.model.libraryElement.Event;
import org.eclipse.fordiac.ide.model.libraryElement.FBType;
+import org.eclipse.fordiac.ide.model.value.DateAndTimeValueConverter;
+import org.eclipse.fordiac.ide.model.value.TimeValueConverter;
public interface FBLaunchConfigurationAttributes extends LaunchConfigurationAttributes {
String ID = "org.eclipse.fordiac.ide.debug.fbLaunch"; //$NON-NLS-1$
@@ -33,8 +37,9 @@ public interface FBLaunchConfigurationAttributes extends LaunchConfigurationAttr
String KEEP_RUNNING_WHEN_IDLE = "org.eclipse.fordiac.ide.debug.keepRunningWhenIdle"; //$NON-NLS-1$
String CLOCK_MODE = "org.eclipse.fordiac.ide.debug.clockMode"; //$NON-NLS-1$
-
- String CLOCK_INTERVAL = "org.eclipse.fordiac.ide.debug.debugTime"; //$NON-NLS-1$
+ String CLOCK_INTERVAL = "org.eclipse.fordiac.ide.debug.clockInterval"; //$NON-NLS-1$
+ String CLOCK_REALTIME_OFFSET = "org.eclipse.fordiac.ide.debug.clockRealtimeOffset"; //$NON-NLS-1$
+ String CLOCK_MONOTONIC_OFFSET = "org.eclipse.fordiac.ide.debug.clockMonotonicOffset"; //$NON-NLS-1$
static Event getEvent(final ILaunchConfiguration configuration, final FBType type, final Event defaultEvent)
throws CoreException {
@@ -62,35 +67,63 @@ static boolean isKeepRunningWhenIdle(final ILaunchConfiguration configuration) t
}
static FBDebugClockMode getClockMode(final ILaunchConfiguration configuration) throws CoreException {
- return FBDebugClockMode.fromString(configuration.getAttribute(CLOCK_MODE, (String) null));
- }
-
- static boolean isSystem(final ILaunchConfiguration configuration) throws CoreException {
- return FBDebugClockMode.SYSTEM.equals(getClockMode(configuration));
+ final String modeAttribute = configuration.getAttribute(CLOCK_MODE, (String) null);
+ if (modeAttribute != null) {
+ try {
+ return FBDebugClockMode.valueOf(modeAttribute);
+ } catch (final IllegalArgumentException e) {
+ throw new CoreException(Status.error("Invalid value for clock mode")); //$NON-NLS-1$
+ }
+ }
+ return FBDebugClockMode.SYSTEM;
}
- static boolean isIncrement(final ILaunchConfiguration configuration) throws CoreException {
- return FBDebugClockMode.INCREMENT.equals(getClockMode(configuration));
+ static Duration getClockInterval(final ILaunchConfiguration configuration) throws CoreException {
+ final var value = configuration.getAttribute(CLOCK_INTERVAL, (String) null);
+ if (value != null) {
+ try {
+ return TimeValueConverter.INSTANCE.toValue(value);
+ } catch (final IllegalArgumentException e) {
+ throw new CoreException(Status.error("Invalid value for clock interval", e)); //$NON-NLS-1$
+ }
+ }
+ return Duration.ZERO;
}
- static boolean isManual(final ILaunchConfiguration configuration) throws CoreException {
- return FBDebugClockMode.MANUAL.equals(getClockMode(configuration));
+ static Instant getClockRealtimeOffset(final ILaunchConfiguration configuration) throws CoreException {
+ final var value = configuration.getAttribute(CLOCK_REALTIME_OFFSET, (String) null);
+ if (value != null) {
+ try {
+ return DateAndTimeValueConverter.INSTANCE.toValue(value).toInstant(ZoneOffset.UTC);
+ } catch (final IllegalArgumentException e) {
+ throw new CoreException(Status.error("Invalid value for clock monotonic offset", e)); //$NON-NLS-1$
+ }
+ }
+ return Instant.EPOCH;
}
- static Duration getClockInterval(final ILaunchConfiguration configuration) throws CoreException {
- final var debugtime = getClockIntervalText(configuration);
- if (debugtime != null) {
+ static Instant getClockMonotonicOffset(final ILaunchConfiguration configuration) throws CoreException {
+ final var value = configuration.getAttribute(CLOCK_MONOTONIC_OFFSET, (String) null);
+ if (value != null) {
try {
- final long value = Long.parseLong(debugtime);
- return Duration.of(value, ChronoUnit.MILLIS);
- } catch (final NumberFormatException | ArithmeticException e) {
- throw new IllegalStateException("Debug clock interval is not accepted!"); //$NON-NLS-1$
+ final Duration duration = TimeValueConverter.INSTANCE.toValue(value);
+ return Instant.ofEpochSecond(duration.getSeconds(), duration.getNano());
+ } catch (final IllegalArgumentException e) {
+ throw new CoreException(Status.error("Invalid value for clock monotonic offset", e)); //$NON-NLS-1$
}
}
- return Duration.ZERO;
+ return Instant.EPOCH;
}
static String getClockIntervalText(final ILaunchConfiguration configuration) throws CoreException {
- return configuration.getAttribute(CLOCK_INTERVAL, "0");
+ return configuration.getAttribute(CLOCK_INTERVAL, "1s"); //$NON-NLS-1$
+ }
+
+ static String getClockRealtimeOffsetText(final ILaunchConfiguration configuration) throws CoreException {
+ return configuration.getAttribute(CLOCK_REALTIME_OFFSET, "1970-01-01-00:00:00.000"); //$NON-NLS-1$
+ }
+
+ static String getClockMonotonicOffsetText(final ILaunchConfiguration configuration) throws CoreException {
+ return configuration.getAttribute(CLOCK_MONOTONIC_OFFSET, "0s"); //$NON-NLS-1$
}
}
diff --git a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationDelegate.java b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationDelegate.java
index dd3c666871..7a77fd113f 100644
--- a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationDelegate.java
+++ b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchConfigurationDelegate.java
@@ -13,6 +13,10 @@
package org.eclipse.fordiac.ide.debug.fb;
import java.text.MessageFormat;
+import java.time.Clock;
+import java.time.Duration;
+import java.time.Instant;
+import java.time.ZoneOffset;
import java.util.List;
import org.eclipse.core.resources.IFile;
@@ -25,8 +29,10 @@
import org.eclipse.fordiac.ide.debug.CommonLaunchConfigurationDelegate;
import org.eclipse.fordiac.ide.debug.LaunchConfigurationAttributes;
import org.eclipse.fordiac.ide.debug.Messages;
+import org.eclipse.fordiac.ide.model.eval.AbstractEvaluator;
import org.eclipse.fordiac.ide.model.eval.EvaluatorFactory;
import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluator;
+import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluatorCountingEventQueue;
import org.eclipse.fordiac.ide.model.eval.variable.FBVariable;
import org.eclipse.fordiac.ide.model.eval.variable.Variable;
import org.eclipse.fordiac.ide.model.libraryElement.Event;
@@ -46,14 +52,17 @@ public void launch(final ILaunchConfiguration configuration, final String mode,
final var keepRunningWhenIdle = FBLaunchConfigurationAttributes.isKeepRunningWhenIdle(configuration);
final var defaultArguments = getDefaultArguments(type);
final var variables = LaunchConfigurationAttributes.getArguments(configuration, defaultArguments);
- final var evaluator = createEvaluator(type, variables);
+ final FBDebugClockMode clockMode = FBLaunchConfigurationAttributes.getClockMode(configuration);
+ final Duration clockInterval = FBLaunchConfigurationAttributes.getClockInterval(configuration);
+ final Instant clockRealtimeOffset = FBLaunchConfigurationAttributes.getClockRealtimeOffset(configuration);
+ final Instant clockMonotonicOffset = FBLaunchConfigurationAttributes.getClockMonotonicOffset(configuration);
- final FBLaunchEventQueue fBLaunchEventQueue = new FBLaunchEventQueue(event, repeatEvent,
- keepRunningWhenIdle);
- fBLaunchEventQueue.setDebugTimeValue(FBLaunchConfigurationAttributes.getClockMode(configuration),
- FBLaunchConfigurationAttributes.getClockInterval(configuration));
- evaluator.setEventQueue(fBLaunchEventQueue);
- launch(evaluator, configuration, mode, launch, resource, monitor);
+ final var eventQueue = new FBLaunchEventQueue(event, repeatEvent, keepRunningWhenIdle);
+ final var realtimeClock = createClock(clockMode, clockRealtimeOffset, clockInterval, eventQueue);
+ final var monotonicClock = createClock(clockMode, clockMonotonicOffset, clockInterval, eventQueue);
+ final var evaluator = createEvaluator(type, variables);
+ evaluator.setEventQueue(eventQueue);
+ launch(evaluator, realtimeClock, monotonicClock, configuration, mode, launch, resource, monitor);
}
}
@@ -63,6 +72,16 @@ protected FBEvaluator> createEvaluator(final FBType type, final List null; // use system default
+ case INTERVAL -> new AbstractEvaluator.IntervalClock(offset, interval, ZoneOffset.UTC, queue,
+ queue.getTotalInputCount().get());
+ case FIXED -> Clock.fixed(offset, ZoneOffset.UTC);
+ };
+ }
+
public static List> getDefaultArguments(final FBType type) throws CoreException {
try {
return List.copyOf(new FBVariable("dummy", type).getChildren().toList()); //$NON-NLS-1$
diff --git a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchEventQueue.java b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchEventQueue.java
index a0c331958d..c6feeeae92 100644
--- a/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchEventQueue.java
+++ b/plugins/org.eclipse.fordiac.ide.debug/src/org/eclipse/fordiac/ide/debug/fb/FBLaunchEventQueue.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2022-2023 Martin Erich Jobst
+ * Copyright (c) 2022, 2025 Martin Erich Jobst
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -14,20 +14,14 @@
*******************************************************************************/
package org.eclipse.fordiac.ide.debug.fb;
-import java.time.Clock;
-import java.time.Duration;
-import java.time.Instant;
-import java.time.ZoneId;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
-import java.util.concurrent.atomic.AtomicReference;
+import java.util.concurrent.atomic.AtomicLong;
-import org.eclipse.fordiac.ide.debug.EvaluatorProcess;
-import org.eclipse.fordiac.ide.model.eval.AbstractEvaluator;
import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluatorCountingEventQueue;
import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluatorExternalEventQueue;
import org.eclipse.fordiac.ide.model.libraryElement.Event;
@@ -35,15 +29,10 @@
public class FBLaunchEventQueue implements FBEvaluatorCountingEventQueue, FBEvaluatorExternalEventQueue {
private final AtomicBoolean repeat;
private final AtomicBoolean blocking;
- private final AtomicReference clockMode = new AtomicReference<>(FBDebugClockMode.SYSTEM);
- private final AtomicReference incrementDebugTime = new AtomicReference<>(Duration.ZERO);
- private final AtomicReference currentDebugTime = new AtomicReference<>(Duration.ZERO);
- private final AtomicReference currentClock = new AtomicReference<>(AbstractEvaluator.MonotonicClock.UTC);
- private EvaluatorProcess evaluator;
- private boolean initialized = false;
private final BlockingQueue queue = new LinkedBlockingQueue<>();
+ private final AtomicLong totalInputEventCount = new AtomicLong();
private final ConcurrentMap eventCounts = new ConcurrentHashMap<>();
/**
@@ -69,7 +58,6 @@ public Event receiveInputEvent() throws InterruptedException {
final Event result = blocking.get() ? queue.take() : queue.poll();
if (result != null) {
incrementEventCount(result);
- setDebugTime();
if (repeat.get()) {
queue.add(result);
}
@@ -91,6 +79,9 @@ public boolean triggerInputEvent(final Event event) {
protected void incrementEventCount(final Event ev) {
final AtomicInteger count = getCount(ev);
count.incrementAndGet();
+ if (ev.isIsInput()) {
+ totalInputEventCount.incrementAndGet();
+ }
}
@Override
@@ -98,6 +89,11 @@ public AtomicInteger getCount(final Event ev) {
return eventCounts.computeIfAbsent(ev, e -> new AtomicInteger());
}
+ @Override
+ public AtomicLong getTotalInputCount() {
+ return totalInputEventCount;
+ }
+
/**
* Get whether to repeat the last event
*
@@ -133,89 +129,4 @@ public boolean isBlocking() {
public void setBlocking(final boolean blocking) {
this.blocking.set(blocking);
}
-
- /**
- * Get whether Debug Time is incremented every time an event is triggered.
- *
- * @return the Debug Time state
- */
- public boolean isDebugTimeIncremental() {
- return clockMode.get() == FBDebugClockMode.INCREMENT;
- }
-
- /**
- * Get whether Debug Time is is fixed to manual time value.
- *
- * @return the Debug Time state
- */
- public boolean isDebugTimeManual() {
- return clockMode.get() == FBDebugClockMode.MANUAL;
- }
-
- /**
- * Get whether Debug Time is set to default system time (UTC).
- *
- */
- public boolean isDebugTimeSystem() {
- return clockMode.get() == FBDebugClockMode.SYSTEM;
- }
-
- /**
- * Set the value of the Debug Time. This value is used to set the Executor
- * Clock. Depending on the selected mode, this value is either fixed or
- * incremented each time an event is triggered.
- *
- * @param clockMode the clock mode to be enabled
- * @param sleepTime the time value
- */
- public void setDebugTimeValue(final FBDebugClockMode clockMode, final Duration sleepTime) {
- this.clockMode.set(clockMode);
- if (isDebugTimeIncremental()) {
- incrementDebugTime.set(sleepTime);
- } else if (isDebugTimeManual()) {
- currentDebugTime.set(sleepTime);
- }
- }
-
- /**
- * Get the current intervall time.
- *
- * @return the time as Duration
- */
- public Duration getDebugTimeValue() {
- return incrementDebugTime.get();
- }
-
- /**
- * Set the Evaluator Process.
- *
- * @param evaluatorProcess the current EvaluatorProcess
- */
- public void setEvaluatorProcess(final EvaluatorProcess evaluatorProcess) {
- evaluator = evaluatorProcess;
- updateEvaluatorClock();
- }
-
- /**
- * Set Debug Time in Evaluator depending on mode selection.
- *
- */
- void setDebugTime() {
- Duration debugTime = currentDebugTime.get();
- if (isDebugTimeIncremental() && initialized) {
- debugTime = debugTime.plus(incrementDebugTime.get());
- }
- currentDebugTime.set(debugTime);
- final Instant instant = Instant.ofEpochSecond(debugTime.getSeconds(), debugTime.getNano());
- currentClock.set(isDebugTimeSystem() ? AbstractEvaluator.MonotonicClock.UTC
- : Clock.fixed(instant, ZoneId.systemDefault()));
- updateEvaluatorClock();
- initialized = true;
- }
-
- private void updateEvaluatorClock() {
- if (evaluator != null) {
- evaluator.getExecutor().setClock(currentClock.get());
- }
- }
-}
\ No newline at end of file
+}
diff --git a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/AbstractEvaluator.java b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/AbstractEvaluator.java
index 07ae50248f..f0cbdf2d2e 100644
--- a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/AbstractEvaluator.java
+++ b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/AbstractEvaluator.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2022, 2023 Martin Erich Jobst
+ * Copyright (c) 2022, 2025 Martin Erich Jobst
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -14,6 +14,7 @@
import java.io.Closeable;
import java.time.Clock;
+import java.time.Duration;
import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
@@ -23,6 +24,7 @@
import java.util.Objects;
import java.util.Set;
+import org.eclipse.fordiac.ide.model.eval.fb.FBEvaluatorCountingEventQueue;
import org.eclipse.fordiac.ide.model.eval.variable.Variable;
public abstract class AbstractEvaluator implements Evaluator {
@@ -101,18 +103,32 @@ public static Map getSharedResources() {
return evaluatorThread.getExecutor().getSharedResources();
}
- public static Clock currentClock() {
+ public static Clock currentMonotonicClock() {
if (Thread.currentThread() instanceof final EvaluatorThread evaluatorThread) {
- return evaluatorThread.getExecutor().getClock();
+ return evaluatorThread.getExecutor().getMonotonicClock();
}
return AbstractEvaluator.MonotonicClock.UTC;
}
- public static void setClock(final Clock clock) {
+ public static void setMonotonicClock(final Clock clock) {
if (!(Thread.currentThread() instanceof final EvaluatorThread evaluatorThread)) {
throw new IllegalStateException("Cannot set clock without evaluator thread"); //$NON-NLS-1$
}
- evaluatorThread.getExecutor().setClock(clock);
+ evaluatorThread.getExecutor().setMonotonicClock(clock);
+ }
+
+ public static Clock currentRealtimeClock() {
+ if (Thread.currentThread() instanceof final EvaluatorThread evaluatorThread) {
+ return evaluatorThread.getExecutor().getRealtimeClock();
+ }
+ return Clock.systemUTC();
+ }
+
+ public static void setRealtimeClock(final Clock clock) {
+ if (!(Thread.currentThread() instanceof final EvaluatorThread evaluatorThread)) {
+ throw new IllegalStateException("Cannot set clock without evaluator thread"); //$NON-NLS-1$
+ }
+ evaluatorThread.getExecutor().setRealtimeClock(clock);
}
public static class MonotonicClock extends Clock {
@@ -164,4 +180,92 @@ public String toString() {
return String.format("%s [zone=%s]", getClass().getName(), zone); //$NON-NLS-1$
}
}
+
+ public static class IntervalClock extends Clock {
+ private final Instant offset;
+ private final Duration interval;
+ private final ZoneId zone;
+ private final FBEvaluatorCountingEventQueue queue;
+ private final long queueOffset;
+
+ public IntervalClock(final Instant offset, final Duration interval, final ZoneId zone,
+ final FBEvaluatorCountingEventQueue queue, final long queueOffset) {
+ this.offset = Objects.requireNonNull(offset);
+ this.interval = Objects.requireNonNull(interval);
+ this.zone = Objects.requireNonNull(zone);
+ this.queue = Objects.requireNonNull(queue);
+ this.queueOffset = queueOffset;
+ }
+
+ @Override
+ public Instant instant() {
+ return instant(queue.getTotalInputCount().get());
+ }
+
+ private Instant instant(final long totalCount) {
+ return offset.plus(interval.multipliedBy(totalCount - queueOffset));
+ }
+
+ @Override
+ public Clock withZone(final ZoneId zone) {
+ final long totalCount = queue.getTotalInputCount().get();
+ return new IntervalClock(instant(totalCount), interval, zone, queue, totalCount);
+ }
+
+ public static Clock startingAt(final Clock clock, final Duration interval,
+ final FBEvaluatorCountingEventQueue queue) {
+ final long totalCount = queue.getTotalInputCount().get();
+ return new IntervalClock(clock != null ? clock.instant() : Instant.EPOCH, interval,
+ clock != null ? clock.getZone() : ZoneOffset.UTC, queue, totalCount);
+ }
+
+ public Instant getStart() {
+ return offset;
+ }
+
+ public Duration getInterval() {
+ return interval;
+ }
+
+ @Override
+ public ZoneId getZone() {
+ return zone;
+ }
+
+ public FBEvaluatorCountingEventQueue getQueue() {
+ return queue;
+ }
+
+ public long getQueueOffset() {
+ return queueOffset;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(offset, interval, zone, queue, Long.valueOf(queueOffset));
+ }
+
+ @Override
+ public boolean equals(final Object obj) {
+ if (this == obj) {
+ return true;
+ }
+ if (!super.equals(obj)) {
+ return false;
+ }
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+ final IntervalClock other = (IntervalClock) obj;
+ return Objects.equals(offset, other.offset) && Objects.equals(interval, other.interval)
+ && Objects.equals(zone, other.zone) && Objects.equals(queue, other.queue)
+ && queueOffset == other.queueOffset;
+ }
+
+ @Override
+ public String toString() {
+ return String.format("%s [offset=%s, interval=%s, zone=%s]", getClass().getName(), offset, interval, //$NON-NLS-1$
+ zone);
+ }
+ }
}
diff --git a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/EvaluatorThreadPoolExecutor.java b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/EvaluatorThreadPoolExecutor.java
index ca1b53127f..5d1e6eba79 100644
--- a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/EvaluatorThreadPoolExecutor.java
+++ b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/EvaluatorThreadPoolExecutor.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2023 Martin Erich Jobst
+ * Copyright (c) 2023, 2025 Martin Erich Jobst
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -15,6 +15,7 @@
import java.io.Closeable;
import java.time.Clock;
import java.util.Map;
+import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.SynchronousQueue;
@@ -35,7 +36,8 @@ public class EvaluatorThreadPoolExecutor extends ThreadPoolExecutor {
private final Set monitorSet = ConcurrentHashMap.newKeySet();
private final Map context = new ConcurrentHashMap<>();
private final Map sharedResources = new ConcurrentHashMap<>();
- private Clock clock = AbstractEvaluator.MonotonicClock.UTC;
+ private Clock monotonicClock = AbstractEvaluator.MonotonicClock.UTC;
+ private Clock realtimeClock = Clock.systemUTC();
/**
* Create a new evaluator thread pool executor with unlimited number of threads
@@ -142,21 +144,39 @@ public Map getSharedResources() {
}
/**
- * Get the current clock for this executor
+ * Get the current monotonic clock for this executor
*
- * @return The clock
+ * @return The monotonic clock
*/
- public Clock getClock() {
- return clock;
+ public Clock getMonotonicClock() {
+ return monotonicClock;
}
/**
- * Set the current clock for this executor
+ * Set the current monotonic clock for this executor
*
- * @param clock The clock
+ * @param monotonicClock The monotonic clock or null to reset to system default
*/
- public void setClock(final Clock clock) {
- this.clock = clock;
+ public void setMonotonicClock(final Clock monotonicClock) {
+ this.monotonicClock = Objects.requireNonNullElse(monotonicClock, AbstractEvaluator.MonotonicClock.UTC);
+ }
+
+ /**
+ * Get the current realtime clock for this executor
+ *
+ * @return The realtime clock
+ */
+ public Clock getRealtimeClock() {
+ return realtimeClock;
+ }
+
+ /**
+ * Set the current realtime clock for this executor
+ *
+ * @param realtimeClock The realtime clock or null to reset to system default
+ */
+ public void setRealtimeClock(final Clock realtimeClock) {
+ this.realtimeClock = Objects.requireNonNullElseGet(realtimeClock, Clock::systemUTC);
}
@Override
diff --git a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/fb/FBEvaluatorCountingEventQueue.java b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/fb/FBEvaluatorCountingEventQueue.java
index 65295549a6..229fbb5535 100644
--- a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/fb/FBEvaluatorCountingEventQueue.java
+++ b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/fb/FBEvaluatorCountingEventQueue.java
@@ -1,5 +1,5 @@
/**
- * Copyright (c) 2023, 2024 Martin Erich Jobst
+ * Copyright (c) 2023, 2025 Martin Erich Jobst
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
@@ -13,6 +13,7 @@
package org.eclipse.fordiac.ide.model.eval.fb;
import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
import org.eclipse.fordiac.ide.model.libraryElement.Event;
@@ -21,4 +22,9 @@ public interface FBEvaluatorCountingEventQueue extends FBEvaluatorEventQueue {
* Get the number of events processed by this queue
*/
AtomicInteger getCount(final Event event);
+
+ /**
+ * Get the total number of input events processed by this queue
+ */
+ AtomicLong getTotalInputCount();
}
diff --git a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/function/StandardFunctions.java b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/function/StandardFunctions.java
index 3b3dea5503..24c7291092 100644
--- a/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/function/StandardFunctions.java
+++ b/plugins/org.eclipse.fordiac.ide.model.eval/src/org/eclipse/fordiac/ide/model/eval/function/StandardFunctions.java
@@ -1,5 +1,5 @@
/*******************************************************************************
- * Copyright (c) 2022, 2023 Martin Erich Jobst,
+ * Copyright (c) 2022, 2025 Martin Erich Jobst,
* Primetals Technologies Austria GmbH
*
* This program and the accompanying materials are made available under the
@@ -25,7 +25,7 @@
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
-import java.time.ZoneId;
+import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.util.function.BinaryOperator;
import java.util.function.DoubleBinaryOperator;
@@ -2226,20 +2226,28 @@ private static BigInteger convertFromBCD(final BigInteger value) {
@Comment("Returns relative time stamp.")
static TimeValue NOW_MONOTONIC() {
- return TimeValue.toTimeValue(Instant.EPOCH.until(AbstractEvaluator.currentClock().instant(), ChronoUnit.NANOS));
+ return TimeValue.toTimeValue(
+ Instant.EPOCH.until(AbstractEvaluator.currentMonotonicClock().instant(), ChronoUnit.NANOS));
}
static DateAndTimeValue NOW() {
- return DateAndTimeValue
- .toDateAndTimeValue(Instant.EPOCH.until(AbstractEvaluator.currentClock().instant(), ChronoUnit.NANOS));
+ return DateAndTimeValue.toDateAndTimeValue(
+ Instant.EPOCH.until(AbstractEvaluator.currentRealtimeClock().instant(), ChronoUnit.NANOS));
}
@OnlySupportedBy("4diac IDE")
- @Comment("Returns relative time stamp.")
+ @Comment("Override the monotonic clock.")
static void OVERRIDE_NOW_MONOTONIC(final TimeValue value) {
final Duration duration = value.toDuration();
final Instant instant = Instant.ofEpochSecond(duration.getSeconds(), duration.getNano());
- AbstractEvaluator.setClock(Clock.fixed(instant, ZoneId.systemDefault()));
+ AbstractEvaluator.setMonotonicClock(Clock.fixed(instant, ZoneOffset.UTC));
+ }
+
+ @OnlySupportedBy("4diac IDE")
+ @Comment("Override the real-time clock.")
+ static void OVERRIDE_NOW(final DateAndTimeValue value) {
+ final Instant instant = value.toLocalDateTime().toInstant(ZoneOffset.UTC);
+ AbstractEvaluator.setRealtimeClock(Clock.fixed(instant, ZoneOffset.UTC));
}
/* Date and Time conversions */