Closed eeberhard closed 2 months ago
Example of the new syntax:
# yaml-language-server: $schema=https://raw.githubusercontent.com/aica-technology/api/feat/draft-v2.0.0/docs/static/schemas/draft/2-0-0/application.schema.json
schema: draft/2-0-0
dependencies:
base: v4.0.0
packages:
- { package: "@aica/components/pose-detection", version: "v1.0.0" }
- { package: "@aica/collections/ur-collection", version: "v3.1.0", constraints: ">= 3.0.0" }
metadata:
name: My application
description: Some description of the application
tags:
- "motion"
- "force control"
- "universal robots"
userdata:
info: "..."
website: github.com/eeberhard
on_start:
load:
- component: my_component
- hardware: my_hardware
sequence:
start: my_sequence
conditions:
my_condition:
display_name: My cool condition
condition:
any:
- component: foo
state: active
- controller: foo
hardware: bar
predicate: baz
events:
unload:
component: foo
sequences:
my_sequence:
display_name: My cool sequence
steps:
- delay: 10
- check:
condition:
condition: my_condition
else:
load:
component: foo
- check:
condition:
component: foo
state: active
wait_forever: true
- check:
condition:
sequence: foo
state: active
timeout: 10
else:
unload:
component: foo
hardware:
my_hardware:
urdf: foo
rate: 12.5
rate_tolerance: 0.99
strict: true
events:
transitions:
on_load:
load:
controller: my_controller
hardware: my_hardware
controllers:
my_controller:
plugin: my_controller/Foo
events:
transitions:
on_load:
switch_controllers:
hardware: my_hardware
activate: my_controller
predicates:
is_stable:
sequence:
start: my_sequence
components:
foo:
component: my_package::Foo
parameters:
a: foo
b: bar
inputs:
my_input: /foo/my_input
outputs:
my_output: /foo/my_output
events:
transitions:
on_configure:
lifecycle: activate
predicates:
is_in_range:
unload:
component: foo
graph:
positions:
on_start:
x: 20
y: 40
conditions:
my_condition:
x: 0
y: 0
sequences:
my_sequence:
x: 0
y: 0
components:
foo:
x: 0
y: 0
hardware:
my_hardware:
x: 0
y: 0
Thanks for the feedback! Actually a lot of your points come down to this new way of triggering events from state transitions.
now under events we need to have an additional property transitions or predicates
As you said, we need to distinguish these two types of event drivers. I considered a flatter structure like this but I didn't like it as much:
my_component:
component: my_component::Foo
transition_events: # or just `transitions:`
on_load: ...
predicate_events: # or just `predicates:`
is_in_range: ...
on load, we activate the controller but how can you be sure that the controller is already loaded..
Predicates trigger events on any rising edge. But the intention with state transition events is that they trigger an event iff the specific transition is completed. In the implementation of transition events, I would only trigger on_activate
events after the component / controller / etc has actually been activated. Because the underlying engine already manages and monitors the states of all components, we can actually avoid race conditions with this pattern better than the current implementation.
I can see the confusion because in the current implementation, on_load
is a fake predicate that is actually triggered asynchronously with component loading and does not actually guarantee that the component has been fully constructed. The other potentially confusing point is that a transition like on_activate
looks a lot like the transition callback function in the modulo component SDK (which is partly intentional), but in the SDK that function is called during the transition, not after. On that note...
Why do we go from is_active to on_activate?
I wanted to use a recognizable and consistent "transition callback" language similar to the component SDK. The previous predicate formulation is_active
triggered the event every time the component state changed from not active to active. For that particular transition it is equivalent. But now consider the is_inactive
predicate, which could be triggered both after configuring or after deactivating. So the name of state transitions events should reflect the triggering transition like on_configure
and on_deactivate
.
You might call it after_load
, after_configure
, ... but to me the on_
feels right.
Are those really all the states [...] about controllers? no inactive or configured?
Actually controllers have full lifecycle states but are automatically configured and go to inactive
when loaded. The only transitions we really control are loading, activating, deactivating and unloading (and thereby the only primary states the controller stays in are unloaded, inactive or active).
Part of the reason of this being a draft is to just try the implementation and see what states actually make sense to control and expose for hardware and controllers.
As you said, we need to distinguish these two types of event drivers. I considered a flatter structure like this but I didn't like it as much
Agreed.
Because the underlying engine already manages and monitors the states of all components, we can actually avoid race conditions with this pattern better than the current implementation.
Absolutely, and this will constitute a huge improvement obviously. I know all that but that underlying mechanism might not fully transparent to the user.
I wanted to use a recognizable and consistent "transition callback" language similar to the component SDK
I agree about similar language but part of me still think it's too similar with the on_activate
. A user that writes a component with the SDK has to know what to put in the on_activate_callback
and then we tell him that if you put on_activate
into the YAML app, it will be triggered after on activate. I don't know...
after_
just doesn't look nice as a prefix :smile: I think we start with this version and see how we feel about it later
Very cool stuff! 🔥
One thing that we should consider is enforcing the jsonschema to not allow a list of load/lifecycle events in a given sequence step.
One thing that we should consider is enforcing the jsonschema to not allow a list of load/lifecycle events in a given sequence step.
Can you elaborate with an example? Is that something that's already not correct in the current schema or did we miss this in this PR?
One thing that we should consider is enforcing the jsonschema to not allow a list of load/lifecycle events in a given sequence step.
Can you elaborate with an example? Is that something that's already not correct in the current schema or did we miss this in this PR?
Here is an example with a small sequence that loads a list of components. In the current implementation, this is allowed in the schema but fails at execution indicating invalid load payload.
sequences:
prepare_components:
# load the components
- load:
- component: assembled_frame_broadcaster
- component: approach_frame_broadcaster
- component: approach_tf_to_signal
when changed to a load step for each component, it works as expected.
sequences:
prepare_components:
# load the components
- load:
component: assembled_frame_broadcaster
- load:
component: approach_frame_broadcaster
- load:
component: approach_tf_to_signal
This PR also addresses issues [...]
Thanks @domire8 !
One thing that we should consider is enforcing the jsonschema to not allow a list of load/lifecycle events in a given sequence step
I think this is something to discuss further in the parent issue #147, but as @yrh012 points out, the current schema allows a sequence step to contain a "multi-event structure". However, in the current backend implementation (using base v3.4.0 for example), a sequence step can only support a single event.
This basically means there is a discrepancy between the current schema and implementation, and the observation of that is more of a general bug / issue report. However the relevant thing to discuss for the point of syntax 2.0 is whether to [1] change the schema to match the implementation (restrict a sequence step to a single event) or [2] do not change the schema and instead fix the implementation to support a multi-event structure on a sequence step. The option [1] is a breaking syntax change and so would need t be part of 2.0.
As discussed, I will merge this now such that we can start to roll out the draft/2-0-0 implementations. We can then make additional changes based on the comments as discussed before we officially release it.
Description
147
Review guidelines
Estimated Time of Review: 90 minutes
Review by commit
Checklist before merging: