primitives 0.1.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.
- primitives-0.1.0/.gitignore +84 -0
- primitives-0.1.0/PKG-INFO +73 -0
- primitives-0.1.0/README.md +64 -0
- primitives-0.1.0/pyproject.toml +24 -0
- primitives-0.1.0/src/primitives/__init__.py +4 -0
- primitives-0.1.0/src/primitives/py.typed +0 -0
- primitives-0.1.0/src/primitives/sr.py +12 -0
- primitives-0.1.0/src/primitives/ton.py +53 -0
- primitives-0.1.0/tests/test_sr.py +0 -0
- primitives-0.1.0/tests/test_ton.py +0 -0
- primitives-0.1.0/uv.lock +8 -0
@@ -0,0 +1,84 @@
|
|
1
|
+
# Python
|
2
|
+
__pycache__/
|
3
|
+
*.py[cod]
|
4
|
+
*$py.class
|
5
|
+
*.so
|
6
|
+
.Python
|
7
|
+
build/
|
8
|
+
develop-eggs/
|
9
|
+
dist/
|
10
|
+
downloads/
|
11
|
+
eggs/
|
12
|
+
.eggs/
|
13
|
+
lib/
|
14
|
+
lib64/
|
15
|
+
parts/
|
16
|
+
sdist/
|
17
|
+
var/
|
18
|
+
wheels/
|
19
|
+
share/python-wheels/
|
20
|
+
*.egg-info/
|
21
|
+
.installed.cfg
|
22
|
+
*.egg
|
23
|
+
MANIFEST
|
24
|
+
|
25
|
+
# Virtual environments
|
26
|
+
.env
|
27
|
+
.venv
|
28
|
+
env/
|
29
|
+
venv/
|
30
|
+
ENV/
|
31
|
+
env.bak/
|
32
|
+
venv.bak/
|
33
|
+
|
34
|
+
# uv
|
35
|
+
.uv/
|
36
|
+
|
37
|
+
# IDE
|
38
|
+
.vscode/
|
39
|
+
.idea/
|
40
|
+
*.swp
|
41
|
+
*.swo
|
42
|
+
*~
|
43
|
+
|
44
|
+
# OS
|
45
|
+
.DS_Store
|
46
|
+
Thumbs.db
|
47
|
+
|
48
|
+
# Docker
|
49
|
+
.dockerignore
|
50
|
+
|
51
|
+
# Logs
|
52
|
+
*.log
|
53
|
+
|
54
|
+
# Environment variables
|
55
|
+
.env.local
|
56
|
+
.env.development.local
|
57
|
+
.env.test.local
|
58
|
+
.env.production.local
|
59
|
+
|
60
|
+
# Coverage reports
|
61
|
+
htmlcov/
|
62
|
+
.coverage
|
63
|
+
.coverage.*
|
64
|
+
coverage.xml
|
65
|
+
*.cover
|
66
|
+
.hypothesis/
|
67
|
+
.pytest_cache/
|
68
|
+
|
69
|
+
# Jupyter Notebook
|
70
|
+
.ipynb_checkpoints
|
71
|
+
|
72
|
+
# pyenv
|
73
|
+
.python-version
|
74
|
+
|
75
|
+
# pipenv
|
76
|
+
Pipfile.lock
|
77
|
+
|
78
|
+
# poetry
|
79
|
+
poetry.lock
|
80
|
+
|
81
|
+
# mypy
|
82
|
+
.mypy_cache/
|
83
|
+
.dmypy.json
|
84
|
+
dmypy.json
|
@@ -0,0 +1,73 @@
|
|
1
|
+
Metadata-Version: 2.4
|
2
|
+
Name: primitives
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Shared PLC-style primitives in Python
|
5
|
+
Author-email: Neil Doell <neil@vococo.ca>
|
6
|
+
License-Expression: MIT
|
7
|
+
Requires-Python: >=3.9
|
8
|
+
Description-Content-Type: text/markdown
|
9
|
+
|
10
|
+
timers-latches
|
11
|
+
|
12
|
+
Lightweight, production-ready implementations of PLC-style primitives in Python:
|
13
|
+
|
14
|
+
TON — On-delay timer
|
15
|
+
|
16
|
+
SR — Set/Reset latch (reset dominant)
|
17
|
+
|
18
|
+
These primitives are useful for industrial automation logic, simulations, or anywhere you want PLC-like control behavior in Python.
|
19
|
+
|
20
|
+
Installation
|
21
|
+
pip install timers-latches
|
22
|
+
|
23
|
+
|
24
|
+
Or, for development:
|
25
|
+
|
26
|
+
git clone https://github.com/yourorg/timers-latches.git
|
27
|
+
cd timers-latches
|
28
|
+
pip install -e .
|
29
|
+
|
30
|
+
Usage
|
31
|
+
from timers_latches import TON, SR
|
32
|
+
|
33
|
+
# --- TON example ---
|
34
|
+
ton = TON(preset_s=2.0) # 2-second delay
|
35
|
+
for step in range(5):
|
36
|
+
q = ton.update(enable=True, dt_s=0.5)
|
37
|
+
print(f"t={step*0.5:.1f}s, Q={q}")
|
38
|
+
# Q will turn True after 2.0 seconds of accumulated enable time
|
39
|
+
|
40
|
+
# --- SR example ---
|
41
|
+
latch = SR(initial=False)
|
42
|
+
print(latch.update(set_=True, reset=False)) # True
|
43
|
+
print(latch.update(set_=False, reset=True)) # False (reset dominates)
|
44
|
+
|
45
|
+
API
|
46
|
+
TON(preset_s: float)
|
47
|
+
|
48
|
+
preset_s: delay time in seconds (must be ≥ 0).
|
49
|
+
|
50
|
+
.update(enable: bool, dt_s: float) -> bool: call once per scan; returns the done bit (Q).
|
51
|
+
|
52
|
+
.Q: current output (done bit).
|
53
|
+
|
54
|
+
SR(initial: bool = False)
|
55
|
+
|
56
|
+
initial: starting state.
|
57
|
+
|
58
|
+
.update(set_: bool, reset: bool) -> bool: updates and returns current state. Reset dominates set.
|
59
|
+
|
60
|
+
.state: current latched state.
|
61
|
+
|
62
|
+
Versioning
|
63
|
+
|
64
|
+
Follows Semantic Versioning
|
65
|
+
.
|
66
|
+
|
67
|
+
API is stable from 1.0.0 onward.
|
68
|
+
|
69
|
+
License
|
70
|
+
|
71
|
+
MIT © Your Name / Your Organization
|
72
|
+
|
73
|
+
⚡️ This package is intentionally minimal and focused: no dependencies, only core primitives. You can drop it into simulations, control logic, or larger automation frameworks.
|
@@ -0,0 +1,64 @@
|
|
1
|
+
timers-latches
|
2
|
+
|
3
|
+
Lightweight, production-ready implementations of PLC-style primitives in Python:
|
4
|
+
|
5
|
+
TON — On-delay timer
|
6
|
+
|
7
|
+
SR — Set/Reset latch (reset dominant)
|
8
|
+
|
9
|
+
These primitives are useful for industrial automation logic, simulations, or anywhere you want PLC-like control behavior in Python.
|
10
|
+
|
11
|
+
Installation
|
12
|
+
pip install timers-latches
|
13
|
+
|
14
|
+
|
15
|
+
Or, for development:
|
16
|
+
|
17
|
+
git clone https://github.com/yourorg/timers-latches.git
|
18
|
+
cd timers-latches
|
19
|
+
pip install -e .
|
20
|
+
|
21
|
+
Usage
|
22
|
+
from timers_latches import TON, SR
|
23
|
+
|
24
|
+
# --- TON example ---
|
25
|
+
ton = TON(preset_s=2.0) # 2-second delay
|
26
|
+
for step in range(5):
|
27
|
+
q = ton.update(enable=True, dt_s=0.5)
|
28
|
+
print(f"t={step*0.5:.1f}s, Q={q}")
|
29
|
+
# Q will turn True after 2.0 seconds of accumulated enable time
|
30
|
+
|
31
|
+
# --- SR example ---
|
32
|
+
latch = SR(initial=False)
|
33
|
+
print(latch.update(set_=True, reset=False)) # True
|
34
|
+
print(latch.update(set_=False, reset=True)) # False (reset dominates)
|
35
|
+
|
36
|
+
API
|
37
|
+
TON(preset_s: float)
|
38
|
+
|
39
|
+
preset_s: delay time in seconds (must be ≥ 0).
|
40
|
+
|
41
|
+
.update(enable: bool, dt_s: float) -> bool: call once per scan; returns the done bit (Q).
|
42
|
+
|
43
|
+
.Q: current output (done bit).
|
44
|
+
|
45
|
+
SR(initial: bool = False)
|
46
|
+
|
47
|
+
initial: starting state.
|
48
|
+
|
49
|
+
.update(set_: bool, reset: bool) -> bool: updates and returns current state. Reset dominates set.
|
50
|
+
|
51
|
+
.state: current latched state.
|
52
|
+
|
53
|
+
Versioning
|
54
|
+
|
55
|
+
Follows Semantic Versioning
|
56
|
+
.
|
57
|
+
|
58
|
+
API is stable from 1.0.0 onward.
|
59
|
+
|
60
|
+
License
|
61
|
+
|
62
|
+
MIT © Your Name / Your Organization
|
63
|
+
|
64
|
+
⚡️ This package is intentionally minimal and focused: no dependencies, only core primitives. You can drop it into simulations, control logic, or larger automation frameworks.
|
@@ -0,0 +1,24 @@
|
|
1
|
+
|
2
|
+
[build-system]
|
3
|
+
requires = ["hatchling"]
|
4
|
+
build-backend = "hatchling.build"
|
5
|
+
|
6
|
+
[project]
|
7
|
+
name = "primitives"
|
8
|
+
version = "0.1.0"
|
9
|
+
description = "Shared PLC-style primitives in Python"
|
10
|
+
authors = [{name="Neil Doell", email="neil@vococo.ca"}]
|
11
|
+
readme = "README.md"
|
12
|
+
license = "MIT"
|
13
|
+
requires-python = ">=3.9"
|
14
|
+
dependencies = []
|
15
|
+
|
16
|
+
[tool.hatch.build.targets.wheel]
|
17
|
+
packages = ["src/primitives"]
|
18
|
+
|
19
|
+
[tool.hatch.build.targets.wheel.force-include]
|
20
|
+
"src/primitives/py.typed" = "primitives/py.typed"
|
21
|
+
|
22
|
+
[tool.ruff.lint.per-file-ignores]
|
23
|
+
"primitives/__init__.py" = ["F403", "F405"]
|
24
|
+
|
File without changes
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# primitives/ton.py
|
2
|
+
from __future__ import annotations
|
3
|
+
|
4
|
+
from dataclasses import dataclass
|
5
|
+
from time import monotonic
|
6
|
+
from typing import Optional
|
7
|
+
|
8
|
+
|
9
|
+
@dataclass(slots=True)
|
10
|
+
class TON:
|
11
|
+
"""IEC-style TON (timer on-delay).
|
12
|
+
|
13
|
+
Usage:
|
14
|
+
t = TON(2.0) # 2 seconds preset
|
15
|
+
t.update(True) # call each scan with enable signal
|
16
|
+
if t.q: ... # output goes True after preset elapses
|
17
|
+
"""
|
18
|
+
preset: float # seconds
|
19
|
+
_start: Optional[float] = None # monotonic start time
|
20
|
+
q: bool = False # done/output
|
21
|
+
|
22
|
+
def reset(self) -> None:
|
23
|
+
self._start = None
|
24
|
+
self.q = False
|
25
|
+
|
26
|
+
def update(self, enable: bool, now: Optional[float] = None) -> bool:
|
27
|
+
"""Update the timer; return output (q).
|
28
|
+
|
29
|
+
Args:
|
30
|
+
enable: input signal; when False, timer resets.
|
31
|
+
now: optional monotonic time for testing; defaults to time.monotonic().
|
32
|
+
"""
|
33
|
+
if not enable:
|
34
|
+
self.reset()
|
35
|
+
return self.q
|
36
|
+
|
37
|
+
t = monotonic() if now is None else now
|
38
|
+
if self._start is None:
|
39
|
+
self._start = t
|
40
|
+
self.q = False
|
41
|
+
return self.q
|
42
|
+
|
43
|
+
if (t - self._start) >= self.preset:
|
44
|
+
self.q = True
|
45
|
+
return self.q
|
46
|
+
|
47
|
+
@property
|
48
|
+
def elapsed(self) -> float:
|
49
|
+
"""Seconds since enable; 0 if not running."""
|
50
|
+
if self._start is None:
|
51
|
+
return 0.0
|
52
|
+
t = monotonic()
|
53
|
+
return max(0.0, t - self._start)
|
File without changes
|
File without changes
|