Skip to content

Commit 418b2c4

Browse files
committed
make pv update handling more generic, remove instrumentpage
1 parent 777863f commit 418b2c4

File tree

5 files changed

+165
-140
lines changed

5 files changed

+165
-140
lines changed

app/components/Instrument.ts

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
import { DashboardArr, IfcGroup, IfcPV } from "@/app/types";
1+
import { DashboardArr, IfcBlock, IfcGroup, IfcPV } from "@/app/types";
2+
import { CSSB } from "@/app/components/InstrumentData";
23

34
const DASHBOARD = "CS:DASHBOARD:TAB:";
45

@@ -187,3 +188,16 @@ export function findPVInDashboard(
187188
): undefined | IfcPV {
188189
return dashboard.flat(3).find((pv: IfcPV) => pv.pvaddress == pvAddress);
189190
}
191+
192+
export function findPVInGroups(
193+
groups: Array<IfcGroup>,
194+
prefix: string,
195+
updatedPVName: string,
196+
): undefined | IfcBlock {
197+
return groups
198+
.flatMap((group: IfcGroup) => group.blocks)
199+
.find(
200+
(block: IfcBlock) =>
201+
updatedPVName == prefix + CSSB + block.human_readable_name,
202+
);
203+
}
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,15 @@ import {
44
IfcPVWSRequest,
55
PVWSRequestType,
66
} from "@/app/types";
7+
78
import {
8-
getGroupsWithBlocksFromConfigOutput,
99
RC_ENABLE,
1010
RC_INRANGE,
1111
SP_RBV,
1212
subscribeToBlockPVs,
1313
toPrecision,
14-
} from "@/app/components/InstrumentPage";
14+
getGroupsWithBlocksFromConfigOutput,
15+
} from "@/app/components/InstrumentData";
1516

1617
test("subscribeToBlockPVs subscribes to all run control PVs", () => {
1718
const mockSendJsonMessage = jest.fn();

app/components/InstrumentData.tsx

Lines changed: 139 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,115 @@
11
"use client";
22
import React, { useEffect, useState } from "react";
33
import {
4+
ConfigOutput,
5+
ConfigOutputBlock,
6+
IfcBlock,
7+
IfcGroup,
8+
IfcPV,
49
IfcPVWSMessage,
510
IfcPVWSRequest,
611
instList,
712
PVWSRequestType,
813
} from "@/app/types";
9-
import { findPVInDashboard, Instrument } from "@/app/components/Instrument";
14+
import {
15+
findPVInDashboard,
16+
findPVInGroups,
17+
Instrument,
18+
} from "@/app/components/Instrument";
1019
import useWebSocket from "react-use-websocket";
1120
import { instListPV, instListSubscription, socketURL } from "@/app/commonVars";
1221
import {
1322
dehex_and_decompress,
1423
instListFromBytes,
1524
} from "@/app/components/dehex_and_decompress";
16-
import { findPVByAddress } from "@/app/components/PVutils";
25+
import {
26+
ExponentialOnThresholdFormat,
27+
findPVByAddress,
28+
} from "@/app/components/PVutils";
1729
import TopBar from "@/app/components/TopBar";
1830
import CheckToggle from "@/app/components/CheckToggle";
1931
import Groups from "@/app/components/Groups";
20-
import {
21-
CSSB,
22-
getGroupsWithBlocksFromConfigOutput,
23-
RC_ENABLE,
24-
RC_INRANGE,
25-
SP_RBV,
26-
toPrecision,
27-
} from "@/app/components/InstrumentPage";
2832

2933
let lastUpdate: string = "";
34+
export const RC_ENABLE = ":RC:ENABLE";
35+
36+
export const RC_INRANGE = ":RC:INRANGE";
37+
38+
export const SP_RBV = ":SP:RBV";
39+
40+
export const CSSB = "CS:SB:";
41+
42+
export function toPrecision(
43+
block: IfcPV,
44+
pvVal: number | string,
45+
): string | number {
46+
return block.precision
47+
? ExponentialOnThresholdFormat(pvVal, block.precision)
48+
: pvVal;
49+
}
50+
51+
function storePrecision(updatedPV: IfcPVWSMessage, block: IfcBlock) {
52+
const prec = updatedPV.precision;
53+
if (prec != null && prec > 0 && !block.precision) {
54+
// this is likely the first update, and contains precision information which is not repeated on a normal value update - store this in the block for later truncation (see below)
55+
block.precision = prec;
56+
}
57+
}
58+
function yesToBoolean(pvVal: string | number) {
59+
return pvVal == "YES";
60+
}
61+
62+
export function subscribeToBlockPVs(
63+
sendJsonMessage: (a: IfcPVWSRequest) => void,
64+
block_address: string,
65+
) {
66+
/**
67+
* Subscribes to a block and its associated run control PVs
68+
*/
69+
sendJsonMessage({
70+
type: PVWSRequestType.subscribe,
71+
pvs: [
72+
block_address,
73+
block_address + RC_ENABLE,
74+
block_address + RC_INRANGE,
75+
block_address + SP_RBV,
76+
],
77+
});
78+
}
79+
80+
export function getGroupsWithBlocksFromConfigOutput(
81+
configOutput: ConfigOutput,
82+
sendJsonMessage: (a: IfcPVWSRequest) => void,
83+
prefix: string,
84+
): Array<IfcGroup> {
85+
const groups = configOutput.groups;
86+
let newGroups: Array<IfcGroup> = [];
87+
for (const group of groups) {
88+
const groupName = group.name;
89+
let blocks: Array<IfcBlock> = [];
90+
for (const block of group.blocks) {
91+
const newBlock = configOutput.blocks.find(
92+
(b: ConfigOutputBlock) => b.name === block,
93+
);
94+
if (newBlock) {
95+
blocks.push({
96+
pvaddress: newBlock.pv,
97+
human_readable_name: newBlock.name,
98+
low_rc: newBlock.lowlimit,
99+
high_rc: newBlock.highlimit,
100+
visible: newBlock.visible,
101+
});
102+
const fullyQualifiedBlockPVAddress = prefix + CSSB + newBlock.name;
103+
subscribeToBlockPVs(sendJsonMessage, fullyQualifiedBlockPVAddress);
104+
}
105+
}
106+
newGroups.push({
107+
name: groupName,
108+
blocks: blocks,
109+
});
110+
}
111+
return newGroups;
112+
}
30113

31114
export function InstrumentData({ instrumentName }: { instrumentName: string }) {
32115
const [showHiddenBlocks, setShowHiddenBlocks] = useState(false);
@@ -106,6 +189,7 @@ export function InstrumentData({ instrumentName }: { instrumentName: string }) {
106189

107190
if (updatedPVName == instListPV && updatedPVbytes != null) {
108191
setInstlist(instListFromBytes(updatedPVbytes));
192+
return;
109193
}
110194

111195
if (!currentInstrument) {
@@ -140,44 +224,55 @@ export function InstrumentData({ instrumentName }: { instrumentName: string }) {
140224
// PV value is a number
141225
pvVal = updatedPV.value;
142226
} else {
227+
console.debug(`initial/blank message from ${updatedPVName}`);
143228
return;
144229
}
145230

146-
if (findPVInDashboard(currentInstrument.dashboard, updatedPVName)) {
147-
// This is a dashboard block update.
148-
findPVInDashboard(currentInstrument.dashboard, updatedPVName)!.value =
149-
pvVal;
150-
} else if (findPVByAddress(currentInstrument.runInfoPVs, updatedPVName)) {
151-
// This is a run information PV
152-
findPVByAddress(currentInstrument.runInfoPVs, updatedPVName)!.value =
153-
pvVal;
231+
// Check if this is a dashboard, run info, or block PV update.
232+
const pv =
233+
findPVInDashboard(currentInstrument.dashboard, updatedPVName) ||
234+
findPVByAddress(currentInstrument.runInfoPVs, updatedPVName) ||
235+
findPVInGroups(
236+
currentInstrument.groups,
237+
currentInstrument.prefix,
238+
updatedPVName,
239+
);
240+
if (pv) {
241+
storePrecision(updatedPV, pv);
242+
pv.value = toPrecision(pv, pvVal);
243+
if (updatedPV.seconds) pv.updateSeconds = updatedPV.seconds;
244+
if (updatedPV.units) pv.units = updatedPV.units;
245+
if (updatedPV.severity) pv.severity = updatedPV.severity;
154246
} else {
155-
// This is a block - check if in groups
156-
for (const group of currentInstrument.groups) {
157-
for (const block of group.blocks) {
158-
let block_full_pv_name =
159-
currentInstrument.prefix + CSSB + block.human_readable_name;
160-
if (updatedPVName == block_full_pv_name) {
161-
let prec = updatedPV.precision;
162-
163-
if (prec != null && prec > 0 && !block.precision) {
164-
// this is likely the first update, and contains precision information which is not repeated on a normal value update - store this in the block for later truncation (see below)
165-
block.precision = prec;
166-
}
167-
// if a block has precision truncate it here
168-
block.value = toPrecision(block, pvVal);
169-
if (updatedPV.seconds) block.updateSeconds = updatedPV.seconds;
170-
171-
if (updatedPV.units) block.units = updatedPV.units;
172-
if (updatedPV.severity) block.severity = updatedPV.severity;
173-
} else if (updatedPVName == block_full_pv_name + RC_INRANGE) {
174-
block.runcontrol_inrange = updatedPV.value == 1;
175-
} else if (updatedPVName == block_full_pv_name + RC_ENABLE) {
176-
block.runcontrol_enabled = updatedPV.value == 1;
177-
} else if (updatedPVName == block_full_pv_name + SP_RBV) {
178-
block.sp_value = toPrecision(block, pvVal);
179-
}
180-
}
247+
// OK, we haven't found the block, but we may have an update for its object such as its run control status
248+
if (updatedPVName.endsWith(RC_INRANGE)) {
249+
const underlyingBlock = findPVInGroups(
250+
currentInstrument.groups,
251+
currentInstrument.prefix,
252+
updatedPVName.replace(RC_INRANGE, ""),
253+
);
254+
if (underlyingBlock)
255+
underlyingBlock.runcontrol_inrange = yesToBoolean(pvVal);
256+
} else if (updatedPVName.endsWith(RC_ENABLE)) {
257+
const underlyingBlock = findPVInGroups(
258+
currentInstrument.groups,
259+
currentInstrument.prefix,
260+
updatedPVName.replace(RC_ENABLE, ""),
261+
);
262+
if (underlyingBlock)
263+
underlyingBlock.runcontrol_enabled = yesToBoolean(pvVal);
264+
} else if (updatedPVName.endsWith(SP_RBV)) {
265+
const underlyingBlock = findPVInGroups(
266+
currentInstrument.groups,
267+
currentInstrument.prefix,
268+
updatedPVName.replace(SP_RBV, ""),
269+
);
270+
if (underlyingBlock)
271+
underlyingBlock.sp_value = toPrecision(underlyingBlock, pvVal);
272+
} else {
273+
console.warn(
274+
`update from unknown PV: ${updatedPVName} with value ${pvVal}`,
275+
);
181276
}
182277
}
183278
}

app/components/InstrumentPage.tsx

Lines changed: 0 additions & 89 deletions
This file was deleted.

app/instrument/page.tsx

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
1+
"use client"
12
import { Inter } from "next/font/google";
2-
import InstrumentPage from "@/app/components/InstrumentPage";
33
const inter = Inter({ subsets: ["latin"] });
4-
import { Suspense } from "react";
5-
import type { Metadata, ResolvingMetadata } from "next";
4+
import React, { Suspense } from "react";
5+
import {InstrumentData} from "@/app/components/InstrumentData";
6+
import {useSearchParams} from "next/navigation";
67

78
export default function Instrument() {
9+
const searchParams = useSearchParams();
10+
const instrument = searchParams.get("name")!;
11+
812
return (
913
<main
1014
className={`flex min-h-screen bg-white flex-col items-center justify-start ${inter.className}`}
1115
>
1216
<Suspense>
13-
<InstrumentPage />
17+
<InstrumentData instrumentName={instrument} />;
1418
</Suspense>
1519
</main>
1620
);

0 commit comments

Comments
 (0)