From a65a68614d1a34c768458c4009ff181b94d32c56 Mon Sep 17 00:00:00 2001 From: tomtzook Date: Tue, 16 Jan 2024 01:14:11 +0200 Subject: [PATCH 1/3] added watchdog --- .../flashlib/app/BasicFlashLibControl.java | 30 +++ .../flashlib/app/DelegatingAppBase.java | 13 ++ .../flashlib/app/FlashLibControl.java | 43 +++++ .../flashlib/app/watchdog/FeedReporter.java | 11 ++ .../app/watchdog/InternalWatchdog.java | 10 + .../app/watchdog/LoggingFeedReporter.java | 33 ++++ .../app/watchdog/MultiFeedReporters.java | 29 +++ .../app/watchdog/TimestampReport.java | 22 +++ .../flashlib/app/watchdog/Watchdog.java | 63 +++++++ .../flashlib/app/watchdog/WatchdogImpl.java | 176 ++++++++++++++++++ .../app/watchdog/WatchdogService.java | 77 ++++++++ .../robot/base/DelegatingRobotControl.java | 13 ++ .../robot/base/GenericRobotControl.java | 32 ++++ .../base/iterative/LoopingRobotBase.java | 21 ++- .../base/iterative/RobotIntervalLooper.java | 5 + .../robot/base/iterative/RobotLooper.java | 3 + 16 files changed, 578 insertions(+), 3 deletions(-) create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/FeedReporter.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/InternalWatchdog.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/LoggingFeedReporter.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/MultiFeedReporters.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/TimestampReport.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/Watchdog.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java create mode 100644 flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java index 44cb7a7e7..79feb698b 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java @@ -3,8 +3,16 @@ import com.flash3388.flashlib.app.net.NetworkConfiguration; import com.flash3388.flashlib.app.net.NetworkInterface; import com.flash3388.flashlib.app.net.NetworkInterfaceImpl; +import com.flash3388.flashlib.app.watchdog.FeedReporter; +import com.flash3388.flashlib.app.watchdog.InternalWatchdog; +import com.flash3388.flashlib.app.watchdog.LoggingFeedReporter; +import com.flash3388.flashlib.app.watchdog.MultiFeedReporters; +import com.flash3388.flashlib.app.watchdog.Watchdog; +import com.flash3388.flashlib.app.watchdog.WatchdogImpl; +import com.flash3388.flashlib.app.watchdog.WatchdogService; import com.flash3388.flashlib.time.Clock; import com.flash3388.flashlib.time.SystemNanoClock; +import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.FlashLibMainThread; import com.flash3388.flashlib.util.FlashLibMainThreadImpl; import com.flash3388.flashlib.util.logging.Logging; @@ -12,6 +20,7 @@ import com.flash3388.flashlib.util.unique.InstanceId; import org.slf4j.Logger; +import java.util.Arrays; import java.util.Collection; public class BasicFlashLibControl implements FlashLibControl { @@ -25,6 +34,7 @@ public class BasicFlashLibControl implements FlashLibControl { private final ServiceRegistry mServiceRegistry; private final NetworkInterface mNetworkInterface; private final FlashLibMainThread mMainThread; + private final WatchdogService mWatchdogService; public BasicFlashLibControl(InstanceId instanceId, ResourceHolder resourceHolder, @@ -37,6 +47,9 @@ public BasicFlashLibControl(InstanceId instanceId, mServiceRegistry = new BasicServiceRegistry(mMainThread); mNetworkInterface = new NetworkInterfaceImpl( networkConfiguration, instanceId, mServiceRegistry, mClock, mMainThread); + mWatchdogService = new WatchdogService(); + + mServiceRegistry.register(mWatchdogService); } public BasicFlashLibControl(InstanceId instanceId, ResourceHolder resourceHolder) { @@ -78,4 +91,21 @@ public NetworkInterface getNetworkInterface() { public FlashLibMainThread getMainThread() { return mMainThread; } + + @Override + public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + FeedReporter feedReporter = new MultiFeedReporters(Arrays.asList(new LoggingFeedReporter(), reporter)); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter); + mWatchdogService.register(watchdog); + + return watchdog; + } + + @Override + public Watchdog newWatchdog(String name, Time timeout) { + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter()); + mWatchdogService.register(watchdog); + + return watchdog; + } } diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/DelegatingAppBase.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/DelegatingAppBase.java index 3dd903e13..3d7161d09 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/DelegatingAppBase.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/DelegatingAppBase.java @@ -1,7 +1,10 @@ package com.flash3388.flashlib.app; import com.flash3388.flashlib.app.net.NetworkInterface; +import com.flash3388.flashlib.app.watchdog.FeedReporter; +import com.flash3388.flashlib.app.watchdog.Watchdog; import com.flash3388.flashlib.time.Clock; +import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.FlashLibMainThread; import com.flash3388.flashlib.util.unique.InstanceId; import org.slf4j.Logger; @@ -50,4 +53,14 @@ public NetworkInterface getNetworkInterface() { public FlashLibMainThread getMainThread() { return mControl.getMainThread(); } + + @Override + public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + return mControl.newWatchdog(name, timeout, reporter); + } + + @Override + public Watchdog newWatchdog(String name, Time timeout) { + return mControl.newWatchdog(name, timeout); + } } diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/FlashLibControl.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/FlashLibControl.java index 36fcf00dd..8f729809c 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/FlashLibControl.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/FlashLibControl.java @@ -3,7 +3,10 @@ import com.flash3388.flashlib.annotations.MainThreadOnly; import com.flash3388.flashlib.app.net.NetworkInterface; import com.flash3388.flashlib.app.net.NetworkingMode; +import com.flash3388.flashlib.app.watchdog.FeedReporter; +import com.flash3388.flashlib.app.watchdog.Watchdog; import com.flash3388.flashlib.time.Clock; +import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.FlashLibMainThread; import com.flash3388.flashlib.util.unique.InstanceId; import org.slf4j.Logger; @@ -114,4 +117,44 @@ default void registerCloseables(AutoCloseable... closeables) { */ FlashLibMainThread getMainThread(); + /** + * Creates a new watchdog, used to track run of services, threads or loops. As a way to ensure + * it runs within a specific time. + * + * To use the watchdog, the target must periodically call {@link Watchdog#feed()}. As long as + * it keeps updating the {@link Watchdog} in time, then we can assume that it is up and running. + * If the target does not update the watchdog within a set amount of time, then it is considered expired + * while will be reported. + * + * The watchdog must first be enabled for usage with {@link Watchdog#enable()}. + * + * @param name name of the watchdog + * @param timeout time within the watchdog is expected to be updated, if the watchdog was not updated within + * the given time, it is expired and this will be reported. + * @param reporter reporter to updated on expiration + * @return {@link Watchdog} + * + * @see #newWatchdog(String, Time) + */ + Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter); + + /** + * Creates a new watchdog, used to track run of services, threads or loops. As a way to ensure + * it runs within a specific time. + * + * To use the watchdog, the target must periodically call {@link Watchdog#feed()}. As long as + * it keeps updating the {@link Watchdog} in time, then we can assume that it is up and running. + * If the target does not update the watchdog within a set amount of time, then it is considered expired + * while will be reported to the log. + * + * The watchdog must first be enabled for usage with {@link Watchdog#enable()}. + * + * @param name name of the watchdog + * @param timeout time within the watchdog is expected to be updated, if the watchdog was not updated within + * the given time, it is expired and this will be reported. + * @return {@link Watchdog} + * + * @see #newWatchdog(String, Time, FeedReporter) + */ + Watchdog newWatchdog(String name, Time timeout); } diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/FeedReporter.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/FeedReporter.java new file mode 100644 index 000000000..1a93cad48 --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/FeedReporter.java @@ -0,0 +1,11 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; + +import java.util.List; + +public interface FeedReporter { + + void reportFeed(String watchdogName); + void reportFeedExpired(String watchdogName, Time feedOverrun, List reports); +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/InternalWatchdog.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/InternalWatchdog.java new file mode 100644 index 000000000..51a85edce --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/InternalWatchdog.java @@ -0,0 +1,10 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; + +public interface InternalWatchdog extends Watchdog { + + Time getTimeLeftToTimeout(); + + void checkFed(); +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/LoggingFeedReporter.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/LoggingFeedReporter.java new file mode 100644 index 000000000..5d0f69d3c --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/LoggingFeedReporter.java @@ -0,0 +1,33 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; +import com.flash3388.flashlib.util.logging.Logging; +import org.slf4j.Logger; + +import java.util.List; + +public class LoggingFeedReporter implements FeedReporter { + + private static final Logger LOGGER = Logging.getLogger("Watchdog"); + + @Override + public void reportFeed(String watchdogName) { + // do not report + } + + @Override + public void reportFeedExpired(String watchdogName, Time feedOverrun, List reports) { + StringBuilder builder = new StringBuilder(); + + builder.append("Watchdog ").append(watchdogName).append(" overrun report\n"); + builder.append("Time overrun:\t").append(String.format("%.3fs\n", feedOverrun.valueAsSeconds())); + builder.append("Timestamps:\n"); + + for (TimestampReport report : reports) { + builder.append('\t').append(report.getKey()).append(":") + .append(String.format("%.3fs\n", report.getReportTime().valueAsSeconds())); + } + + LOGGER.warn(builder.toString()); + } +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/MultiFeedReporters.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/MultiFeedReporters.java new file mode 100644 index 000000000..3dd5cb077 --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/MultiFeedReporters.java @@ -0,0 +1,29 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; + +import java.util.Collection; +import java.util.List; + +public class MultiFeedReporters implements FeedReporter { + + private final Collection mReporters; + + public MultiFeedReporters(Collection reporters) { + mReporters = reporters; + } + + @Override + public void reportFeed(String watchdogName) { + for (FeedReporter reporter : mReporters) { + reporter.reportFeed(watchdogName); + } + } + + @Override + public void reportFeedExpired(String watchdogName, Time feedOverrun, List reports) { + for (FeedReporter reporter : mReporters) { + reporter.reportFeedExpired(watchdogName, feedOverrun, reports); + } + } +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/TimestampReport.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/TimestampReport.java new file mode 100644 index 000000000..c92e3c5e3 --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/TimestampReport.java @@ -0,0 +1,22 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; + +public class TimestampReport { + + private final String mKey; + private final Time mReportTime; + + public TimestampReport(String key, Time reportTime) { + mKey = key; + mReportTime = reportTime; + } + + public String getKey() { + return mKey; + } + + public Time getReportTime() { + return mReportTime; + } +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/Watchdog.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/Watchdog.java new file mode 100644 index 000000000..4132b9db1 --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/Watchdog.java @@ -0,0 +1,63 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Time; + +public interface Watchdog { + + /** + * Gets the name of the watchdog. + * + * @return name, representative of the watchdog. + */ + String getName(); + + /** + * Gets the feed timeout of the watchdog. + * + * @return time + */ + Time getTimeout(); + + /** + * Gets whether the watchdog is currently enabled. + * + * @return true if enabled, false otherwise. + */ + boolean isEnabled(); + + /** + * Gets whether the feeding timer has expired. Measured according to the last + * call to {@link #feed()}. + * + * @return true if expired, false otherwise. + */ + boolean isExpired(); + + /** + * Disables the feeding check of the watchdog. + * Should be used when the target for the watchdog is not running or executing a + * segment which should not be measured. + */ + void disable(); + + /** + * Enables the feeding check of the watchdog. Effectively restarting + * each functionality. + */ + void enable(); + + /** + * Register a timestamp report. Use this to report on the progress of the target + * for help debugging which parts of the target cause delays in execution. + * + * @param key storage key identifier, should be unique for this timestamp. + */ + void reportTimestamp(String key); + + /** + * Feeds the watchdog, updating it that the target is still functioning. Should + * be called periodically at specific location to indicate that the target is still + * functioning. + */ + void feed(); +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java new file mode 100644 index 000000000..76bb58534 --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java @@ -0,0 +1,176 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.flash3388.flashlib.time.Clock; +import com.flash3388.flashlib.time.Time; +import com.flash3388.flashlib.util.logging.Logging; +import org.slf4j.Logger; + +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; + +public class WatchdogImpl implements InternalWatchdog { + + private static final Logger LOGGER = Logging.getLogger("Watchdog"); + + private final Clock mClock; + private final String mName; + private final Time mTimeout; + private final FeedReporter mReporter; + + private final Lock mLock; + private final List mReports; + private Time mLastFeedTime; + private boolean mDisabled; + private boolean mIsExpired; + + public WatchdogImpl(Clock clock, String name, Time timeout, FeedReporter reporter) { + mClock = clock; + mName = name; + mTimeout = timeout; + mReporter = reporter; + + mLock = new ReentrantLock(); + mReports = new ArrayList<>(5); + + disable(); + } + + @Override + public String getName() { + return mName; + } + + @Override + public Time getTimeout() { + return mTimeout; + } + + @Override + public boolean isEnabled() { + return !mDisabled; + } + + @Override + public boolean isExpired() { + // possible because boolean assignment is atomic in java memory model. + return mIsExpired; + } + + @Override + public void disable() { + mLock.lock(); + try { + if (mDisabled) { + throw new IllegalStateException("already disabled"); + } + + mDisabled = true; + mIsExpired = false; + mLastFeedTime = Time.INVALID; + mReports.clear(); + } finally { + mLock.unlock(); + } + } + + @Override + public void enable() { + mLock.lock(); + try { + if (!mDisabled) { + throw new IllegalStateException("already enabled"); + } + + mReports.clear(); + mIsExpired = false; + mLastFeedTime = mClock.currentTime(); + mDisabled = false; + } finally { + mLock.unlock(); + } + } + + @Override + public void reportTimestamp(String key) { + mLock.lock(); + try { + if (mDisabled) { + throw new IllegalStateException("disabled"); + } + + Time now = mClock.currentTime(); + Time timePassed = now.sub(mLastFeedTime); + + mReports.add(new TimestampReport(key, timePassed)); + } finally { + mLock.unlock(); + } + } + + @Override + public void feed() { + mLock.lock(); + try { + if (mDisabled) { + throw new IllegalStateException("disabled"); + } + + Time now = mClock.currentTime(); + Time timePassed = now.sub(mLastFeedTime); + if (timePassed.after(mTimeout)) { + // feed overrun + LOGGER.trace("Watchdog {} overrun on feed call", mName); + + Time overrun = timePassed.sub(mTimeout); + mReporter.reportFeedExpired(mName, overrun, mReports); + } else { + LOGGER.trace("Watchdog {} feed", mName); + mReporter.reportFeed(mName); + } + + mIsExpired = false; + mLastFeedTime = now; + mReports.clear(); + } finally { + mLock.unlock(); + } + } + + @Override + public Time getTimeLeftToTimeout() { + mLock.lock(); + try { + Time now = mClock.currentTime(); + return now.sub(mLastFeedTime); + } finally { + mLock.unlock(); + } + } + + @Override + public void checkFed() { + mLock.lock(); + try { + if (mDisabled) { + // swallow as this is an inner API + return; + } + + Time now = mClock.currentTime(); + Time timePassed = now.sub(mLastFeedTime); + if (timePassed.after(mTimeout)) { + // feed overrun + LOGGER.trace("Watchdog {} overrun on check feed", mName); + + mIsExpired = true; + + Time overrun = timePassed.sub(mTimeout); + mReporter.reportFeedExpired(mName, overrun, mReports); + } + } finally { + mLock.unlock(); + } + } +} diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java new file mode 100644 index 000000000..7c8a6871e --- /dev/null +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java @@ -0,0 +1,77 @@ +package com.flash3388.flashlib.app.watchdog; + +import com.castle.concurrent.service.TerminalServiceBase; +import com.castle.exceptions.ServiceException; +import com.flash3388.flashlib.util.concurrent.Sleeper; +import com.flash3388.flashlib.util.logging.Logging; +import org.slf4j.Logger; + +import java.util.Comparator; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.PriorityBlockingQueue; + +public class WatchdogService extends TerminalServiceBase { + + private static final Logger LOGGER = Logging.getLogger("Watchdog"); + + private final PriorityBlockingQueue mWatchdogs; + private Thread mThread; + + public WatchdogService() { + mWatchdogs = new PriorityBlockingQueue<>(3, + Comparator.comparing(InternalWatchdog::getTimeout)); + } + + public void register(InternalWatchdog watchdog) { + mWatchdogs.add(watchdog); + } + + @Override + protected void startRunning() throws ServiceException { + mThread = new Thread(new Task(mWatchdogs), "WatchdogThread"); + mThread.start(); + } + + @Override + protected void stopRunning() { + if (mThread != null) { + mThread.interrupt(); + mThread = null; + } + } + + private static class Task implements Runnable { + + private final BlockingQueue mWatchdogs; + private final Sleeper mSleeper; + + private Task(BlockingQueue watchdogs) { + mWatchdogs = watchdogs; + mSleeper = new Sleeper(); + } + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + InternalWatchdog watchdog = mWatchdogs.take(); + try { + if (watchdog.isExpired() || !watchdog.isEnabled()) { + continue; + } + + // TODO: BETTER WAIT FOR TIMEOUT + mSleeper.sleep(watchdog.getTimeLeftToTimeout()); + watchdog.checkFed(); + } finally { + mWatchdogs.add(watchdog); + } + } catch (InterruptedException e) { + break; + } catch (Throwable t) { + LOGGER.error("Error while checking watchdog", t); + } + } + } + } +} diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/DelegatingRobotControl.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/DelegatingRobotControl.java index cd91969d4..3acaff6fa 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/DelegatingRobotControl.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/DelegatingRobotControl.java @@ -2,6 +2,8 @@ import com.flash3388.flashlib.app.ServiceRegistry; import com.flash3388.flashlib.app.net.NetworkInterface; +import com.flash3388.flashlib.app.watchdog.FeedReporter; +import com.flash3388.flashlib.app.watchdog.Watchdog; import com.flash3388.flashlib.hid.HidInterface; import com.flash3388.flashlib.io.IoInterface; import com.flash3388.flashlib.io.devices.DeviceInterface; @@ -9,6 +11,7 @@ import com.flash3388.flashlib.robot.modes.RobotModeSupplier; import com.flash3388.flashlib.scheduling.Scheduler; import com.flash3388.flashlib.time.Clock; +import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.FlashLibMainThread; import com.flash3388.flashlib.util.unique.InstanceId; import org.slf4j.Logger; @@ -117,4 +120,14 @@ public DeviceInterface getDeviceInterface() { public FlashLibMainThread getMainThread() { return mRobotControl.getMainThread(); } + + @Override + public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + return mRobotControl.newWatchdog(name, timeout, reporter); + } + + @Override + public Watchdog newWatchdog(String name, Time timeout) { + return mRobotControl.newWatchdog(name, timeout); + } } diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java index 923378ff1..a4dc2f19e 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java @@ -5,6 +5,13 @@ import com.flash3388.flashlib.app.net.NetworkConfiguration; import com.flash3388.flashlib.app.net.NetworkInterface; import com.flash3388.flashlib.app.net.NetworkInterfaceImpl; +import com.flash3388.flashlib.app.watchdog.FeedReporter; +import com.flash3388.flashlib.app.watchdog.InternalWatchdog; +import com.flash3388.flashlib.app.watchdog.LoggingFeedReporter; +import com.flash3388.flashlib.app.watchdog.MultiFeedReporters; +import com.flash3388.flashlib.app.watchdog.Watchdog; +import com.flash3388.flashlib.app.watchdog.WatchdogImpl; +import com.flash3388.flashlib.app.watchdog.WatchdogService; import com.flash3388.flashlib.hid.HidInterface; import com.flash3388.flashlib.io.IoInterface; import com.flash3388.flashlib.io.devices.DeviceInterface; @@ -30,6 +37,7 @@ import com.flash3388.flashlib.util.unique.InstanceId; import org.slf4j.Logger; +import java.util.Arrays; import java.util.Collection; import java.util.function.Supplier; @@ -77,6 +85,7 @@ public static Configuration controlStubs() { private final ServiceRegistry mServiceRegistry; private final FlashLibMainThread mMainThread; private final DeviceInterface mDeviceInterface; + private final WatchdogService mWatchdogService; public GenericRobotControl(InstanceId instanceId, ResourceHolder resourceHolder, @@ -100,6 +109,9 @@ public GenericRobotControl(InstanceId instanceId, mServiceRegistry = serviceRegistry; mMainThread = mainThread; mDeviceInterface = deviceInterface; + + mWatchdogService = new WatchdogService(); + mServiceRegistry.register(mWatchdogService); } public GenericRobotControl(InstanceId instanceId, @@ -151,6 +163,9 @@ public GenericRobotControl(InstanceId instanceId, mHidInterface = configuration.hidBackend.createInterface(this); mIoInterface = configuration.ioBackend.createInterface(this); + + mWatchdogService = new WatchdogService(); + mServiceRegistry.register(mWatchdogService); } public GenericRobotControl(InstanceId instanceId, @@ -224,6 +239,23 @@ public FlashLibMainThread getMainThread() { return mMainThread; } + @Override + public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + FeedReporter feedReporter = new MultiFeedReporters(Arrays.asList(new LoggingFeedReporter(), reporter)); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter); + mWatchdogService.register(watchdog); + + return watchdog; + } + + @Override + public Watchdog newWatchdog(String name, Time timeout) { + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter()); + mWatchdogService.register(watchdog); + + return watchdog; + } + @Override public ServiceRegistry getServiceRegistry() { return mServiceRegistry; diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/LoopingRobotBase.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/LoopingRobotBase.java index 2900da980..2e33c7df8 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/LoopingRobotBase.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/LoopingRobotBase.java @@ -1,6 +1,7 @@ package com.flash3388.flashlib.robot.base.iterative; import com.flash3388.flashlib.app.StartupException; +import com.flash3388.flashlib.app.watchdog.Watchdog; import com.flash3388.flashlib.robot.RobotControl; import com.flash3388.flashlib.robot.base.RobotBase; import com.flash3388.flashlib.robot.modes.RobotMode; @@ -21,6 +22,7 @@ public class LoopingRobotBase implements RobotBase { private RobotControl mRobotControl; private IterativeRobot mRobot; + private Watchdog mLoopWatchdog; private RobotMode mCurrentMode; private RobotMode mLastMode; private boolean mWasCurrentModeInitialized; @@ -29,7 +31,7 @@ public class LoopingRobotBase implements RobotBase { * Creates a new looping base. * * @param robotInitializer initializer for user robot logic. - * @param robotLooper looper object to run the loops periodically. + * @param robotLooper looper object to run the loops periodically. */ public LoopingRobotBase(IterativeRobot.Initializer robotInitializer, RobotLooper robotLooper) { mRobotInitializer = robotInitializer; @@ -59,6 +61,8 @@ public LoopingRobotBase(IterativeRobot.Initializer robotInitializer) { @Override public final void robotInit(RobotControl robotControl) throws StartupException { mRobotControl = robotControl; + mLoopWatchdog = robotControl.newWatchdog("LoopingRobotBase", mRobotLooper.getLoopRunPeriod()); + mRobot = mRobotInitializer.init(robotControl); } @@ -80,9 +84,11 @@ public final void robotShutdown(){ } protected final void robotLoop(){ + mLoopWatchdog.enable(); + mCurrentMode = mRobotControl.getMode(); - if (!mCurrentMode.equals(mLastMode)) { + if (!mCurrentMode.equals(mLastMode) && mWasCurrentModeInitialized) { exitMode(mCurrentMode); mLastMode = mCurrentMode; mWasCurrentModeInitialized = false; @@ -94,6 +100,9 @@ protected final void robotLoop(){ } periodicMode(mCurrentMode); + + mLoopWatchdog.feed(); + mLoopWatchdog.disable(); } private void initMode(RobotMode mode) { @@ -111,18 +120,24 @@ private void periodicMode(RobotMode mode) { mRobotControl.getLogger().debug("Periodic mode {}", mode); mRobotControl.getMainThread().executePendingTasks(); + mLoopWatchdog.reportTimestamp("MainThread.executePendingTasks"); + mRobotControl.getServiceRegistry().startAll(); + mLoopWatchdog.reportTimestamp("ServiceRegistry.startAll"); + mRobotControl.getScheduler().run(mode); + mLoopWatchdog.reportTimestamp("Scheduler.run"); if (mode.isDisabled()) { mRobot.disabledPeriodic(); } else { mRobot.modePeriodic(mode); } + mLoopWatchdog.reportTimestamp("ModePeriodic"); mRobotControl.getLogger().debug("Robot periodic"); - mRobot.robotPeriodic(); + mLoopWatchdog.reportTimestamp("robotPeriodic"); } private void exitMode(RobotMode mode) { diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotIntervalLooper.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotIntervalLooper.java index 56ce76906..f01ecb5fa 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotIntervalLooper.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotIntervalLooper.java @@ -28,6 +28,11 @@ public RobotIntervalLooper() { this(new Sleeper(), DEFAULT_ITERATION_INTERVAL); } + @Override + public Time getLoopRunPeriod() { + return mIterationInterval; + } + @Override public void startLooping(Clock clock, Runnable loopTask) { while(mRunLoopProperty.getAsBoolean() && !Thread.interrupted()){ diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotLooper.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotLooper.java index b8a5fef63..6754852b1 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotLooper.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/iterative/RobotLooper.java @@ -1,9 +1,12 @@ package com.flash3388.flashlib.robot.base.iterative; import com.flash3388.flashlib.time.Clock; +import com.flash3388.flashlib.time.Time; public interface RobotLooper { + Time getLoopRunPeriod(); + void startLooping(Clock clock, Runnable loopTask); void stop(); } From e3bef5aca53113f5b12aa367fcc2ff5e7c7138fc Mon Sep 17 00:00:00 2001 From: tomtzook Date: Tue, 16 Jan 2024 01:38:31 +0200 Subject: [PATCH 2/3] some fixes to watchdog + obsr usage --- .../flashlib/app/BasicFlashLibControl.java | 8 +++- .../flashlib/app/watchdog/WatchdogImpl.java | 44 ++++++++++++++++++- .../app/watchdog/WatchdogService.java | 7 ++- .../robot/base/GenericRobotControl.java | 7 ++- 4 files changed, 60 insertions(+), 6 deletions(-) diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java index 79feb698b..b7702caf9 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/BasicFlashLibControl.java @@ -10,6 +10,8 @@ import com.flash3388.flashlib.app.watchdog.Watchdog; import com.flash3388.flashlib.app.watchdog.WatchdogImpl; import com.flash3388.flashlib.app.watchdog.WatchdogService; +import com.flash3388.flashlib.net.obsr.ObjectStorage; +import com.flash3388.flashlib.net.obsr.StoredObject; import com.flash3388.flashlib.time.Clock; import com.flash3388.flashlib.time.SystemNanoClock; import com.flash3388.flashlib.time.Time; @@ -94,8 +96,9 @@ public FlashLibMainThread getMainThread() { @Override public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + StoredObject rootObject = WatchdogImpl.getWatchdogStoredObject(this, name); FeedReporter feedReporter = new MultiFeedReporters(Arrays.asList(new LoggingFeedReporter(), reporter)); - InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter, rootObject); mWatchdogService.register(watchdog); return watchdog; @@ -103,7 +106,8 @@ public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { @Override public Watchdog newWatchdog(String name, Time timeout) { - InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter()); + StoredObject rootObject = WatchdogImpl.getWatchdogStoredObject(this, name); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter(), rootObject); mWatchdogService.register(watchdog); return watchdog; diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java index 76bb58534..22dd33b16 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogImpl.java @@ -1,5 +1,10 @@ package com.flash3388.flashlib.app.watchdog; +import com.flash3388.flashlib.app.FlashLibControl; +import com.flash3388.flashlib.app.net.NetworkInterface; +import com.flash3388.flashlib.net.obsr.ObjectStorage; +import com.flash3388.flashlib.net.obsr.StoredEntry; +import com.flash3388.flashlib.net.obsr.StoredObject; import com.flash3388.flashlib.time.Clock; import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.logging.Logging; @@ -19,18 +24,26 @@ public class WatchdogImpl implements InternalWatchdog { private final Time mTimeout; private final FeedReporter mReporter; + private final StoredEntry mLastFeedTimeEntry; + private final StoredEntry mIsEnabledEntry; + private final StoredEntry mIsExpiredEntry; + private final Lock mLock; private final List mReports; private Time mLastFeedTime; private boolean mDisabled; private boolean mIsExpired; - public WatchdogImpl(Clock clock, String name, Time timeout, FeedReporter reporter) { + public WatchdogImpl(Clock clock, String name, Time timeout, FeedReporter reporter, StoredObject rootObject) { mClock = clock; mName = name; mTimeout = timeout; mReporter = reporter; + mLastFeedTimeEntry = rootObject.getEntry("LastFeedTime"); + mIsEnabledEntry = rootObject.getEntry("IsEnabled"); + mIsExpiredEntry = rootObject.getEntry("IsExpired"); + mLock = new ReentrantLock(); mReports = new ArrayList<>(5); @@ -70,6 +83,10 @@ public void disable() { mIsExpired = false; mLastFeedTime = Time.INVALID; mReports.clear(); + + mIsEnabledEntry.setBoolean(false); + mIsExpiredEntry.setBoolean(false); + mLastFeedTimeEntry.setDouble(-1); } finally { mLock.unlock(); } @@ -86,6 +103,11 @@ public void enable() { mReports.clear(); mIsExpired = false; mLastFeedTime = mClock.currentTime(); + + mIsEnabledEntry.setBoolean(true); + mIsExpiredEntry.setBoolean(false); + mLastFeedTimeEntry.setDouble(mLastFeedTime.valueAsSeconds()); + mDisabled = false; } finally { mLock.unlock(); @@ -133,6 +155,9 @@ public void feed() { mIsExpired = false; mLastFeedTime = now; mReports.clear(); + + mIsExpiredEntry.setBoolean(false); + mLastFeedTimeEntry.setDouble(mLastFeedTime.valueAsSeconds()); } finally { mLock.unlock(); } @@ -142,6 +167,10 @@ public void feed() { public Time getTimeLeftToTimeout() { mLock.lock(); try { + if (mDisabled) { + return Time.INVALID; + } + Time now = mClock.currentTime(); return now.sub(mLastFeedTime); } finally { @@ -168,9 +197,22 @@ public void checkFed() { Time overrun = timePassed.sub(mTimeout); mReporter.reportFeedExpired(mName, overrun, mReports); + + mIsExpiredEntry.setBoolean(true); } } finally { mLock.unlock(); } } + + public static StoredObject getWatchdogStoredObject(FlashLibControl control, String name) { + NetworkInterface networkInterface = control.getNetworkInterface(); + if (networkInterface.getMode().isObjectStorageEnabled()) { + ObjectStorage objectStorage = networkInterface.getObjectStorage(); + return objectStorage.getInstanceRoot().getChild("Watchdogs").getChild(name); + } else { + control.getLogger().warn("OBSR not enabled, creating non attached Watchdog"); + return new StoredObject.Stub(); + } + } } diff --git a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java index 7c8a6871e..69e59b5f6 100644 --- a/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java +++ b/flashlib.core.app/src/main/java/com/flash3388/flashlib/app/watchdog/WatchdogService.java @@ -2,6 +2,7 @@ import com.castle.concurrent.service.TerminalServiceBase; import com.castle.exceptions.ServiceException; +import com.flash3388.flashlib.time.Time; import com.flash3388.flashlib.util.concurrent.Sleeper; import com.flash3388.flashlib.util.logging.Logging; import org.slf4j.Logger; @@ -61,7 +62,11 @@ public void run() { } // TODO: BETTER WAIT FOR TIMEOUT - mSleeper.sleep(watchdog.getTimeLeftToTimeout()); + Time waitTime = watchdog.getTimeLeftToTimeout(); + if (waitTime.isValid()) { + mSleeper.sleep(waitTime); + } + watchdog.checkFed(); } finally { mWatchdogs.add(watchdog); diff --git a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java index a4dc2f19e..de998b70d 100644 --- a/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java +++ b/flashlib.core.robot/src/main/java/com/flash3388/flashlib/robot/base/GenericRobotControl.java @@ -19,6 +19,7 @@ import com.flash3388.flashlib.net.hfcs.HfcsRegistry; import com.flash3388.flashlib.net.hfcs.ping.HfcsPing; import com.flash3388.flashlib.net.obsr.ObjectStorage; +import com.flash3388.flashlib.net.obsr.StoredObject; import com.flash3388.flashlib.robot.RobotControl; import com.flash3388.flashlib.robot.RobotFactory; import com.flash3388.flashlib.robot.hfcs.control.HfcsRobotControl; @@ -241,8 +242,9 @@ public FlashLibMainThread getMainThread() { @Override public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { + StoredObject rootObject = WatchdogImpl.getWatchdogStoredObject(this, name); FeedReporter feedReporter = new MultiFeedReporters(Arrays.asList(new LoggingFeedReporter(), reporter)); - InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, feedReporter, rootObject); mWatchdogService.register(watchdog); return watchdog; @@ -250,7 +252,8 @@ public Watchdog newWatchdog(String name, Time timeout, FeedReporter reporter) { @Override public Watchdog newWatchdog(String name, Time timeout) { - InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter()); + StoredObject rootObject = WatchdogImpl.getWatchdogStoredObject(this, name); + InternalWatchdog watchdog = new WatchdogImpl(getClock(), name, timeout, new LoggingFeedReporter(), rootObject); mWatchdogService.register(watchdog); return watchdog; From f0a0e0daf83e2f305fe9cf824fe468428ea8cd03 Mon Sep 17 00:00:00 2001 From: tomtzook Date: Tue, 16 Jan 2024 01:38:56 +0200 Subject: [PATCH 3/3] version update --- gradle.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gradle.properties b/gradle.properties index ca2e69762..561d086a9 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,5 +1,5 @@ GROUP = com.flash3388.flashlib -VERSION = 3.2.2 +VERSION = 3.2.3 NEXUS_SNAPSHOT_REPOSITORY_URL = https\://oss.sonatype.org/content/repositories/snapshots NEXUS_RELEASE_REPOSITORY_URL = https\://oss.sonatype.org/service/local/staging/deploy/maven2