scirodev 0.1.0__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.
- sciro/__init__.py +6 -0
- sciro/_common.py +31 -0
- sciro/floorpro.py +148 -0
- sciro/iodevices.py +79 -0
- sciro/parameters.py +30 -0
- sciro/py.typed +0 -0
- scirodev/__init__.py +3 -0
- scirodev/cli.py +17 -0
- scirodev-0.1.0.dist-info/METADATA +62 -0
- scirodev-0.1.0.dist-info/RECORD +14 -0
- scirodev-0.1.0.dist-info/WHEEL +5 -0
- scirodev-0.1.0.dist-info/entry_points.txt +2 -0
- scirodev-0.1.0.dist-info/licenses/LICENSE +21 -0
- scirodev-0.1.0.dist-info/top_level.txt +2 -0
sciro/__init__.py
ADDED
sciro/_common.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Typing helpers for the sciro stubs, in the style of ``pybricks._common``:
|
|
2
|
+
a value that is returned directly, or awaitable under multitasking. The
|
|
3
|
+
upstream ``MaybeAwaitableTuple[T]`` is a 1-tuple, so fixed-shape results get
|
|
4
|
+
their own classes here.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Tuple
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from typing import Awaitable
|
|
13
|
+
|
|
14
|
+
_Line = Tuple[float, float, float, float, int, bool]
|
|
15
|
+
_Ints = Tuple[int, ...]
|
|
16
|
+
_IRCalib = Tuple[Tuple[int, ...], Tuple[int, ...], int, int]
|
|
17
|
+
_RGBC = Tuple[int, int, int, int, int]
|
|
18
|
+
_Euler = Tuple[float, float, float, int]
|
|
19
|
+
_StateData = Tuple[bytes, bytes]
|
|
20
|
+
|
|
21
|
+
class MaybeAwaitableLine(_Line, Awaitable[_Line]): ...
|
|
22
|
+
|
|
23
|
+
class MaybeAwaitableInts(_Ints, Awaitable[_Ints]): ...
|
|
24
|
+
|
|
25
|
+
class MaybeAwaitableIRCalib(_IRCalib, Awaitable[_IRCalib]): ...
|
|
26
|
+
|
|
27
|
+
class MaybeAwaitableRGBC(_RGBC, Awaitable[_RGBC]): ...
|
|
28
|
+
|
|
29
|
+
class MaybeAwaitableEuler(_Euler, Awaitable[_Euler]): ...
|
|
30
|
+
|
|
31
|
+
class MaybeAwaitableStateData(_StateData, Awaitable[_StateData]): ...
|
sciro/floorpro.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""sciro.floorpro -- the LP-FloorPro line sensor over PUMP (PeakHub).
|
|
2
|
+
|
|
3
|
+
Stub for the frozen module of the same name. Every reading method returns the
|
|
4
|
+
value directly, or an awaitable under multitasking.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from typing import TYPE_CHECKING, Optional, Tuple
|
|
10
|
+
|
|
11
|
+
if TYPE_CHECKING:
|
|
12
|
+
from pybricks._common import MaybeAwaitable, MaybeAwaitableFloat
|
|
13
|
+
|
|
14
|
+
from ._common import (
|
|
15
|
+
MaybeAwaitableEuler,
|
|
16
|
+
MaybeAwaitableInts,
|
|
17
|
+
MaybeAwaitableIRCalib,
|
|
18
|
+
MaybeAwaitableLine,
|
|
19
|
+
MaybeAwaitableRGBC,
|
|
20
|
+
)
|
|
21
|
+
|
|
22
|
+
from .iodevices import PUMPDevice, StreamInfo
|
|
23
|
+
from .parameters import Port as _Port
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class _Stream:
|
|
27
|
+
dev: PUMPDevice
|
|
28
|
+
id: int
|
|
29
|
+
|
|
30
|
+
def subscribe(self, mode: int, rate: int = 0) -> MaybeAwaitable:
|
|
31
|
+
"""subscribe(mode, rate=0) -- see :meth:`PUMPDevice.subscribe`."""
|
|
32
|
+
|
|
33
|
+
def state(self) -> Optional[bytes]:
|
|
34
|
+
"""state() -> bytes | None -- the stream's current state prefix."""
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class Line(_Stream):
|
|
38
|
+
"""IR array derived data (default: every sample, 400 Hz)."""
|
|
39
|
+
|
|
40
|
+
def read(self) -> MaybeAwaitableLine:
|
|
41
|
+
"""read() -> Tuple
|
|
42
|
+
|
|
43
|
+
Returns ``(cog_dark, cog_bright, brightness, darkness, mask, calibrating)``:
|
|
44
|
+
centre of gravity of the dark / bright pixels in sensor pitches from the
|
|
45
|
+
middle sensor (-7 .. +7), overall brightness / darkness (0 .. 1), a
|
|
46
|
+
15-bit bright-pixel mask (bit i = sensor i), and whether calibration is
|
|
47
|
+
active.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def calibrate(self, enable: bool = True) -> MaybeAwaitable:
|
|
51
|
+
"""calibrate(enable=True) -- start/stop min-max calibration of the array."""
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
class IRRaw(_Stream):
|
|
55
|
+
"""15 raw 12-bit ADC readings. Off by default: use :meth:`oneshot` or subscribe."""
|
|
56
|
+
|
|
57
|
+
def read(self) -> MaybeAwaitableInts:
|
|
58
|
+
"""read() -> Tuple[int, ...] -- 15 values, 0 .. 4095, sensor 0 first."""
|
|
59
|
+
|
|
60
|
+
def oneshot(self) -> MaybeAwaitableInts:
|
|
61
|
+
"""oneshot() -> Tuple[int, ...] -- request one frame and return it."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class IRCalib(_Stream):
|
|
65
|
+
"""Calibration table of the IR array. Off by default."""
|
|
66
|
+
|
|
67
|
+
def read(self) -> MaybeAwaitableIRCalib:
|
|
68
|
+
"""read() -> Tuple
|
|
69
|
+
|
|
70
|
+
Returns ``(min, max, min_visited_mask, max_visited_mask)`` with ``min`` and
|
|
71
|
+
``max`` tuples of 15 raw counts.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def oneshot(self) -> MaybeAwaitableIRCalib:
|
|
75
|
+
"""oneshot() -> Tuple -- request the table once and return it."""
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
class ColorSensor(_Stream):
|
|
79
|
+
"""TCS3400 on an extension port: raw R, G, B, C at device resolution.
|
|
80
|
+
|
|
81
|
+
Every sample is tagged with the settings it was measured under, so a read
|
|
82
|
+
after a settings change waits for the first sample taken with the new
|
|
83
|
+
settings.
|
|
84
|
+
"""
|
|
85
|
+
|
|
86
|
+
def read(self) -> MaybeAwaitableRGBC:
|
|
87
|
+
"""read() -> Tuple[int, int, int, int, int] -- (red, green, blue, clear, status)."""
|
|
88
|
+
|
|
89
|
+
def settings(self) -> Tuple[int, int, int]:
|
|
90
|
+
"""settings() -> Tuple[int, int, int] -- (led_percent, gain_x, atime) in effect."""
|
|
91
|
+
|
|
92
|
+
def set_light(self, percent: int) -> MaybeAwaitable:
|
|
93
|
+
"""set_light(percent) -- illumination LED duty, 0 .. 100."""
|
|
94
|
+
|
|
95
|
+
def set_gain(self, gain_x: int) -> MaybeAwaitable:
|
|
96
|
+
"""set_gain(gain_x) -- analog gain: 1, 4, 16 or 64."""
|
|
97
|
+
|
|
98
|
+
def set_integration(self, atime: int) -> MaybeAwaitable:
|
|
99
|
+
"""set_integration(atime) -- TCS3400 ATIME register 0 .. 255: (256 - atime) * 2.78 ms."""
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
class IMU(_Stream):
|
|
103
|
+
"""BNO086 on an extension port: roll, pitch, yaw in degrees.
|
|
104
|
+
|
|
105
|
+
The heading offset lives on the hub: :meth:`reset_heading` makes the current
|
|
106
|
+
yaw read as the given angle.
|
|
107
|
+
"""
|
|
108
|
+
|
|
109
|
+
def read(self) -> MaybeAwaitableEuler:
|
|
110
|
+
"""read() -> Tuple -- (roll, pitch, yaw, status), degrees; yaw includes the offset."""
|
|
111
|
+
|
|
112
|
+
def heading(self) -> MaybeAwaitableFloat:
|
|
113
|
+
"""heading() -> float -- yaw in degrees (-180 .. 180], with the offset applied."""
|
|
114
|
+
|
|
115
|
+
def reset_heading(self, angle: float = 0) -> MaybeAwaitable:
|
|
116
|
+
"""reset_heading(angle=0) -- make the current heading read as ``angle``."""
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
class FloorPro:
|
|
120
|
+
"""LP-FloorPro connected to a PeakHub port, speaking PUMP."""
|
|
121
|
+
|
|
122
|
+
dev: PUMPDevice
|
|
123
|
+
"""The underlying generic device."""
|
|
124
|
+
serial: str
|
|
125
|
+
"""The device's 8-character short ID."""
|
|
126
|
+
line: Line
|
|
127
|
+
ir_raw: IRRaw
|
|
128
|
+
ir_calib: IRCalib
|
|
129
|
+
|
|
130
|
+
def __init__(self, port: _Port):
|
|
131
|
+
"""FloorPro(port)
|
|
132
|
+
|
|
133
|
+
Arguments:
|
|
134
|
+
port (Port): Port the sensor is connected to. Raises ``OSError`` if
|
|
135
|
+
no device is there or it is not a FloorPro.
|
|
136
|
+
"""
|
|
137
|
+
|
|
138
|
+
def color_sensor(self, ext_port: int = 1) -> ColorSensor:
|
|
139
|
+
"""color_sensor(ext_port=1) -> ColorSensor
|
|
140
|
+
|
|
141
|
+
The TCS3400 on extension port 1 or 2; raises ``OSError`` if none.
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
def imu(self, ext_port: int = 2) -> IMU:
|
|
145
|
+
"""imu(ext_port=2) -> IMU -- the BNO086 on extension port 1 or 2; raises ``OSError`` if none."""
|
|
146
|
+
|
|
147
|
+
def streams(self) -> Tuple[StreamInfo, ...]:
|
|
148
|
+
"""streams() -> Tuple -- the enumerated streams ``(id, url, ext_port, state_len)``."""
|
sciro/iodevices.py
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
"""sciro.iodevices -- PeakHub-only device classes (re-exported from
|
|
2
|
+
``pybricks.iodevices`` on the hub; declared here for the IDE).
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import TYPE_CHECKING, Dict, Optional, Tuple, Union
|
|
8
|
+
|
|
9
|
+
if TYPE_CHECKING:
|
|
10
|
+
from pybricks._common import MaybeAwaitable
|
|
11
|
+
|
|
12
|
+
from ._common import MaybeAwaitableStateData
|
|
13
|
+
|
|
14
|
+
from .parameters import Port as _Port
|
|
15
|
+
|
|
16
|
+
# (stream id, url, ext_port, state length)
|
|
17
|
+
StreamInfo = Tuple[int, str, int, int]
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class PUMPDevice:
|
|
21
|
+
"""Generic access to a PUMP device (Power UART Multiplex Protocol) on a
|
|
22
|
+
PeakHub port. Streams are addressed by id (1..N); state and data are raw
|
|
23
|
+
``bytes`` decoded by the user or a convenience class such as
|
|
24
|
+
:class:`sciro.floorpro.FloorPro`. Spec: ``PUMP-Protocol.md``.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
OFF: int = 0
|
|
28
|
+
"""Stream-control mode: stream is silent."""
|
|
29
|
+
ONESHOT: int = 1
|
|
30
|
+
"""Stream-control mode: one frame, then the mode self-clears to OFF."""
|
|
31
|
+
ON_CHANGE: int = 2
|
|
32
|
+
"""Stream-control mode: a frame whenever a new sample differs."""
|
|
33
|
+
PERIODIC: int = 3
|
|
34
|
+
"""Stream-control mode: every sample (rate 0) or at the given rate."""
|
|
35
|
+
|
|
36
|
+
def __init__(self, port: _Port):
|
|
37
|
+
"""PUMPDevice(port)
|
|
38
|
+
|
|
39
|
+
Arguments:
|
|
40
|
+
port (Port): Port the device is connected to. Raises ``OSError``
|
|
41
|
+
(``ENODEV``) if no PUMP device has completed its handshake there
|
|
42
|
+
within a few seconds.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
def info(self) -> Dict[str, Union[str, Tuple[StreamInfo, ...]]]:
|
|
46
|
+
"""info() -> Dict
|
|
47
|
+
|
|
48
|
+
Returns:
|
|
49
|
+
``{"url": str, "serial": str, "streams": ((id, url, ext_port, state_len), ...)}``
|
|
50
|
+
"""
|
|
51
|
+
|
|
52
|
+
def state(self, stream: int) -> Optional[bytes]:
|
|
53
|
+
"""state(stream) -> bytes | None
|
|
54
|
+
|
|
55
|
+
The last state prefix echoed by the stream (first two bytes are the
|
|
56
|
+
stream-control word: mode, rate), or ``None`` if none was received yet.
|
|
57
|
+
"""
|
|
58
|
+
|
|
59
|
+
def read(self, stream: int) -> MaybeAwaitableStateData:
|
|
60
|
+
"""read(stream) -> Tuple[bytes, bytes]
|
|
61
|
+
|
|
62
|
+
Waits for a frame measured under the currently requested settings and
|
|
63
|
+
returns ``(state, data)``. Awaitable under multitasking.
|
|
64
|
+
"""
|
|
65
|
+
|
|
66
|
+
def change_state(self, stream: int, state: bytes) -> MaybeAwaitable:
|
|
67
|
+
"""change_state(stream, state)
|
|
68
|
+
|
|
69
|
+
Sets the stream's whole state prefix (must be exactly its length) and
|
|
70
|
+
waits until the device echoes it. Raises ``OSError`` (``ETIMEDOUT``) if
|
|
71
|
+
the device never acknowledges.
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def subscribe(self, stream: int, mode: int, rate: int = 0) -> MaybeAwaitable:
|
|
75
|
+
"""subscribe(stream, mode, rate=0)
|
|
76
|
+
|
|
77
|
+
Changes only the stream-control word (keeps the device-specific state
|
|
78
|
+
bytes). ``rate`` is in units of 10 Hz, 0 = every sample.
|
|
79
|
+
"""
|
sciro/parameters.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
"""sciro.parameters -- the Pybricks parameter types, with the PeakHub port set.
|
|
2
|
+
|
|
3
|
+
On the hub this module re-exports ``pybricks.parameters`` unchanged (the firmware
|
|
4
|
+
has always had ``Port.G`` and ``Port.H``); this stub exists because the upstream
|
|
5
|
+
stubs declare ``Port.A`` .. ``Port.F`` only.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from pybricks.parameters import ( # noqa: F401 (re-exports)
|
|
9
|
+
Axis as Axis,
|
|
10
|
+
Button as Button,
|
|
11
|
+
Color as Color,
|
|
12
|
+
Direction as Direction,
|
|
13
|
+
Icon as Icon,
|
|
14
|
+
Side as Side,
|
|
15
|
+
Stop as Stop,
|
|
16
|
+
)
|
|
17
|
+
from pybricks.parameters import _PybricksEnum
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class Port(_PybricksEnum):
|
|
21
|
+
"""Port on the PeakHub. Eight LPF2/PUP ports, A .. H."""
|
|
22
|
+
|
|
23
|
+
A: Port = ord("A")
|
|
24
|
+
B: Port = ord("B")
|
|
25
|
+
C: Port = ord("C")
|
|
26
|
+
D: Port = ord("D")
|
|
27
|
+
E: Port = ord("E")
|
|
28
|
+
F: Port = ord("F")
|
|
29
|
+
G: Port = ord("G")
|
|
30
|
+
H: Port = ord("H")
|
sciro/py.typed
ADDED
|
File without changes
|
scirodev/__init__.py
ADDED
scirodev/cli.py
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
"""`scirodev` command line: pybricksdev's CLI (run, download, flash, ...) under
|
|
2
|
+
our name, so one tool covers PeakHub as well. PeakHub-specific commands can be
|
|
3
|
+
added here later; everything else is forwarded unchanged.
|
|
4
|
+
"""
|
|
5
|
+
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def main() -> None:
|
|
10
|
+
from pybricksdev.cli import main as pybricksdev_main
|
|
11
|
+
|
|
12
|
+
sys.argv[0] = "scirodev"
|
|
13
|
+
pybricksdev_main()
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
if __name__ == "__main__":
|
|
17
|
+
main()
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: scirodev
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: PeakHub / sciro extensions to Pybricks: typed API stubs (sciro.*) and hub tooling on top of pybricksdev
|
|
5
|
+
Author: Thomas Schank
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/sciurus-robotics/scirodev
|
|
8
|
+
Project-URL: Repository, https://github.com/sciurus-robotics/scirodev
|
|
9
|
+
Keywords: pybricks,lego,peakhub,micropython,stubs
|
|
10
|
+
Classifier: Programming Language :: Python :: 3
|
|
11
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
12
|
+
Classifier: Topic :: Software Development :: Embedded Systems
|
|
13
|
+
Classifier: Typing :: Typed
|
|
14
|
+
Requires-Python: >=3.10
|
|
15
|
+
Description-Content-Type: text/markdown
|
|
16
|
+
License-File: LICENSE
|
|
17
|
+
Requires-Dist: pybricksdev>=2.3
|
|
18
|
+
Requires-Dist: pybricks>=3.6
|
|
19
|
+
Dynamic: license-file
|
|
20
|
+
|
|
21
|
+
# scirodev
|
|
22
|
+
|
|
23
|
+
Host-side companion to the PeakHub firmware: **typed API stubs** for the `sciro`
|
|
24
|
+
namespace that PeakHub programs import, plus the hub tooling (it depends on
|
|
25
|
+
`pybricksdev`, so `scirodev run ble prog.py` works exactly like `pybricksdev`).
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
pip install scirodev # from PyPI
|
|
29
|
+
pip install "scirodev @ git+https://github.com/sciurus-robotics/scirodev.git@v0.1.0"
|
|
30
|
+
pipx install scirodev # just the CLI, in its own venv
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Hacking on it:
|
|
34
|
+
|
|
35
|
+
```
|
|
36
|
+
pip install -e .
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Then your IDE/type checker knows these, which the upstream `pybricks` stubs don't:
|
|
40
|
+
|
|
41
|
+
```python
|
|
42
|
+
from sciro.parameters import Port # Port.A .. Port.H (PeakHub has 8 ports)
|
|
43
|
+
from sciro.iodevices import PUMPDevice # generic PUMP device access
|
|
44
|
+
from sciro.floorpro import FloorPro # LP-FloorPro convenience class
|
|
45
|
+
|
|
46
|
+
fp = FloorPro(Port.G)
|
|
47
|
+
cog_dark, cog_bright, brightness, darkness, mask, calibrating = fp.line.read()
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
On the hub these modules are frozen into the PeakHub firmware:
|
|
51
|
+
`sciro.parameters` and `sciro.iodevices` re-export the same runtime objects as
|
|
52
|
+
their `pybricks.*` counterparts, so `sciro.parameters.Port is
|
|
53
|
+
pybricks.parameters.Port` — the stubs only add what the IDE is missing. The
|
|
54
|
+
stub files here mirror the frozen modules; a release of this package matches
|
|
55
|
+
the PeakHub firmware of the same date.
|
|
56
|
+
|
|
57
|
+
## Releasing
|
|
58
|
+
|
|
59
|
+
Bump `version` in `pyproject.toml`, commit, tag `vX.Y.Z` and push the tag. The
|
|
60
|
+
GitHub workflow builds, type-checks and publishes to PyPI via Trusted Publishing
|
|
61
|
+
(configured on pypi.org: owner `sciurus-robotics`, repo `scirodev`, workflow
|
|
62
|
+
`publish.yml`, environment `pypi`).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
sciro/__init__.py,sha256=auvDdSvjOd3FXLu8XV0mOGyahF06zkmfkGEj7US783o,238
|
|
2
|
+
sciro/_common.py,sha256=g2e5p1jEZ4Z1s2EtyL2sOY6cau1r4xbtZI1RKXQDTZk,1046
|
|
3
|
+
sciro/floorpro.py,sha256=fRj6IycPNEzYCp2BlYJ2rdZipKUGjO8n-9wdEXUOlo8,5005
|
|
4
|
+
sciro/iodevices.py,sha256=H_iE44JkJwzYR41BVylG8wJ8mR4a72pcQKBdbAbJp7g,2787
|
|
5
|
+
sciro/parameters.py,sha256=POB0G5i5bsmXDLoKkjZ_M14xLT_3GyaidpzRQrhHLqk,821
|
|
6
|
+
sciro/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
7
|
+
scirodev/__init__.py,sha256=PigYDhaDY9FpENpagsmRhOKNt7suuiLz__kHrf-WHJw,107
|
|
8
|
+
scirodev/cli.py,sha256=EedyRghlMPpNQ4qA0E9pIDK_tNBdfwf7sRBIiaNuO1s,405
|
|
9
|
+
scirodev-0.1.0.dist-info/licenses/LICENSE,sha256=5eMIVXEq0VbDVA3Y9p3IXJemflDENP4sQA2eSx_PNgI,1088
|
|
10
|
+
scirodev-0.1.0.dist-info/METADATA,sha256=q1Ox66tdgnC05FpLNsqclwWRclZpKtLWojNqHhRM_4A,2407
|
|
11
|
+
scirodev-0.1.0.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
12
|
+
scirodev-0.1.0.dist-info/entry_points.txt,sha256=G0501dWnjtPC7Bjp0jzKPlNXKS63Q1rD8acTcrw-AGI,47
|
|
13
|
+
scirodev-0.1.0.dist-info/top_level.txt,sha256=jwUBs_F6VA_ZrXFXxuZGG4rKLSaGoTUjsHZejBvrY8U,15
|
|
14
|
+
scirodev-0.1.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sciurus Robotics, Thomas Schank
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|