diff --git a/src/Components/Assets/AssetType/HL7Monitor.tsx b/src/Components/Assets/AssetType/HL7Monitor.tsx index e50a9312467..40895b5c53a 100644 --- a/src/Components/Assets/AssetType/HL7Monitor.tsx +++ b/src/Components/Assets/AssetType/HL7Monitor.tsx @@ -15,6 +15,7 @@ import CareIcon from "../../../CAREUI/icons/CareIcon"; import TextFormField from "../../Form/FormFields/TextFormField"; import HL7PatientVitalsMonitor from "../../VitalsMonitor/HL7PatientVitalsMonitor"; import VentilatorPatientVitalsMonitor from "../../VitalsMonitor/VentilatorPatientVitalsMonitor"; +import CheckBoxFormField from "../../Form/FormFields/CheckBoxFormField"; interface HL7MonitorProps { assetId: string; @@ -31,6 +32,8 @@ const HL7Monitor = (props: HL7MonitorProps) => { const [isLoading, setIsLoading] = useState(true); const [localipAddress, setLocalIPAddress] = useState(""); const [ipadrdress_error, setIpAddress_error] = useState(""); + const [gains, setGains] = useState(); + const [editGains, setEditGains] = useState(false); const dispatch = useDispatch(); @@ -50,6 +53,17 @@ const HL7Monitor = (props: HL7MonitorProps) => { setAssetType(asset?.asset_class); setMiddlewareHostname(asset?.meta?.middleware_hostname); setLocalIPAddress(asset?.meta?.local_ip_address); + if (asset?.meta?.gains) { + setGains(asset?.meta?.gains); + setEditGains(true); + } else { + setGains( + asset?.asset_class === "HL7MONITOR" + ? { ecg: 1, pleth: 1, resp: 1 } + : { pressure: 1, flow: 1, volume: 1 } + ); + setEditGains(false); + } setIsLoading(false); }, [asset]); @@ -57,13 +71,16 @@ const HL7Monitor = (props: HL7MonitorProps) => { e.preventDefault(); if (checkIfValidIP(localipAddress)) { setIpAddress_error(""); + const data = { meta: { asset_type: assetType, middleware_hostname: middlewareHostname, // TODO: remove this infavour of facility.middleware_address local_ip_address: localipAddress, + gains: editGains ? gains : undefined, }, }; + const res: any = await Promise.resolve( dispatch(partialUpdateAsset(assetId, data)) ); @@ -87,20 +104,27 @@ const HL7Monitor = (props: HL7MonitorProps) => { return (
-
+
-

Connection

-
+
+

Connection

+ + + Save + +
+
setMiddlewareHostname(e.value)} - errorClassName="hidden" /> { required error={ipadrdress_error} /> - - - Save Configuration - +
+
+

Waveform Configurations

+ setEditGains(e.value)} + errorClassName="hidden" + /> +
+
+ {gains && + Object.keys(gains).map((key) => ( + + setGains((prev: any) => ({ ...prev, [key]: e.value })) + } + step={0.01} + /> + ))}
@@ -124,7 +173,9 @@ const HL7Monitor = (props: HL7MonitorProps) => { {assetType === "HL7MONITOR" && ( )} {assetType === "VENTILATOR" && ( diff --git a/src/Components/Assets/configure/MonitorConfigure.tsx b/src/Components/Assets/configure/MonitorConfigure.tsx index 5e86f380a7b..d1bc042f444 100644 --- a/src/Components/Assets/configure/MonitorConfigure.tsx +++ b/src/Components/Assets/configure/MonitorConfigure.tsx @@ -10,7 +10,6 @@ import { import * as Notification from "../../../Utils/Notifications.js"; import { useDispatch } from "react-redux"; import { Submit } from "../../Common/components/ButtonV2"; -import { FieldLabel } from "../../Form/FormFields/FormField"; const saveLink = (assetId: string, bedId: string, dispatch: Dispatch) => { dispatch(createAssetBed({}, assetId, bedId)); @@ -75,9 +74,15 @@ export default function MonitorConfigure({ asset }: { asset: AssetData }) { } }} > -
-
- Bed +
+
+

Bed

+ + + {updateLink ? "Update" : "Save"} + +
+
setBed(selected as BedModel)} @@ -89,10 +94,6 @@ export default function MonitorConfigure({ asset }: { asset: AssetData }) { className="w-full" />
- - - {updateLink ? "Update Bed" : "Save Bed"} -
); diff --git a/src/Components/Form/FormFields/TextFormField.tsx b/src/Components/Form/FormFields/TextFormField.tsx index 8cf61d97907..27de931e975 100644 --- a/src/Components/Form/FormFields/TextFormField.tsx +++ b/src/Components/Form/FormFields/TextFormField.tsx @@ -18,6 +18,7 @@ export type TextFormFieldProps = FormFieldBaseProps & { leadingPadding?: string | undefined; min?: string | number; max?: string | number; + step?: string | number; onFocus?: (event: React.FocusEvent) => void; onBlur?: (event: React.FocusEvent) => void; }; @@ -59,6 +60,7 @@ const TextFormField = React.forwardRef((props: TextFormFieldProps, ref) => { onFocus={props.onFocus} onBlur={props.onBlur} onChange={(e) => field.handleChange(e.target.value)} + step={props.step} /> ); diff --git a/src/Components/VitalsMonitor/HL7DeviceClient.ts b/src/Components/VitalsMonitor/HL7DeviceClient.ts index 72f6406193a..a6b75e05256 100644 --- a/src/Components/VitalsMonitor/HL7DeviceClient.ts +++ b/src/Components/VitalsMonitor/HL7DeviceClient.ts @@ -4,7 +4,7 @@ import { VitalsDataBase, VitalsValueBase, VitalsWaveformBase } from "./types"; const WAVEFORM_KEY_MAP: Record = { II: "ecg-waveform", Pleth: "pleth-waveform", - Respiration: "spo2-waveform", + Respiration: "resp-waveform", }; /** @@ -101,7 +101,7 @@ type EventName = | HL7MonitorData["observation_id"] | "ecg-waveform" | "pleth-waveform" - | "spo2-waveform"; + | "resp-waveform"; const parseObservations = (data: string) => { return JSON.parse(data || "[]") as HL7MonitorData[]; diff --git a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx index abc3c298a1f..978bea63fba 100644 --- a/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx +++ b/src/Components/VitalsMonitor/HL7PatientVitalsMonitor.tsx @@ -1,5 +1,5 @@ import { useEffect } from "react"; -import useHL7VitalsMonitor from "./useHL7VitalsMonitor"; +import useHL7VitalsMonitor, { Options } from "./useHL7VitalsMonitor"; import { PatientAssetBed } from "../Assets/AssetTypes"; import { Link } from "raviger"; import { GENDER_TYPES } from "../../Common/constants"; @@ -11,14 +11,17 @@ interface Props { patientAssetBed?: PatientAssetBed; socketUrl: string; size?: { width: number; height: number }; + options?: Options; } export default function HL7PatientVitalsMonitor({ patientAssetBed, socketUrl, size, + options, }: Props) { - const { connect, waveformCanvas, data, isOnline } = useHL7VitalsMonitor(); + const { connect, waveformCanvas, data, isOnline } = + useHL7VitalsMonitor(options); const { patient, bed } = patientAssetBed ?? {}; useEffect(() => { @@ -145,10 +148,10 @@ export default function HL7PatientVitalsMonitor({
SpO2 - {data.spo2?.unit ?? "--"} + {data.resp?.unit ?? "--"}
- {data.spo2?.value ?? "--"} + {data.resp?.value ?? "--"}
diff --git a/src/Components/VitalsMonitor/HL7VitalsRenderer.ts b/src/Components/VitalsMonitor/HL7VitalsRenderer.ts index 1a4df38aabc..a6964a975b7 100644 --- a/src/Components/VitalsMonitor/HL7VitalsRenderer.ts +++ b/src/Components/VitalsMonitor/HL7VitalsRenderer.ts @@ -52,7 +52,7 @@ interface Options { /** * Options for SPO2 channel. */ - spo2: ChannelOptions; + resp: ChannelOptions; } /** @@ -67,7 +67,7 @@ class HL7VitalsRenderer { const { ecg, pleth, - spo2, + resp, size: { height: h, width: w }, } = options; @@ -95,14 +95,14 @@ class HL7VitalsRenderer { rows: 1, }, - spo2: { + resp: { color: "#03a9f4", buffer: [], cursor: { x: 0, y: 0 }, - deltaX: w / (DURATION * spo2.samplingRate), - transform: lerp(spo2.lowLimit, spo2.highLimit, h, h * 0.75), - chunkSize: spo2.samplingRate * options.animationInterval * 1e-3, - options: spo2, + deltaX: w / (DURATION * resp.samplingRate), + transform: lerp(resp.lowLimit, resp.highLimit, h, h * 0.75), + chunkSize: resp.samplingRate * options.animationInterval * 1e-3, + options: resp, rows: 1, }, }; @@ -110,23 +110,23 @@ class HL7VitalsRenderer { // Draw baseline for each channel. this.initialize(this.state.ecg); this.initialize(this.state.pleth); - this.initialize(this.state.spo2); + this.initialize(this.state.resp); // Start rendering. setInterval(() => { this.render(this.state.ecg); this.render(this.state.pleth); - this.render(this.state.spo2); + this.render(this.state.resp); }, options.animationInterval); } private options: Options; - private state: { ecg: ChannelState; pleth: ChannelState; spo2: ChannelState }; + private state: { ecg: ChannelState; pleth: ChannelState; resp: ChannelState }; /** * Appends data to the buffer of the specified channel. */ - append(channel: "ecg" | "pleth" | "spo2", data: number[]) { + append(channel: "ecg" | "pleth" | "resp", data: number[]) { const state = this.state[channel]; state.buffer.push(...data.map(state.transform)); } diff --git a/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts b/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts index 8b14dff12e3..37ba2499f05 100644 --- a/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts +++ b/src/Components/VitalsMonitor/useHL7VitalsMonitor.ts @@ -28,7 +28,15 @@ interface VitalsBPValue { map: VitalsValue; } -export default function useHL7VitalsMonitor() { +export interface Options { + gains?: { + ecg?: number; + pleth?: number; + resp?: number; + }; +} + +export default function useHL7VitalsMonitor(options?: Options) { const waveformForegroundCanvas = useCanvas(); const waveformBackgroundCanvas = useCanvas(); @@ -37,7 +45,7 @@ export default function useHL7VitalsMonitor() { const [pulseRate, setPulseRate] = useState(); const [heartRate, setHeartRate] = useState(); const [bp, setBp] = useState(); - const [spo2, setSpo2] = useState(); + const [resp, setResp] = useState(); const [respiratoryRate, setRespiratoryRate] = useState(); const [temperature1, setTemperature1] = useState(); const [temperature2, setTemperature2] = useState(); @@ -48,7 +56,7 @@ export default function useHL7VitalsMonitor() { const ecgOptionsRef = useRef(); const plethOptionsRef = useRef(); - const spo2OptionsRef = useRef(); + const respOptionsRef = useRef(); const connect = useCallback( (socketUrl: string) => { @@ -62,7 +70,7 @@ export default function useHL7VitalsMonitor() { if ( !ecgOptionsRef.current || !plethOptionsRef.current || - !spo2OptionsRef.current + !respOptionsRef.current ) return; @@ -75,19 +83,28 @@ export default function useHL7VitalsMonitor() { animationInterval: 50, ecg: ecgOptionsRef.current, pleth: plethOptionsRef.current, - spo2: spo2OptionsRef.current, + resp: respOptionsRef.current, }); const _renderer = renderer.current; - device.current!.on("ecg-waveform", ingestTo(_renderer, "ecg")); - device.current!.on("pleth-waveform", ingestTo(_renderer, "pleth")); - device.current!.on("spo2-waveform", ingestTo(_renderer, "spo2")); + device.current!.on( + "ecg-waveform", + ingestTo(_renderer, "ecg", options?.gains?.ecg) + ); + device.current!.on( + "pleth-waveform", + ingestTo(_renderer, "pleth", options?.gains?.pleth) + ); + device.current!.on( + "resp-waveform", + ingestTo(_renderer, "resp", options?.gains?.resp) + ); const hook = (set: (data: any) => void) => (d: HL7MonitorData) => set(d); device.current!.on("pulse-rate", hook(setPulseRate)); device.current!.on("heart-rate", hook(setHeartRate)); - device.current!.on("SpO2", hook(setSpo2)); + device.current!.on("SpO2", hook(setResp)); device.current!.on("respiratory-rate", hook(setRespiratoryRate)); device.current!.on("body-temperature1", hook(setTemperature1)); device.current!.on("body-temperature2", hook(setTemperature2)); @@ -108,8 +125,8 @@ export default function useHL7VitalsMonitor() { obtainRenderer(); }); - device.current.once("spo2-waveform", (observation) => { - spo2OptionsRef.current = getChannel( + device.current.once("resp-waveform", (observation) => { + respOptionsRef.current = getChannel( observation as HL7VitalsWaveformData ); obtainRenderer(); @@ -129,7 +146,7 @@ export default function useHL7VitalsMonitor() { pulseRate, heartRate, bp, - spo2, + resp, respiratoryRate, temperature1, temperature2, @@ -141,14 +158,23 @@ export default function useHL7VitalsMonitor() { const ingestTo = ( vitalsRenderer: HL7VitalsRenderer, - channel: "ecg" | "pleth" | "spo2" + channel: "ecg" | "pleth" | "resp", + gain = 1 ) => { return (observation: HL7MonitorData) => { - vitalsRenderer.append( - channel, + let data = (observation as HL7VitalsWaveformData).data .split(" ") - .map((x) => parseInt(x)) || [] - ); + .map((x) => parseInt(x)) || []; + + const baseline = (observation as HL7VitalsWaveformData)["data-baseline"]; + + if (gain !== 1) { + data = data.map((x: number) => + Math.round((x - baseline) * gain + baseline) + ); + } + + vitalsRenderer.append(channel, data); }; };