KeepAliveMonitor.java
/*
* Copyright © 2016 Greg Chabala
*
* This file is part of brick-control-lab.
*
* brick-control-lab is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* brick-control-lab is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with brick-control-lab. If not, see http://www.gnu.org/licenses/.
*/
package org.chabala.brick.controllab;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.Closeable;
import java.io.IOException;
import java.time.Duration;
import java.util.concurrent.*;
/**
* Handles sending keep alive messages in the absence of other commands.
*/
class KeepAliveMonitor implements Closeable {
private final Logger log = LoggerFactory.getLogger(getClass());
private final SerialPortWriter serialPortWriter;
private final ScheduledExecutorService executor;
private final long keepAlivePeriodMs;
private static final byte KEEP_ALIVE_COMMAND = 0x02;
/** Slightly less than two seconds. */
private static final int KEEP_ALIVE_PERIOD_MS = 1800;
private ScheduledFuture<?> task;
/**
* Creates a keep alive monitor for the serial port managed by the specified
* writer. As soon as it's constructed, will send a keep alive message to the
* serial port with a frequency just under two seconds, as long as the port
* is open.
* @param serialPortWriter the port writer to receive keep alive messages
*/
KeepAliveMonitor(SerialPortWriter serialPortWriter) {
this(serialPortWriter, Duration.ofMillis(KEEP_ALIVE_PERIOD_MS));
}
KeepAliveMonitor(SerialPortWriter serialPortWriter, Duration keepAlivePeriod) {
this.serialPortWriter = serialPortWriter;
this.keepAlivePeriodMs = keepAlivePeriod.toMillis();
executor = Executors.newSingleThreadScheduledExecutor(
new NamedDaemonThreadFactory(
"KeepAlive " + serialPortWriter.getPortName()));
scheduleTask();
}
/**
* Call this method when a command is being sent, to reset the countdown
* on the keep alive monitor. It will reschedule the next keep alive
* message from this moment.
*/
void reset() {
task.cancel(false);
scheduleTask();
}
private void scheduleTask() {
task = executor.scheduleAtFixedRate(() -> sendCommand(KEEP_ALIVE_COMMAND),
keepAlivePeriodMs, keepAlivePeriodMs, TimeUnit.MILLISECONDS);
}
private void sendCommand(byte b) {
try {
serialPortWriter.sendCommand(b, log);
} catch (IOException e) {
log.error(e.getMessage(), e);
}
}
/**
* Cancel the next keep alive message and shutdown the monitor.
*/
@Override
public void close() {
if (executor != null) {
executor.shutdownNow();
}
}
/**
* A {@link ThreadFactory} which produces named, daemon threads.
*/
private static class NamedDaemonThreadFactory implements ThreadFactory {
private ThreadFactory defaultThreadFactory = Executors.defaultThreadFactory();
private final String threadName;
NamedDaemonThreadFactory(String threadName) {
this.threadName = threadName;
}
@Override
public Thread newThread(Runnable r) {
Thread thread = defaultThreadFactory.newThread(r);
thread.setName(threadName);
thread.setDaemon(true);
return thread;
}
}
}