diff --git a/components/LiveTrainingView.js b/components/LiveTrainingView.js index 7dd881f..d15f478 100644 --- a/components/LiveTrainingView.js +++ b/components/LiveTrainingView.js @@ -7,6 +7,7 @@ import Graph from './Graph'; import { connect } from 'react-redux'; import { stopSession } from '../state/ActionCreators'; import backgroundColors from './Themes'; +import { useKeepAwake } from 'expo-keep-awake'; function LiveTrainingView(props) @@ -19,6 +20,8 @@ function LiveTrainingView(props) const laps = (analysis.peaks.size / props.peaksPerLap).toFixed(1); const totalMomentum = Math.trunc(analysis.totalMomentum / 10000); + useKeepAwake(); + return ( { @@ -37,13 +29,23 @@ class DataProcessing { if (newState.session.running !== this.state.session.running) { this.onRunningChanged(newState.session.running, newState.settings.deviceURL); }; - if(newState.settings.peakDetectorSimpleThreshold !== this.state.settings.peakDetectorSimpleThreshold) { - this.peakDetectorSimple = new PeakDetectorSimple(newState.settings.peakDetectorSimpleThreshold, this.onNewPeak); - this.peaks = List(this.peakDetectorSimple.addVector(this.rawMeasurements)); + if (newState.settings.peakDetectorSimpleThreshold !== this.state.settings.peakDetectorSimpleThreshold) { + this.onAnalysisParameterChange(); }; this.state = newState; } + resetAnalysis = () => { + this.peakDetectorSimple = new PeakDetectorSimple(this.state.settings.peakDetectorSimpleThreshold); + } + + onAnalysisParameterChange = () => { + this.resetAnalysis(); + this.peakDetectorSimple.addVector(this.state.session.rawData.toArray()); + const analysis = this.analyzeNewMeasurements(data.values, List()); + this.store.dispatch(reportDeviceData(this.state.session.sessionId, this.state.session.rawData.size, this.state.session.rawData, analysis)); + } + onDataSourceChanged = (newDeviceURL) => { if (this.dataSource !== null) { this.dataSource.stop(); @@ -55,12 +57,12 @@ class DataProcessing { onRunningChanged = (running, deviceURL) => { let req = new XMLHttpRequest(); if (running) { - console.log("Starting session", deviceURL + "/api/session/start" ); + //console.log("Starting session", deviceURL + "/api/session/start"); req.open("GET", deviceURL + "/api/session/start"); this.dataSource.startIndex = 0; this.dataSource.start(); } else { - console.log("Stopping session"); + //console.log("Stopping session"); req.open("GET", deviceURL + "/api/session/stop"); this.dataSource.stop(); this.dataSource.startIndex = 0; @@ -71,55 +73,46 @@ class DataProcessing { } onNewData = (data) => { - data.values; - data.sessionStartTime; - data.startIndex; - let success = false; - if (data.sessionStartTime == this.sessionStartTime && data.startIndex == this.rawMeasurements.size) { + if (data.sessionStartTime == this.state.session.sessionId && + data.startIndex == this.state.session.rawData.size) { // normal case, add received data to measurement array - this.rawMeasurements = this.rawMeasurements.concat(List(data.values)); - success = true; + const newData = this.state.session.rawData.concat(List(data.values)); + const analysis = this.analyzeNewMeasurements(data.values, this.state.session.rawData); + this.store.dispatch(reportDeviceData(data.sessionStartTime, data.startIndex, newData, analysis)); } else if (data.startIndex === 0) { - // new start - this.sessionStartTime = data.sessionStartTime; - this.rawMeasurements = List(data.values); - success = true; + this.resetAnalysis(); + const newData = List(data.values); + const analysis = this.analyzeNewMeasurements(data.values, this.state.session.rawData); + this.store.dispatch(reportDeviceData(data.sessionStartTime, data.startIndex, newData, analysis)); } else { - console.log("Requery :("); - console.log("this.sessionStartTime", this.sessionStartTime); - console.log("this.rawMeasurements", this.rawMeasurements.toArray()); - console.log("data", data); // missed some data -> re-query + console.log("Requery :("); + //console.log("Session times", data.sessionStartTime == this.state.session.sessionId, data.sessionStartTime, this.state.session.sessionId); + //console.log("Index ",data.startIndex == this.state.session.rawData.size, data.startIndex, this.state.session.rawData.size); + this.resetAnalysis(); this.dataSource.startIndex = 0; - this.sessionStartTime = 0; - //console.log("Problem: got non-consequtive data. Received:", data, - // "Own state ", { startTime: this.sessionStartTime, values: this.rawMeasurements }); - } - - if (success) { - const analysis = this.analyzeNewMeasurements(data.startIndex); - const report = reportDeviceData(this.sessionStartTime, data.startIndex, this.rawMeasurements, analysis); - this.store.dispatch(report); + this.store.dispatch(resetDeviceData()); } } - analyzeNewMeasurements = (newDataStartIdx) => { - //TODO is ".toArray()" really necessary here? - const newPeaks = this.peakDetectorSimple.addVector(this.rawMeasurements.slice(newDataStartIdx).toArray()); - this.peaks = this.peaks.concat(List(newPeaks)); - const totalMomentum = this.rawMeasurements.reduce((sum, x) => sum + x, 0); - const peakMax = this.rawMeasurements.reduce((running, x) => Math.max(x, running), 0); + analyzeNewMeasurements = (newData, oldData) => { + const newPeaks = this.peakDetectorSimple.addVector(newData); + const allPeaks = this.state.session.analysis.peaks.concat(List(newPeaks)); + + const allMeasurements = oldData.concat(List(newData)); + const totalMomentum = allMeasurements.reduce((sum, x) => sum + x, 0); + const peakMax = allMeasurements.reduce((running, x) => Math.max(x, running), 0); // windowed quantities const windowSizeMeasurements = WINDOW_SIZE_SECS * NUM_MEASUREMENTS_PER_SECOND; - const windowedSeq = this.rawMeasurements.slice(-windowSizeMeasurements); + const windowedSeq = allMeasurements.slice(-windowSizeMeasurements); const peakMaxWindow = windowedSeq.reduce((running, x) => Math.max(x, running), 0); const momentumWindow = windowedSeq.reduce((sum, x) => sum + x, 0); return { - peaks: this.peaks, - totalTime: this.rawMeasurements.length / NUM_MEASUREMENTS_PER_SECOND, + peaks: allPeaks, + totalTime: allMeasurements.length / NUM_MEASUREMENTS_PER_SECOND, activeTime: 0, totalMomentum: totalMomentum, peakFrequency: 0, @@ -130,6 +123,6 @@ class DataProcessing { peakMaxWindow: peakMaxWindow, } }; -}; +} export default DataProcessing; \ No newline at end of file diff --git a/data_processing/DeviceDataSource.js b/data_processing/DeviceDataSource.js index ced3784..c3456b9 100644 --- a/data_processing/DeviceDataSource.js +++ b/data_processing/DeviceDataSource.js @@ -45,7 +45,6 @@ class DeviceHttpDataSource { const arrayBuffer = await this.getUrl(url); const decoded = msgpack.decode(new Uint8Array(arrayBuffer), { codec: this.msgpackCodec }); this.startIndex += decoded["values"].length; - //"values", "sessionStartTime", "startIndex" this.onNewData(decoded); } catch (err) { console.log(err); diff --git a/state/ActionCreators.js b/state/ActionCreators.js index c641279..f49133b 100644 --- a/state/ActionCreators.js +++ b/state/ActionCreators.js @@ -4,6 +4,7 @@ export const CHANGE_USER_NAME = "SET_USERNAME"; export const CHANGE_THEME = "CHANGE_THEME"; export const START_SESSION = "START_SESSION"; export const STOP_SESSION = "STOP_SESSION"; +export const RESET_DEVICE_DATA = "RESET_DEVICE_DATA"; export const reportDeviceData = (sessionId, newDataStart, data, analysis) => ({ type: NEW_DEVICE_DATA, @@ -13,6 +14,10 @@ export const reportDeviceData = (sessionId, newDataStart, data, analysis) => ({ analysis: analysis, }) +export const resetDeviceData = () => ({ + type: RESET_DEVICE_DATA, +}); + export const changeUsername = newUsername => ({ type: CHANGE_USER_NAME, newUserName: newUsername, diff --git a/state/Reducer.js b/state/Reducer.js index 3ad26fc..24490a4 100644 --- a/state/Reducer.js +++ b/state/Reducer.js @@ -1,16 +1,16 @@ import { combineReducers } from 'redux'; import { List } from 'immutable'; -import { CHANGE_THEME, CHANGE_USER_NAME, NEW_DEVICE_DATA, START_SESSION, STOP_SESSION } from './ActionCreators'; +import { CHANGE_THEME, CHANGE_USER_NAME, NEW_DEVICE_DATA, START_SESSION, STOP_SESSION, RESET_DEVICE_DATA } from './ActionCreators'; const INITIAL_SETTINGS = { theme: "hot", username: "", - deviceURL: "http://192.168.178.110", + deviceURL: "http://192.168.178.107", peaksPerLap: 30, // advanced peakDetector: 'SIMPLE', // either 'SIMPLE' or 'ZSCORE' - peakDetectorSimpleThreshold: 4000, + peakDetectorSimpleThreshold: 2500, peakDetectorZScoreLag: 8, // peak detector z-score values peakDetectorZScoreThreshold: 2, @@ -19,6 +19,7 @@ const INITIAL_SETTINGS = { const INITIAL_CURRENT_SESSION = { running: false, + sessionId: 0, rawData: List(), analysis: { 'peaks': List(), @@ -62,9 +63,12 @@ const currentSessionReducer = (state = INITIAL_CURRENT_SESSION, action) => { case NEW_DEVICE_DATA: return { running: action.data.size > 0, + sessionId: action.sessionId, rawData: action.data, analysis: { ...state.analysis, ...action.analysis }, } + case RESET_DEVICE_DATA: + return INITIAL_CURRENT_SESSION default: return state }