Skip to content

Commit c1a010d

Browse files
Merge pull request #71 from ISISComputingGroup/show_update_timestamps
Show update timestamps
2 parents 54e9c97 + 4b8d728 commit c1a010d

File tree

10 files changed

+657
-393
lines changed

10 files changed

+657
-393
lines changed

app/components/Block.test.tsx

Lines changed: 46 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { render } from "@testing-library/react";
1+
import { fireEvent, render } from "@testing-library/react";
22
import Block from "@/app/components/Block";
33
import { IfcBlock } from "@/app/types";
44

@@ -20,12 +20,7 @@ it("renders topbar unchanged", () => {
2020
};
2121
const instName = "Instrument";
2222
const { container } = render(
23-
<Block
24-
pv={aBlock}
25-
instName={instName}
26-
showHiddenBlocks={false}
27-
showSetpoints={false}
28-
/>,
23+
<Block pv={aBlock} instName={instName} showHiddenBlocks={false} />,
2924
{
3025
container: tableBody,
3126
},
@@ -36,12 +31,7 @@ it("renders topbar unchanged", () => {
3631
it("renders nothing if pv is hidden", () => {
3732
const aBlock: IfcBlock = { pvaddress: "SOME:PV", visible: false };
3833
const { container } = render(
39-
<Block
40-
pv={aBlock}
41-
instName={""}
42-
showHiddenBlocks={false}
43-
showSetpoints={false}
44-
/>,
34+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
4535
{
4636
container: tableBody,
4737
},
@@ -56,12 +46,7 @@ it("renders block with correct name", () => {
5646
human_readable_name: "MyBlock",
5747
};
5848
const { container } = render(
59-
<Block
60-
pv={aBlock}
61-
instName={""}
62-
showHiddenBlocks={false}
63-
showSetpoints={false}
64-
/>,
49+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
6550
{
6651
container: tableBody,
6752
},
@@ -80,12 +65,7 @@ it("renders block with run control that is in range as a tick", () => {
8065
runcontrol_enabled: true,
8166
};
8267
const { container } = render(
83-
<Block
84-
pv={aBlock}
85-
instName={""}
86-
showHiddenBlocks={false}
87-
showSetpoints={false}
88-
/>,
68+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
8969
{
9070
container: tableBody,
9171
},
@@ -105,12 +85,7 @@ it("renders block with run control that is not in range as a cross", () => {
10585
runcontrol_enabled: true,
10686
};
10787
const { container } = render(
108-
<Block
109-
pv={aBlock}
110-
instName={""}
111-
showHiddenBlocks={false}
112-
showSetpoints={false}
113-
/>,
88+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
11489
{
11590
container: tableBody,
11691
},
@@ -130,12 +105,7 @@ it("renders block without run control without tick or cross", () => {
130105
runcontrol_enabled: false,
131106
};
132107
const { container } = render(
133-
<Block
134-
pv={aBlock}
135-
instName={""}
136-
showHiddenBlocks={false}
137-
showSetpoints={false}
138-
/>,
108+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
139109
{
140110
container: tableBody,
141111
},
@@ -159,19 +129,49 @@ it("renders block with SP and shows SP value", () => {
159129
value: expectedValue,
160130
};
161131
const { container } = render(
162-
<Block
163-
pv={aBlock}
164-
instName={""}
165-
showHiddenBlocks={false}
166-
showSetpoints={true}
167-
/>,
132+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
168133
{
169134
container: tableBody,
170135
},
171136
);
137+
const valueSpan = container.querySelector(
138+
`#${aBlock.human_readable_name}_VALUE`,
139+
)!;
140+
141+
fireEvent.click(valueSpan);
142+
expect(valueSpan.innerHTML).toContain(`${expectedValue}`);
172143
expect(
173-
container.querySelector(`#${aBlock.human_readable_name}_VALUE`)!.innerHTML,
174-
).toContain(`${expectedValue} <br>(SP: ${expectedSpValue})`);
144+
container.querySelector(`#${aBlock.human_readable_name}_SP`)!.innerHTML,
145+
).toContain(expectedSpValue.toString());
146+
});
147+
148+
it("renders block with timestamp and shows timestamp value", () => {
149+
const expectedValue = 123;
150+
const expectedTimeStamp = 1731342022;
151+
const aBlock: IfcBlock = {
152+
pvaddress: "SOME:PV",
153+
visible: true,
154+
human_readable_name: "MyBlock",
155+
runcontrol_inrange: false,
156+
runcontrol_enabled: false,
157+
updateSeconds: expectedTimeStamp,
158+
value: expectedValue,
159+
};
160+
const { container } = render(
161+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
162+
{
163+
container: tableBody,
164+
},
165+
);
166+
const valueSpan = container.querySelector(
167+
`#${aBlock.human_readable_name}_VALUE`,
168+
)!;
169+
fireEvent.click(valueSpan);
170+
expect(valueSpan.innerHTML).toContain(`${expectedValue}`);
171+
expect(
172+
container.querySelector(`#${aBlock.human_readable_name}_TIMESTAMP`)!
173+
.innerHTML,
174+
).toContain(new Date(expectedTimeStamp * 1000).toLocaleString());
175175
});
176176

177177
it("renders block without SP and hides SP value", () => {
@@ -187,12 +187,7 @@ it("renders block without SP and hides SP value", () => {
187187
sp_value: expectedSpValue,
188188
};
189189
const { container } = render(
190-
<Block
191-
pv={aBlock}
192-
instName={""}
193-
showHiddenBlocks={false}
194-
showSetpoints={false}
195-
/>,
190+
<Block pv={aBlock} instName={""} showHiddenBlocks={false} />,
196191
{
197192
container: tableBody,
198193
},

app/components/Block.tsx

Lines changed: 65 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22
import { IfcBlock } from "@/app/types";
3-
import { useState } from "react";
3+
import React, { useState } from "react";
44

55
const grafana_stub =
66
"https://shadow.nd.rl.ac.uk/grafana/d/wMlwwaHMk/block-history?viewPanel=2&orgId=1&var-block=";
@@ -9,16 +9,15 @@ export default function Block({
99
pv,
1010
instName,
1111
showHiddenBlocks,
12-
showSetpoints,
1312
}: {
1413
pv: IfcBlock;
1514
instName: string;
1615
showHiddenBlocks: boolean;
17-
showSetpoints: boolean;
1816
}) {
1917
const [currentValue, setCurrentValue] = useState<
2018
string | number | undefined
2119
>();
20+
const [showAdvanced, setShowAdvanced] = useState(false);
2221
if (!pv.visible && !showHiddenBlocks && !instName) {
2322
return null;
2423
}
@@ -38,12 +37,16 @@ export default function Block({
3837
}, 2000);
3938
}
4039

40+
const minimum_date_to_be_shown = 631152000; // This is what PVWS thinks epoch time is for some reason. don't bother showing it as the instrument wasn't running EPICS on 01/01/1990
4141
return (
4242
<tr
4343
key={pv.human_readable_name}
4444
className="border-b border-blue-gray-200 transition duration-100 hover:bg-gray-100 hover:text-black"
45+
onClick={() => {
46+
setShowAdvanced(!showAdvanced);
47+
}}
4548
>
46-
<td className="py-1 px-4">
49+
<td className="py-1 px-2 w-1/3 flex-row">
4750
<a
4851
className="underline"
4952
href={
@@ -58,39 +61,70 @@ export default function Block({
5861
</a>
5962
</td>
6063

61-
<td className="py-1 px-4 ">
62-
<span id={pv.human_readable_name + "_VALUE"}>
63-
{pv.value} {pv.units != null && pv.units}
64-
{showSetpoints && pv.sp_value != null ? (
65-
<>
66-
<br />
67-
{`(SP: ${pv.sp_value})`}
68-
</>
69-
) : null}
70-
{pv.severity != "NONE" ? (
71-
<a
72-
href="https://github.com/ISISComputingGroup/ibex_user_manual/wiki/Blocks#alarms"
73-
className="text-red-600"
64+
<td className="py-1 px-2 w-7/12">
65+
<span id={pv.human_readable_name + "_VALUE_ROW"}>
66+
<div className="flex justify-between">
67+
<span
68+
id={pv.human_readable_name + "_VALUE"}
69+
className={pv.severity != "NONE" ? "text-red-400" : ""}
7470
>
75-
<br />
76-
{pv.severity}
77-
</a>
78-
) : null}
71+
{showAdvanced && "Readback: "}
72+
{pv.value} {pv.units != null && pv.units}
73+
</span>
74+
<svg
75+
id={pv.human_readable_name + "_CIRCLE"}
76+
className="min-w-2 min-h-2 max-w-2 max-h-2 transition-all text-transparent"
77+
xmlns="http://www.w3.org/2000/svg"
78+
fill="currentColor"
79+
viewBox="0 0 24 24"
80+
>
81+
<circle cx="12" cy="12" r="12" />
82+
</svg>
83+
</div>
84+
85+
{showAdvanced && (
86+
<div>
87+
<hr />
88+
{pv.severity != "NONE" ? (
89+
<a
90+
href="https://github.com/ISISComputingGroup/ibex_user_manual/wiki/Blocks#alarms"
91+
className="text-red-400"
92+
>
93+
Alarm: {pv.severity}
94+
</a>
95+
) : null}
96+
<hr />
97+
{pv.sp_value != null ? (
98+
<span id={pv.human_readable_name + "_SP"}>
99+
{`Setpoint: ${pv.sp_value}`}
100+
<hr />
101+
</span>
102+
) : null}
103+
{pv.updateSeconds != null &&
104+
pv.updateSeconds > minimum_date_to_be_shown ? (
105+
<span id={pv.human_readable_name + "_TIMESTAMP"}>
106+
{/*Multiply by 1000 here as Date() expects milliseconds*/}
107+
{`Last update: ${new Date(pv.updateSeconds * 1000).toLocaleString()}`}
108+
</span>
109+
) : null}
110+
</div>
111+
)}
79112
</span>
80113
</td>
81-
<td className="py-1 px-4 flex justify-between items-center">
82-
<span id={pv.human_readable_name + "_VALUE_RC"}>
114+
<td className="py-1 px-2 flex justify-between items-center">
115+
<span
116+
id={pv.human_readable_name + "_VALUE_RC"}
117+
title={"Run control in-range?"}
118+
>
83119
{pv.runcontrol_enabled && (pv.runcontrol_inrange ? "✅" : "❌")}
84120
</span>
85-
<svg
86-
id={pv.human_readable_name + "_CIRCLE"}
87-
className="min-w-2 min-h-2 max-w-2 max-h-2 transition-all text-transparent"
88-
xmlns="http://www.w3.org/2000/svg"
89-
fill="currentColor"
90-
viewBox="0 0 24 24"
121+
122+
<span
123+
className={"cursor-pointer font-bold"}
124+
title={"Show/Hide advanced statuses"}
91125
>
92-
<circle cx="12" cy="12" r="12" />
93-
</svg>
126+
{showAdvanced ? "-" : "+"}
127+
</span>
94128
</td>
95129
</tr>
96130
);

app/components/Group.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,10 @@ export default function Group({
88
group,
99
instName,
1010
showHiddenBlocks,
11-
showSetpoints,
1211
}: {
1312
group: IfcGroup;
1413
instName: string;
1514
showHiddenBlocks: boolean;
16-
showSetpoints: boolean;
1715
}) {
1816
if (!group) {
1917
return <h1>Loading...</h1>;
@@ -30,22 +28,21 @@ export default function Group({
3028
{group.name}
3129
</h1>
3230
<table className="text-sm table-fixed">
33-
<thead>
34-
<tr className="bg-blue-gray-100 text-gray-100">
35-
<th className="py-3 px-4 text-left">Block</th>
36-
<th className="py-3 px-4 text-left">Value</th>
37-
<th className="py-3 px-4 text-left">In-Range</th>
31+
<thead className="sticky">
32+
<tr className="bg-blue-gray-100 text-gray-100 border-b-2 border-b-gray-400">
33+
<th className="py-2 px-2 text-left w-1/3">Block</th>
34+
<th className="py-2 px-2 text-left w-7/12">Value</th>
35+
<th className="py-2 px-2 text-left"></th>
3836
</tr>
3937
</thead>
40-
<tbody className="text-gray-200 ">
38+
<tbody className="text-gray-200 sticky">
4139
{group.blocks.map((pv) => {
4240
return (
4341
<Block
4442
key={pv.human_readable_name}
4543
pv={pv}
4644
instName={instName}
4745
showHiddenBlocks={showHiddenBlocks}
48-
showSetpoints={showSetpoints}
4946
/>
5047
);
5148
})}

app/components/Groups.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,27 +7,24 @@ export default function Groups({
77
groupsMap,
88
instName,
99
showHiddenBlocks,
10-
showSetpoints,
1110
}: {
1211
groupsMap: Array<IfcGroup>;
1312
instName: string;
1413
showHiddenBlocks: boolean;
15-
showSetpoints: boolean;
1614
}) {
1715
if (!groupsMap) {
1816
return <h1>Loading...</h1>;
1917
}
2018

2119
return (
22-
<div className="rounded-xl grid grid-cols-1 md:grid-cols-2 xl:grid-cols-3 gap-4 mt-2">
20+
<div className="rounded-xl grid grid-cols-1 md:grid-cols-2 xl:grid-cols-4 gap-4 mt-2">
2321
{groupsMap.map((group) => {
2422
return (
2523
<Group
2624
key={group.name}
2725
group={group}
2826
instName={instName}
2927
showHiddenBlocks={showHiddenBlocks}
30-
showSetpoints={showSetpoints}
3128
/>
3229
);
3330
})}

0 commit comments

Comments
 (0)