new device communication & analysis
- switched to websockets - analysis as pure function with internal hidden cache - new redux reducers
This commit is contained in:
66
data_processing/DataAnalysis.js
Normal file
66
data_processing/DataAnalysis.js
Normal file
@@ -0,0 +1,66 @@
|
||||
import { PeakDetectorSimple } from './PeakDetection';
|
||||
import { List } from 'immutable';
|
||||
|
||||
|
||||
export default class DataAnalysis {
|
||||
|
||||
constructor() {
|
||||
this._resetCache(null, 0);
|
||||
}
|
||||
|
||||
analyze(analysisParameters, sessionId, allMeasurements) {
|
||||
const cacheValid = (
|
||||
this.sessionId === sessionId &&
|
||||
this.analyzedUpToIdx <= allMeasurements.size &&
|
||||
this.analysisParameters === analysisParameters);
|
||||
|
||||
let newData = null;
|
||||
|
||||
if (cacheValid) {
|
||||
newData = allMeasurements.slice(this.analyzedUpToIdx);
|
||||
}
|
||||
else {
|
||||
this._resetCache(analysisParameters, sessionId);
|
||||
newData = allMeasurements;
|
||||
}
|
||||
|
||||
// peaks
|
||||
const newPeaks = this.peakDetectorSimple.addVector(newData);
|
||||
this.allPeaks = this.allPeaks.concat(List(newPeaks));
|
||||
|
||||
// aggregated sum/max
|
||||
this.aggregatedMomentum = newData.reduce((sum, x) => sum + x, this.aggregatedMomentum);
|
||||
this.peakMax = newData.reduce((running, x) => Math.max(x, running), this.peakMax);
|
||||
|
||||
// windowed
|
||||
const windowNumDataPoints = analysisParameters.windowSizeInSecs * analysisParameters.numMeasurementsPerSec;
|
||||
const windowed = allMeasurements.slice(-windowNumDataPoints);
|
||||
const peakMaxWindow = windowed.reduce((running, x) => Math.max(x, running), 0);
|
||||
const momentumWindow = windowed.reduce((sum, x) => sum + x, 0);
|
||||
|
||||
this.analyzedUpToIdx = allMeasurements.size;
|
||||
return {
|
||||
peaks: this.allPeaks,
|
||||
totalTime: allMeasurements / analysisParameters.numMeasurementsPerSec,
|
||||
|
||||
totalMomentum: this.aggregatedMomentum / allMeasurements.size,
|
||||
peakMax: this.peakMax,
|
||||
|
||||
momentumWindow: momentumWindow,
|
||||
peakMaxWindow: peakMaxWindow,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
_resetCache(analysisParameters, sessionId) {
|
||||
this.peakDetectorSimple = analysisParameters ? new PeakDetectorSimple(analysisParameters.peakDetectorSimpleThreshold) : null;
|
||||
this.allPeaks = List();
|
||||
|
||||
this.aggregatedMomentum = 0;
|
||||
this.peakMax = 0;
|
||||
|
||||
this.sessionId = sessionId;
|
||||
this.analyzedUpToIdx = 0;
|
||||
this.analysisParameters = analysisParameters;
|
||||
}
|
||||
};
|
||||
84
data_processing/SwimTrackerWebsocketConnection.js
Normal file
84
data_processing/SwimTrackerWebsocketConnection.js
Normal file
@@ -0,0 +1,84 @@
|
||||
import ReconnectingWebSocket from 'reconnecting-websocket';
|
||||
|
||||
const OpCodes = {
|
||||
// from device to frontend
|
||||
INITIAL_INFO: 1,
|
||||
SESSION_STARTED: 2,
|
||||
SESSION_STOPPED: 3,
|
||||
SESSION_NEW_DATA: 4,
|
||||
|
||||
// from frontend to device
|
||||
START_SESSION: 5,
|
||||
STOP_SESSION: 6,
|
||||
TARE: 7
|
||||
};
|
||||
|
||||
|
||||
export default class SwimTrackerWebsocketConnection {
|
||||
constructor(swimTrackerHost, onData, onStarted, onStopped, onConnect, onDisconnect) {
|
||||
this.swimTrackerHost = swimTrackerHost;
|
||||
|
||||
this.onData = onData;
|
||||
this.onStarted = onStarted;
|
||||
this.onStopped = onStopped;
|
||||
this.onConnect = onConnect;
|
||||
this.onDisconnect = onDisconnect;
|
||||
|
||||
const wsOptions = {
|
||||
maxReconnectionDelay: 4000
|
||||
};
|
||||
this.ws = new ReconnectingWebSocket(`ws://${swimTrackerHost}:81`, [], wsOptions);
|
||||
this.ws.onmessage = this._onMessage;
|
||||
this.ws.onopen = this.onConnect;
|
||||
this.ws.onclose = this.onDisconnect;
|
||||
this.ws.onerror = this._onError;
|
||||
this.ws.binaryType = 'arraybuffer';
|
||||
}
|
||||
|
||||
sendStartCommand() {
|
||||
const data = new Uint8Array(1);
|
||||
data[0] = OpCodes.START_SESSION;
|
||||
this.ws.send(data);
|
||||
}
|
||||
|
||||
sendStopCommand() {
|
||||
const data = new Uint8Array(1);
|
||||
data[0] = OpCodes.STOP_SESSION;
|
||||
this.ws.send(data);
|
||||
}
|
||||
|
||||
sendTareCommand() {
|
||||
const data = new Uint8Array(1);
|
||||
data[0] = OpCodes.TARE;
|
||||
this.ws.send(data);
|
||||
}
|
||||
|
||||
_onMessage = (e) => {
|
||||
const dv = new DataView(e.data);
|
||||
const opCode = dv.getInt8(0);
|
||||
|
||||
if (opCode === OpCodes.INITIAL_INFO) {
|
||||
const headerSize = 6;
|
||||
const running = Boolean(dv.getInt8(1));
|
||||
const sessionId = dv.getUint32(2);
|
||||
if (running && e.data.byteLength > headerSize) {
|
||||
const data = new Uint16Array(e.data.slice(headerSize));
|
||||
this.onStarted(sessionId);
|
||||
this.onData(data);
|
||||
} else
|
||||
this.onStopped();
|
||||
} else if (opCode === OpCodes.SESSION_STARTED) {
|
||||
const sessionId = dv.getUint32(1);
|
||||
this.onStarted(sessionId);
|
||||
} else if (opCode === OpCodes.SESSION_STOPPED) {
|
||||
this.onStopped();
|
||||
} else if (opCode === OpCodes.SESSION_NEW_DATA) {
|
||||
const data = new Uint16Array(e.data.slice(1));
|
||||
this.onData(data);
|
||||
}
|
||||
}
|
||||
|
||||
_onError = (ev) => {
|
||||
console.log("Websocket error", ev);
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user