Skip to content

Commit f91bf3a

Browse files
authored
Merge pull request #235 from ottowayi/develop
1.2.10
2 parents 5af55af + 0c2c2f5 commit f91bf3a

File tree

5 files changed

+49
-32
lines changed

5 files changed

+49
-32
lines changed

docs/getting_started.rst

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,12 @@ There are three possible forms:
3939
.. note::
4040

4141
Both the IP Address and IP Address/Slot options are shortcuts, they will be replaced with the
42-
CIP path automatically in the LogixDriver and SLCDriver.
42+
CIP path automatically in the LogixDriver and SLCDriver, the CIPDriver will not modify the path.
43+
44+
.. note::
45+
46+
Path segments may be delimited by forward or back slashes or commas, e.g. ``10.10.30.100,bp,0``.
47+
To use a custom port, provide it following a colon with the IP address, e.g. ``10.20.30.100:4444``.
4348

4449
>>> from pycomm3 import CIPDriver
4550
>>> with CIPDriver('10.20.30.100') as drive:

docs/releases.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22
Release History
33
===============
44

5+
1.2.10
6+
======
7+
8+
CIPDriver
9+
---------
10+
- |:sparkles:| support port customization in the connection path
11+
- |:sparkles:| support comma delimiters in the connection path
12+
513
1.2.9
614
=====
715

pycomm3/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,5 @@
2222
# SOFTWARE.
2323
#
2424

25-
__version_info__ = (1, 2, 9)
25+
__version_info__ = (1, 2, 10)
2626
__version__ = ".".join(f"{x}" for x in __version_info__)

pycomm3/cip_driver.py

Lines changed: 17 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -112,23 +112,6 @@ class CIPDriver:
112112
_auto_slot_cip_path = False
113113

114114
def __init__(self, path: str, *args, **kwargs):
115-
"""
116-
:param path: CIP path to intended target
117-
118-
The path may contain 3 forms:
119-
120-
- IP Address Only (``10.20.30.100``) - Use for a ControlLogix PLC is in slot 0 or if connecting to a CompactLogix or Micro800 PLC.
121-
- IP Address/Slot (``10.20.30.100/1``) - (ControlLogix) if PLC is not in slot 0
122-
- CIP Routing Path (``1.2.3.4/backplane/2/enet/6.7.8.9/backplane/0``) - Use for more complex routing.
123-
124-
.. note::
125-
126-
Both the IP Address and IP Address/Slot options are shortcuts, they will be replaced with the
127-
CIP path automatically. The ``enet`` / ``backplane`` (or ``bp``) segments are symbols for the CIP routing
128-
port numbers and will be replaced with the correct value.
129-
130-
"""
131-
132115
self._sequence: cycle = cycle(65535, start=1)
133116
self._sock: Optional[Socket] = None
134117
self._session: int = 0
@@ -137,13 +120,13 @@ def __init__(self, path: str, *args, **kwargs):
137120
self._target_is_connected: bool = False
138121
self._info: Dict[str, Any] = {}
139122
self._cip_path = path
140-
ip, _path = parse_connection_path(path, self._auto_slot_cip_path)
123+
ip, port, _path = parse_connection_path(path, self._auto_slot_cip_path)
141124

142125
self._cfg = {
143126
"context": b"_pycomm_",
144127
"protocol version": b"\x01\x00",
145128
"rpi": 5000,
146-
"port": 44818,
129+
"port": port or 44818,
147130
"timeout": 10,
148131
"ip address": ip,
149132
"cip_path": _path,
@@ -605,15 +588,27 @@ def _receive(self):
605588
return reply
606589

607590

608-
def parse_connection_path(path: str, auto_slot: bool = False) -> Tuple[str, List[PortSegment]]:
591+
def parse_connection_path(path: str, auto_slot: bool = False) -> Tuple[str, Optional[int], List[PortSegment]]:
609592
"""
610593
Parses and validates the CIP path into the destination IP and
611594
sequence of port/link segments.
612595
Returns the IP and a list of PortSegments
613596
"""
614597
try:
615-
path = path.replace("\\", "/")
598+
path = path.replace("\\", "/").replace(",", "/")
616599
ip, *route = path.split("/")
600+
if ':' in ip:
601+
ip, port = ip.split(':')
602+
try:
603+
port = int(port)
604+
except Exception as err:
605+
raise RequestError(f'Invalid port: {port}')
606+
else:
607+
if 0 > port >= 65535:
608+
raise RequestError(f'Invalid port: {port}')
609+
610+
else:
611+
port = None
617612

618613
try:
619614
ipaddress.ip_address(ip)
@@ -627,7 +622,7 @@ def parse_connection_path(path: str, auto_slot: bool = False) -> Tuple[str, List
627622
except Exception as err:
628623
raise RequestError(f"Failed to parse connection path: {path}") from err
629624
else:
630-
return ip, _path
625+
return ip, port, _path
631626

632627

633628
def parse_cip_route(path: Union[str, List[str]], auto_slot: bool = False) -> List[PortSegment]:

tests/offline/test_cip_driver.py

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,21 +30,19 @@
3030
CONNECT_PATH = "192.168.1.100"
3131

3232

33-
_simple_path = (
34-
"192.168.1.100",
35-
[
36-
PortSegment("bp", 0),
37-
],
38-
)
33+
_simple_path = ("192.168.1.100", None, [PortSegment("bp", 0)])
3934
_simple_paths = [
4035
"192.168.1.100/bp/0",
4136
"192.168.1.100/backplane/0",
4237
r"192.168.1.100\bp\0",
4338
r"192.168.1.100\backplane\0",
39+
"192.168.1.100,bp,0",
40+
"192.168.1.100,1,0",
4441
]
4542

4643
_route_path = (
4744
"192.168.1.100",
45+
None,
4846
[
4947
PortSegment(port="backplane", link_address=1),
5048
PortSegment(port="enet", link_address="10.11.12.13"),
@@ -54,12 +52,15 @@
5452
_route_paths = [
5553
"192.168.1.100/backplane/1/enet/10.11.12.13/bp/0",
5654
r"192.168.1.100\backplane\1\enet\10.11.12.13\bp\0",
55+
"192.168.1.100,backplane,1,enet,10.11.12.13,bp,0",
5756
]
5857

5958
path_tests = [
6059
*[(p, _simple_path) for p in _simple_paths],
6160
*[(p, _route_path) for p in _route_paths],
62-
("192.168.1.100", ("192.168.1.100", [])),
61+
("192.168.1.100", ("192.168.1.100", None, [])),
62+
('192.168.1.100:123', ('192.168.1.100', 123, [])),
63+
('192.168.1.100:123/bp,0', ('192.168.1.100', 123, _simple_path[-1]))
6364
]
6465

6566

@@ -91,8 +92,16 @@ def test_plc_path_auto_slot(path, expected_output):
9192
"1.1.1.300",
9293
"192.168.1.100/Z",
9394
"bp/0",
95+
"192.168.1.100/-1",
9496
"192.168.1.100/backplan/1",
9597
"192.168.1.100/backplane/1/10.11.12.13/bp/0",
98+
"192.168.1.100//bp/1",
99+
"192.168.1.100/",
100+
"192.168.1.100,bp,1,0",
101+
"192.168.1.100,",
102+
"192.168.1.100:abc",
103+
"192.168.1.100:/bp/0",
104+
"192.168.100:-123",
96105
]
97106

98107

@@ -101,7 +110,7 @@ def test_bad_plc_paths(path):
101110
with pytest.raises(
102111
(DataError, RequestError),
103112
):
104-
ip, segments = parse_connection_path(path)
113+
ip, port, segments = parse_connection_path(path)
105114
PADDED_EPATH.encode(segments, length=True)
106115

107116

0 commit comments

Comments
 (0)