Skip to content

Commit

Permalink
Waveform Refresh Mode (#3768)
Browse files Browse the repository at this point in the history
* stuff

* made refresh mode

* made changes

* bypassed settimeout restraint

* speed improvements

* number errors

* switched from initial null array from 0

* fix grid bug for other waveforms

* remove console log
  • Loading branch information
skks1212 committed Nov 10, 2022
1 parent c703b65 commit 47becbc
Show file tree
Hide file tree
Showing 3 changed files with 103 additions and 62 deletions.
19 changes: 14 additions & 5 deletions src/Components/Facility/Consultations/components/LinePlot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,11 @@ export const LinePlot = (props: any) => {
yData,
low = null,
high = null,
defaultSpace = true,
defaultSpace,
} = props;
let generalOptions: any = {
grid: {
top: "40px",
left: "20px",
right: "30px",
containLabel: true,
Expand Down Expand Up @@ -102,8 +103,10 @@ export const LinePlot = (props: any) => {
text: "",
},
grid: {
left: "15px",
right: "30px",
show: false,
borderColor: "transparent",
left: "40px",
right: "20px",
},
animation: false,
xAxis: {
Expand All @@ -112,9 +115,14 @@ export const LinePlot = (props: any) => {
},
yAxis: {
...generalOptions.yAxis,
show: false,
show: true,
min: props.yStart,
max: props.yEnd,
splitLine: {
lineStyle: {
color: "#4E4E4E",
},
},
},
toolbox: {
...generalOptions.toolbox,
Expand Down Expand Up @@ -144,12 +152,13 @@ export const LinePlot = (props: any) => {
],
},
},
connectNulls: false,
},
],
};
}

if (!defaultSpace) {
if (props.type === "WAVEFORM" && defaultSpace != true) {
generalOptions = {
...generalOptions,
grid: {
Expand Down
32 changes: 16 additions & 16 deletions src/Components/TeleIcu/Patient/VitalsCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ export default function TeleICUPatientVitalsCard({
waveformColor?: string;
waveformName?: string;
waveformDefaultSpace?: boolean;
wavetype?: "STREAM" | "REFRESH";
};

const vitals: VitalType[] = [
Expand All @@ -125,8 +126,9 @@ export default function TeleICUPatientVitalsCard({
liveKey: "pulse-rate",
vitalKey: "pulse",
waveformKey: "II",
waveformColor: "blue",
waveformColor: "limegreen",
waveformName: "ECG",
wavetype: "REFRESH",
},
{
label: <>Blood Pressure</>,
Expand All @@ -142,15 +144,15 @@ export default function TeleICUPatientVitalsCard({
liveKey: "SpO2",
vitalKey: "ventilator_spo2",
waveformKey: "Pleth",
waveformColor: "red",
waveformColor: "yellow",
},
{
label: <>R. Rate</>,
liveKey: "respiratory-rate",
vitalKey: "resp",
waveformKey: "Respiration",
waveformColor: "green",
waveformDefaultSpace: true,
waveformColor: "cyan",
//waveformDefaultSpace: true
},
{
label: <>Temperature (F)</>,
Expand Down Expand Up @@ -184,6 +186,7 @@ export default function TeleICUPatientVitalsCard({
metrics={stats}
classes={"h-[150px]"}
defaultSpace={v.waveformDefaultSpace}
wavetype={v.wavetype || "STREAM"}
/>
) : (
<div className="flex items-center justify-center text-gray-900"></div>
Expand Down Expand Up @@ -214,23 +217,20 @@ export default function TeleICUPatientVitalsCard({
<h2 className="font-bold text-xl md:text-3xl">
{liveReading ||
(vital.vitalKey === "bp"
? `${
patient.last_consultation?.last_daily_round?.bp
.systolic || "--"
}/${
patient.last_consultation?.last_daily_round?.bp
.diastolic || "--"
}`
? `${patient.last_consultation?.last_daily_round?.bp
.systolic || "--"
}/${patient.last_consultation?.last_daily_round?.bp
.diastolic || "--"
}`
: patient.last_consultation?.last_daily_round?.[
vital.vitalKey || ""
]) ||
vital.vitalKey || ""
]) ||
"--"}
</h2>
<div className="text-xs md:text-base">
<i
className={`fas fa-circle text-xs mr-2 ${
liveReading ? "text-green-600" : "text-gray-400"
}`}
className={`fas fa-circle text-xs mr-2 ${liveReading ? "text-green-600" : "text-gray-400"
}`}
/>
{vital.label}
</div>
Expand Down
114 changes: 73 additions & 41 deletions src/Components/TeleIcu/Patient/Waveform.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { useEffect, useState } from "react";
import { WAVEFORM_VIEWABLE_LENGTH } from "../../../Common/constants";
import { useEffect, useRef, useState } from "react";
import { LinePlot } from "../../Facility/Consultations/components/LinePlot";

export type WaveformType = {
Expand All @@ -24,20 +23,34 @@ export default function Waveform(props: {
metrics?: boolean;
classes?: string;
defaultSpace?: boolean;
wavetype?: "STREAM" | "REFRESH";
}) {
const wave = props.wave;
const data = wave.data.split(" ").map(Number);
const viewable = data.length;
const [queueData, setQueueData] = useState<number[]>(
Array(WAVEFORM_VIEWABLE_LENGTH).fill(0)
Array(viewable).fill(null)
);
const [refreshData, setRefreshData] = useState<number[]>([]);
const [lastData, setLastData] = useState<number[]>([]);
const [xData, setXData] = useState<number[]>([]);
const [lastStream, setLastStream] = useState(0);
const [rPointer, setRPointer] = useState(0);

const tpf = 4000 / data.length;
const initialRender = useRef(true);

useEffect(() => {
setQueueData(queueData.concat(data));
setXData(Array.from(Array(WAVEFORM_VIEWABLE_LENGTH).keys()));
if (props.wavetype === "STREAM") {
setQueueData(queueData.concat(data));
} else {
if (lastData.length === 0) {
setLastData(data);
} else {
setRefreshData(data);
}
setRPointer(0);
}
setXData(Array.from(Array(viewable).keys()));

let seconds = 1;
setLastStream(0);
Expand All @@ -46,15 +59,50 @@ export default function Waveform(props: {
seconds++;
}, 1000);
return () => clearInterval(timer);
}, [props.wave.data]);
}, [props.wave]);

useEffect(() => {
const timeout = setTimeout(() => {
setQueueData(queueData.slice(1));
}, tpf);
if (props.wavetype === "STREAM") {
if (queueData.length > 30000) {
setQueueData(queueData.slice(-viewable));
} else {
setQueueData(queueData.slice(2));
}
}
}, 6);
return () => clearTimeout(timeout);
}, [queueData]);

useEffect(() => {
let timeout: NodeJS.Timeout;
if (initialRender.current) {
initialRender.current = false;
} else {
timeout = setTimeout(() => {
setRefreshData([
...data.slice(0, rPointer - 25),
...Array(50).fill(null),
...lastData.slice(rPointer - 25),
]);
setRPointer(rPointer + Math.round(viewable / 150));
}, 2);
}
return () => clearTimeout(timeout);
}, [refreshData]);

useEffect(() => {
if (refreshData.length === 0) {
setRefreshData(data);
}
}, [lastData]);

useEffect(() => {
if (rPointer >= data.length) {
setLastData(data);
}
}, [rPointer]);

return (
<div className="w-full relative">
<div className="text-gray-400 absolute top-0 left-5 text-xs">
Expand All @@ -64,9 +112,13 @@ export default function Waveform(props: {
title={props.title}
name={props.title}
xData={xData}
yData={queueData.slice(0, WAVEFORM_VIEWABLE_LENGTH)}
yStart={Math.min(...queueData)}
yEnd={Math.max(...queueData)}
yData={
props.wavetype === "STREAM"
? queueData.slice(0, viewable)
: refreshData
}
yStart={wave["data-low-limit"]}
yEnd={wave["data-high-limit"]}
classes={props.classes || "h-[90px]"}
type="WAVEFORM"
color={props.color || "green"}
Expand All @@ -75,37 +127,17 @@ export default function Waveform(props: {
<div className="absolute bottom-0 right-5 w-full md:w-[70%]">
{props.metrics && (
<div className="flex flex-row flex-wrap justify-end gap-2 text-[10px] text-gray-400">
<div>
<div>
Lowest:{" "}
{Math.min(...queueData.slice(0, WAVEFORM_VIEWABLE_LENGTH))}
</div>
<div>
Highest:{" "}
{Math.max(...queueData.slice(0, WAVEFORM_VIEWABLE_LENGTH))}
</div>
<div>Stream Length: {data.length}</div>
<div>
Lag:{" "}
{Number(
(tpf * (queueData.length - WAVEFORM_VIEWABLE_LENGTH)) / 1000
).toFixed(2)}{" "}
sec
</div>
</div>
<div>
<div>Buffer Length: {queueData.length}</div>
<div>Flow Rate: {Number(tpf).toFixed(2)} ms</div>
<div>Sampling Rate: {wave["sampling rate"]}</div>
<div>Last response: {lastStream} sec ago</div>
</div>

{queueData.length > WAVEFORM_VIEWABLE_LENGTH && (
<div>Lowest: {Math.min(...queueData.slice(0, viewable))}</div>
<div>Highest: {Math.max(...queueData.slice(0, viewable))}</div>
<div>Stream Length: {data.length}</div>
<div>Buffer Length: {queueData.length}</div>
<div>Sampling Rate: {wave["sampling rate"]}</div>
<div>Lag: {Math.round(queueData.length / viewable)} seconds</div>
<div>Last response: {lastStream} sec ago</div>
{queueData.length > viewable && (
<button
className="text-blue-400"
onClick={() =>
setQueueData(queueData.slice(-1 * WAVEFORM_VIEWABLE_LENGTH))
}
onClick={() => setQueueData(queueData.slice(-1 * viewable))}
>
Clear Buffer
</button>
Expand Down

0 comments on commit 47becbc

Please sign in to comment.