SLASD 0.0.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.
slasd/__init__.py ADDED
@@ -0,0 +1,8 @@
1
+ """Marks the package containing the SLASD driver."""
2
+
3
+ import importlib.metadata
4
+
5
+ try:
6
+ __version__ = importlib.metadata.version(__name__)
7
+ except importlib.metadata.PackageNotFoundError:
8
+ __version__ = '0.0.0' # Fallback for development mode
@@ -0,0 +1 @@
1
+ """Marks the package containing the SLASD Commands."""
@@ -0,0 +1,34 @@
1
+ """Collection of serial command interfaces."""
2
+
3
+ from typing import Protocol
4
+
5
+ import serial
6
+
7
+
8
+ class SerialCommand(Protocol):
9
+ """Interface describing a generic serial command to the Arduino.
10
+
11
+ Attributes
12
+ ----------
13
+ _commandId : str
14
+ ID of the command. Usually a single ASCII char.
15
+ _payload : bytearray
16
+ Data payload to send with command.
17
+
18
+ """
19
+
20
+ _commandId: str
21
+ _payload: bytearray
22
+
23
+ def send(self, ser: serial.Serial) -> None:
24
+ """Send a single serial command to the Arduino.
25
+
26
+ Parameters
27
+ ----------
28
+ ser : serial.Serial
29
+ Serial connection to send the frame on.
30
+
31
+
32
+ """
33
+ cmdBytes = bytes(self._commandId, 'ASCII')
34
+ ser.write(cmdBytes + self._payload)
@@ -0,0 +1,24 @@
1
+ """Fastrak serial commands with no response path."""
2
+
3
+ from .command import SerialCommand
4
+
5
+
6
+ class SetLedState(SerialCommand):
7
+ """Send a command to the Arduino to set the LED array state."""
8
+
9
+ def __init__(
10
+ self,
11
+ data: bytearray,
12
+ ) -> None:
13
+ """Class constructor.
14
+
15
+ Parameters
16
+ ----------
17
+ data : bytearray
18
+ The data payload to provide to the Arduino.
19
+
20
+ """
21
+ self._commandId = 'S'
22
+ self._payload = bytearray()
23
+ self._payload = data
24
+ self._payload += b'\n'
@@ -0,0 +1,133 @@
1
+ """Contains the Fastrak device angular illuminator class."""
2
+
3
+ import struct
4
+ from math import floor
5
+ from typing import TypedDict
6
+
7
+ from fastrakSerialDriver.fastrakPosition import FastrakPostion
8
+ from typing_extensions import Unpack
9
+
10
+ from .ledAnimationDriver import LedAnimationDevice
11
+
12
+
13
+ class FastrakParams(TypedDict):
14
+ r"""Describe the typing of the kwargs for the Fastrak animation state computation.
15
+
16
+ Attributes
17
+ ----------
18
+ posData : FastrakPostion
19
+ A position reported by a Fastrak hardware device.
20
+ angleToLight : int
21
+ The angular range $\\theta$, in degrees, to light up. In the example below the `O`
22
+ correspond to lit LED and `X` to unlit LED.
23
+ ```ascii
24
+ OOOOOOOOOOO
25
+ OOOOO OOOOO
26
+ OO OO
27
+ XO θ .'XX
28
+ X `. ------- .' X
29
+ X `. / \ .' X
30
+ X `. / X
31
+ X `. .' X
32
+ X ,-. .' X
33
+ X _(*_*)_ X
34
+ X (_ o _) X
35
+ X / o \ X
36
+ X (_/ \_) X
37
+ X X
38
+ X X
39
+ X X
40
+ X X
41
+ XX XX
42
+ XX XX
43
+ XXXXX XXXXX
44
+ XXXXXXXXXXX
45
+ ```
46
+
47
+ colorR : int
48
+ The red component of the LED lit color.
49
+ colorG : int
50
+ The green component of the LED lit color.
51
+ colorB : int
52
+ The blue component of the LED lit color.
53
+ """
54
+
55
+ posData: FastrakPostion
56
+ angleToLight: int
57
+ colorR: int
58
+ colorG: int
59
+ colorB: int
60
+
61
+
62
+ class FastrakAnimationDevice(LedAnimationDevice):
63
+ """Implements LedAnimationDevice for the Fastrak look position use case."""
64
+
65
+ def _computeState(self, **kwargs: Unpack[FastrakParams]) -> bytearray: # ty:ignore[invalid-method-override]
66
+ """Compute the state array for the LED in the Fastrak look position use case.
67
+
68
+ Parameters
69
+ ----------
70
+ **kwargs : Unpack[FastrakParams]
71
+ Collection of key word arguments for the Fastrak look position use case.
72
+
73
+ Returns
74
+ -------
75
+ bytearray
76
+ The on/off and color state for each LED in the array.
77
+
78
+
79
+ """
80
+ if (
81
+ kwargs is None
82
+ or 'posData' not in kwargs
83
+ or 'angleToLight' not in kwargs
84
+ or 'colorR' not in kwargs
85
+ or 'colorG' not in kwargs
86
+ or 'colorB' not in kwargs
87
+ or type(kwargs['posData']) is not FastrakPostion
88
+ or type(kwargs['angleToLight']) is not int
89
+ or type(kwargs['colorR']) is not int
90
+ or type(kwargs['colorG']) is not int
91
+ or type(kwargs['colorB']) is not int
92
+ ):
93
+ raise TypeError
94
+
95
+ if kwargs['colorR'] < 0 or kwargs['colorR'] > 255:
96
+ raise TypeError
97
+ if kwargs['colorG'] < 0 or kwargs['colorG'] > 255:
98
+ raise TypeError
99
+ if kwargs['colorB'] < 0 or kwargs['colorB'] > 255:
100
+ raise TypeError
101
+
102
+ byteCr = struct.pack('<B', kwargs['colorR'])
103
+ byteCg = struct.pack('<B', kwargs['colorG'])
104
+ byteCb = struct.pack('<B', kwargs['colorB'])
105
+
106
+ if len(byteCb) != 1 or len(byteCg) != 1 or len(byteCr) != 1:
107
+ raise TypeError
108
+
109
+ pos = kwargs['posData']
110
+ lightAngle = kwargs['angleToLight']
111
+ litCenterLed = floor((self._ledCount / 360) * pos.psi)
112
+ lookAngleCnt = floor((self._ledCount / 360) * lightAngle)
113
+ payload = bytearray(b'\0' * (3 * self._ledCount))
114
+
115
+ for i in range(
116
+ floor(litCenterLed - (lookAngleCnt / 2)),
117
+ litCenterLed,
118
+ 3,
119
+ ):
120
+ payload[i] = byteCr[0]
121
+ payload[i + 1] = byteCg[0]
122
+ payload[i + 2] = byteCb[0]
123
+
124
+ for i in range(
125
+ litCenterLed,
126
+ floor(litCenterLed + (lookAngleCnt / 2)) + 1,
127
+ 3,
128
+ ):
129
+ payload[i] = byteCr[0]
130
+ payload[i + 1] = byteCg[0]
131
+ payload[i + 2] = byteCb[0]
132
+
133
+ return payload
@@ -0,0 +1,121 @@
1
+ """Base class for an LED animation driver."""
2
+
3
+ from serial import Serial
4
+
5
+ from .commands.noResp import SetLedState
6
+
7
+
8
+ class LedAnimationDevice:
9
+ """Defines interface for connecting, setting up, and streaming from a Fastrak.
10
+
11
+ Attributes
12
+ ----------
13
+ _ser : Serial | None
14
+ Serial connection to an Arduino.
15
+
16
+ _COMport :
17
+ String representing the COM port to be connected to.
18
+
19
+ _baud : SerialBaudrates
20
+ Baudrate for the serial connection.
21
+
22
+ _timeout : int, default: 1second
23
+ Serial timeout for the connection.
24
+
25
+ _ledCount: int
26
+ The number of LED in the array.
27
+
28
+ """
29
+
30
+ _ser: Serial | None
31
+ _COMport: str
32
+ _baud: int
33
+ _timeout: int
34
+ _ledCount: int
35
+
36
+ def __init__(
37
+ self,
38
+ COMport: str = 'COM1',
39
+ baud: int = 115200,
40
+ timeout: int = 1,
41
+ ledCount: int = 100,
42
+ setup: bool = True,
43
+ ) -> None:
44
+ """Construct a LedAnimationDevice class.
45
+
46
+ Parameters
47
+ ----------
48
+ COMport : str
49
+ String representing the COM port to be connected to.
50
+
51
+ baud : int, default: 1115200
52
+ Baudrate for the serial connection.
53
+
54
+ timeout : int, default: 1second
55
+ Serial timeout for the connection.
56
+
57
+ ledCount: int, defualt: 100
58
+ The number of LED in the array.
59
+
60
+ setup : int, default: True
61
+ Flag indicating if the device should connect to the serial interface.
62
+
63
+ """
64
+ self._ser = None
65
+ self._COMport = COMport
66
+ self._baud = baud
67
+ self._timeout = timeout
68
+ self._ledCount = ledCount
69
+ if setup:
70
+ self.connect()
71
+
72
+ def connect(self) -> None:
73
+ """Connect to the serial device."""
74
+ if self._ser is not None:
75
+ if not self._ser.is_open:
76
+ self._ser.open()
77
+ else:
78
+ self._ser = Serial(self._COMport, self._baud, timeout=self._timeout)
79
+
80
+ def _sendState(self, data: bytearray) -> None:
81
+ """Send state data to the serial device.
82
+
83
+ Parameters
84
+ ----------
85
+ data : bytearray
86
+ LED array state data to send to the device.
87
+
88
+
89
+ """
90
+ if self._ser is not None:
91
+ SetLedState(data).send(self._ser)
92
+
93
+ def _computeState(self, **kwargs) -> bytearray:
94
+ """State computation interface.
95
+
96
+ Parameters
97
+ ----------
98
+ **kwargs: dict
99
+ Collection of keyword arguments. Unique to each animation class.
100
+
101
+
102
+ Returns
103
+ -------
104
+ bytearray
105
+ The computed state of the LED array.
106
+
107
+
108
+ """
109
+ raise NotImplementedError
110
+
111
+ def compNSndState(self, **kwargs) -> None:
112
+ """Compute and set the state of the LED array.
113
+
114
+ Parameters
115
+ ----------
116
+ **kwargs : dict
117
+ Collection of keyword arguments. Unique to each animation class.
118
+
119
+ """
120
+ payload = self._computeState(**kwargs)
121
+ self._sendState(payload)
@@ -0,0 +1,311 @@
1
+ Metadata-Version: 2.5
2
+ Name: SLASD
3
+ Version: 0.0.0
4
+ Summary: A serial driver and arduino project for 'animating' on a simple LED string.
5
+ License-File: LICENSE
6
+ Requires-Python: >=3.10
7
+ Requires-Dist: fastrakserialdriver>=0.2.1
8
+ Requires-Dist: pyserial>=3.5
9
+ Description-Content-Type: text/markdown
10
+
11
+ ---
12
+ title: Simple Arduino Controlled LED Serial Driver
13
+ authors:
14
+ - joe_starr
15
+ ---
16
+
17
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
18
+ [![White Logo image](https://brainmade.org/white-logo.svg){width=10%}](https://brainmade.org)
19
+
20
+ ![hero](./infra/assets/logo.svg)
21
+
22
+ /// caption
23
+ Arduino Mega from `amperka/hardware-drawings` [@amperka_hardware_drawings_2016]
24
+ ///
25
+
26
+ ## Note to Reader
27
+
28
+ ### What Am I?
29
+
30
+ This repository contains an Arduino sketch which uses the FastLED library to control a collection
31
+ (strip) of NEOPIXEL LED. The repository also contains a Python serial driver for commanding the
32
+ Arduino. The python side is primarily designed around controlling the LED array based on feedback
33
+ from a [Polhemus Fastrak](https://polhemus.com/all-trackers/fastrak).
34
+
35
+ Simple installation by pip. You should probably be using uv projects: `uv add SLASD`
36
+
37
+ ### About the Documentation
38
+
39
+ The following document describes the "rules" and expectation for development. The
40
+ ["API Reference"](./reference/fastrakSerialDriver/) page contains the technical context descriptions
41
+ found in the source files. The ["Use Cases"](./content/usecase/usecase) page contains a collection
42
+ of use cases and a use case diagram for the tool. The ["Decisions"](./content/madr/) page contains a
43
+ collection of [architectural decision records](https://adr.github.io/madr/) [@Kopp2018] giving
44
+ context on why this tool is the way it is.
45
+
46
+ ### Issues
47
+
48
+ If you discover an issue with this repository or have a question, please feel free to open an issue.
49
+ I've included templates for the following issues:
50
+
51
+ - 🖋️ Spelling and Grammar: Found some language that is incorrect?
52
+ - 🤷 Clarity: Found a section that just makes no sense?
53
+ - ❓ Question: Do you have a general question?
54
+ - 🐞 Bug: Found an error in the code?
55
+ - 🚀 Enhancement: Have a suggestion for improving the toolchain?
56
+
57
+ [:fontawesome-solid-paper-plane: Open Issue!](https://github.com/Nontrivial-Solutions/psychopy-6dof/issues/new/choose){ .md-button }
58
+
59
+ ## 📃 Cite Me
60
+
61
+ ## ⚖️ License
62
+
63
+ Documentation:
64
+ [![License: CC BY-SA 4.0](https://licensebuttons.net/l/by-sa/4.0/80x15.png)](https://creativecommons.org/licenses/by-sa/4.0/)
65
+
66
+ Code:
67
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
68
+
69
+ ## Planning and Administration
70
+
71
+ ### Tasks
72
+
73
+ Tasks are tracked as GitHub issues.
74
+
75
+ ### Version Control
76
+
77
+ The toolchain shall be kept under Git versioning. Development shall take place on branches with
78
+ `main` on GitHub as a source of truth. GitHub pull requests shall serve as the arbiter for inclusion
79
+ on main with the following quality gates:
80
+
81
+ - Running and passing the unit test suite.
82
+ - Running and passing linting and style enforcers.
83
+ - Successful generation of documentation.
84
+
85
+ #### Release Tagging
86
+
87
+ The project shall be tagged when a new feature or bug fix is merged into main. The tag shall follow
88
+ [semantic versioning](https://semver.org) for labels.
89
+
90
+ ```text
91
+ vMAJOR.MINOR.PATCH
92
+ ```
93
+
94
+ ### Project Structure
95
+
96
+ Files and directories shall be lower case, where capital is not required by a tool, and contain no
97
+ `' '`.
98
+
99
+ ```text
100
+ 📁 .
101
+ ├── 📁 .github
102
+ │ ├── 📁 ISSUE_TEMPLATE
103
+ │ ├── 📁 PULL_REQUEST_TEMPLATE
104
+ │ ├── 📁 workflows
105
+ │ └── 📝 pull_request_template.md
106
+ ├── 📁 .vscode
107
+ │ └── ⚙️ launch.json
108
+ ├── 📁 docs
109
+ │ ├── 📁 contentt
110
+ │ │ ├── 📁 madr
111
+ │ │ ├── 📁 units
112
+ │ │ └── 📁 usecase
113
+ │ ├── 📁 infra
114
+ │ └── 📖 README.md
115
+ ├── 📁 arduino_src
116
+ │ └── 🇨 arduino_src.ino
117
+ ├── 📁 slasd
118
+ │ └── 🐍 __init__.py
119
+ ├── ⚙️ .editorconfig
120
+ ├── 🙈 .gitignore
121
+ ├── 🛠️ .pre-commit-config.yaml
122
+ ├── ⚙️ .rumdl.toml
123
+ ├── ❄️ flake.lock
124
+ ├── ❄️ flake.nix
125
+ ├── 🛠️ Justfile
126
+ ├── 📜 LICENSE
127
+ ├── 📄 mkdocs.yml
128
+ ├── 🐍 pyproject.toml
129
+ └── 🔒 uv.lock
130
+ ```
131
+
132
+ ### Directories of Interest
133
+
134
+ - docs: This directory contains the high level documentation for the tool.
135
+ - arduino_src: This directory contains the source code of the Arduino side of the tool.
136
+ - slasd: This directory contains the source code of the python side of the tool.
137
+ - .github: This directory contains the GitHub infrastructure.
138
+ - .vscode: This directory contains the debugger configuration.
139
+
140
+ ### Define a Unit
141
+
142
+ A unit shall be a Python module.
143
+
144
+ ### Quality
145
+
146
+ The tool and its units shall fail-safe, that is the tool and its units can fail, but the failure
147
+ must be detectable. A segfault is okay, an off by one error that computes the wrong value is not.
148
+
149
+ #### Unit Testing
150
+
151
+ No unit testing in the project only integration testing. See
152
+ [ADR00002][./content/madr/00002_testing.md].
153
+
154
+ #### Integration Testing
155
+
156
+ The plugin shall have manual integration testing.
157
+
158
+ ### Requirements
159
+
160
+ #### Use Cases
161
+
162
+ Requirements are described as a collection of use cases and actors. There are two collections of the
163
+ artifacts one for the [client](./content/arduino_src/usecase/) and one for the
164
+ [server](./content/python_driver/usecase/usecase/). These are in turn collected into the following
165
+ use case diagrams:
166
+
167
+ ##### Client
168
+
169
+ ```mermaid
170
+ flowchart LR
171
+ aS["👤 Server"]
172
+
173
+ SS(["Set State"])
174
+ RC(["Receive Command"])
175
+
176
+ aS --> RC
177
+
178
+ RC -. include .->SS
179
+ ```
180
+
181
+ ##### Server
182
+
183
+ ```mermaid
184
+ flowchart LR
185
+ aU["👤 User"]
186
+
187
+ SC(["Send Command"])
188
+
189
+ aU --> SC
190
+ ```
191
+
192
+ ##### Architectural Decisions
193
+
194
+ Architectural decisions [MADR](<https://github.com/adr/madr>) [@Kopp2018] serve as the primary
195
+ documentation for architectural decisions.
196
+
197
+ The following is the order of operations for the proposal of a MADR:
198
+
199
+ 1. Create a branch for a proposal with the name:
200
+
201
+ ```text
202
+ proposal-{{short title}}
203
+ ```
204
+
205
+ 1. Create a pull request with this template.
206
+ 1. In the branch create a Markdown file based on the
207
+ [MADR Template](https://github.com/adr/madr/blob/4.0.0/template/adr-template.md). Name the
208
+ Markdown file:
209
+
210
+ ```text
211
+ {{issue# padded to five digits}}-{{title}}
212
+ ```
213
+
214
+ 1. When a decision is made change the status to:
215
+ - "accepted" and pull the branch into main branch
216
+ - "rejected" and pull the branch into main branch
217
+
218
+ #### Nonfunctional Requirements
219
+
220
+ ##### Colors
221
+
222
+ Diagrams included in documentation for features (use case and unit descriptions) are expected to use
223
+ the [COLORS](https://clrs.cc) color palette.
224
+
225
+ ##### Technologies
226
+
227
+ ###### Languages and Frameworks
228
+
229
+ - git
230
+ - Python
231
+ - mermaid.js
232
+ - prek
233
+ - tombi
234
+ - rumdl
235
+ - ruff
236
+ - uv
237
+ - MADR[@Kopp2018]
238
+
239
+ ###### Documentation of Implementation
240
+
241
+ ###### Code Style Guide
242
+
243
+ Python code shall be formatted with ruff using the included style settings. Markdown files shall be
244
+ formatted with ruff using the included style settings. TOML files shall be formatted with tombi
245
+ using the included style settings.
246
+
247
+ ## Design and Documentation
248
+
249
+ ### System
250
+
251
+ #### Block Diagram
252
+
253
+ ```mermaid
254
+ flowchart LR
255
+ device["BaseLedAnimator"]
256
+ cwr@{ shape: processes, label: "{{Collection}}<br>Device specific animators" }
257
+ commands@{ shape: processes, label: "{{Collection}}<br>Available Commands" }
258
+
259
+ device --> cwr
260
+ device --> commands
261
+ ```
262
+
263
+ #### Class Diagram
264
+
265
+ ```mermaid
266
+ classDiagram
267
+ class LedAnimationDevice {
268
+ + init( COMport, baud, timeout, ledCount)
269
+ + connect()
270
+ + compNSndState( **kwargs)
271
+ - sendState( data)
272
+ - computeState( **kwargs)
273
+ }
274
+
275
+ class FastrakAnimationDevice {
276
+ - computeState( **kwargs)
277
+ }
278
+
279
+ class FastrakParams {
280
+ + FastrakPostion posData
281
+ + int angleToLight
282
+ + int colorR
283
+ + int colorG
284
+ + int colorB
285
+ }
286
+
287
+
288
+ class SetLedState {
289
+ + init(self, data)
290
+ }
291
+
292
+ class SerialCommand {
293
+ <<interface>>
294
+ - str commandId
295
+ - bytearray payload
296
+ + send(ser)
297
+ }
298
+
299
+ SetLedState ..|> SerialCommand
300
+
301
+ FastrakAnimationDevice --|> LedAnimationDevice
302
+
303
+ FastrakParams --* FastrakAnimationDevice
304
+ SetLedState --* LedAnimationDevice
305
+
306
+ ```
307
+
308
+ #### Unit Designs
309
+
310
+ Unit designs (and test description) for the client and server units (public members and methods) is
311
+ found under their respective documentation sections.
@@ -0,0 +1,10 @@
1
+ slasd/__init__.py,sha256=1woE1s4Dk7Jc9BGPlw4xKww2jBURu8Nf-ZzIkr8yMEI,248
2
+ slasd/fastrakAnimator.py,sha256=qfE3q4pyyOblf2SyZxkOFsxgeP3PvvelBBxtxrnMX_I,4471
3
+ slasd/ledAnimationDriver.py,sha256=Td-JTnqrrRZ4n3TBGjPxnB-M2xVJoFspRJGGQosnD9Y,2971
4
+ slasd/commands/__init__.py,sha256=n2FGkS4jOSkVD_NJhlYWtpvSDvD_Fxw97gdKEqoSTXw,55
5
+ slasd/commands/command.py,sha256=qPvRmlArAseudxJs-pCy05x8t4Fo9N5Z-aMUnVybt7Y,756
6
+ slasd/commands/noResp.py,sha256=S1o8DEfe_MgGFGBLNAL7dDVoC5bXn_pFiR-uwnvQlRA,555
7
+ slasd-0.0.0.dist-info/METADATA,sha256=yUaTCQIw7AsAuJUxVwc2phaowanh0uPY4-f-GS7jVcs,8553
8
+ slasd-0.0.0.dist-info/WHEEL,sha256=W3fkpkm7-wf9vBI5Z-7s0eWkeM-spu78I8Neb98DeEg,87
9
+ slasd-0.0.0.dist-info/licenses/LICENSE,sha256=1MTKLFfNXi_QF-p6BP0RGjsHRii9jsza5z1ExtHxvwM,1065
10
+ slasd-0.0.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.32.4
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Joe Starr
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.