mccode-plumber 0.13.4__py3-none-any.whl → 0.14.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,125 @@
1
+ from textual.app import App, ComposeResult
2
+ from textual.containers import VerticalScroll
3
+ from textual.widgets import Static
4
+ from textual.reactive import reactive
5
+ from textual import events
6
+
7
+ from p4p.client.thread import Context
8
+ from p4p.client.thread import Disconnected
9
+
10
+ import asyncio
11
+ import threading
12
+
13
+
14
+ class PVWidget(Static):
15
+ value: reactive[str] = reactive("Connecting...")
16
+
17
+ def __init__(self, pvname: str):
18
+ super().__init__()
19
+ self.pvname = pvname
20
+ self.sid = pvname.replace(':', '')
21
+ self.set_class(True, "pv-widget")
22
+ self.value_widget = None # Store reference directly
23
+
24
+ def compose(self) -> ComposeResult:
25
+ yield Static(f"[b]{self.pvname}[/b]", id=f"label-{self.sid}")
26
+ self.value_widget = Static(self.value, id=f"value-{self.sid}")
27
+ yield self.value_widget
28
+
29
+ def watch_value(self, value: str):
30
+ if self.value_widget:
31
+ self.value_widget.update(value)
32
+
33
+ def update_value(self, new_value: str):
34
+ self.value = new_value
35
+
36
+ class PVMonitorApp(App):
37
+ CSS = """
38
+ .pv-widget {
39
+ padding: 1 1;
40
+ border: round $primary;
41
+ margin: 1;
42
+ }
43
+ """
44
+
45
+ def __init__(self, prefix: str, names: list[str]):
46
+ super().__init__()
47
+ self.pv_widgets = {}
48
+ self.ctx = Context("pva") # Or 'ca' if using Channel Access
49
+ self.prefix = prefix
50
+ self.names = names
51
+ self.grid = None
52
+
53
+ def compose(self) -> ComposeResult:
54
+ from textual.containers import Grid
55
+ self.grid = Grid(id="pv-grid")
56
+ yield self.grid
57
+
58
+ def on_mount(self) -> None:
59
+ self.grid.styles.grid_columns = ["1fr", "1fr", "1fr"] # 3 columns
60
+ self.grid.styles.grid_gap = (1, 1)
61
+ for name in self.names:
62
+ pv = f'{self.prefix}{name}'
63
+ widget = PVWidget(pv)
64
+ self.pv_widgets[pv] = widget
65
+ self.grid.mount(widget)
66
+ threading.Thread(target=self.monitor_pv, args=(pv,), daemon=True).start()
67
+
68
+ def monitor_pv(self, pvname: str):
69
+ def callback(value):
70
+ if isinstance(value, Disconnected):
71
+ new_value = "Disconnected"
72
+ else:
73
+ new_value = str(value)
74
+ # Schedule update on the main thread
75
+ asyncio.run_coroutine_threadsafe(
76
+ self.update_widget_value(pvname, new_value),
77
+ self._loop,
78
+ )
79
+
80
+ self.ctx.monitor(pvname, callback)
81
+
82
+ async def update_widget_value(self, pvname: str, new_value: str):
83
+ widget = self.pv_widgets.get(pvname)
84
+ if widget:
85
+ widget.update_value(new_value)
86
+
87
+ async def on_shutdown(self) -> None:
88
+ self.ctx.close()
89
+
90
+
91
+
92
+ def get_names_parser():
93
+ from argparse import ArgumentParser
94
+ from mccode_plumber import __version__
95
+ p = ArgumentParser()
96
+ p.add_argument('name', type=str, nargs='+', help='The NTScalar names to watch')
97
+ p.add_argument('-p', '--prefix', type=str, help='The EPICS PV prefix to use', default='mcstas:')
98
+ p.add_argument('-v', '--version', action='version', version=__version__)
99
+ return p
100
+
101
+
102
+ def run_strings():
103
+ args = get_names_parser().parse_args()
104
+ PVMonitorApp(args.prefix, args.name).run()
105
+
106
+
107
+ def get_instr_parser():
108
+ from argparse import ArgumentParser
109
+ from mccode_plumber import __version__
110
+ p = ArgumentParser()
111
+ p.add_argument('instr', type=str, help='The instrument which defines names to watch')
112
+ p.add_argument('-p', '--prefix', type=str, help='The EPICS PV prefix to use', default='mcstas:')
113
+ p.add_argument('-v', '--version', action='version', version=__version__)
114
+ return p
115
+
116
+ def run_instr():
117
+ from mccode_plumber.manage.orchestrate import get_instr_name_and_parameters
118
+ args = get_instr_parser().parse_args()
119
+ _, parameters = get_instr_name_and_parameters(args.instr)
120
+ names = [p.name for p in parameters]
121
+ PVMonitorApp(args.prefix, names).run()
122
+
123
+
124
+ if __name__ == '__main__':
125
+ run_instr()
@@ -1,15 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mccode-plumber
3
- Version: 0.13.4
3
+ Version: 0.14.0
4
4
  Author-email: Gregory Tucker <gregory.tucker@ess.eu>
5
5
  Classifier: License :: OSI Approved :: BSD License
6
6
  Description-Content-Type: text/markdown
7
7
  Requires-Dist: p4p
8
8
  Requires-Dist: kafka-python>=2.2.11
9
9
  Requires-Dist: ess-streaming-data-types>=0.14.0
10
- Requires-Dist: restage>=0.8.3
10
+ Requires-Dist: restage>=0.9.0
11
11
  Requires-Dist: mccode-to-kafka>=0.2.2
12
- Requires-Dist: moreniius>=0.5.2
12
+ Requires-Dist: moreniius>=0.6.0
13
13
  Requires-Dist: icecream
14
14
  Requires-Dist: ephemeral-port-reserve
15
15
 
@@ -1,6 +1,7 @@
1
1
  mccode_plumber/__init__.py,sha256=ZQMwyPQ6GyoEkKXj7aWETPqhXfwUbDOD5EpvlXj1c18,148
2
2
  mccode_plumber/conductor.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
3
3
  mccode_plumber/epics.py,sha256=SvZQN_vmI0NCGaIykIOQLO3tbgv79AMm60zGCmFQ56s,6167
4
+ mccode_plumber/epics_watcher.py,sha256=Jiz761A5NfoUzJ6ZzBGg8_BewFTmHDo5qYgh_DZHx_4,3973
4
5
  mccode_plumber/forwarder.py,sha256=yzjb--r8M6vNSsWvvOi5IjXqpRn8MvT13cv89ezNEeU,3994
5
6
  mccode_plumber/kafka.py,sha256=Q3oAuk7c-PS7b7zWSDhOR3dOO8R6Q8Y3HPI7K-Kt-RU,2499
6
7
  mccode_plumber/mccode.py,sha256=vc4Gb5WhdOfuDRjEejU3Flps0C2A60sfFbMrxmKLhn0,2189
@@ -28,8 +29,8 @@ mccode_plumber/manage/forwarder.py,sha256=YYvHaOJ4djBXM7PFF2NBbnNDO6nnrwNOfSHaVO
28
29
  mccode_plumber/manage/manager.py,sha256=zzrduroUL-jwQ9BrPTdAm1IW4dGv5bi8-ieIQ6qiQ6M,4141
29
30
  mccode_plumber/manage/orchestrate.py,sha256=bumvHN6jPEOqd8V96f7gEmc5IpWOZ6mSo7_bosi3jY8,17662
30
31
  mccode_plumber/manage/writer.py,sha256=SEv1U14L01Y9-BcaJKPei4Ah2LFfwexDy9FTjpvtSEs,2245
31
- mccode_plumber-0.13.4.dist-info/METADATA,sha256=-rI-6nH0wZjA7SgN-TdrZbJVPAUp7DTV4kco0SJy_cE,594
32
- mccode_plumber-0.13.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
33
- mccode_plumber-0.13.4.dist-info/entry_points.txt,sha256=JiuAHxI6YuGgJ4682TCyeBEJnS0ygDWpErrLaKgAicI,844
34
- mccode_plumber-0.13.4.dist-info/top_level.txt,sha256=kCCIpYtKHCKWxiPEqX9J1UaGEm-ze0Qb-cemBCEPhDA,15
35
- mccode_plumber-0.13.4.dist-info/RECORD,,
32
+ mccode_plumber-0.14.0.dist-info/METADATA,sha256=tceaL7T1Li53p4HVOLaUOpKgvrEx12o8Npoqui_LgDY,594
33
+ mccode_plumber-0.14.0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ mccode_plumber-0.14.0.dist-info/entry_points.txt,sha256=k3LDo_9HG2v4-IgMYlNukphdMmaAT6zkJZYaB1zJh3c,900
35
+ mccode_plumber-0.14.0.dist-info/top_level.txt,sha256=kCCIpYtKHCKWxiPEqX9J1UaGEm-ze0Qb-cemBCEPhDA,15
36
+ mccode_plumber-0.14.0.dist-info/RECORD,,
@@ -2,6 +2,7 @@
2
2
  mp-epics = mccode_plumber.epics:run
3
3
  mp-epics-strings = mccode_plumber.epics:run_strings
4
4
  mp-epics-update = mccode_plumber.epics:update
5
+ mp-epics-watch = mccode_plumber.epics_watcher:run_instr
5
6
  mp-forwarder-setup = mccode_plumber.forwarder:setup
6
7
  mp-forwarder-teardown = mccode_plumber.forwarder:teardown
7
8
  mp-insert-hdf5-instr = mccode_plumber.mccode:insert