
com.flash3388.flashlib.vision.control.SingleThreadVisionControl Maven / Gradle / Ivy
package com.flash3388.flashlib.vision.control;
import com.castle.util.throwables.ThrowableHandler;
import com.castle.util.throwables.Throwables;
import com.flash3388.flashlib.time.Clock;
import com.flash3388.flashlib.time.SystemNanoClock;
import com.flash3388.flashlib.time.Time;
import com.flash3388.flashlib.vision.Pipeline;
import com.flash3388.flashlib.vision.Source;
import com.flash3388.flashlib.vision.VisionException;
import com.flash3388.flashlib.vision.VisionResult;
import com.flash3388.flashlib.vision.analysis.Analysis;
import com.flash3388.flashlib.vision.control.event.NewResultEvent;
import com.flash3388.flashlib.vision.control.event.VisionListener;
import com.flash3388.flashlib.vision.processing.Processor;
import com.notifier.Controllers;
import com.notifier.EventController;
import java.util.Optional;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Function;
public class SingleThreadVisionControl implements VisionControl {
public static class Builder {
private final Function> mRunner;
private EventController mEventController;
private Clock mClock;
private ThrowableHandler mThrowableHandler;
private Source mSource;
private Pipeline> mPreProcessPipeline;
private Processor, Optional> mProcessor;
private Builder(Function> runner) {
mRunner = runner;
mThrowableHandler = Throwables.silentHandler();
}
public Builder eventController(EventController eventController) {
mEventController = eventController;
return this;
}
public Builder clock(Clock clock) {
mClock = clock;
return this;
}
public Builder onError(ThrowableHandler handler) {
mThrowableHandler = handler;
return this;
}
public Builder source(Source source) {
mSource = source;
return this;
}
public Builder preProcessWithOptions(Pipeline> pipeline) {
mPreProcessPipeline = pipeline;
return this;
}
public Builder preProcess(Pipeline pipeline) {
mPreProcessPipeline = Pipeline.mapper(pipeline, VisionData::getData);
return this;
}
public Builder processorWithOptions(Processor, Optional> processor) {
mProcessor = processor;
return this;
}
public Builder processor(Processor> processor) {
mProcessor = Processor.mapper(processor, VisionData::getData);
return this;
}
public SingleThreadVisionControl build() {
if (mClock == null) {
mClock = new SystemNanoClock();
}
if (mEventController == null) {
mEventController = Controllers.newSyncExecutionController();
}
return new SingleThreadVisionControl(mRunner, mClock, mEventController,
mSource,
mProcessor.divergeIn(mPreProcessPipeline),
mThrowableHandler);
}
}
public static Builder withExecutorService(ScheduledExecutorService executorService, Time pollingTime) {
Function> runner = (task)->
executorService.scheduleAtFixedRate(task, pollingTime.value(), pollingTime.value(), pollingTime.unit());
return new Builder(runner);
}
private final Function> mRunner;
private final Clock mClock;
private final EventController mEventController;
private final VisionOptions mVisionOptions;
private final Runnable mTask;
private final AtomicReference> mFuture;
private final AtomicReference mLatestResult;
private SingleThreadVisionControl(Function> runner, Clock clock, EventController eventController,
Source source, Processor, Optional> processor, ThrowableHandler throwableHandler) {
mRunner = runner;
mClock = clock;
mEventController = eventController;
mVisionOptions = new VisionOptions();
mFuture = new AtomicReference<>();
mLatestResult = new AtomicReference<>();
mTask = new Task(source,
processor.pipeTo(new NewAnalysisHandler(eventController, clock, mLatestResult)),
throwableHandler, mVisionOptions);
}
@Override
public boolean isRunning() {
return mFuture.get() != null;
}
@Override
public synchronized void start() {
if (isRunning()) {
return;
}
Future> future = mRunner.apply(mTask);
mFuture.set(future);
}
@Override
public synchronized void stop() {
if (!isRunning()) {
return;
}
Future> future = mFuture.get();
if (future != null) {
future.cancel(true);
}
}
@Override
public void setOption(VisionOption option, T value) {
mVisionOptions.put(option, value);
}
@Override
public Optional getOption(VisionOption option) {
return mVisionOptions.get(option);
}
@Override
public T getOptionOrDefault(VisionOption option, T defaultValue) {
return mVisionOptions.getOrDefault(option, defaultValue);
}
@Override
public Optional getLatestResult() {
return getLatestResult(false);
}
@Override
public Optional getLatestResult(boolean clear) {
return Optional.ofNullable(clear ? mLatestResult.getAndSet(null) : mLatestResult.get());
}
@Override
public Optional getLatestResult(Time maxTimestamp) {
return getLatestResult(maxTimestamp, false);
}
@Override
public Optional getLatestResult(Time maxTimestamp, boolean clear) {
VisionResult result = clear ? mLatestResult.getAndSet(null) : mLatestResult.get();
if (result == null) {
return Optional.empty();
}
Time now = mClock.currentTime();
Time passed = now.sub(result.getTimestamp());
if (passed.after(maxTimestamp)) {
return Optional.empty();
}
return Optional.of(result);
}
@Override
public void addListener(VisionListener listener) {
mEventController.registerListener(listener);
}
private static class Task implements Runnable {
private final Source mSource;
private final Pipeline> mPipeline;
private final ThrowableHandler mThrowableHandler;
private final VisionOptions mVisionOptions;
private Task(Source source, Pipeline> pipeline,
ThrowableHandler throwableHandler, VisionOptions visionOptions) {
mSource = source;
mPipeline = pipeline;
mThrowableHandler = throwableHandler;
mVisionOptions = visionOptions;
}
@Override
public void run() {
try {
S source = mSource.get();
mPipeline.process(new VisionData<>(source, mVisionOptions));
} catch (VisionException e) {
mThrowableHandler.handle(e);
}
}
}
private static class NewAnalysisHandler implements Pipeline> {
private final EventController mEventController;
private final Clock mClock;
private final AtomicReference mVisionResult;
private NewAnalysisHandler(EventController eventController, Clock clock,
AtomicReference visionResult) {
mEventController = eventController;
mClock = clock;
mVisionResult = visionResult;
}
@Override
public void process(Optional input) throws VisionException {
if (!input.isPresent()) {
return;
}
Analysis analysis = input.get();
Time now = mClock.currentTime();
VisionResult visionResult = new VisionResult(analysis, now);
mVisionResult.set(visionResult);
mEventController.fire(new NewResultEvent(visionResult), NewResultEvent.class,
VisionListener.class, VisionListener::onNewResult);
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy