|
1 | 1 | "use client"; |
2 | 2 | import React, { useEffect, useState } from "react"; |
3 | 3 | import { |
| 4 | + ConfigOutput, |
| 5 | + ConfigOutputBlock, |
| 6 | + IfcBlock, |
| 7 | + IfcGroup, |
| 8 | + IfcPV, |
4 | 9 | IfcPVWSMessage, |
5 | 10 | IfcPVWSRequest, |
6 | 11 | instList, |
7 | 12 | PVWSRequestType, |
8 | 13 | } from "@/app/types"; |
9 | | -import { findPVInDashboard, Instrument } from "@/app/components/Instrument"; |
| 14 | +import { |
| 15 | + findPVInDashboard, |
| 16 | + findPVInGroups, |
| 17 | + Instrument, |
| 18 | +} from "@/app/components/Instrument"; |
10 | 19 | import useWebSocket from "react-use-websocket"; |
11 | 20 | import { instListPV, instListSubscription, socketURL } from "@/app/commonVars"; |
12 | 21 | import { |
13 | 22 | dehex_and_decompress, |
14 | 23 | instListFromBytes, |
15 | 24 | } from "@/app/components/dehex_and_decompress"; |
16 | | -import { findPVByAddress } from "@/app/components/PVutils"; |
| 25 | +import { |
| 26 | + ExponentialOnThresholdFormat, |
| 27 | + findPVByAddress, |
| 28 | +} from "@/app/components/PVutils"; |
17 | 29 | import TopBar from "@/app/components/TopBar"; |
18 | 30 | import CheckToggle from "@/app/components/CheckToggle"; |
19 | 31 | 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"; |
28 | 32 |
|
29 | 33 | 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 | +} |
30 | 113 |
|
31 | 114 | export function InstrumentData({ instrumentName }: { instrumentName: string }) { |
32 | 115 | const [showHiddenBlocks, setShowHiddenBlocks] = useState(false); |
@@ -106,6 +189,7 @@ export function InstrumentData({ instrumentName }: { instrumentName: string }) { |
106 | 189 |
|
107 | 190 | if (updatedPVName == instListPV && updatedPVbytes != null) { |
108 | 191 | setInstlist(instListFromBytes(updatedPVbytes)); |
| 192 | + return; |
109 | 193 | } |
110 | 194 |
|
111 | 195 | if (!currentInstrument) { |
@@ -140,44 +224,55 @@ export function InstrumentData({ instrumentName }: { instrumentName: string }) { |
140 | 224 | // PV value is a number |
141 | 225 | pvVal = updatedPV.value; |
142 | 226 | } else { |
| 227 | + console.debug(`initial/blank message from ${updatedPVName}`); |
143 | 228 | return; |
144 | 229 | } |
145 | 230 |
|
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; |
154 | 246 | } 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 | + ); |
181 | 276 | } |
182 | 277 | } |
183 | 278 | } |
|
0 commit comments