Implemented epics read/write
This commit is contained in:
@@ -28,8 +28,9 @@ export default function BarH({ widget, onContextMenu }: Props) {
|
||||
return () => { unsubV(); unsubM(); };
|
||||
}, [sigRef?.ds, sigRef?.name]);
|
||||
|
||||
const label = widget.options['label'] ?? sigRef?.name ?? '';
|
||||
const unit = widget.options['unit'] ?? meta?.unit ?? '';
|
||||
const label = widget.options['label'] || sigRef?.name || '';
|
||||
const rawUnit = widget.options['unit'];
|
||||
const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
|
||||
const minVal = parseFloat(widget.options['min'] ?? String(meta?.displayLow ?? 0));
|
||||
const maxVal = parseFloat(widget.options['max'] ?? String(meta?.displayHigh ?? 100));
|
||||
const quality = sv.quality;
|
||||
|
||||
@@ -28,8 +28,9 @@ export default function BarV({ widget, onContextMenu }: Props) {
|
||||
return () => { unsubV(); unsubM(); };
|
||||
}, [sigRef?.ds, sigRef?.name]);
|
||||
|
||||
const label = widget.options['label'] ?? sigRef?.name ?? '';
|
||||
const unit = widget.options['unit'] ?? meta?.unit ?? '';
|
||||
const label = widget.options['label'] || sigRef?.name || '';
|
||||
const rawUnit = widget.options['unit'];
|
||||
const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
|
||||
const minVal = parseFloat(widget.options['min'] ?? String(meta?.displayLow ?? 0));
|
||||
const maxVal = parseFloat(widget.options['max'] ?? String(meta?.displayHigh ?? 100));
|
||||
const quality = sv.quality;
|
||||
|
||||
+50
-10
@@ -1,24 +1,59 @@
|
||||
import { h } from 'preact';
|
||||
import { useState, useEffect } from 'preact/hooks';
|
||||
import { wsClient } from '../lib/ws';
|
||||
import { getSignalStore } from '../lib/stores';
|
||||
import type { Widget } from '../lib/types';
|
||||
|
||||
interface Props { widget: Widget; onContextMenu?: (e: MouseEvent) => void; }
|
||||
|
||||
export default function Button({ widget, onContextMenu }: Props) {
|
||||
const sigRef = widget.signals[0];
|
||||
const label = widget.options['label'] ?? 'Button';
|
||||
const valueOpt = widget.options['value'] ?? '1';
|
||||
const confirm = widget.options['confirm'] === 'true';
|
||||
const label = widget.options['label'] ?? 'Button';
|
||||
const sendValue = widget.options['value'] ?? '1';
|
||||
const resetValue = widget.options['resetValue'] ?? '0';
|
||||
// mode: oneshot | setReset | persistent
|
||||
const mode = widget.options['mode'] ?? 'oneshot';
|
||||
const resetTimeSec = parseFloat(widget.options['resetTime'] ?? '1');
|
||||
const confirmOpt = widget.options['confirm'] === 'true';
|
||||
|
||||
const [signalVal, setSignalVal] = useState<any>(null);
|
||||
|
||||
// Subscribe to signal only for persistent mode (to track whether it is active)
|
||||
useEffect(() => {
|
||||
if (!sigRef || mode !== 'persistent') return;
|
||||
return getSignalStore(sigRef).subscribe(sv => setSignalVal(sv.value));
|
||||
}, [sigRef?.ds, sigRef?.name, mode]);
|
||||
|
||||
const parsedSend = isNaN(parseFloat(sendValue)) ? sendValue : parseFloat(sendValue);
|
||||
const parsedReset = isNaN(parseFloat(resetValue)) ? resetValue : parseFloat(resetValue);
|
||||
|
||||
// Persistent mode: button appears pressed while signal value matches sendValue
|
||||
const isPressed = mode === 'persistent'
|
||||
&& signalVal !== null
|
||||
&& String(signalVal) === String(parsedSend);
|
||||
|
||||
function doWrite(val: any) {
|
||||
if (!sigRef) return;
|
||||
wsClient.write(sigRef, val);
|
||||
}
|
||||
|
||||
function execute() {
|
||||
if (mode === 'setReset') {
|
||||
doWrite(parsedSend);
|
||||
setTimeout(() => doWrite(parsedReset), Math.max(0, resetTimeSec) * 1000);
|
||||
} else if (mode === 'persistent') {
|
||||
doWrite(isPressed ? parsedReset : parsedSend);
|
||||
} else {
|
||||
doWrite(parsedSend);
|
||||
}
|
||||
}
|
||||
|
||||
function handleClick() {
|
||||
if (!sigRef) return;
|
||||
const parsed = parseFloat(valueOpt);
|
||||
const val = isNaN(parsed) ? valueOpt : parsed;
|
||||
const doWrite = () => wsClient.write(sigRef, val);
|
||||
if (confirm) {
|
||||
if (window.confirm(`Send ${label}?`)) doWrite();
|
||||
if (confirmOpt) {
|
||||
if (window.confirm(`Send ${label}?`)) execute();
|
||||
} else {
|
||||
doWrite();
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,7 +63,12 @@ export default function Button({ widget, onContextMenu }: Props) {
|
||||
style={`left:${widget.x}px;top:${widget.y}px;width:${widget.w}px;height:${widget.h}px;`}
|
||||
onContextMenu={onContextMenu}
|
||||
>
|
||||
<button class="btn" onClick={handleClick}>{label}</button>
|
||||
<button class={`btn${isPressed ? ' btn-pressed' : ''}`} onClick={handleClick}>
|
||||
{label}
|
||||
{mode === 'setReset' && (
|
||||
<span class="btn-mode-hint">{resetTimeSec}s</span>
|
||||
)}
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -51,8 +51,9 @@ export default function Gauge({ widget, onContextMenu }: Props) {
|
||||
return () => { unsubV(); unsubM(); };
|
||||
}, [sigRef?.ds, sigRef?.name]);
|
||||
|
||||
const label = widget.options['label'] ?? sigRef?.name ?? '';
|
||||
const unit = widget.options['unit'] ?? meta?.unit ?? '';
|
||||
const label = widget.options['label'] || sigRef?.name || '';
|
||||
const rawUnit = widget.options['unit'];
|
||||
const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
|
||||
const minVal = parseFloat(widget.options['min'] ?? String(meta?.displayLow ?? 0));
|
||||
const maxVal = parseFloat(widget.options['max'] ?? String(meta?.displayHigh ?? 100));
|
||||
const thresholdLow = widget.options['thresholdLow'] ? parseFloat(widget.options['thresholdLow']) : null;
|
||||
|
||||
@@ -64,15 +64,15 @@ export default function PlotWidget({ widget, onContextMenu, timeRange }: Props)
|
||||
const chartRef = useRef<HTMLDivElement>(null);
|
||||
const [histStatus, setHistStatus] = useState<'idle' | 'loading' | 'empty' | 'error'>('idle');
|
||||
|
||||
// Re-render chart when switching between live and historical mode.
|
||||
// We stringify timeRange so the effect dependency is a stable primitive.
|
||||
// Stable primitive deps so the effect re-runs when plotType or signals change.
|
||||
const timeRangeKey = timeRange ? `${timeRange.start}|${timeRange.end}` : 'live';
|
||||
const plotType = widget.options['plotType'] ?? 'timeseries';
|
||||
const signalsKey = widget.signals.map(s => `${s.ds}:${s.name}${s.color ?? ''}`).join(',');
|
||||
|
||||
useEffect(() => {
|
||||
if (!chartRef.current) return;
|
||||
|
||||
const signals = widget.signals;
|
||||
const plotType = widget.options['plotType'] ?? 'timeseries';
|
||||
const timeWindow = parseFloat(widget.options['timeWindow'] ?? '60');
|
||||
const yMin = widget.options['yMin'];
|
||||
const yMax = widget.options['yMax'];
|
||||
@@ -329,7 +329,7 @@ export default function PlotWidget({ widget, onContextMenu, timeRange }: Props)
|
||||
if (uplot) uplot.destroy();
|
||||
if (echart) echart.dispose();
|
||||
};
|
||||
}, [widget.id, timeRangeKey]);
|
||||
}, [widget.id, timeRangeKey, plotType, signalsKey]);
|
||||
|
||||
return (
|
||||
<div
|
||||
|
||||
@@ -30,10 +30,11 @@ export default function SetValue({ widget, onContextMenu }: Props) {
|
||||
return () => { unsubV(); unsubM(); };
|
||||
}, [sigRef?.ds, sigRef?.name]);
|
||||
|
||||
const label = widget.options['label'] ?? sigRef?.name ?? '';
|
||||
const unit = widget.options['unit'] ?? meta?.unit ?? '';
|
||||
const label = widget.options['label'] || sigRef?.name || '';
|
||||
const rawUnit = widget.options['unit'];
|
||||
const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
|
||||
const confirm = widget.options['confirm'] === 'true';
|
||||
const isNumeric = meta ? meta.type !== 'TypeString' : true;
|
||||
const isNumeric = meta ? meta.type !== 'string' : true;
|
||||
const quality = sv.quality;
|
||||
|
||||
function displayValue(): string {
|
||||
|
||||
@@ -28,8 +28,9 @@ export default function TextView({ widget, onContextMenu }: Props) {
|
||||
return () => { unsubV(); unsubM(); };
|
||||
}, [sigRef?.ds, sigRef?.name]);
|
||||
|
||||
const label = widget.options['label'] ?? sigRef?.name ?? '';
|
||||
const unit = widget.options['unit'] ?? meta?.unit ?? '';
|
||||
const label = widget.options['label'] || sigRef?.name || '';
|
||||
const rawUnit = widget.options['unit'];
|
||||
const unit = rawUnit === 'none' ? '' : (rawUnit || meta?.unit || '');
|
||||
const quality = sv.quality;
|
||||
|
||||
function displayValue(): string {
|
||||
|
||||
Reference in New Issue
Block a user