Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
node_modules/*
dist/*
dist/**/*
node_modules/
release-builds/**/*
32 changes: 32 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
## Cadus Lifesensor (part of the remo²hbo project)
For project description see https://github.com/cadus/remo2hbo-wiki/wiki

The app is an electron app based on react components. This means, it can be published easily to any desktop plattform (windows, linux, mac). With few modifications it can also be deployed in the web. As boilerplate, the following repository has been used: https://github.com/bradtraversy/simple-electron-react

### Install

#### Clone this repo

```
git clone https://github.com/cadus/remo2hbo_oktopus_frontend
```

#### Install dependencies

```
yarn install
```

### Usage

#### Run the app

```
yarn start
```

and with data stream simulation

```
python3 datastream/stream_replay.py -b datastream/3chECG_bp_pulse_spo2.resampled.bin | yarn start
```
Binary file added assets/icon.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added datastream/3chECG_bp_pulse_spo2.d1.bin
Binary file not shown.
552 changes: 552 additions & 0 deletions datastream/3chECG_bp_pulse_spo2.d1t001.json

Large diffs are not rendered by default.

Binary file added datastream/3chECG_bp_pulse_spo2.orig.bin
Binary file not shown.
54,666 changes: 54,666 additions & 0 deletions datastream/3chECG_bp_pulse_spo2.orig.json

Large diffs are not rendered by default.

Binary file added datastream/3chECG_bp_pulse_spo2.resampled.bin
Binary file not shown.
Binary file added datastream/3chECG_bp_pulse_spo2.t001.bin
Binary file not shown.
Binary file added datastream/3chECG_bp_pulse_spo2.t01.bin
Binary file not shown.
5,472 changes: 5,472 additions & 0 deletions datastream/3chECG_bp_pulse_spo2.t01.json

Large diffs are not rendered by default.

79 changes: 79 additions & 0 deletions datastream/stream_gen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import wfdb
import numpy as np
import scipy
import scipy.signal
import argparse
import sys
import random
import json
import struct

parser = argparse.ArgumentParser(description='LifeSensor filestream generator')
parser.add_argument('-i', metavar='<path>', nargs='+', required=True, help='data to import')
parser.add_argument('-s', metavar='<path>/<signal>', required=True,
nargs='+', help='signals to export')
parser.add_argument('-t', metavar='<timescale>', default=1.0,
type=float, help='timing scale factor (=1:untouched, <1:slower, >1:faster)')
parser.add_argument('-m', metavar='<seconds>',
type=float, help='stop after this amount of time')
parser.add_argument('-d', metavar='<timevariation>', type=float,
default=0.0, help='maximum delay factor relative to sampling rate of input (=0:none, >0:delayed, >1:unordered)')
parser.add_argument('-rs', metavar='<path>/<signal>:<samples per sec>', nargs='*',
default=[], help='resample input data')
parser.add_argument('-r', metavar='<path>/<signal>:<name>',
nargs='*', default=[], help='rename <signal> to <name>')
parser.add_argument('-b', help="generate binary output", action="count", default=0)

args = parser.parse_args()

print(f"Arguments: {args}",file=sys.stderr)
timing = {}

rename = {}
for r in args.r:
old, new = r.split(':')
rename[old] = new

resample = {}
for rs in args.rs:
name,rate = rs.split(':')
resample[name] = float(rate)

for path in args.i:
print(f"opening file '{path}'",file=sys.stderr)
signals, fields = wfdb.rdsamp(path)
print(f"Meta data of '{path}': {fields}",file=sys.stderr)
print("Generating data",file=sys.stderr)
for index_sig in range(len(fields['sig_name'])):
name = fields['sig_name'][index_sig]
fullname = f"{path}/{name}"
if fullname in args.s:
rate = fields['fs'] if fullname not in resample else resample[fullname]
fulltime = args.m if args.m else signals[:, index_sig].shape[0]/rate
samples = signals[0:int(rate*fulltime), index_sig] if fullname not in resample else scipy.signal.resample(
signals[0:int(fields['fs']*fulltime), index_sig], int(rate * fulltime))
for index_beat in range(samples.shape[0]):
time = (index_beat/rate) * args.t
if args.m and time > args.m:
break
time_ms = int(time*1000)
delay = random.random() * args.d / rate
value = samples[index_beat]
name = rename.get(f"{path}/{name}", name)
event = {"type":name, "timestamp":time_ms, "value":value, "no":index_beat, "_replay":time+delay}
timing.setdefault(time + delay, {}).setdefault(name,[]).append(event)


times = list(timing.keys())
times.sort()

for time in times:
for sig, vals in timing[time].items():
for val in vals:
if args.b:
payload = struct.pack("Qf", val["timestamp"], val["value"]) + val["type"].encode('utf-8') + b'\0\n'
length = struct.pack("H", len(payload))
replay = struct.pack("f", val["_replay"])
sys.stdout.buffer.write(replay+length+payload)
else:
print(json.dumps(val))
69 changes: 69 additions & 0 deletions datastream/stream_replay.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
import fileinput
import json
import time
import sys
from datetime import datetime
import argparse
import struct

parser = argparse.ArgumentParser(description='LifeSensor filestream replay')
parser.add_argument('-b', help="replay binary format",
action="count", default=0)
parser.add_argument('-d', help="debug output",
action="count", default=0)
parser.add_argument('file', nargs='?', help='file to replay, if omitted use stdin', default=sys.stdin)
args = parser.parse_args()

while True:
last = 0
postEmit = datetime.now().timestamp()
timeSkewNeg = 0
timeSkewPos = 0
lines = 0
if args.b:
f = open(args.file,"rb")
while True:
b_curr = f.read(4)
if (b_curr == b''): break
curr, = struct.unpack("f", b_curr)
b_length = f.read(2)
length, = struct.unpack("H", b_length)
b_payload = f.read(length)
preEmit = datetime.now().timestamp()
wait = curr - last - (preEmit-postEmit)
if wait > 0:
time.sleep(wait)
sys.stdout.buffer.write(b_length + b_payload)
if args.d:
timestamp, value, type = struct.unpack("Qf"+ str(length-12) + "s", b_payload)
print(length, timestamp, value, type, file=sys.stderr)
sys.stdout.flush()
postEmit = datetime.now().timestamp()
timeSkew = postEmit-preEmit-wait
if timeSkew > 0:
timeSkewPos += timeSkew
else:
timeSkewNeg += timeSkew
lines += 1
last = curr
else:
for line in fileinput.input(args.file):
obj = json.loads(line)
curr = obj['_replay']
del obj['_replay']
preEmit = datetime.now().timestamp()
wait = curr - last - (preEmit-postEmit)
if wait > 0:
time.sleep(wait)
print(json.dumps(obj))
if args.d:
print(obj,file=sys.stderr)
postEmit = datetime.now().timestamp()
timeSkew = postEmit-preEmit-wait
if timeSkew > 0:
timeSkewPos += timeSkew
else:
timeSkewNeg += timeSkew
lines += 1
last = curr
print(f"timeSkew error: total neg {timeSkewNeg}, total pos {timeSkewPos}, avg {(timeSkewPos-timeSkewNeg)/lines}", file=sys.stderr)
21 changes: 21 additions & 0 deletions datastream/test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
console.log("start");
console.log(process.stdin.isPaused());

currLength = undefined;
process.stdin.on('readable', () => {
console.log("readable")
while (true) {
currLength = process.stdin.read(2)?.readInt16LE();
if (currLength === undefined) return;
console.log(currLength)
const buf = process.stdin.read(currLength);
if (buf === null) return;
const timestamp = buf.readBigUInt64LE(0);
const value = buf.readFloatLE(8);
const string = buf.toString('utf-8', 12, currLength - 2);
currLength = undefined
console.log(timestamp, string, value)
}
});

process.stdin.resume();
Binary file removed dist/0de337afda3c4d93385d4716b81bba11.png
Binary file not shown.
Binary file removed dist/11ed501826eceab4fceeb57e382afc4f.png
Binary file not shown.
Loading