slmp-connect-python 0.8.0__tar.gz → 1.0.0__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.
Files changed (24) hide show
  1. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/MANIFEST.in +2 -2
  2. {slmp_connect_python-0.8.0/slmp_connect_python.egg-info → slmp_connect_python-1.0.0}/PKG-INFO +3 -8
  3. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/README.md +30 -35
  4. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/pyproject.toml +111 -112
  5. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/__init__.py +206 -206
  6. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/async_client.py +3 -2
  7. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/cli.py +51 -49
  8. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/client.py +4 -3
  9. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/core.py +18 -0
  10. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/utils.py +9 -3
  11. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0/slmp_connect_python.egg-info}/PKG-INFO +3 -8
  12. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/LICENSE +0 -0
  13. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/setup.cfg +0 -0
  14. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/_operations.py +0 -0
  15. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/constants.py +0 -0
  16. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/device_ranges.py +0 -0
  17. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/error_codes.py +0 -0
  18. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/errors.py +0 -0
  19. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp/py.typed +0 -0
  20. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp_connect_python.egg-info/SOURCES.txt +0 -0
  21. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp_connect_python.egg-info/dependency_links.txt +0 -0
  22. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp_connect_python.egg-info/entry_points.txt +0 -0
  23. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp_connect_python.egg-info/requires.txt +0 -0
  24. {slmp_connect_python-0.8.0 → slmp_connect_python-1.0.0}/slmp_connect_python.egg-info/top_level.txt +0 -0
@@ -1,2 +1,2 @@
1
- prune tests
2
- prune internal_docs
1
+ prune tests
2
+ prune internal_docs
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slmp-connect-python
3
- Version: 0.8.0
3
+ Version: 1.0.0
4
4
  Summary: SLMP Connect Python: client library for MELSEC SLMP binary communication
5
5
  Author: fa-yoshinobu
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/fa-yoshinobu/plc-comm-slmp-python
8
8
  Project-URL: Repository, https://github.com/fa-yoshinobu/plc-comm-slmp-python
9
9
  Project-URL: Issues, https://github.com/fa-yoshinobu/plc-comm-slmp-python/issues
10
- Keywords: slmp,melsec,mitsubishi,plc,3e,4e,binary
11
- Classifier: Development Status :: 3 - Alpha
10
+ Keywords: slmp,melsec,plc,3e,4e,binary
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.10
@@ -33,14 +33,9 @@ Requires-Dist: twine>=5.1; extra == "dev"
33
33
  Dynamic: license-file
34
34
 
35
35
  [![CI](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml)
36
- [![Documentation](https://img.shields.io/badge/docs-GitHub_Pages-blue.svg)](https://fa-yoshinobu.github.io/plc-comm-docs-site/slmp/python/GETTING_STARTED/)
37
36
  [![PyPI](https://img.shields.io/pypi/v/slmp-connect-python.svg)](https://pypi.org/project/slmp-connect-python/)
38
37
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
39
38
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
40
- [![Static Analysis: Ruff](https://img.shields.io/badge/Lint-Ruff-black.svg)](https://github.com/astral-sh/ruff)
41
- [![Release](https://img.shields.io/github/v/release/fa-yoshinobu/plc-comm-slmp-python?label=release)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/releases/latest)
42
- [![Python](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)](https://www.python.org/)
43
- [![MkDocs](https://img.shields.io/badge/MkDocs-526CFE?logo=materialformkdocs&logoColor=white)](https://www.mkdocs.org/)
44
39
 
45
40
  # MELSEC SLMP for Python
46
41
 
@@ -1,25 +1,20 @@
1
1
  [![CI](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml)
2
- [![Documentation](https://img.shields.io/badge/docs-GitHub_Pages-blue.svg)](https://fa-yoshinobu.github.io/plc-comm-docs-site/slmp/python/GETTING_STARTED/)
3
2
  [![PyPI](https://img.shields.io/pypi/v/slmp-connect-python.svg)](https://pypi.org/project/slmp-connect-python/)
4
3
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
5
4
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
6
- [![Static Analysis: Ruff](https://img.shields.io/badge/Lint-Ruff-black.svg)](https://github.com/astral-sh/ruff)
7
- [![Release](https://img.shields.io/github/v/release/fa-yoshinobu/plc-comm-slmp-python?label=release)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/releases/latest)
8
- [![Python](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)](https://www.python.org/)
9
- [![MkDocs](https://img.shields.io/badge/MkDocs-526CFE?logo=materialformkdocs&logoColor=white)](https://www.mkdocs.org/)
10
-
11
- # MELSEC SLMP for Python
12
-
13
- Python library for MELSEC SLMP (Binary 3E/4E) PLC communication.
14
-
15
- ## Supported PLC profiles
16
-
17
- The maintained profile table is in [PLC profiles](docsrc/user/PROFILES.md). Choose one exact canonical PLC profile from that table.
18
-
19
- ## Supported device types
20
-
21
- The maintained device and range tables are in [Supported registers](docsrc/user/SUPPORTED_REGISTERS.md). Use that page for supported device families, address syntax, and profile-specific notes.
22
-
5
+
6
+ # MELSEC SLMP for Python
7
+
8
+ Python library for MELSEC SLMP (Binary 3E/4E) PLC communication.
9
+
10
+ ## Supported PLC profiles
11
+
12
+ The maintained profile table is in [PLC profiles](docsrc/user/PROFILES.md). Choose one exact canonical PLC profile from that table.
13
+
14
+ ## Supported device types
15
+
16
+ The maintained device and range tables are in [Supported registers](docsrc/user/SUPPORTED_REGISTERS.md). Use that page for supported device families, address syntax, and profile-specific notes.
17
+
23
18
  ## Installation
24
19
 
25
20
  ```bash
@@ -43,24 +38,24 @@ asyncio.run(main())
43
38
 
44
39
  ## Documentation
45
40
 
46
- | Page | Use it for |
47
- | --- | --- |
48
- | [Full documentation site](https://fa-yoshinobu.github.io/plc-comm-docs-site/) | Unified docs for all PLC communication libraries. |
49
- | [Getting started](docsrc/user/GETTING_STARTED.md) | Install the package, connect to your PLC, and run your first SLMP read/write. |
50
- | [Usage guide](docsrc/user/USAGE_GUIDE.md) | Use the high-level API and common SLMP workflows. |
51
- | [Supported registers](docsrc/user/SUPPORTED_REGISTERS.md) | Check supported device families, address syntax, and numbering rules. |
52
- | [PLC profiles](docsrc/user/PROFILES.md) | Choose the canonical MELSEC profile and frame behavior. |
53
- | [Examples](samples/README.md) | Run maintained Python samples. |
41
+ | Page | Use it for |
42
+ | --- | --- |
43
+ | [Full documentation site](https://fa-yoshinobu.github.io/plc-comm-docs-site/) | Unified docs for all PLC communication libraries. |
44
+ | [Getting started](docsrc/user/GETTING_STARTED.md) | Install the package, connect to your PLC, and run your first SLMP read/write. |
45
+ | [Usage guide](docsrc/user/USAGE_GUIDE.md) | Use the high-level API and common SLMP workflows. |
46
+ | [Supported registers](docsrc/user/SUPPORTED_REGISTERS.md) | Check supported device families, address syntax, and numbering rules. |
47
+ | [PLC profiles](docsrc/user/PROFILES.md) | Choose the canonical MELSEC profile and frame behavior. |
48
+ | [Examples](samples/README.md) | Run maintained Python samples. |
49
+
50
+ ## Hardware verified
54
51
 
55
- ## Hardware verified
56
-
57
- Live-device verification is maintained in [Latest communication verification](docsrc/user/LATEST_COMMUNICATION_VERIFICATION.md).
58
- See that page for verified PLC models, transports, dates, limitations, and retained validation notes.
52
+ Live-device verification is maintained in [Latest communication verification](docsrc/user/LATEST_COMMUNICATION_VERIFICATION.md).
53
+ See that page for verified PLC models, transports, dates, limitations, and retained validation notes.
59
54
 
60
55
  ## License and registry
61
56
 
62
- | Item | Value |
63
- | --- | --- |
64
- | License | [MIT](LICENSE) |
65
- | Registry | [PyPI](https://pypi.org/project/slmp-connect-python/) |
66
- | Package | `slmp-connect-python` |
57
+ | Item | Value |
58
+ | --- | --- |
59
+ | License | [MIT](LICENSE) |
60
+ | Registry | [PyPI](https://pypi.org/project/slmp-connect-python/) |
61
+ | Package | `slmp-connect-python` |
@@ -1,112 +1,111 @@
1
- [build-system]
2
- requires = ["setuptools>=69", "wheel"]
3
- build-backend = "setuptools.build_meta"
4
-
5
- [project]
6
- name = "slmp-connect-python"
7
- version = "0.8.0"
8
- description = "SLMP Connect Python: client library for MELSEC SLMP binary communication"
9
- readme = "README.md"
10
- requires-python = ">=3.10"
11
- dependencies = []
12
- authors = [{ name = "fa-yoshinobu" }]
13
- license = "MIT"
14
-
15
- license-files = ["LICENSE"]
16
- keywords = ["slmp", "melsec", "mitsubishi", "plc", "3e", "4e", "binary"]
17
- classifiers = [
18
- "Development Status :: 3 - Alpha",
19
- "Intended Audience :: Developers",
20
- "Programming Language :: Python :: 3",
21
- "Programming Language :: Python :: 3.10",
22
- "Programming Language :: Python :: 3.11",
23
- "Programming Language :: Python :: 3.12",
24
- "Programming Language :: Python :: 3.13",
25
- "Programming Language :: Python :: 3.14",
26
- "Topic :: Software Development :: Libraries :: Python Modules",
27
- "Topic :: System :: Networking",
28
- ]
29
-
30
- [project.urls]
31
- Homepage = "https://github.com/fa-yoshinobu/plc-comm-slmp-python"
32
- Repository = "https://github.com/fa-yoshinobu/plc-comm-slmp-python"
33
- Issues = "https://github.com/fa-yoshinobu/plc-comm-slmp-python/issues"
34
-
35
- [project.optional-dependencies]
36
- dev = [
37
- "build>=1.2",
38
- "mypy>=1.10",
39
- "pre-commit>=3.7",
40
- "pytest>=8.0",
41
- "pytest-asyncio>=0.23",
42
- "pytest-cov>=5.0",
43
- "ruff>=0.6",
44
- "twine>=5.1",
45
- ]
46
-
47
- [project.scripts]
48
- slmp-connection-check = "slmp.cli:connection_check_main"
49
- slmp-device-range-probe = "slmp.cli:device_range_probe_main"
50
- slmp-register-boundary-probe = "slmp.cli:register_boundary_probe_main"
51
- slmp-other-station-check = "slmp.cli:other_station_check_main"
52
- slmp-open-items-recheck = "slmp.cli:open_items_recheck_main"
53
- slmp-g-hg-extended-device-recheck = "slmp.cli:g_hg_extended_device_recheck_main"
54
- slmp-g-hg-extended-device-coverage = "slmp.cli:g_hg_extended_device_coverage_main"
55
- slmp-extended-device-recheck = "slmp.cli:extended_device_recheck_main"
56
- slmp-pending-live-verification = "slmp.cli:pending_live_verification_main"
57
- slmp-init-model-docs = "slmp.cli:init_model_docs_main"
58
- slmp-manual-label-verification = "slmp.cli:manual_label_verification_main"
59
- slmp-read-soak = "slmp.cli:read_soak_main"
60
- slmp-mixed-read-load = "slmp.cli:mixed_read_load_main"
61
- slmp-tcp-concurrency = "slmp.cli:tcp_concurrency_main"
62
- slmp-regression-suite = "slmp.cli:regression_suite_main"
63
-
64
- [tool.setuptools.packages.find]
65
- include = ["slmp*"]
66
- exclude = ["tests*", "scripts*", "internal_docs*"]
67
-
68
- [tool.setuptools]
69
- include-package-data = true
70
- exclude-package-data = { "*" = ["TODO.md", ".pre-commit-config.yaml"] }
71
-
72
- [tool.setuptools.package-data]
73
- slmp = ["py.typed"]
74
-
75
- [tool.ruff]
76
- line-length = 120
77
- target-version = "py310"
78
-
79
- [tool.ruff.lint]
80
- select = ["E", "F", "I", "B", "UP"]
81
-
82
- [tool.ruff.lint.per-file-ignores]
83
- "slmp/error_codes.py" = ["E501"]
84
-
85
- [tool.ruff.lint.isort]
86
- known-first-party = ["slmp"]
87
-
88
- [tool.mypy]
89
- files = ["slmp"]
90
- warn_unused_configs = true
91
- disallow_untyped_defs = true
92
- check_untyped_defs = true
93
- disallow_incomplete_defs = true
94
- no_implicit_optional = true
95
- warn_redundant_casts = true
96
- warn_unused_ignores = true
97
- warn_return_any = true
98
- strict_equality = true
99
-
100
- [tool.coverage.run]
101
- source = ["slmp"]
102
- branch = true
103
-
104
- [tool.pytest.ini_options]
105
- testpaths = ["tests"]
106
- asyncio_mode = "auto"
107
-
108
- [tool.coverage.report]
109
- show_missing = true
110
-
111
-
112
-
1
+ [build-system]
2
+ requires = ["setuptools>=69", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "slmp-connect-python"
7
+ version = "1.0.0"
8
+ description = "SLMP Connect Python: client library for MELSEC SLMP binary communication"
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ dependencies = []
12
+ authors = [{ name = "fa-yoshinobu" }]
13
+ license = "MIT"
14
+
15
+ license-files = ["LICENSE"]
16
+ keywords = ["slmp", "melsec", "plc", "3e", "4e", "binary"]
17
+ classifiers = [
18
+ "Development Status :: 5 - Production/Stable",
19
+ "Intended Audience :: Developers",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ "Topic :: Software Development :: Libraries :: Python Modules",
27
+ "Topic :: System :: Networking",
28
+ ]
29
+
30
+ [project.urls]
31
+ Homepage = "https://github.com/fa-yoshinobu/plc-comm-slmp-python"
32
+ Repository = "https://github.com/fa-yoshinobu/plc-comm-slmp-python"
33
+ Issues = "https://github.com/fa-yoshinobu/plc-comm-slmp-python/issues"
34
+
35
+ [project.optional-dependencies]
36
+ dev = [
37
+ "build>=1.2",
38
+ "mypy>=1.10",
39
+ "pre-commit>=3.7",
40
+ "pytest>=8.0",
41
+ "pytest-asyncio>=0.23",
42
+ "pytest-cov>=5.0",
43
+ "ruff>=0.6",
44
+ "twine>=5.1",
45
+ ]
46
+
47
+ [project.scripts]
48
+ slmp-connection-check = "slmp.cli:connection_check_main"
49
+ slmp-device-range-probe = "slmp.cli:device_range_probe_main"
50
+ slmp-register-boundary-probe = "slmp.cli:register_boundary_probe_main"
51
+ slmp-other-station-check = "slmp.cli:other_station_check_main"
52
+ slmp-open-items-recheck = "slmp.cli:open_items_recheck_main"
53
+ slmp-g-hg-extended-device-recheck = "slmp.cli:g_hg_extended_device_recheck_main"
54
+ slmp-g-hg-extended-device-coverage = "slmp.cli:g_hg_extended_device_coverage_main"
55
+ slmp-extended-device-recheck = "slmp.cli:extended_device_recheck_main"
56
+ slmp-pending-live-verification = "slmp.cli:pending_live_verification_main"
57
+ slmp-init-model-docs = "slmp.cli:init_model_docs_main"
58
+ slmp-manual-label-verification = "slmp.cli:manual_label_verification_main"
59
+ slmp-read-soak = "slmp.cli:read_soak_main"
60
+ slmp-mixed-read-load = "slmp.cli:mixed_read_load_main"
61
+ slmp-tcp-concurrency = "slmp.cli:tcp_concurrency_main"
62
+ slmp-regression-suite = "slmp.cli:regression_suite_main"
63
+
64
+ [tool.setuptools.packages.find]
65
+ include = ["slmp*"]
66
+ exclude = ["tests*", "scripts*", "internal_docs*"]
67
+
68
+ [tool.setuptools]
69
+ include-package-data = true
70
+ exclude-package-data = { "*" = ["TODO.md", ".pre-commit-config.yaml"] }
71
+
72
+ [tool.setuptools.package-data]
73
+ slmp = ["py.typed"]
74
+
75
+ [tool.ruff]
76
+ line-length = 120
77
+ target-version = "py310"
78
+
79
+ [tool.ruff.lint]
80
+ select = ["E", "F", "I", "B", "UP"]
81
+
82
+ [tool.ruff.lint.per-file-ignores]
83
+ "slmp/error_codes.py" = ["E501"]
84
+
85
+ [tool.ruff.lint.isort]
86
+ known-first-party = ["slmp"]
87
+
88
+ [tool.mypy]
89
+ files = ["slmp"]
90
+ warn_unused_configs = true
91
+ disallow_untyped_defs = true
92
+ check_untyped_defs = true
93
+ disallow_incomplete_defs = true
94
+ no_implicit_optional = true
95
+ warn_redundant_casts = true
96
+ warn_unused_ignores = true
97
+ warn_return_any = true
98
+ strict_equality = true
99
+
100
+ [tool.coverage.run]
101
+ source = ["slmp"]
102
+ branch = true
103
+
104
+ [tool.pytest.ini_options]
105
+ testpaths = ["tests"]
106
+ asyncio_mode = "auto"
107
+
108
+ [tool.coverage.report]
109
+ show_missing = true
110
+
111
+
@@ -1,206 +1,206 @@
1
- """SLMP client library with high-level helpers as the recommended user surface.
2
-
3
- The primary user-facing entry points are:
4
-
5
- - ``read_typed`` / ``write_typed``
6
- - ``read_words_single_request`` / ``read_dwords_single_request``
7
- - ``read_words_chunked`` / ``read_dwords_chunked``
8
- - ``write_bit_in_word``
9
- - ``read_named`` / ``write_named``
10
- - ``poll``
11
- """
12
-
13
- __version__ = "0.8.0"
14
-
15
- from .async_client import AsyncSlmpClient
16
- from .client import SlmpClient
17
- from .constants import Command, FrameType, ModuleIONo, PLCSeries
18
- from .core import (
19
- DEVICE_CODES,
20
- BlockReadResult,
21
- CpuOperationState,
22
- CpuOperationStatus,
23
- DeviceBlockResult,
24
- DeviceRef,
25
- ExtensionSpec,
26
- LabelArrayReadPoint,
27
- LabelArrayReadResult,
28
- LabelArrayWritePoint,
29
- LabelRandomReadResult,
30
- LabelRandomWritePoint,
31
- LongTimerResult,
32
- MonitorResult,
33
- RandomReadResult,
34
- SlmpPlcProfile,
35
- SlmpResponse,
36
- SlmpTarget,
37
- SlmpTraceFrame,
38
- TypeNameInfo,
39
- decode_cpu_operation_state,
40
- parse_device,
41
- parse_extended_device,
42
- )
43
- from .device_ranges import (
44
- SlmpDeviceRangeCatalog,
45
- SlmpDeviceRangeCategory,
46
- SlmpDeviceRangeEntry,
47
- SlmpDeviceRangeNotation,
48
- build_device_range_catalog_for_plc_profile,
49
- normalize_plc_profile,
50
- plc_profile_label,
51
- read_device_range_catalog_for_plc_profile,
52
- read_device_range_catalog_for_plc_profile_sync,
53
- )
54
- from .error_codes import (
55
- SlmpEndCodeLanguage,
56
- get_end_code_message,
57
- get_end_code_name,
58
- is_remote_password_end_code,
59
- )
60
- from .errors import (
61
- SlmpBoundaryBehaviorWarning,
62
- SlmpError,
63
- SlmpPracticalPathWarning,
64
- SlmpUnsupportedDeviceError,
65
- )
66
- from .utils import (
67
- QueuedAsyncSlmpClient,
68
- SlmpAddress,
69
- SlmpConnectionOptions,
70
- format_address,
71
- normalize_address,
72
- open_and_connect,
73
- open_and_connect_sync,
74
- parse_address,
75
- poll,
76
- poll_sync,
77
- read_bits,
78
- read_bits_sync,
79
- read_dwords,
80
- read_dwords_chunked,
81
- read_dwords_chunked_sync,
82
- read_dwords_single_request,
83
- read_dwords_single_request_sync,
84
- read_dwords_sync,
85
- read_named,
86
- read_named_sync,
87
- read_typed,
88
- read_typed_sync,
89
- read_words,
90
- read_words_chunked,
91
- read_words_chunked_sync,
92
- read_words_single_request,
93
- read_words_single_request_sync,
94
- read_words_sync,
95
- try_parse_address,
96
- write_bit_in_word,
97
- write_bit_in_word_sync,
98
- write_bits,
99
- write_bits_sync,
100
- write_dwords_chunked,
101
- write_dwords_chunked_sync,
102
- write_dwords_single_request,
103
- write_dwords_single_request_sync,
104
- write_named,
105
- write_named_sync,
106
- write_typed,
107
- write_typed_sync,
108
- write_words_chunked,
109
- write_words_chunked_sync,
110
- write_words_single_request,
111
- write_words_single_request_sync,
112
- )
113
-
114
- __all__ = [
115
- "AsyncSlmpClient",
116
- "BlockReadResult",
117
- "Command",
118
- "CpuOperationState",
119
- "CpuOperationStatus",
120
- "DEVICE_CODES",
121
- "DeviceBlockResult",
122
- "DeviceRef",
123
- "ExtensionSpec",
124
- "FrameType",
125
- "LabelArrayReadPoint",
126
- "LabelArrayReadResult",
127
- "LabelArrayWritePoint",
128
- "LabelRandomReadResult",
129
- "LabelRandomWritePoint",
130
- "LongTimerResult",
131
- "ModuleIONo",
132
- "MonitorResult",
133
- "PLCSeries",
134
- "QueuedAsyncSlmpClient",
135
- "SlmpConnectionOptions",
136
- "SlmpEndCodeLanguage",
137
- "SlmpAddress",
138
- "RandomReadResult",
139
- "SlmpClient",
140
- "SlmpBoundaryBehaviorWarning",
141
- "SlmpError",
142
- "SlmpPlcProfile",
143
- "SlmpPracticalPathWarning",
144
- "SlmpUnsupportedDeviceError",
145
- "SlmpResponse",
146
- "SlmpTarget",
147
- "SlmpTraceFrame",
148
- "TypeNameInfo",
149
- "decode_cpu_operation_state",
150
- "format_address",
151
- "get_end_code_message",
152
- "get_end_code_name",
153
- "is_remote_password_end_code",
154
- "SlmpDeviceRangeCatalog",
155
- "SlmpDeviceRangeCategory",
156
- "SlmpDeviceRangeEntry",
157
- "SlmpDeviceRangeNotation",
158
- "normalize_address",
159
- "normalize_plc_profile",
160
- "open_and_connect",
161
- "open_and_connect_sync",
162
- "parse_address",
163
- "parse_extended_device",
164
- "parse_device",
165
- "poll",
166
- "poll_sync",
167
- "read_bits",
168
- "read_bits_sync",
169
- "read_dwords",
170
- "read_dwords_chunked",
171
- "read_dwords_chunked_sync",
172
- "read_dwords_single_request",
173
- "read_dwords_single_request_sync",
174
- "read_dwords_sync",
175
- "read_named",
176
- "read_named_sync",
177
- "read_typed",
178
- "read_typed_sync",
179
- "read_device_range_catalog_for_plc_profile",
180
- "read_device_range_catalog_for_plc_profile_sync",
181
- "read_words",
182
- "read_words_chunked",
183
- "read_words_chunked_sync",
184
- "read_words_single_request",
185
- "read_words_single_request_sync",
186
- "read_words_sync",
187
- "write_bit_in_word",
188
- "write_bit_in_word_sync",
189
- "write_bits",
190
- "write_bits_sync",
191
- "write_dwords_chunked",
192
- "write_dwords_chunked_sync",
193
- "write_dwords_single_request",
194
- "write_dwords_single_request_sync",
195
- "write_named",
196
- "write_named_sync",
197
- "write_typed",
198
- "write_typed_sync",
199
- "write_words_chunked",
200
- "write_words_chunked_sync",
201
- "write_words_single_request",
202
- "write_words_single_request_sync",
203
- "try_parse_address",
204
- "build_device_range_catalog_for_plc_profile",
205
- "plc_profile_label",
206
- ]
1
+ """SLMP client library with high-level helpers as the recommended user surface.
2
+
3
+ The primary user-facing entry points are:
4
+
5
+ - ``read_typed`` / ``write_typed``
6
+ - ``read_words_single_request`` / ``read_dwords_single_request``
7
+ - ``read_words_chunked`` / ``read_dwords_chunked``
8
+ - ``write_bit_in_word``
9
+ - ``read_named`` / ``write_named``
10
+ - ``poll``
11
+ """
12
+
13
+ __version__ = "0.8.0"
14
+
15
+ from .async_client import AsyncSlmpClient
16
+ from .client import SlmpClient
17
+ from .constants import Command, FrameType, ModuleIONo, PLCSeries
18
+ from .core import (
19
+ DEVICE_CODES,
20
+ BlockReadResult,
21
+ CpuOperationState,
22
+ CpuOperationStatus,
23
+ DeviceBlockResult,
24
+ DeviceRef,
25
+ ExtensionSpec,
26
+ LabelArrayReadPoint,
27
+ LabelArrayReadResult,
28
+ LabelArrayWritePoint,
29
+ LabelRandomReadResult,
30
+ LabelRandomWritePoint,
31
+ LongTimerResult,
32
+ MonitorResult,
33
+ RandomReadResult,
34
+ SlmpPlcProfile,
35
+ SlmpResponse,
36
+ SlmpTarget,
37
+ SlmpTraceFrame,
38
+ TypeNameInfo,
39
+ decode_cpu_operation_state,
40
+ parse_device,
41
+ parse_extended_device,
42
+ )
43
+ from .device_ranges import (
44
+ SlmpDeviceRangeCatalog,
45
+ SlmpDeviceRangeCategory,
46
+ SlmpDeviceRangeEntry,
47
+ SlmpDeviceRangeNotation,
48
+ build_device_range_catalog_for_plc_profile,
49
+ normalize_plc_profile,
50
+ plc_profile_label,
51
+ read_device_range_catalog_for_plc_profile,
52
+ read_device_range_catalog_for_plc_profile_sync,
53
+ )
54
+ from .error_codes import (
55
+ SlmpEndCodeLanguage,
56
+ get_end_code_message,
57
+ get_end_code_name,
58
+ is_remote_password_end_code,
59
+ )
60
+ from .errors import (
61
+ SlmpBoundaryBehaviorWarning,
62
+ SlmpError,
63
+ SlmpPracticalPathWarning,
64
+ SlmpUnsupportedDeviceError,
65
+ )
66
+ from .utils import (
67
+ QueuedAsyncSlmpClient,
68
+ SlmpAddress,
69
+ SlmpConnectionOptions,
70
+ format_address,
71
+ normalize_address,
72
+ open_and_connect,
73
+ open_and_connect_sync,
74
+ parse_address,
75
+ poll,
76
+ poll_sync,
77
+ read_bits,
78
+ read_bits_sync,
79
+ read_dwords,
80
+ read_dwords_chunked,
81
+ read_dwords_chunked_sync,
82
+ read_dwords_single_request,
83
+ read_dwords_single_request_sync,
84
+ read_dwords_sync,
85
+ read_named,
86
+ read_named_sync,
87
+ read_typed,
88
+ read_typed_sync,
89
+ read_words,
90
+ read_words_chunked,
91
+ read_words_chunked_sync,
92
+ read_words_single_request,
93
+ read_words_single_request_sync,
94
+ read_words_sync,
95
+ try_parse_address,
96
+ write_bit_in_word,
97
+ write_bit_in_word_sync,
98
+ write_bits,
99
+ write_bits_sync,
100
+ write_dwords_chunked,
101
+ write_dwords_chunked_sync,
102
+ write_dwords_single_request,
103
+ write_dwords_single_request_sync,
104
+ write_named,
105
+ write_named_sync,
106
+ write_typed,
107
+ write_typed_sync,
108
+ write_words_chunked,
109
+ write_words_chunked_sync,
110
+ write_words_single_request,
111
+ write_words_single_request_sync,
112
+ )
113
+
114
+ __all__ = [
115
+ "AsyncSlmpClient",
116
+ "BlockReadResult",
117
+ "Command",
118
+ "CpuOperationState",
119
+ "CpuOperationStatus",
120
+ "DEVICE_CODES",
121
+ "DeviceBlockResult",
122
+ "DeviceRef",
123
+ "ExtensionSpec",
124
+ "FrameType",
125
+ "LabelArrayReadPoint",
126
+ "LabelArrayReadResult",
127
+ "LabelArrayWritePoint",
128
+ "LabelRandomReadResult",
129
+ "LabelRandomWritePoint",
130
+ "LongTimerResult",
131
+ "ModuleIONo",
132
+ "MonitorResult",
133
+ "PLCSeries",
134
+ "QueuedAsyncSlmpClient",
135
+ "SlmpConnectionOptions",
136
+ "SlmpEndCodeLanguage",
137
+ "SlmpAddress",
138
+ "RandomReadResult",
139
+ "SlmpClient",
140
+ "SlmpBoundaryBehaviorWarning",
141
+ "SlmpError",
142
+ "SlmpPlcProfile",
143
+ "SlmpPracticalPathWarning",
144
+ "SlmpUnsupportedDeviceError",
145
+ "SlmpResponse",
146
+ "SlmpTarget",
147
+ "SlmpTraceFrame",
148
+ "TypeNameInfo",
149
+ "decode_cpu_operation_state",
150
+ "format_address",
151
+ "get_end_code_message",
152
+ "get_end_code_name",
153
+ "is_remote_password_end_code",
154
+ "SlmpDeviceRangeCatalog",
155
+ "SlmpDeviceRangeCategory",
156
+ "SlmpDeviceRangeEntry",
157
+ "SlmpDeviceRangeNotation",
158
+ "normalize_address",
159
+ "normalize_plc_profile",
160
+ "open_and_connect",
161
+ "open_and_connect_sync",
162
+ "parse_address",
163
+ "parse_extended_device",
164
+ "parse_device",
165
+ "poll",
166
+ "poll_sync",
167
+ "read_bits",
168
+ "read_bits_sync",
169
+ "read_dwords",
170
+ "read_dwords_chunked",
171
+ "read_dwords_chunked_sync",
172
+ "read_dwords_single_request",
173
+ "read_dwords_single_request_sync",
174
+ "read_dwords_sync",
175
+ "read_named",
176
+ "read_named_sync",
177
+ "read_typed",
178
+ "read_typed_sync",
179
+ "read_device_range_catalog_for_plc_profile",
180
+ "read_device_range_catalog_for_plc_profile_sync",
181
+ "read_words",
182
+ "read_words_chunked",
183
+ "read_words_chunked_sync",
184
+ "read_words_single_request",
185
+ "read_words_single_request_sync",
186
+ "read_words_sync",
187
+ "write_bit_in_word",
188
+ "write_bit_in_word_sync",
189
+ "write_bits",
190
+ "write_bits_sync",
191
+ "write_dwords_chunked",
192
+ "write_dwords_chunked_sync",
193
+ "write_dwords_single_request",
194
+ "write_dwords_single_request_sync",
195
+ "write_named",
196
+ "write_named_sync",
197
+ "write_typed",
198
+ "write_typed_sync",
199
+ "write_words_chunked",
200
+ "write_words_chunked_sync",
201
+ "write_words_single_request",
202
+ "write_words_single_request_sync",
203
+ "try_parse_address",
204
+ "build_device_range_catalog_for_plc_profile",
205
+ "plc_profile_label",
206
+ ]
@@ -30,6 +30,7 @@ from .core import (
30
30
  _raise_response_error,
31
31
  _require_explicit_plc_profile_for_xy,
32
32
  _resolve_connection_profile,
33
+ _resolve_port,
33
34
  build_device_modification_flags,
34
35
  decode_cpu_operation_state,
35
36
  decode_response,
@@ -72,7 +73,7 @@ class AsyncSlmpClient:
72
73
  def __init__(
73
74
  self,
74
75
  host: str,
75
- port: int = 5000,
76
+ port: int | None = None,
76
77
  *,
77
78
  transport: str = "tcp",
78
79
  timeout: float = 3.0,
@@ -93,10 +94,10 @@ class AsyncSlmpClient:
93
94
  explicit family.
94
95
  """
95
96
  self.host = host
96
- self.port = port
97
97
  self.transport_type = transport.lower()
98
98
  if self.transport_type not in {"tcp", "udp"}:
99
99
  raise ValueError("transport must be 'tcp' or 'udp'")
100
+ self.port = _resolve_port(port, self.transport_type)
100
101
  self.timeout = timeout
101
102
  if not _allow_manual_profile:
102
103
  if plc_profile is None:
@@ -41,6 +41,7 @@ from .core import (
41
41
  SlmpTarget,
42
42
  SlmpTraceFrame,
43
43
  TypeNameInfo,
44
+ _resolve_port,
44
45
  decode_device_words,
45
46
  encode_device_spec,
46
47
  pack_bit_values,
@@ -58,7 +59,7 @@ class SlmpClient(_StandardSlmpClient):
58
59
  def __init__(
59
60
  self,
60
61
  host: str,
61
- port: int = 5000,
62
+ port: int | None = None,
62
63
  *,
63
64
  transport: str = "tcp",
64
65
  timeout: float = 3.0,
@@ -915,18 +916,18 @@ def _render_compatibility_matrix_markdown(
915
916
 
916
917
  def _default_regression_help_scripts() -> tuple[str, ...]:
917
918
  return (
918
- "slmp_connection_check.py",
919
- "slmp_device_range_probe.py",
920
- "slmp_register_boundary_probe.py",
921
- "slmp_init_model_docs.py",
922
- "slmp_other_station_check.py",
923
- "slmp_open_items_recheck.py",
924
- "slmp_pending_live_verification.py",
925
- "slmp_manual_label_verification.py",
926
- "slmp_special_device_probe.py",
927
- "slmp_read_soak.py",
928
- "slmp_mixed_read_load.py",
929
- "slmp_tcp_concurrency.py",
919
+ "slmp_connection_check.py",
920
+ "slmp_device_range_probe.py",
921
+ "slmp_register_boundary_probe.py",
922
+ "slmp_init_model_docs.py",
923
+ "slmp_other_station_check.py",
924
+ "slmp_open_items_recheck.py",
925
+ "slmp_pending_live_verification.py",
926
+ "slmp_manual_label_verification.py",
927
+ "slmp_special_device_probe.py",
928
+ "slmp_read_soak.py",
929
+ "slmp_mixed_read_load.py",
930
+ "slmp_tcp_concurrency.py",
930
931
  "slmp_extended_device_device_recheck.py",
931
932
  "slmp_g_hg_extended_device_recheck.py",
932
933
  "slmp_g_hg_extended_device_coverage.py",
@@ -1151,9 +1152,9 @@ class ExtendedDeviceWordProbeSpec:
1151
1152
  direct_memory_specification: int
1152
1153
 
1153
1154
 
1154
- @dataclass(frozen=True)
1155
- class LatencySummary:
1156
- """Summary statistics for request latency."""
1155
+ @dataclass(frozen=True)
1156
+ class LatencySummary:
1157
+ """Summary statistics for request latency."""
1157
1158
 
1158
1159
  count: int
1159
1160
  avg_ms: float
@@ -1774,20 +1775,20 @@ def _write_scaffold_file(path: Path, content: str, *, force: bool) -> bool:
1774
1775
  return True
1775
1776
 
1776
1777
 
1777
- def _parse_manual_verdict(text: str) -> str | None:
1778
- lowered = text.strip().lower()
1779
- if lowered in {"y", "yes"}:
1778
+ def _parse_manual_verdict(text: str) -> str | None:
1779
+ lowered = text.strip().lower()
1780
+ if lowered in {"y", "yes"}:
1780
1781
  return "OK"
1781
1782
  if lowered in {"n", "no"}:
1782
1783
  return "NG"
1783
- if lowered in {"s", "skip"}:
1784
- return "SKIP"
1785
- return None
1786
-
1787
-
1788
- def _parse_positive_int_list(text: str) -> tuple[int, ...]:
1789
- parts = [part.strip() for part in text.split(",")]
1790
- if not parts or any(not part for part in parts):
1784
+ if lowered in {"s", "skip"}:
1785
+ return "SKIP"
1786
+ return None
1787
+
1788
+
1789
+ def _parse_positive_int_list(text: str) -> tuple[int, ...]:
1790
+ parts = [part.strip() for part in text.split(",")]
1791
+ if not parts or any(not part for part in parts):
1791
1792
  raise ValueError(f"comma-separated positive integer list expected: {text!r}")
1792
1793
  values = tuple(_int_auto(part) for part in parts)
1793
1794
  if any(value <= 0 for value in values):
@@ -1811,12 +1812,12 @@ def _render_model_docs_readme(*, series: str, model: str, folder_name: str) -> s
1811
1812
  f"- Model: `{model}`",
1812
1813
  "- Host: fill in when the target is assigned",
1813
1814
  "- Primary SLMP ports: fill in when the target is assigned",
1814
- "",
1815
- "## Expected Files",
1816
- "",
1817
- "- `read_soak_latest.md`",
1818
- " - repeated single-command read soak result",
1819
- "- `mixed_read_load_latest.md`",
1815
+ "",
1816
+ "## Expected Files",
1817
+ "",
1818
+ "- `read_soak_latest.md`",
1819
+ " - repeated single-command read soak result",
1820
+ "- `mixed_read_load_latest.md`",
1820
1821
  " - mixed 0401/0403/0406 read load result",
1821
1822
  "- `tcp_concurrency_latest.md`",
1822
1823
  " - practical multi-client TCP read concurrency result",
@@ -1923,13 +1924,13 @@ def _render_model_other_station_targets_example() -> str:
1923
1924
  "remote_station_01,0x00,0x01,0x03FF,0x00",
1924
1925
  "remote_station_02,0x00,0x02,0x03FF,0x00",
1925
1926
  "",
1926
- ]
1927
- )
1928
-
1929
-
1930
- def _initialize_model_docs(
1931
- *,
1932
- root: Path,
1927
+ ]
1928
+ )
1929
+
1930
+
1931
+ def _initialize_model_docs(
1932
+ *,
1933
+ root: Path,
1933
1934
  series: str,
1934
1935
  model: str,
1935
1936
  force: bool = False,
@@ -1937,12 +1938,12 @@ def _initialize_model_docs(
1937
1938
  folder_name = f"{_sanitize_report_component(series)}_{_sanitize_report_component(model)}"
1938
1939
  model_dir = root / folder_name
1939
1940
  created: list[Path] = []
1940
- skipped: list[Path] = []
1941
- files: list[tuple[Path, str]] = [
1942
- (model_dir / "README.md", _render_model_docs_readme(series=series, model=model, folder_name=folder_name)),
1943
- (model_dir / "current_plc_boundary_specs_example.txt", _render_model_boundary_specs_example()),
1944
- (
1945
- model_dir / "current_register_boundary_focus_specs_example.txt",
1941
+ skipped: list[Path] = []
1942
+ files: list[tuple[Path, str]] = [
1943
+ (model_dir / "README.md", _render_model_docs_readme(series=series, model=model, folder_name=folder_name)),
1944
+ (model_dir / "current_plc_boundary_specs_example.txt", _render_model_boundary_specs_example()),
1945
+ (
1946
+ model_dir / "current_register_boundary_focus_specs_example.txt",
1946
1947
  _render_model_register_boundary_specs_example(),
1947
1948
  ),
1948
1949
  (model_dir / "other_station_targets_example.txt", _render_model_other_station_targets_example()),
@@ -2265,7 +2266,7 @@ def connection_check_main(argv: Sequence[str] | None = None) -> int:
2265
2266
  """Perform SLMP binary connection check."""
2266
2267
  parser = argparse.ArgumentParser(description="SLMP binary connection check")
2267
2268
  parser.add_argument("--host", required=True, help="PLC IP address or host name")
2268
- parser.add_argument("--port", type=int, default=5000, help="SLMP port (default: 5000)")
2269
+ parser.add_argument("--port", type=int, default=None, help="SLMP port (default: 1025 for TCP, 1035 for UDP)")
2269
2270
  parser.add_argument("--transport", choices=("tcp", "udp"), default="tcp")
2270
2271
  parser.add_argument("--timeout", type=float, default=3.0)
2271
2272
  parser.add_argument("--series", choices=("ql", "iqr"), default="ql")
@@ -2341,6 +2342,7 @@ def connection_check_main(argv: Sequence[str] | None = None) -> int:
2341
2342
  help="use indirect specification flag (@)",
2342
2343
  )
2343
2344
  args = parser.parse_args(list(argv) if argv is not None else None)
2345
+ args.port = _resolve_port(args.port, args.transport)
2344
2346
 
2345
2347
  target = SlmpTarget(
2346
2348
  network=args.network,
@@ -4238,8 +4240,8 @@ def pending_live_verification_main(argv: Sequence[str] | None = None) -> int:
4238
4240
  return 0
4239
4241
 
4240
4242
 
4241
- def manual_label_verification_main(argv: Sequence[str] | None = None) -> int:
4242
- """Perform interactive manual label write verification with restore."""
4243
+ def manual_label_verification_main(argv: Sequence[str] | None = None) -> int:
4244
+ """Perform interactive manual label write verification with restore."""
4243
4245
  parser = argparse.ArgumentParser(
4244
4246
  description=(
4245
4247
  "Write temporary values to explicit label tags, let a human verify the effect, "
@@ -29,6 +29,7 @@ from .core import (
29
29
  _raise_response_error,
30
30
  _require_explicit_plc_profile_for_xy,
31
31
  _resolve_connection_profile,
32
+ _resolve_port,
32
33
  build_device_modification_flags,
33
34
  decode_cpu_operation_state,
34
35
  decode_response,
@@ -60,7 +61,7 @@ class SlmpClient:
60
61
  def __init__(
61
62
  self,
62
63
  host: str,
63
- port: int = 5000,
64
+ port: int | None = None,
64
65
  *,
65
66
  transport: str = "tcp",
66
67
  timeout: float = 3.0,
@@ -78,7 +79,7 @@ class SlmpClient:
78
79
 
79
80
  Args:
80
81
  host: PLC IP address.
81
- port: PLC port number. Defaults to 5000.
82
+ port: PLC port number. Defaults to 1025 for TCP and 1035 for UDP.
82
83
  transport: Transport protocol ('tcp' or 'udp'). Defaults to 'tcp'.
83
84
  timeout: Socket timeout in seconds. Defaults to 3.0.
84
85
  plc_profile: Canonical high-level PLC profile. The standard client
@@ -90,10 +91,10 @@ class SlmpClient:
90
91
  trace_hook: Optional callback for tracing requests and responses.
91
92
  """
92
93
  self.host = host
93
- self.port = port
94
94
  self.transport = transport.lower()
95
95
  if self.transport not in {"tcp", "udp"}:
96
96
  raise ValueError("transport must be 'tcp' or 'udp'")
97
+ self.port = _resolve_port(port, self.transport)
97
98
  self.timeout = timeout
98
99
  if not _allow_manual_profile:
99
100
  if plc_profile is None:
@@ -39,6 +39,24 @@ from .errors import (
39
39
  SlmpUnsupportedDeviceError,
40
40
  )
41
41
 
42
+ DEFAULT_TCP_PORT = 1025
43
+ DEFAULT_UDP_PORT = 1035
44
+
45
+
46
+ def _default_port_for_transport(transport: str) -> int:
47
+ normalized = transport.lower()
48
+ if normalized == "tcp":
49
+ return DEFAULT_TCP_PORT
50
+ if normalized == "udp":
51
+ return DEFAULT_UDP_PORT
52
+ raise ValueError("transport must be 'tcp' or 'udp'")
53
+
54
+
55
+ def _resolve_port(port: int | None, transport: str) -> int:
56
+ if port is None:
57
+ return _default_port_for_transport(transport)
58
+ return port
59
+
42
60
 
43
61
  @dataclass(frozen=True)
44
62
  class SlmpTarget:
@@ -17,6 +17,7 @@ from .core import (
17
17
  _require_explicit_plc_profile_for_xy,
18
18
  _resolve_connection_profile,
19
19
  _resolve_plc_profile_defaults,
20
+ _resolve_port,
20
21
  _validate_direct_dword_read_device,
21
22
  parse_device,
22
23
  )
@@ -68,14 +69,15 @@ class SlmpConnectionOptions:
68
69
 
69
70
  The options object is the recommended input for :func:`open_and_connect`
70
71
  and :func:`open_and_connect_sync`. It keeps transport-level settings and
71
- protocol-level defaults together so maintained documentation can point users to
72
+ protocol-level defaults together so maintained documentation can point users to
72
73
  one explicit connection entry point.
73
74
 
74
75
  Attributes:
75
76
  host: PLC hostname or IP address.
76
77
  plc_profile: Canonical high-level PLC profile. This is the only
77
78
  application-level PLC selector for the recommended helper layer.
78
- port: TCP or UDP port used by the SLMP endpoint.
79
+ port: TCP or UDP port used by the SLMP endpoint. Defaults to
80
+ ``1025`` for TCP and ``1035`` for UDP when omitted.
79
81
  transport: Transport name such as ``"tcp"`` or ``"udp"``.
80
82
  timeout: Socket timeout in seconds.
81
83
  default_target: Optional routing target applied to requests.
@@ -90,7 +92,7 @@ class SlmpConnectionOptions:
90
92
 
91
93
  host: str
92
94
  plc_profile: object
93
- port: int = 5000
95
+ port: int | None = None
94
96
  transport: str = "tcp"
95
97
  timeout: float = 3.0
96
98
  default_target: SlmpTarget | None = None
@@ -105,6 +107,8 @@ class SlmpConnectionOptions:
105
107
  def __post_init__(self) -> None:
106
108
  if self.plc_profile is None:
107
109
  raise ValueError("plc_profile is required. Use an explicit canonical PLC profile such as 'melsec:iq-r'.")
110
+ transport = self.transport.lower()
111
+ port = _resolve_port(self.port, transport)
108
112
  (
109
113
  normalized_plc_profile,
110
114
  plc_series,
@@ -117,6 +121,8 @@ class SlmpConnectionOptions:
117
121
  frame_type=None,
118
122
  address_profile=None,
119
123
  )
124
+ object.__setattr__(self, "transport", transport)
125
+ object.__setattr__(self, "port", port)
120
126
  object.__setattr__(self, "plc_profile", normalized_plc_profile)
121
127
  object.__setattr__(self, "plc_series", plc_series)
122
128
  object.__setattr__(self, "frame_type", frame_type)
@@ -1,14 +1,14 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: slmp-connect-python
3
- Version: 0.8.0
3
+ Version: 1.0.0
4
4
  Summary: SLMP Connect Python: client library for MELSEC SLMP binary communication
5
5
  Author: fa-yoshinobu
6
6
  License-Expression: MIT
7
7
  Project-URL: Homepage, https://github.com/fa-yoshinobu/plc-comm-slmp-python
8
8
  Project-URL: Repository, https://github.com/fa-yoshinobu/plc-comm-slmp-python
9
9
  Project-URL: Issues, https://github.com/fa-yoshinobu/plc-comm-slmp-python/issues
10
- Keywords: slmp,melsec,mitsubishi,plc,3e,4e,binary
11
- Classifier: Development Status :: 3 - Alpha
10
+ Keywords: slmp,melsec,plc,3e,4e,binary
11
+ Classifier: Development Status :: 5 - Production/Stable
12
12
  Classifier: Intended Audience :: Developers
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Programming Language :: Python :: 3.10
@@ -33,14 +33,9 @@ Requires-Dist: twine>=5.1; extra == "dev"
33
33
  Dynamic: license-file
34
34
 
35
35
  [![CI](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml/badge.svg)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/actions/workflows/ci.yml)
36
- [![Documentation](https://img.shields.io/badge/docs-GitHub_Pages-blue.svg)](https://fa-yoshinobu.github.io/plc-comm-docs-site/slmp/python/GETTING_STARTED/)
37
36
  [![PyPI](https://img.shields.io/pypi/v/slmp-connect-python.svg)](https://pypi.org/project/slmp-connect-python/)
38
37
  [![Python 3.10+](https://img.shields.io/badge/python-3.10+-blue.svg)](https://www.python.org/downloads/)
39
38
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE)
40
- [![Static Analysis: Ruff](https://img.shields.io/badge/Lint-Ruff-black.svg)](https://github.com/astral-sh/ruff)
41
- [![Release](https://img.shields.io/github/v/release/fa-yoshinobu/plc-comm-slmp-python?label=release)](https://github.com/fa-yoshinobu/plc-comm-slmp-python/releases/latest)
42
- [![Python](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)](https://www.python.org/)
43
- [![MkDocs](https://img.shields.io/badge/MkDocs-526CFE?logo=materialformkdocs&logoColor=white)](https://www.mkdocs.org/)
44
39
 
45
40
  # MELSEC SLMP for Python
46
41