Skip to content

Commit 2076c23

Browse files
committed
ENH: implementation of 'compare' API
1 parent 4d4d302 commit 2076c23

File tree

4 files changed

+155
-0
lines changed

4 files changed

+155
-0
lines changed

src/save_and_restore_api/_api_async.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -220,6 +220,17 @@ async def restore_items(self, *, snapshotItems, auth=None):
220220
method, url, params = self._prepare_restore_items(snapshotItems=snapshotItems)
221221
return await self.send_request(method, url, params=params, auth=auth)
222222

223+
# =============================================================================================
224+
# COMPARISON-CONTROLLER API METHODS
225+
# =============================================================================================
226+
227+
async def compare(self, nodeId, *, tolerance=None, compareMode=None, skipReadback=None):
228+
# Reusing docstrings from the threaded version
229+
method, url, url_params = self._prepare_compare(
230+
nodeId=nodeId, tolerance=tolerance, compareMode=compareMode, skipReadback=skipReadback
231+
)
232+
return await self.send_request(method, url, url_params=url_params)
233+
223234

224235
SaveRestoreAPI.info_get.__doc__ = _SaveRestoreAPI_Threads.info_get.__doc__
225236
SaveRestoreAPI.version_get.__doc__ = _SaveRestoreAPI_Threads.version_get.__doc__
@@ -246,3 +257,4 @@ async def restore_items(self, *, snapshotItems, auth=None):
246257
SaveRestoreAPI.snapshots_get.__doc__ = _SaveRestoreAPI_Threads.snapshots_get.__doc__
247258
SaveRestoreAPI.restore_node.__doc__ = _SaveRestoreAPI_Threads.restore_node.__doc__
248259
SaveRestoreAPI.restore_items.__doc__ = _SaveRestoreAPI_Threads.restore_items.__doc__
260+
SaveRestoreAPI.compare.__doc__ = _SaveRestoreAPI_Threads.compare.__doc__

src/save_and_restore_api/_api_base.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -328,6 +328,23 @@ def _prepare_restore_items(self, *, snapshotItems):
328328
params = snapshotItems
329329
return method, url, params
330330

331+
# =============================================================================================
332+
# COMPARISON-CONTROLLER API METHODS
333+
# =============================================================================================
334+
335+
def _prepare_compare(self, *, nodeId, tolerance, compareMode, skipReadback):
336+
method, url = "GET", f"/compare/{nodeId}"
337+
url_params = {}
338+
if tolerance:
339+
url_params["tolerance"] = tolerance
340+
if compareMode:
341+
url_params["compareMode"] = compareMode
342+
if skipReadback is not None:
343+
url_params["skipReadback"] = str(skipReadback).lower()
344+
if not url_params:
345+
url_params = None
346+
return method, url, url_params
347+
331348
# =============================================================================================
332349

333350
# def create_config(self, parent_node_uid, name, pv_list):

src/save_and_restore_api/_api_threads.py

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,3 +358,20 @@ def restore_items(self, *, snapshotItems, auth=None):
358358
"""
359359
method, url, params = self._prepare_restore_items(snapshotItems=snapshotItems)
360360
return self.send_request(method, url, params=params, auth=auth)
361+
362+
# =============================================================================================
363+
# COMPARISON-CONTROLLER API METHODS
364+
# =============================================================================================
365+
366+
def compare(self, nodeId, *, tolerance=None, compareMode=None, skipReadback=None):
367+
"""
368+
Compare stored PV values with live values for the selected snapshot or composite snapshot.
369+
The snapshot is selected by ``nodeId``. The API returns a list of results for each PV in
370+
the snapshot.
371+
372+
API: GET /compare/{nodeId}
373+
"""
374+
method, url, url_params = self._prepare_compare(
375+
nodeId=nodeId, tolerance=tolerance, compareMode=compareMode, skipReadback=skipReadback
376+
)
377+
return self.send_request(method, url, url_params=url_params)

tests/test_compare_control.py

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
from __future__ import annotations
2+
3+
import asyncio
4+
5+
import pytest
6+
from epics import caput
7+
8+
from save_and_restore_api import SaveRestoreAPI as SaveRestoreAPI_Threads
9+
from save_and_restore_api.aio import SaveRestoreAPI as SaveRestoreAPI_Async
10+
11+
from .common import (
12+
_is_async,
13+
_select_auth,
14+
base_url,
15+
clear_sar, # noqa: F401
16+
create_root_folder,
17+
ioc, # noqa: F401
18+
ioc_pvs,
19+
)
20+
21+
# =============================================================================================
22+
# TESTS FOR COMPARISON-CONTROLLER API METHODS
23+
# =============================================================================================
24+
25+
26+
# fmt: off
27+
@pytest.mark.parametrize("usesetauth", [True, False])
28+
@pytest.mark.parametrize("library", ["THREADS", "ASYNC"])
29+
# fmt: on
30+
def test_compare_01(clear_sar, ioc, library, usesetauth): # noqa: F811
31+
"""
32+
Basic tests for the 'compare' API.
33+
"""
34+
root_folder_uid = create_root_folder()
35+
36+
if not _is_async(library):
37+
with SaveRestoreAPI_Threads(base_url=base_url, timeout=10) as SR:
38+
auth = _select_auth(SR=SR, usesetauth=usesetauth)
39+
40+
configurationNode = {"name": "Test Config"}
41+
configurationData = {"pvList": [{"pvName": _} for _ in ioc_pvs.keys()]}
42+
43+
response = SR.config_create(
44+
root_folder_uid,
45+
configurationNode=configurationNode,
46+
configurationData=configurationData,
47+
**auth
48+
)
49+
config_uid = response["configurationNode"]["uniqueId"]
50+
51+
response = SR.take_snapshot_save(config_uid)
52+
shot_uid = response["snapshotData"]["uniqueId"]
53+
54+
caput("simulated:C", 100)
55+
caput("simulated:D", 200)
56+
57+
response = SR.compare(shot_uid)
58+
pv_sim_C, pv_sim_D = None, None
59+
for v in response:
60+
if v["pvName"] == "simulated:C":
61+
pv_sim_C = v
62+
if v["pvName"] == "simulated:D":
63+
pv_sim_D = v
64+
65+
assert pv_sim_C["equal"] is False
66+
assert pv_sim_C["storedValue"]["value"] == 3
67+
assert pv_sim_C["liveValue"]["value"] == 100
68+
assert pv_sim_D["equal"] is False
69+
assert pv_sim_D["storedValue"]["value"] == 4
70+
assert pv_sim_D["liveValue"]["value"] == 200
71+
72+
else:
73+
async def testing():
74+
async with SaveRestoreAPI_Async(base_url=base_url, timeout=2) as SR:
75+
auth = _select_auth(SR=SR, usesetauth=usesetauth)
76+
77+
configurationNode = {"name": "Test Config"}
78+
configurationData = {"pvList": [{"pvName": _} for _ in ioc_pvs.keys()]}
79+
80+
response = await SR.config_create(
81+
root_folder_uid,
82+
configurationNode=configurationNode,
83+
configurationData=configurationData,
84+
**auth
85+
)
86+
config_uid = response["configurationNode"]["uniqueId"]
87+
88+
response = await SR.take_snapshot_save(config_uid)
89+
shot_uid = response["snapshotData"]["uniqueId"]
90+
91+
caput("simulated:C", 100)
92+
caput("simulated:D", 200)
93+
94+
response = await SR.compare(shot_uid)
95+
pv_sim_C, pv_sim_D = None, None
96+
for v in response:
97+
if v["pvName"] == "simulated:C":
98+
pv_sim_C = v
99+
if v["pvName"] == "simulated:D":
100+
pv_sim_D = v
101+
102+
assert pv_sim_C["equal"] is False
103+
assert pv_sim_C["storedValue"]["value"] == 3
104+
assert pv_sim_C["liveValue"]["value"] == 100
105+
assert pv_sim_D["equal"] is False
106+
assert pv_sim_D["storedValue"]["value"] == 4
107+
assert pv_sim_D["liveValue"]["value"] == 200
108+
109+
asyncio.run(testing())

0 commit comments

Comments
 (0)