yrcom 0.3.0__tar.gz → 0.3.2__tar.gz
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.
- {yrcom-0.3.0/src/yrcom.egg-info → yrcom-0.3.2}/PKG-INFO +10 -1
- {yrcom-0.3.0 → yrcom-0.3.2}/README.md +4 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/pyproject.toml +2 -1
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/__init__.py +1 -1
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/hses_client.py +21 -1
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/yaskawa_client.py +43 -21
- {yrcom-0.3.0 → yrcom-0.3.2/src/yrcom.egg-info}/PKG-INFO +10 -1
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom.egg-info/SOURCES.txt +1 -0
- yrcom-0.3.2/src/yrcom.egg-info/requires.txt +9 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_hses_client.py +24 -0
- yrcom-0.3.2/tests/test_tcp_framing.py +103 -0
- yrcom-0.3.0/src/yrcom.egg-info/requires.txt +0 -3
- {yrcom-0.3.0 → yrcom-0.3.2}/LICENSE +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/setup.cfg +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/hses_errors.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/hses_file.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/hses_server_codec.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/hses_sim.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom/robot_connection.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom.egg-info/dependency_links.txt +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom.egg-info/entry_points.txt +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/src/yrcom.egg-info/top_level.txt +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_hses_file.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_hses_server_codec.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_hses_sim.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_robot_connection.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_yaskawa_client.py +0 -0
- {yrcom-0.3.0 → yrcom-0.3.2}/tests/test_yaskawa_commands.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yrcom
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Yaskawa robot communication — Ethernet Server, HSES and HSES File Control (pure stdlib)
|
|
5
5
|
Author: cmosbk
|
|
6
6
|
License: MIT
|
|
@@ -12,12 +12,21 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
+
Provides-Extra: docs
|
|
16
|
+
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
17
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
18
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
|
|
19
|
+
Requires-Dist: black>=24; extra == "docs"
|
|
15
20
|
Dynamic: license-file
|
|
16
21
|
|
|
17
22
|
# yrcom
|
|
18
23
|
|
|
19
24
|
[](https://github.com/CMOSBK/yrcom/actions/workflows/ci.yml)
|
|
20
25
|
|
|
26
|
+
📖 **Documentation: <https://cmosbk.github.io/yrcom/>** — getting started, the
|
|
27
|
+
transport guide and the full API reference (every command, generated from the
|
|
28
|
+
source).
|
|
29
|
+
|
|
21
30
|
Yaskawa robot communication as a standalone Python library — a single,
|
|
22
31
|
hardware-verified communication core meant to be shared across projects
|
|
23
32
|
instead of being duplicated.
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/CMOSBK/yrcom/actions/workflows/ci.yml)
|
|
4
4
|
|
|
5
|
+
📖 **Documentation: <https://cmosbk.github.io/yrcom/>** — getting started, the
|
|
6
|
+
transport guide and the full API reference (every command, generated from the
|
|
7
|
+
source).
|
|
8
|
+
|
|
5
9
|
Yaskawa robot communication as a standalone Python library — a single,
|
|
6
10
|
hardware-verified communication core meant to be shared across projects
|
|
7
11
|
instead of being duplicated.
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "yrcom"
|
|
7
|
-
version = "0.3.
|
|
7
|
+
version = "0.3.2"
|
|
8
8
|
description = "Yaskawa robot communication — Ethernet Server, HSES and HSES File Control (pure stdlib)"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -15,6 +15,7 @@ dependencies = []
|
|
|
15
15
|
|
|
16
16
|
[project.optional-dependencies]
|
|
17
17
|
dev = ["pytest>=8"]
|
|
18
|
+
docs = ["mkdocs>=1.6", "mkdocs-material>=9.5", "mkdocstrings[python]>=0.26", "black>=24"]
|
|
18
19
|
|
|
19
20
|
[project.scripts]
|
|
20
21
|
yrcom-sim = "yrcom.hses_sim:_main"
|
|
@@ -581,6 +581,8 @@ class HsesClient(_HsesSocket):
|
|
|
581
581
|
position_group: int = 0,
|
|
582
582
|
b_vars: "list[dict]" = [],
|
|
583
583
|
i_vars: "list[dict]" = [],
|
|
584
|
+
d_vars: "list[dict]" = [],
|
|
585
|
+
r_vars: "list[dict]" = [],
|
|
584
586
|
io_signals: "list[dict]" = [],
|
|
585
587
|
read_cio: bool = False,
|
|
586
588
|
) -> dict:
|
|
@@ -590,7 +592,11 @@ class HsesClient(_HsesSocket):
|
|
|
590
592
|
alarm_slots: number of alarm history slots (0=skip, max 4)
|
|
591
593
|
include_job: read executing job info
|
|
592
594
|
position_group: 0=skip, 1-8=pulse, 101-108=cartesian
|
|
593
|
-
b_vars/i_vars:
|
|
595
|
+
b_vars/i_vars/d_vars/r_vars:
|
|
596
|
+
list of {"label": str, "no": int}. Each becomes one
|
|
597
|
+
request inside the burst, so its value shares the
|
|
598
|
+
instant of the rest of the row rather than arriving a
|
|
599
|
+
round trip later.
|
|
594
600
|
io_signals: list of {"label": str, "logical": int}
|
|
595
601
|
|
|
596
602
|
Returns flat dict of all decoded fields.
|
|
@@ -622,6 +628,14 @@ class HsesClient(_HsesSocket):
|
|
|
622
628
|
reqs.append((_CMD_I_VAR, v["no"], 1, _SVC_GET_SINGLE, b""))
|
|
623
629
|
tags.append(f"i_var:{v['label']}")
|
|
624
630
|
|
|
631
|
+
for v in d_vars:
|
|
632
|
+
reqs.append((_CMD_D_VAR, v["no"], 1, _SVC_GET_SINGLE, b""))
|
|
633
|
+
tags.append(f"d_var:{v['label']}")
|
|
634
|
+
|
|
635
|
+
for v in r_vars:
|
|
636
|
+
reqs.append((_CMD_R_VAR, v["no"], 1, _SVC_GET_SINGLE, b""))
|
|
637
|
+
tags.append(f"r_var:{v['label']}")
|
|
638
|
+
|
|
625
639
|
# CIO counter: low byte at logical 72000 (inst 7200), high byte at 72010 (inst 7201)
|
|
626
640
|
if read_cio:
|
|
627
641
|
reqs.append((_CMD_IO_PLURAL, 7200, 0, _SVC_READ_ALL, struct.pack("<I", 2)))
|
|
@@ -666,6 +680,12 @@ class HsesClient(_HsesSocket):
|
|
|
666
680
|
elif tag.startswith("i_var:"):
|
|
667
681
|
if resp.status == 0 and len(resp.data) >= 2:
|
|
668
682
|
out[f"var_{tag[6:]}"] = struct.unpack_from("<h", resp.data, 0)[0]
|
|
683
|
+
elif tag.startswith("d_var:"):
|
|
684
|
+
if resp.status == 0 and len(resp.data) >= 4:
|
|
685
|
+
out[f"var_{tag[6:]}"] = struct.unpack_from("<i", resp.data, 0)[0]
|
|
686
|
+
elif tag.startswith("r_var:"):
|
|
687
|
+
if resp.status == 0 and len(resp.data) >= 4:
|
|
688
|
+
out[f"var_{tag[6:]}"] = struct.unpack_from("<f", resp.data, 0)[0]
|
|
669
689
|
elif tag == "cio":
|
|
670
690
|
if resp.status == 0 and len(resp.data) >= 6:
|
|
671
691
|
# response: 4-byte count + 2 data bytes
|
|
@@ -104,6 +104,9 @@ class YaskawaClient:
|
|
|
104
104
|
self.port = port
|
|
105
105
|
self.timeout = timeout
|
|
106
106
|
self.sock: socket.socket | None = None
|
|
107
|
+
# Bytes already received but not yet consumed. TCP is a byte stream: one recv() may
|
|
108
|
+
# hold a header and its answer together, or only part of a line.
|
|
109
|
+
self._rx = b""
|
|
107
110
|
|
|
108
111
|
# ── Context manager ───────────────────────────────────────────────────────
|
|
109
112
|
|
|
@@ -120,6 +123,7 @@ class YaskawaClient:
|
|
|
120
123
|
"""Open TCP socket to the controller. No-op if already connected."""
|
|
121
124
|
if self.sock is not None:
|
|
122
125
|
return
|
|
126
|
+
self._rx = b""
|
|
123
127
|
try:
|
|
124
128
|
self.sock = socket.create_connection((self.host, self.port), timeout=self.timeout)
|
|
125
129
|
except Exception:
|
|
@@ -131,6 +135,7 @@ class YaskawaClient:
|
|
|
131
135
|
if self.sock:
|
|
132
136
|
self.sock.close()
|
|
133
137
|
self.sock = None
|
|
138
|
+
self._rx = b""
|
|
134
139
|
|
|
135
140
|
# ── Protocol primitives ───────────────────────────────────────────────────
|
|
136
141
|
|
|
@@ -160,39 +165,56 @@ class YaskawaClient:
|
|
|
160
165
|
raise RuntimeError("Socket not connected")
|
|
161
166
|
header = f"HOSTCTRL_REQUEST {command} {size}\r\n"
|
|
162
167
|
self.sock.sendall(header.encode("ascii"))
|
|
163
|
-
self._recv_until_ok()
|
|
168
|
+
reply = self._recv_until_ok()
|
|
169
|
+
if reply.startswith("NG"):
|
|
170
|
+
# Request refused: no answer line follows, so waiting for one would only time out.
|
|
171
|
+
return reply
|
|
164
172
|
if size > 0 and data:
|
|
165
173
|
payload = data if data.endswith("\r") else data + "\r"
|
|
166
174
|
self.sock.sendall(payload.encode("ascii"))
|
|
167
175
|
return self._recv_answer()
|
|
168
176
|
|
|
169
|
-
def
|
|
170
|
-
"""
|
|
177
|
+
def _recv_line(self) -> str | None:
|
|
178
|
+
"""Return the next line from the stream without its terminator, or None
|
|
179
|
+
if the peer closed the connection with nothing left to read.
|
|
180
|
+
|
|
181
|
+
Lines end in ``\r\n`` (handshake and header replies) or a bare ``\r``
|
|
182
|
+
(answer data). Reads until a terminator is buffered, so a reply split
|
|
183
|
+
over several segments, or several replies in one segment, are handled
|
|
184
|
+
the same way. A ``\n`` left over from ``\r\n`` split across segments is
|
|
185
|
+
skipped at the start of the next line.
|
|
186
|
+
"""
|
|
171
187
|
if self.sock is None:
|
|
172
188
|
raise RuntimeError("Socket not connected")
|
|
173
|
-
buf = ""
|
|
174
189
|
while True:
|
|
190
|
+
self._rx = self._rx.lstrip(b"\n")
|
|
191
|
+
end = self._rx.find(b"\r")
|
|
192
|
+
if end >= 0:
|
|
193
|
+
line, self._rx = self._rx[:end], self._rx[end + 1:]
|
|
194
|
+
if self._rx.startswith(b"\n"):
|
|
195
|
+
self._rx = self._rx[1:]
|
|
196
|
+
return line.decode("ascii")
|
|
175
197
|
chunk = self.sock.recv(_RECV_SIZE)
|
|
176
198
|
if not chunk:
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
return buf
|
|
199
|
+
# Peer closed: hand out an unterminated rest once, then report the end.
|
|
200
|
+
line, self._rx = self._rx, b""
|
|
201
|
+
return line.decode("ascii") if line else None
|
|
202
|
+
self._rx += chunk
|
|
182
203
|
|
|
183
|
-
def
|
|
184
|
-
"""Receive
|
|
185
|
-
if
|
|
186
|
-
raise RuntimeError("Socket not connected")
|
|
187
|
-
buf = ""
|
|
204
|
+
def _recv_until_ok(self) -> str:
|
|
205
|
+
"""Receive lines until one starts with OK or NG and return it (with ``\r\n``);
|
|
206
|
+
"" if the peer closed first."""
|
|
188
207
|
while True:
|
|
189
|
-
|
|
190
|
-
if
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
208
|
+
line = self._recv_line()
|
|
209
|
+
if line is None:
|
|
210
|
+
return ""
|
|
211
|
+
if line.startswith(("OK", "NG")):
|
|
212
|
+
return line + "\r\n"
|
|
213
|
+
|
|
214
|
+
def _recv_answer(self) -> str:
|
|
215
|
+
"""Receive one answer line (data ends with ``\r`` or ``\r\n``); "" if the peer closed."""
|
|
216
|
+
line = self._recv_line()
|
|
217
|
+
return "" if line is None else line + "\r"
|
|
196
218
|
|
|
197
219
|
# ── Transaction patterns ──────────────────────────────────────────────────
|
|
198
220
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: yrcom
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.2
|
|
4
4
|
Summary: Yaskawa robot communication — Ethernet Server, HSES and HSES File Control (pure stdlib)
|
|
5
5
|
Author: cmosbk
|
|
6
6
|
License: MIT
|
|
@@ -12,12 +12,21 @@ Description-Content-Type: text/markdown
|
|
|
12
12
|
License-File: LICENSE
|
|
13
13
|
Provides-Extra: dev
|
|
14
14
|
Requires-Dist: pytest>=8; extra == "dev"
|
|
15
|
+
Provides-Extra: docs
|
|
16
|
+
Requires-Dist: mkdocs>=1.6; extra == "docs"
|
|
17
|
+
Requires-Dist: mkdocs-material>=9.5; extra == "docs"
|
|
18
|
+
Requires-Dist: mkdocstrings[python]>=0.26; extra == "docs"
|
|
19
|
+
Requires-Dist: black>=24; extra == "docs"
|
|
15
20
|
Dynamic: license-file
|
|
16
21
|
|
|
17
22
|
# yrcom
|
|
18
23
|
|
|
19
24
|
[](https://github.com/CMOSBK/yrcom/actions/workflows/ci.yml)
|
|
20
25
|
|
|
26
|
+
📖 **Documentation: <https://cmosbk.github.io/yrcom/>** — getting started, the
|
|
27
|
+
transport guide and the full API reference (every command, generated from the
|
|
28
|
+
source).
|
|
29
|
+
|
|
21
30
|
Yaskawa robot communication as a standalone Python library — a single,
|
|
22
31
|
hardware-verified communication core meant to be shared across projects
|
|
23
32
|
instead of being duplicated.
|
|
@@ -624,6 +624,30 @@ def test_poll_all_status_and_job():
|
|
|
624
624
|
assert out["job_name"] == "MASTER"
|
|
625
625
|
|
|
626
626
|
|
|
627
|
+
def test_poll_all_reads_every_variable_kind_in_one_burst():
|
|
628
|
+
"""D and R used to be reachable only as standalone reads, which put their
|
|
629
|
+
value a round trip away from the rest of the row."""
|
|
630
|
+
status_data = struct.pack("<II", 0x40, 0x40)
|
|
631
|
+
c, _ = _client([
|
|
632
|
+
_reply(hc._CMD_STATUS, status_data, req_id=1),
|
|
633
|
+
_reply(hc._CMD_B_VAR, struct.pack("<B", 7), req_id=2),
|
|
634
|
+
_reply(hc._CMD_I_VAR, struct.pack("<h", -3), req_id=3),
|
|
635
|
+
_reply(hc._CMD_D_VAR, struct.pack("<i", -70000), req_id=4),
|
|
636
|
+
_reply(hc._CMD_R_VAR, struct.pack("<f", 1.5), req_id=5),
|
|
637
|
+
])
|
|
638
|
+
out = c.poll_all(
|
|
639
|
+
include_job=False,
|
|
640
|
+
b_vars=[{"label": "b", "no": 1}],
|
|
641
|
+
i_vars=[{"label": "i", "no": 2}],
|
|
642
|
+
d_vars=[{"label": "d", "no": 3}],
|
|
643
|
+
r_vars=[{"label": "r", "no": 4}],
|
|
644
|
+
)
|
|
645
|
+
assert out["var_b"] == 7
|
|
646
|
+
assert out["var_i"] == -3
|
|
647
|
+
assert out["var_d"] == -70000 # signed 32-bit, beyond an I variable
|
|
648
|
+
assert out["var_r"] == pytest.approx(1.5)
|
|
649
|
+
|
|
650
|
+
|
|
627
651
|
def test_poll_all_raises_on_status_error():
|
|
628
652
|
c, _ = _client([_reply(hc._CMD_STATUS, b"", req_id=1, status=0x08)])
|
|
629
653
|
with pytest.raises(HsesError):
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
"""Ethernet Server replies arrive as a TCP byte stream: a header and its answer may come
|
|
2
|
+
in one segment, a line may be split over several, and ``\\r\\n`` may be cut between
|
|
3
|
+
``\\r`` and ``\\n``. The client must read lines, not whatever one recv() returns."""
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
import socket
|
|
7
|
+
import threading
|
|
8
|
+
import time
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from yrcom.robot_connection import PersistentTcpClient
|
|
13
|
+
from yrcom.yaskawa_client import YaskawaClient
|
|
14
|
+
|
|
15
|
+
GREETING = b"OK: NX Information Server(Ver. W4.00)\r\n"
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def _serve(chunks_for_request, keep=0.3):
|
|
19
|
+
"""One-connection server: answers CONNECT with GREETING, then every request header
|
|
20
|
+
with the chunks `chunks_for_request(n)` returns (n = request number, from 0)."""
|
|
21
|
+
srv = socket.socket()
|
|
22
|
+
srv.bind(("127.0.0.1", 0))
|
|
23
|
+
srv.listen(1)
|
|
24
|
+
|
|
25
|
+
def run():
|
|
26
|
+
conn, _ = srv.accept()
|
|
27
|
+
f = conn.makefile("rb")
|
|
28
|
+
f.readline() # CONNECT Robot_access ...
|
|
29
|
+
conn.sendall(GREETING)
|
|
30
|
+
n = 0
|
|
31
|
+
while True:
|
|
32
|
+
if not f.readline(): # HOSTCTRL_REQUEST ...
|
|
33
|
+
break
|
|
34
|
+
for c in chunks_for_request(n):
|
|
35
|
+
conn.sendall(c)
|
|
36
|
+
time.sleep(0.005) # separate segments where intended
|
|
37
|
+
n += 1
|
|
38
|
+
time.sleep(keep)
|
|
39
|
+
conn.close()
|
|
40
|
+
|
|
41
|
+
threading.Thread(target=run, daemon=True).start()
|
|
42
|
+
return srv, srv.getsockname()[1]
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
REPLY = b"OK: RSTATS\r\n" + b"194,64\r"
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _bytewise(data: bytes) -> list[bytes]:
|
|
49
|
+
return [bytes([b]) for b in data]
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize("chunks", [
|
|
53
|
+
[b"OK: RSTATS\r\n", b"194,64\r"], # header and answer apart
|
|
54
|
+
[REPLY], # header and answer in one segment
|
|
55
|
+
_bytewise(REPLY), # one byte per segment
|
|
56
|
+
[b"OK: RSTATS\r", b"\n194,", b"64\r"], # \r\n cut between \r and \n
|
|
57
|
+
[b"OK: RSTATS\r\n194,64\r\n"], # answer terminated with \r\n
|
|
58
|
+
], ids=["apart", "together", "bytewise", "crlf-split", "answer-crlf"])
|
|
59
|
+
def test_rstats_survives_any_segmentation(chunks):
|
|
60
|
+
srv, port = _serve(lambda n: chunks)
|
|
61
|
+
try:
|
|
62
|
+
with YaskawaClient("127.0.0.1", port, timeout=2.0) as c:
|
|
63
|
+
c.send_start(keep_alive=-1)
|
|
64
|
+
assert c.read_rstats(decode=False, persistent=True) == [194, 64]
|
|
65
|
+
finally:
|
|
66
|
+
srv.close()
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def test_two_replies_in_one_segment_stay_apart():
|
|
70
|
+
# first request: both replies of the session arrive at once; the second request must
|
|
71
|
+
# get its own answer from the buffer, not wait for the network
|
|
72
|
+
both = b"OK: RSTATS\r\n1,2\r" + b"OK: RSTATS\r\n3,4\r"
|
|
73
|
+
srv, port = _serve(lambda n: [both] if n == 0 else [])
|
|
74
|
+
try:
|
|
75
|
+
with YaskawaClient("127.0.0.1", port, timeout=2.0) as c:
|
|
76
|
+
c.send_start(keep_alive=-1)
|
|
77
|
+
assert c.read_rstats(decode=False, persistent=True) == [1, 2]
|
|
78
|
+
assert c.read_rstats(decode=False, persistent=True) == [3, 4]
|
|
79
|
+
finally:
|
|
80
|
+
srv.close()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def test_ng_header_returns_without_waiting():
|
|
84
|
+
srv, port = _serve(lambda n: [b"NG: HOSTCTRL_REQUEST\r\n"])
|
|
85
|
+
try:
|
|
86
|
+
with YaskawaClient("127.0.0.1", port, timeout=2.0) as c:
|
|
87
|
+
c.send_start(keep_alive=-1)
|
|
88
|
+
t0 = time.monotonic()
|
|
89
|
+
assert c.send_command("RSTATS").startswith("NG")
|
|
90
|
+
assert time.monotonic() - t0 < 1.0 # no timeout wait for an answer line
|
|
91
|
+
finally:
|
|
92
|
+
srv.close()
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def test_persistent_connection_together_and_bytewise():
|
|
96
|
+
for chunks in ([REPLY], _bytewise(REPLY)):
|
|
97
|
+
srv, port = _serve(lambda n, ch=chunks: ch)
|
|
98
|
+
try:
|
|
99
|
+
tcp = PersistentTcpClient("127.0.0.1", port, timeout=2.0)
|
|
100
|
+
assert tcp.read_rstats(decode=False) == [194, 64]
|
|
101
|
+
tcp.close()
|
|
102
|
+
finally:
|
|
103
|
+
srv.close()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|