keithley-tempcontrol 0.17.1__py3-none-any.whl → 0.17.2__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.
- egse/tempcontrol/keithley/daq6510_sim.py +98 -42
- {keithley_tempcontrol-0.17.1.dist-info → keithley_tempcontrol-0.17.2.dist-info}/METADATA +1 -1
- {keithley_tempcontrol-0.17.1.dist-info → keithley_tempcontrol-0.17.2.dist-info}/RECORD +5 -5
- {keithley_tempcontrol-0.17.1.dist-info → keithley_tempcontrol-0.17.2.dist-info}/WHEEL +1 -1
- {keithley_tempcontrol-0.17.1.dist-info → keithley_tempcontrol-0.17.2.dist-info}/entry_points.txt +0 -0
|
@@ -3,19 +3,28 @@ import datetime
|
|
|
3
3
|
import re
|
|
4
4
|
import socket
|
|
5
5
|
import time
|
|
6
|
+
from functools import partial
|
|
6
7
|
from typing import Annotated
|
|
7
8
|
|
|
8
9
|
import typer
|
|
9
10
|
|
|
11
|
+
from egse.env import bool_env
|
|
10
12
|
from egse.log import logging
|
|
11
13
|
from egse.settings import Settings
|
|
12
14
|
from egse.system import SignalCatcher
|
|
13
15
|
|
|
14
16
|
logger = logging.getLogger("egse.daq6510-sim")
|
|
15
17
|
|
|
18
|
+
VERSION = "0.1.0"
|
|
19
|
+
VERBOSE_DEBUG = bool_env("VERBOSE_DEBUG")
|
|
16
20
|
HOST = "localhost"
|
|
17
21
|
DAQ_SETTINGS = Settings.load("Keithley DAQ6510")
|
|
18
22
|
|
|
23
|
+
READ_TIMEOUT = 2.0
|
|
24
|
+
"""The timeout set on the connection socket, applicable when reading from the socket with `recv`."""
|
|
25
|
+
CONNECTION_TIMEOUT = 2.0
|
|
26
|
+
"""The timeout set on the socket before accepting a connection."""
|
|
27
|
+
|
|
19
28
|
SEPARATOR = b"\n"
|
|
20
29
|
SEPARATOR_STR = SEPARATOR.decode()
|
|
21
30
|
|
|
@@ -25,8 +34,8 @@ reference_time = device_time
|
|
|
25
34
|
|
|
26
35
|
app = typer.Typer(help="DAQ6510 Simulator")
|
|
27
36
|
|
|
28
|
-
error_msg: str
|
|
29
|
-
"""Global error message, always contains the last error. Reset in the inner loop of run_simulator."""
|
|
37
|
+
error_msg: str = ""
|
|
38
|
+
"""Global error message, always contains the last error. Reset to an empty string in the inner loop of run_simulator."""
|
|
30
39
|
|
|
31
40
|
|
|
32
41
|
def create_datetime(year, month, day, hour, minute, second):
|
|
@@ -64,8 +73,14 @@ def reset():
|
|
|
64
73
|
logger.info("RESET")
|
|
65
74
|
|
|
66
75
|
|
|
76
|
+
def log(level: int, msg: str):
|
|
77
|
+
logger.log(level, msg)
|
|
78
|
+
|
|
79
|
+
|
|
67
80
|
COMMAND_ACTIONS_RESPONSES = {
|
|
68
|
-
"*IDN?": (None, "KEITHLEY INSTRUMENTS,
|
|
81
|
+
"*IDN?": (None, f"KEITHLEY INSTRUMENTS,DAQ6510,SIMULATOR,{VERSION}"),
|
|
82
|
+
"*ACTION-RESPONSE?": (partial(log, logging.INFO, "Requested action with response."), get_time),
|
|
83
|
+
"*ACTION-NO-RESPONSE": (partial(log, logging.INFO, "Requested action without response."), None),
|
|
69
84
|
}
|
|
70
85
|
|
|
71
86
|
# Check the regex at https://regex101.com
|
|
@@ -82,64 +97,104 @@ COMMAND_PATTERNS_ACTIONS_RESPONSES = {
|
|
|
82
97
|
|
|
83
98
|
def write(conn, response: str):
|
|
84
99
|
response = f"{response}{SEPARATOR_STR}".encode()
|
|
85
|
-
|
|
100
|
+
if VERBOSE_DEBUG:
|
|
101
|
+
logger.debug(f"write: {response = }")
|
|
86
102
|
conn.sendall(response)
|
|
87
103
|
|
|
88
104
|
|
|
105
|
+
# Keep a receive buffer per connection
|
|
106
|
+
_recv_buffers: dict[int, bytes] = {}
|
|
107
|
+
|
|
108
|
+
|
|
89
109
|
def read(conn) -> str:
|
|
90
110
|
"""
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
The command string with the linefeed stripped off.
|
|
111
|
+
Read bytes from `conn` until a `SEPARATOR` is found (or connection closed / timeout).
|
|
112
|
+
Returns the first chunk (separator stripped). Any bytes after the separator are kept
|
|
113
|
+
in a per-connection buffer for the next call.
|
|
95
114
|
"""
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
buf_size = 1024 * 4
|
|
99
|
-
command_string = bytes()
|
|
115
|
+
fileno = conn.fileno()
|
|
116
|
+
buf = _recv_buffers.get(fileno, b"")
|
|
100
117
|
|
|
101
118
|
try:
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
119
|
+
while True:
|
|
120
|
+
# If we already have a full line in the buffer, split and return it.
|
|
121
|
+
if SEPARATOR in buf:
|
|
122
|
+
line, rest = buf.split(SEPARATOR, 1)
|
|
123
|
+
_recv_buffers[fileno] = rest
|
|
124
|
+
logger.info(f"read: {line=}")
|
|
125
|
+
return line.decode().rstrip()
|
|
126
|
+
|
|
127
|
+
# Read more data
|
|
128
|
+
data = conn.recv(1024 * 4)
|
|
129
|
+
if not data:
|
|
130
|
+
# Connection closed by peer; return whatever we have (may be empty)
|
|
131
|
+
_recv_buffers.pop(fileno, None)
|
|
132
|
+
logger.info(f"read (connection closed): {buf=}")
|
|
133
|
+
return buf.decode().rstrip()
|
|
134
|
+
buf += data
|
|
135
|
+
_recv_buffers[fileno] = buf
|
|
136
|
+
|
|
110
137
|
except socket.timeout:
|
|
111
|
-
#
|
|
138
|
+
# If we have accumulated data without a separator, return it (partial read),
|
|
139
|
+
# otherwise propagate the timeout so caller can handle/suppress it.
|
|
140
|
+
if buf:
|
|
141
|
+
_recv_buffers[fileno] = buf
|
|
142
|
+
logger.info(f"read (timeout, partial): {buf=}")
|
|
143
|
+
return buf.decode().rstrip()
|
|
112
144
|
raise
|
|
113
145
|
|
|
114
|
-
logger.info(f"read: {command_string=}")
|
|
115
|
-
|
|
116
|
-
return command_string.decode().rstrip()
|
|
117
|
-
|
|
118
146
|
|
|
119
|
-
def process_command(command_string: str) -> str:
|
|
147
|
+
def process_command(command_string: str) -> str | None:
|
|
148
|
+
"""Process the given command string and return a response."""
|
|
120
149
|
global COMMAND_ACTIONS_RESPONSES
|
|
121
150
|
global COMMAND_PATTERNS_ACTIONS_RESPONSES
|
|
122
151
|
global error_msg
|
|
123
152
|
|
|
124
|
-
|
|
153
|
+
if VERBOSE_DEBUG:
|
|
154
|
+
logger.debug(f"{command_string=}")
|
|
125
155
|
|
|
126
156
|
try:
|
|
127
157
|
action, response = COMMAND_ACTIONS_RESPONSES[command_string]
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
158
|
+
if VERBOSE_DEBUG:
|
|
159
|
+
logger.debug(f"{action=}, {response=}")
|
|
160
|
+
|
|
161
|
+
if action:
|
|
162
|
+
action()
|
|
163
|
+
|
|
164
|
+
if response:
|
|
165
|
+
if error_msg:
|
|
166
|
+
return error_msg
|
|
167
|
+
else:
|
|
168
|
+
return response() if callable(response) else response
|
|
131
169
|
else:
|
|
132
|
-
|
|
170
|
+
if error_msg:
|
|
171
|
+
logger.error(f"Error occurred during process command: {error_msg}")
|
|
172
|
+
return None
|
|
133
173
|
except KeyError:
|
|
134
174
|
# try to match with a value
|
|
135
175
|
for key, value in COMMAND_PATTERNS_ACTIONS_RESPONSES.items():
|
|
136
176
|
if match := re.match(key, command_string, flags=re.IGNORECASE):
|
|
137
|
-
|
|
177
|
+
if VERBOSE_DEBUG:
|
|
178
|
+
logger.debug(f"{match=}, {match.groups()}")
|
|
138
179
|
action, response = value
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
180
|
+
if VERBOSE_DEBUG:
|
|
181
|
+
logger.debug(f"{action=}, {response=}")
|
|
182
|
+
|
|
183
|
+
if action:
|
|
184
|
+
action(*match.groups())
|
|
185
|
+
|
|
186
|
+
if response:
|
|
187
|
+
if error_msg:
|
|
188
|
+
return error_msg
|
|
189
|
+
else:
|
|
190
|
+
return response() if callable(response) else response
|
|
191
|
+
else:
|
|
192
|
+
if error_msg:
|
|
193
|
+
logger.error(f"Error occurred during process command: {error_msg}")
|
|
194
|
+
return None
|
|
195
|
+
|
|
196
|
+
logger.error(f"ERROR: unknown command string: {command_string}")
|
|
197
|
+
return None
|
|
143
198
|
|
|
144
199
|
|
|
145
200
|
def run_simulator():
|
|
@@ -152,7 +207,7 @@ def run_simulator():
|
|
|
152
207
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
|
|
153
208
|
s.bind((HOST, DAQ_SETTINGS.PORT))
|
|
154
209
|
s.listen()
|
|
155
|
-
s.settimeout(
|
|
210
|
+
s.settimeout(CONNECTION_TIMEOUT)
|
|
156
211
|
while True:
|
|
157
212
|
while True:
|
|
158
213
|
with contextlib.suppress(socket.timeout):
|
|
@@ -163,13 +218,17 @@ def run_simulator():
|
|
|
163
218
|
with conn:
|
|
164
219
|
logger.info(f"Accepted connection from {addr}")
|
|
165
220
|
write(conn, "This is PLATO DAQ6510 X.X.sim")
|
|
166
|
-
conn.settimeout(
|
|
221
|
+
conn.settimeout(READ_TIMEOUT)
|
|
167
222
|
try:
|
|
168
223
|
while True:
|
|
169
224
|
error_msg = ""
|
|
170
225
|
with contextlib.suppress(socket.timeout):
|
|
171
226
|
data = read(conn)
|
|
172
|
-
|
|
227
|
+
if VERBOSE_DEBUG:
|
|
228
|
+
logger.debug(f"{data = }")
|
|
229
|
+
if not data:
|
|
230
|
+
logger.info("Client closed connection, accepting new connection...")
|
|
231
|
+
break
|
|
173
232
|
if data.strip() == "STOP":
|
|
174
233
|
logger.info("Client requested to terminate...")
|
|
175
234
|
s.close()
|
|
@@ -178,9 +237,6 @@ def run_simulator():
|
|
|
178
237
|
response = process_command(cmd.strip())
|
|
179
238
|
if response is not None:
|
|
180
239
|
write(conn, response)
|
|
181
|
-
if not data:
|
|
182
|
-
logger.info("Client closed connection, accepting new connection...")
|
|
183
|
-
break
|
|
184
240
|
if killer.term_signal_received:
|
|
185
241
|
logger.info("Terminating...")
|
|
186
242
|
s.close()
|
|
@@ -7,12 +7,12 @@ egse/tempcontrol/keithley/daq6510_cs.py,sha256=Ga7z8S6z0oTxL_qQP8FXPaNKlJ6o9RrsP
|
|
|
7
7
|
egse/tempcontrol/keithley/daq6510_dev.py,sha256=EKFFDhP8-FdIPBPosc1DfzE3h4DQmNU1d0Fi7Yhx4I4,12906
|
|
8
8
|
egse/tempcontrol/keithley/daq6510_mon.py,sha256=Xbn2U-l9uxPwNN1-aYW72oJodL2sx13suCiPPbDSti0,20932
|
|
9
9
|
egse/tempcontrol/keithley/daq6510_protocol.py,sha256=v8FUrxEm7bnRzM_iQzW0mMCHTgAMZw4f2Ronl8fdKIE,2676
|
|
10
|
-
egse/tempcontrol/keithley/daq6510_sim.py,sha256=
|
|
10
|
+
egse/tempcontrol/keithley/daq6510_sim.py,sha256=Ys5jroT-2i-V_qCR0L6yOYM9CiktnNbB3LvVLGwaD8A,9917
|
|
11
11
|
keithley_tempcontrol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
12
12
|
keithley_tempcontrol/cgse_explore.py,sha256=y_FkFxJW0vdqGNp9yTU0ELBKxby74-ev3fTuf99Vl1s,400
|
|
13
13
|
keithley_tempcontrol/cgse_services.py,sha256=tndviv2rvygkNSsGy1oA43VfFpyVkdB9If-9sVlLbK4,2466
|
|
14
14
|
keithley_tempcontrol/settings.yaml,sha256=wbrgSZQAdqFl6AxiLJIN36UsdiVHQCzdsgi7Hs7dv7o,1467
|
|
15
|
-
keithley_tempcontrol-0.17.
|
|
16
|
-
keithley_tempcontrol-0.17.
|
|
17
|
-
keithley_tempcontrol-0.17.
|
|
18
|
-
keithley_tempcontrol-0.17.
|
|
15
|
+
keithley_tempcontrol-0.17.2.dist-info/METADATA,sha256=wX8ndoLOCImQ_LuO8U2JxzCodlil3X0Ydp6_GB8Dg70,962
|
|
16
|
+
keithley_tempcontrol-0.17.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
17
|
+
keithley_tempcontrol-0.17.2.dist-info/entry_points.txt,sha256=_0j2BwcwPi4LlRrhvEWfp9GO9KT8WhCkJe2gFgMzOPs,491
|
|
18
|
+
keithley_tempcontrol-0.17.2.dist-info/RECORD,,
|
{keithley_tempcontrol-0.17.1.dist-info → keithley_tempcontrol-0.17.2.dist-info}/entry_points.txt
RENAMED
|
File without changes
|