Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Publisher command: evaluate user expressions, support joysticks #19

Merged
merged 45 commits into from
Apr 15, 2021
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
6a9201f
Add initial MIDI support
pavel-kirienko Apr 9, 2021
9d3cf7d
Fix channel treatment
pavel-kirienko Apr 9, 2021
d64ec76
Add joystick support via SDL2 and a monitoring command
pavel-kirienko Apr 9, 2021
2d6196f
Linting
pavel-kirienko Apr 10, 2021
5c6fc94
Add tests for the joystick command and fix deps
pavel-kirienko Apr 10, 2021
fd7f279
Fix SDL2 deps
pavel-kirienko Apr 10, 2021
72f5454
Fix syntax error in setup.cfg and add libsdl2
pavel-kirienko Apr 10, 2021
d25d3ad
Fix libsdl2 package name
pavel-kirienko Apr 10, 2021
b2fbf53
Add RtMidi
pavel-kirienko Apr 10, 2021
26fab46
Update package index
pavel-kirienko Apr 10, 2021
e2b09e9
Refactor the publisher into a package and add MessageBuilder and Cont…
pavel-kirienko Apr 11, 2021
a1ebcf7
Refactor things slightly
pavel-kirienko Apr 11, 2021
ac60785
Fix the state coupling problem
pavel-kirienko Apr 11, 2021
0a25d6c
Add message factory parser test
pavel-kirienko Apr 11, 2021
9789b0a
Add null controller
pavel-kirienko Apr 11, 2021
0baae49
Enhance tests
pavel-kirienko Apr 11, 2021
4e2204a
Message factory test
pavel-kirienko Apr 11, 2021
43c5a4e
Control reader test
pavel-kirienko Apr 11, 2021
3f0dba7
Log controller listing exception instead of propagating it to support…
pavel-kirienko Apr 11, 2021
6f804e2
Rename hid_controller
pavel-kirienko Apr 11, 2021
333c548
Suppress MyPy
pavel-kirienko Apr 11, 2021
b309c47
Mention the virtual controller
pavel-kirienko Apr 11, 2021
73b3843
Fix linter warnings
pavel-kirienko Apr 11, 2021
bd6272f
Refactor YAML
pavel-kirienko Apr 12, 2021
51b9730
Rename YAML classes
pavel-kirienko Apr 12, 2021
f9648d1
Fix list_controllers
pavel-kirienko Apr 12, 2021
107bee9
Ignore missing APT deps
pavel-kirienko Apr 12, 2021
160fe10
MyPy fix
pavel-kirienko Apr 12, 2021
ed39923
Ignore apt-get update failure
pavel-kirienko Apr 12, 2021
5a95b5b
MIDI: return defaultdicts
pavel-kirienko Apr 13, 2021
aa62277
Catch unsupported tags
pavel-kirienko Apr 13, 2021
5cff4c9
Integrate the evaluable loader with the publisher command
pavel-kirienko Apr 13, 2021
65593de
Fix YAML traversal
pavel-kirienko Apr 13, 2021
cd2fb8c
Fix linting errors
pavel-kirienko Apr 13, 2021
49b7956
Unbreak test
pavel-kirienko Apr 13, 2021
aba3f72
Add expression tests
pavel-kirienko Apr 13, 2021
f82b14f
Fix test
pavel-kirienko Apr 13, 2021
fe624fb
Fix the other test
pavel-kirienko Apr 13, 2021
551efdd
Advance the README
pavel-kirienko Apr 13, 2021
3c3e2fa
Add 'y' alias
pavel-kirienko Apr 15, 2021
e9c2702
Add pub docs
pavel-kirienko Apr 15, 2021
f125a37
Update the README
pavel-kirienko Apr 15, 2021
914706e
Update README
pavel-kirienko Apr 15, 2021
f4d6437
Manage blank lines
pavel-kirienko Apr 15, 2021
3bc32fd
Windows-specific SDL2-related fix: SDL_INIT_VIDEO is required on Windows
pavel-kirienko Apr 15, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Update the README
  • Loading branch information
pavel-kirienko committed Apr 15, 2021
commit f125a3759770f9faa5669d1304c90983eb8c76f9
111 changes: 60 additions & 51 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ as long as the resulting abbreviation is unambiguous.

There is a dedicated `--help` option for every subcommand.

Yakut may also be invoked via its alias **`y`** as long as this name does not conflict with another installed program.


## Compiling DSDL

Suppose we have our custom DSDL namespace that we want to use.
Expand Down Expand Up @@ -110,6 +113,7 @@ $env:YAKUT_PATH="$env:YAKUT_COMPILE_OUTPUT"
So that you say simply `yakut compile path/to/my_namespace`
knowing that the outputs will be always stored to and read from a fixed place unless you override it.


## Communicating

Commands that access the network need to know how to do so.
Expand Down Expand Up @@ -172,82 +176,85 @@ consider configuring it as the default via environment variables as shown earlie

Next there are practical examples (configuring the transport is left as an exercise to the reader).

### Subscribing to subjects

Subscribe to subject 33 of type `uavcan.si.unit.angle.Scalar.1.0` as shown below;
notice how we specify the subject-ID before the data type name.
You will see output if there is a publisher on this subject (see the next section).

```bash
$ export UAVCAN__UDP__IFACE=127.63.0.0
$ yakut sub 33:uavcan.si.unit.angle.Scalar.1.0
---
33:
_metadata_:
timestamp: {system: 1608987583.298886, monotonic: 788272.540747}
priority: nominal
transfer_id: 0
source_node_id: 42
radian: 2.309999942779541

---
33:
_metadata_:
timestamp: {system: 1608987583.298886, monotonic: 788272.540747}
priority: nominal
transfer_id: 1
source_node_id: 42
radian: 2.309999942779541
```

### Publishing messages

Publishing two messages synchronously twice (four messages total);
notice how we specify the subject-ID before the data type name:
Publishing two messages synchronously twice (four messages total):

```bash
export UAVCAN__UDP__IFACE=127.63.0.0
export UAVCAN__NODE__ID=42
yakut pub 33:uavcan.si.unit.angle.Scalar.1.0 'radian: 2.31' uavcan.diagnostic.Record.1.1 'text: "2.31 rad"' -N2
yakut pub -N2 33:uavcan.si.unit.angle.Scalar.1.0 'radian: 2.31' uavcan.diagnostic.Record.1.1 'text: "2.31 rad"'
```

We did not specify the subject-ID for the second subject, so Yakut defaulted to the fixed subject-ID.

The above example publishes constant values which is rarely useful.
You can define arbitrary Python expressions that are evaluated by Yakut before publication.
Such expressions are entered as strings marked with YAML tag `!$`.
There may be an arbitrary number of such expressions in a YAML string,
and their results may be arbitrary as long as the final structure can initialize the specified message type.
In the following example, we define an array of three elements, where the first and last elements are evaluated
dynamically (try it):
There may be an arbitrary number of such expressions in a YAML document,
and their results may be arbitrary as long as the final structure can initialize the specified message.
The following example will publish a sinewave with frequency 1 Hz, amplitude 10 meters:

```bash
yakut pub 33:uavcan.primitive.array.Real64.1.0 'value: [!$ 1+n+t*0.1, 123456, !$ time()]'
yakut pub -T 0.01 1234:uavcan.si.unit.length.Scalar.1.0 '{meter: !$ "sin(t * pi * 2) * 10"}'
```

At least the following symbols are available for use in expressions (see `yakut pub --help` for the full list):

- Variables:
- `n :int` --- message index starting from 0.
- `t :float` --- time delta since first message.
Notice that we make use of variables like `t` or standard functions like `sin` in the expression.
You will see the full list of available symbols and functions if you run `yakut pub --help`.

- HID controller access functions (more on this below):
- `A(controller, axis :int) -> float` --- read controller axis value normalized into `[-1, +1]` or `[0, +1]`,
depending on the type of controller (unipolar or reversible).
- `B(controller, button :int) -> bool` --- read controller push button state (true if currently pressed).
- `T(controller, toggle :int) -> bool` --- read controller toggle switch state.
One particularly important capability of this command is the ability to read data from connected
joysticks or MIDI controllers.
It allows the user to control UAVCAN processes or equipment in real time, simulate sensor feeds, etc.
Function `A(x,y)` returns the normalized value of channel `y` from connected controller `x`
(for full details see `yakut pub --help`);
likewise, there is `B(x,y)` for push buttons and `T(x,y)` for toggle switches.
The next example shows how to publish 3D angular velocity setpoint, thrust setpoint, and the arming switch state,
allowing the user to control these parameters interactively:

- The following Python standard library modules are wildcard-imported:
- [random](https://docs.python.org/library/random.html) (e.g., `random()`)
- [time]( https://docs.python.org/library/time.html) (e.g., `monotonic()`)
- [math]( https://docs.python.org/library/math.html) (e.g., `sin(x)`)
```bash
yakut pub -T 0.1 \
5:uavcan.si.unit.angular_velocity.Vector3.1.0 'radian_per_second: !$ "[A(1,0)*10, A(1,1)*10, (A(1,2)-A(1,5))*5]"' \
6:uavcan.si.unit.power.Scalar.1.0 'watt: !$ A(2,10)*1e3' \
7:uavcan.primitive.scalar.Bit.1.0 'value: !$ T(1,5)'
```

- The following modules are imported:
- [pyuavcan](https://github.com/UAVCAN/pyuavcan/)
The list of available controllers and how their axes are mapped can be seen using `yakut joystick`,
as shown in the video:

[![yakut joystick](https://img.youtube.com/vi/YPr98KM1RFM/maxresdefault.jpg)](https://www.youtube.com/watch?v=YPr98KM1RFM)

[![yakut publish](https://img.youtube.com/vi/DSsI882ZYh0/maxresdefault.jpg)](https://www.youtube.com/watch?v=DSsI882ZYh0)


### Subscribing to subjects

Subscribe to subject 33 of type `uavcan.si.unit.angle.Scalar.1.0`
to receive messages published by the above command:
Here is an example where a MIDI controller is used to interactively change the frequency and amplitude of a sinewave:

```bash
$ export UAVCAN__UDP__IFACE=127.63.0.0
$ yakut sub 33:uavcan.si.unit.angle.Scalar.1.0
---
33:
_metadata_:
timestamp: {system: 1608987583.298886, monotonic: 788272.540747}
priority: nominal
transfer_id: 0
source_node_id: 42
radian: 2.309999942779541
[![yakut publish](https://img.youtube.com/vi/DSsI882ZYh0/maxresdefault.jpg)](https://www.youtube.com/watch?v=DSsI882ZYh0)

---
33:
_metadata_:
timestamp: {system: 1608987583.298886, monotonic: 788272.540747}
priority: nominal
transfer_id: 1
source_node_id: 42
radian: 2.309999942779541
```

### Invoking RPC-services

Expand Down Expand Up @@ -283,6 +290,7 @@ $ yakut call 42 123:sirius_cyber_corp.PerformLinearLeastSquaresFit.1.0 'points:
y_intercept: 0.0
```


## Monitoring the network

The command `yakut monitor` can be used to display *all* activity on the network in a compact representation.
Expand All @@ -306,6 +314,7 @@ In the latter case it will actively query other nodes using the standard introsp
Some transports, UAVCAN/UDP in particular, require special privileges to run this tool due to the security
implications of low-level packet capture.


## Updating node software

The file server command can be used to serve files,
Expand Down
7 changes: 3 additions & 4 deletions yakut/cmd/publish/_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,6 @@ def load(what: Sequence[ExpressionContextModule]) -> Dict[str, Any]:
ExpressionContextModule("scipy.spatial", "https://docs.scipy.org/doc/scipy/reference/spatial.html"),
]

# TODO: update/extend the examples
_EXAMPLES = """
Example: publish constant messages (no embedded expressions, just regular YAML):

Expand All @@ -88,7 +87,7 @@ def load(what: Sequence[ExpressionContextModule]) -> Dict[str, Any]:
yakut pub -T 0.01 1234:uavcan.si.unit.length.Scalar.1.0 '{meter: !$ "sin(t * pi * 2) * 10"}'

Example: as above, but control the frequency of the sinewave and its amplitude using sliders 10 and 11
of the first connected HID controller (use `yakut joystick` to find connected HID controllers and their axis mappings):
of the first connected controller (use `yakut joystick` to find connected controllers and their axis mappings):

\b
yakut pub -T 0.01 1234:uavcan.si.unit.length.Scalar.1.0 '{meter: !$ "sin(t * pi * 2 * A(1,10)) * 10 * A(1,11)"}'
Expand Down Expand Up @@ -144,8 +143,8 @@ def load(what: Sequence[ExpressionContextModule]) -> Dict[str, Any]:
{Executor.SYM_DTYPE}: Type[pyuavcan.dsdl.CompositeType] --- message class.

{Executor.SYM_CTRL_AXIS}: (controller,axis:int)->float ---
read the normalized value of `axis` from HID `controller` (e.g., joystick or MIDI fader).
To see the list of available HID controllers and determine their channel mapping, refer to `yakut joystick --help`.
read the normalized value of `axis` from `controller` (e.g., joystick or MIDI fader).
To see the list of available controllers and determine their channel mapping, refer to `yakut joystick --help`.

{Executor.SYM_CTRL_BUTTON}: (controller,button:int)->bool ---
read the state of `button` from `controller` (true while held down).
Expand Down