Skip to content

Commit de0ac0f

Browse files
committed
show runstate on instruments page
1 parent 726f733 commit de0ac0f

File tree

11 files changed

+241
-245
lines changed

11 files changed

+241
-245
lines changed

app/components/CheckToggle.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ export default function CheckToggle({
44
checked,
55
setChecked,
66
text,
7+
textColour = "text-gray-900",
78
}: {
89
checked: boolean;
910
setChecked: Dispatch<SetStateAction<boolean>>;
1011
text: string;
12+
textColour?: string;
1113
}) {
1214
return (
1315
<div className="pt-4">
@@ -21,7 +23,7 @@ export default function CheckToggle({
2123
className="sr-only peer"
2224
/>
2325
<div className="relative w-11 h-6 bg-gray-200 rounded-full peer peer-focus:ring-4 peer-focus:ring-blue-300 dark:peer-focus:ring-blue-800 dark:bg-gray-700 peer-checked:after:translate-x-full rtl:peer-checked:after:-translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-0.5 after:start-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all dark:border-gray-600 peer-checked:bg-blue-600"></div>
24-
<span className="ms-3 text-sm font-medium text-gray-900">{text}</span>
26+
<span className={"ms-3 text-sm font-medium" + textColour}>{text}</span>
2527
</label>
2628
</div>
2729
);

app/wall/components/InstrumentWallCard.tsx renamed to app/components/InstrumentWallCard.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,5 @@
11
import Link from "next/link";
2-
import {
3-
getForegroundColour,
4-
getStatusColour,
5-
} from "../../components/getRunstateColours";
2+
import { getForegroundColour, getStatusColour } from "./getRunstateColours";
63

74
import { IfcInstrumentStatus } from "@/app/types";
85

Lines changed: 198 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,198 @@
1+
import { useEffect, useState } from "react";
2+
import {
3+
IfcInstrumentStatus,
4+
IfcPVWSMessage,
5+
IfcPVWSRequest,
6+
targetStation,
7+
} from "@/app/types";
8+
import useWebSocket from "react-use-websocket";
9+
import { instListPV, instListSubscription, socketURL } from "@/app/commonVars";
10+
import { instListFromBytes } from "@/app/components/dehex_and_decompress";
11+
import {
12+
updateInstrumentRunstate,
13+
updateInstrumentRunstatePV,
14+
} from "@/app/wall/utils";
15+
import TargetStation from "@/app/components/TargetStation";
16+
import ScienceGroup from "@/app/components/ScienceGroup";
17+
18+
export function createInstrumentGroups(
19+
targetStations: Array<targetStation>,
20+
): Map<string, Array<IfcInstrumentStatus>> {
21+
let newInstrumentGroups: Map<string, Array<IfcInstrumentStatus>> = new Map();
22+
for (const targetStation of targetStations) {
23+
for (const inst of targetStation.instruments) {
24+
if (inst.scienceGroups) {
25+
for (const group of inst.scienceGroups) {
26+
if (!newInstrumentGroups.has(group)) {
27+
newInstrumentGroups.set(group, []);
28+
}
29+
newInstrumentGroups.get(group)!.push(inst);
30+
}
31+
}
32+
}
33+
}
34+
return newInstrumentGroups;
35+
}
36+
37+
export default function InstrumentsDisplay({
38+
sortByGroups = false,
39+
}: {
40+
sortByGroups?: boolean;
41+
}) {
42+
const runstatePV = "DAE:RUNSTATE_STR";
43+
44+
const [data, setData] = useState<Array<targetStation>>([
45+
{
46+
targetStation: "Target Station 1",
47+
instruments: [
48+
{ name: "ALF" },
49+
{ name: "CRISP" },
50+
{ name: "EMMA-A" },
51+
{ name: "EMU" },
52+
{ name: "ENGINX" },
53+
{ name: "GEM" },
54+
{
55+
name: "HIFI-CRYOMAG",
56+
},
57+
{ name: "HRPD" },
58+
{ name: "INES" },
59+
{ name: "IRIS" },
60+
{ name: "LOQ" },
61+
{ name: "MAPS" },
62+
{ name: "MARI" },
63+
{ name: "MERLIN" },
64+
{ name: "MUONFE" },
65+
{ name: "MUSR" },
66+
{ name: "OSIRIS" },
67+
{ name: "PEARL" },
68+
{ name: "POLARIS" },
69+
{ name: "RIKENFE" },
70+
{ name: "SANDALS" },
71+
{ name: "SCIDEMO" },
72+
{ name: "SURF" },
73+
{ name: "SXD" },
74+
{ name: "TOSCA" },
75+
{ name: "VESUVIO" },
76+
],
77+
},
78+
{
79+
targetStation: "Target Station 2",
80+
instruments: [
81+
{ name: "CHIPIR" },
82+
{ name: "IMAT" },
83+
{ name: "INTER" },
84+
{ name: "LARMOR" },
85+
{ name: "LET" },
86+
{ name: "NIMROD" },
87+
{ name: "OFFSPEC" },
88+
{ name: "POLREF" },
89+
{ name: "SANS2D" },
90+
{ name: "WISH" },
91+
{ name: "ZOOM" },
92+
],
93+
},
94+
{
95+
targetStation: "Miscellaneous",
96+
instruments: [
97+
{ name: "ARGUS" },
98+
{ name: "CHRONUS" },
99+
{
100+
name: "CRYOLAB_R80",
101+
},
102+
{ name: "DCLAB" },
103+
{ name: "DEMO" },
104+
{ name: "DETMON" },
105+
{
106+
name: "ENGINX_SETUP",
107+
},
108+
{ name: "HIFI" },
109+
{
110+
name: "HRPD_SETUP",
111+
},
112+
{
113+
name: "IBEXGUITEST",
114+
},
115+
{
116+
name: "IRIS_SETUP",
117+
},
118+
{ name: "MOTION" },
119+
{
120+
name: "PEARL_SETUP",
121+
},
122+
{ name: "SELAB" },
123+
{ name: "SOFTMAT" },
124+
{
125+
name: "WISH_SETUP",
126+
},
127+
],
128+
},
129+
]);
130+
131+
const {
132+
sendJsonMessage,
133+
lastJsonMessage,
134+
}: {
135+
sendJsonMessage: (a: IfcPVWSRequest) => void;
136+
lastJsonMessage: IfcPVWSMessage;
137+
} = useWebSocket(socketURL, {
138+
shouldReconnect: (closeEvent) => true,
139+
});
140+
141+
useEffect(() => {
142+
// On page load, subscribe to the instrument list as it's required to get each instrument's PV prefix.
143+
sendJsonMessage(instListSubscription);
144+
}, [sendJsonMessage]);
145+
146+
useEffect(() => {
147+
// This is a PV update, it could be either the instlist or an instrument's runstate that has changed
148+
if (!lastJsonMessage) {
149+
return;
150+
}
151+
152+
const updatedPV: IfcPVWSMessage = lastJsonMessage;
153+
const updatedPVName: string = updatedPV.pv;
154+
const updatedPVbytes: string | null | undefined = updatedPV.b64byt;
155+
let updatedPVvalue: string | null | undefined = updatedPV.text;
156+
157+
if (updatedPVName == instListPV && updatedPVbytes != null) {
158+
const instListDict = instListFromBytes(updatedPVbytes);
159+
for (const instrument of instListDict) {
160+
setData((prev) => {
161+
return updateInstrumentRunstatePV(
162+
prev,
163+
instrument,
164+
runstatePV,
165+
sendJsonMessage,
166+
);
167+
});
168+
}
169+
} else if (updatedPVvalue) {
170+
setData((prev) => {
171+
return updateInstrumentRunstate(prev, updatedPVName, updatedPVvalue);
172+
});
173+
}
174+
}, [lastJsonMessage, sendJsonMessage]);
175+
176+
return (
177+
<div>
178+
{sortByGroups &&
179+
Array.from(createInstrumentGroups(data).entries()).map(
180+
([name, instruments]) => {
181+
return (
182+
<ScienceGroup key={name} name={name} instruments={instruments} />
183+
);
184+
},
185+
)}
186+
{!sortByGroups &&
187+
data.map((targetStation) => {
188+
return (
189+
<TargetStation
190+
key={targetStation.targetStation}
191+
groupName={targetStation.targetStation}
192+
data={targetStation.instruments}
193+
/>
194+
);
195+
})}
196+
</div>
197+
);
198+
}

app/components/ScienceGroup.tsx

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import { IfcInstrumentStatus } from "@/app/types";
2+
import InstrumentWallCard from "@/app/components/InstrumentWallCard";
3+
4+
export default function ScienceGroup({
5+
name,
6+
instruments,
7+
}: {
8+
name: string;
9+
instruments: Array<IfcInstrumentStatus>;
10+
}) {
11+
return (
12+
<div
13+
key={name}
14+
className={"flex flex-col justify-center items-start w-full"}
15+
>
16+
<h2 className="w-full text-left text-gray-600 dark:text-gray-200 font-semibold text-md mt-2 py-2 ">
17+
{name}
18+
</h2>
19+
<div className={"flex flex-wrap gap-1"}>
20+
{instruments.map((instrument: IfcInstrumentStatus) => {
21+
return (
22+
<InstrumentWallCard key={instrument.name} instrument={instrument} />
23+
);
24+
})}
25+
</div>
26+
</div>
27+
);
28+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import InstrumentWallCard from "./InstrumentWallCard";
22

33
import { IfcInstrumentStatus } from "@/app/types";
44

5-
export default function InstrumentGroup({
5+
export default function TargetStation({
66
groupName,
77
data,
88
}: {

app/instruments/page.tsx

Lines changed: 4 additions & 83 deletions
Original file line numberDiff line numberDiff line change
@@ -1,91 +1,12 @@
11
"use client";
2-
import Link from "next/link";
3-
import { useEffect, useState } from "react";
4-
import { IfcPVWSMessage, IfcPVWSRequest } from "@/app/types";
5-
import { instListFromBytes } from "@/app/components/dehex_and_decompress";
6-
import useWebSocket from "react-use-websocket";
7-
import { instListPV, instListSubscription, socketURL } from "@/app/commonVars";
8-
import createInstrumentGroupsFromInstlist from "@/app/instruments/utils";
2+
import InstrumentsDisplay from "@/app/components/InstrumentsDisplay";
93

104
export default function Instruments() {
11-
const [instrumentGroups, setInstrumentGroups] = useState<
12-
Map<string, Array<string>>
13-
>(new Map());
14-
15-
const {
16-
sendJsonMessage,
17-
lastJsonMessage,
18-
}: {
19-
sendJsonMessage: (a: IfcPVWSRequest) => void;
20-
lastJsonMessage: IfcPVWSMessage;
21-
} = useWebSocket(socketURL, {
22-
shouldReconnect: (closeEvent) => true,
23-
});
24-
25-
useEffect(() => {
26-
// On page load, subscribe to the instrument list as it's required to get each instrument.
27-
sendJsonMessage(instListSubscription);
28-
}, [sendJsonMessage]);
29-
30-
useEffect(() => {
31-
// Instlist has changed
32-
if (!lastJsonMessage) {
33-
return;
34-
}
35-
36-
const updatedPV: IfcPVWSMessage = lastJsonMessage;
37-
const updatedPVbytes: string | null | undefined = updatedPV.b64byt;
38-
39-
if (updatedPV.pv == instListPV && updatedPVbytes != null) {
40-
const newInstrumentGroups = createInstrumentGroupsFromInstlist(
41-
instListFromBytes(updatedPVbytes),
42-
);
43-
setInstrumentGroups(newInstrumentGroups);
44-
}
45-
}, [lastJsonMessage]);
46-
47-
if (!instrumentGroups.size) {
48-
return <h1>Loading...</h1>;
49-
}
50-
515
return (
52-
<main
6+
<div
537
className={`flex min-h-screen bg-gray-100 dark:bg-zinc-800 flex-col items-center justify-between`}
548
>
55-
<section className=" flex flex-col items-start justify-center rounded-xl w-full p-1">
56-
<div className=" mx-auto max-w-2/3">
57-
<div className="flex mt-6 flex-col justify-center items-center space-y-2">
58-
{Array.from(instrumentGroups.entries()).map(([group, insts]) => {
59-
return (
60-
<div
61-
key={group}
62-
className="flex flex-col justify-center items-center w-full "
63-
>
64-
<h1 className="text-2xl font-bold text-black dark:text-gray-100 text-left w-full">
65-
{group}
66-
</h1>
67-
<div className="flex-wrap flex justify-left items-center w-full mx-auto text-left md:text-center">
68-
{insts.sort().map((instrument: string) => {
69-
return (
70-
<Link
71-
href={`/instrument?name=${instrument}`}
72-
key={instrument}
73-
>
74-
<div className="shadow-lg rounded-lg border-2 border-gray-600 bg-slate-800 hover:bg-slate-700 py-2 px-3 m-1 transition-all duration-100">
75-
<h1 className="text-xl flex items-center justify-center text-left leading-none transition-all text-black dark:text-white">
76-
{instrument}
77-
</h1>
78-
</div>
79-
</Link>
80-
);
81-
})}
82-
</div>
83-
</div>
84-
);
85-
})}
86-
</div>
87-
</div>
88-
</section>
89-
</main>
9+
<InstrumentsDisplay sortByGroups={true} />
10+
</div>
9011
);
9112
}

app/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ export interface IfcInstrumentStatus {
8282
name: string; // Name of the instrument
8383
runstate?: string; // Runstate
8484
runstatePV?: string; // Runstate PV address
85+
scienceGroups?: Array<string>;
8586
}
8687

8788
// Column[Row[labelPV, valuePV]]

0 commit comments

Comments
 (0)