/*
* StreamProducer.java - Copyright(c) 2014 Joe Pasqua
* Provided under the MIT License. See the LICENSE file for details.
* Created: Apr 27, 2014
*/
package org.noroomattheinn.visibletesla.data;
import org.noroomattheinn.tesla.StreamState;
import org.noroomattheinn.tesla.Streamer;
import org.noroomattheinn.utils.Executor;
import org.noroomattheinn.utils.ThreadManager;
import org.noroomattheinn.visibletesla.vehicle.VTVehicle;
import static org.noroomattheinn.tesla.Tesla.logger;
/**
* StreamProducer: Generate a stream of locations on demand.
*
* @author Joe Pasqua <joe at NoRoomAtTheInn dot org>
*/
class StreamProducer extends Executor<StreamProducer.Request>
implements ThreadManager.Stoppable {
/*------------------------------------------------------------------------------
*
* Constants and Enums
*
*----------------------------------------------------------------------------*/
private static final long StreamingThreshold = 400; // 0.4 seconds
/*------------------------------------------------------------------------------
*
* Internal State
*
*----------------------------------------------------------------------------*/
private final Streamer streamer;
private final VTVehicle vtVehicle;
private long lastSnapshotTime = 0;
/*==============================================================================
* ------- -------
* ------- Public Interface To This Class -------
* ------- -------
*============================================================================*/
StreamProducer(VTVehicle v, FeedbackListener feedbackListener) {
super("StreamProducer", feedbackListener);
this.vtVehicle = v;
this.streamer = v.getVehicle().getStreamer();
ThreadManager.get().addStoppable((ThreadManager.Stoppable)this);
}
void produce(boolean stream) {
super.produce(new Request(stream, false));
}
@Override public void stop() { if (streamer != null) { streamer.forceClose(); } }
/*------------------------------------------------------------------------------
*
* Internal Methods - Some declared protected since they implement interfaces
*
*----------------------------------------------------------------------------*/
@Override protected boolean execRequest(Request r) throws Exception {
StreamState snapshot = r.continuation ? streamer.tryExistingStream() :
streamer.beginStreamIfNeeded();
if (snapshot == null) {
if (r.continuation && isInMotion()) { produce(true); }
return r.continuation; // Null is ok on a continuation, not otherwise
}
if (!r.continuation || snapshot.timestamp - lastSnapshotTime > StreamingThreshold) {
lastSnapshotTime = snapshot.timestamp;
vtVehicle.noteUpdatedState(snapshot);
}
if (r.stream) super.produce(new Request(true, true));
return true;
}
@Override protected Request filter(Request r) {
Request filtered = r;
Request pending = queue.peek();
if (r.equals(pending) || queue.remainingCapacity() == 0) {
filtered = null;
logger.finest("Filtering (s: " + r.stream + ", " + r.continuation +"), rqs = " + queue.remainingCapacity());
}
return filtered;
}
private boolean isInMotion() {
return vtVehicle.streamState.get().isInMotion();
}
@Override protected void addToHistogram(Executor.Request r) {
if (!((Request)r).continuation) super.addToHistogram(r);
}
static class Request extends Executor.Request {
final boolean stream;
final boolean continuation;
Request(boolean stream, boolean continuation) {
super(null);
this.stream = stream;
this.continuation = continuation;
}
@Override protected String getRequestName() { return "Stream"; }
@Override protected int maxRetries() { return 2; }
@Override public boolean equals(Object o) {
if (o == null) return false;
if (!(o instanceof Request)) return false;
Request r2 = (Request)o;
return (stream == r2.stream && continuation == r2.continuation);
}
@Override public int hashCode() {
return (this.stream ? 2 : 0) + (this.continuation ? 1 : 0);
}
}
}