-
Notifications
You must be signed in to change notification settings - Fork 35
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
Documentation / Examples -- Queue Handling and Safety #5
Comments
Thank you, I hope it will be useful for you. I've deliberately not included variables in events because of the complexity it would add to the queuing, but I welcome a discussion/suggestions on how this could be done. I've just not found a way that I'm happy with yet. It's possible to use uFSM without the queue, but with some limitations;
I think generally you would want to use the queue function but there are cases where it might not be needed, for example the test cases, as you have noted, the queue is not needed for some of them. I wanted to allow for some flexibility with the queue to support different kind of locking mechanisms. The queue has a few optional event handlers, 'on_data', 'lock' and 'unlock'. the dhcpclient example, although not complete, might be a good example to show how these can be used in a linux program. The lock/unlock tries to lock a mutex. I imagined that in an embedded application the 'main loop' would try to de-queue an event and post it with the ufsm_process function and if there are no more events for processing it would run the 'WFI' instruction, stopping the CPU until the next interrupt. In the embedded context, the lock/unlock would disable/enable global interrupts. That's why only the get function calls on these callbacks, I wanted a non blocking put-function that can be called from an interrupt context. I think you should be able to do what you want in a safe way. Have a look at the dhcpclient -example. Hope this helps! |
Actually thinking about this I need to make some adjustments to the queueing system. I think it could work for a scenario where there is one producer and one consumer, but with many thread or nested IRQ's it will probably break. |
Thanks! I like the elegant design of the system overall. I'm having some difficulty understanding some of the semantics and mapping those into my embedded use case. The semantics of SCXML are still much more clear to me, though the XMI events are starting to make a bit of sense. The SCXML spec includes a lot more details on what data events need to have, etc but most concepts have a mapping.
That is very similar to what I'd like to do. The main difference is that I want to have states which repeat until some condition is met (timeout, counting, or value based). A good example would be a sensor reading:
Most of this could be done readily with global variables and custom functions. Ideally for a fully user-configurable system, only base functions like SPI xfers and a general count functionality would be needed. Then if you flash an Arduino with a a general Sensor SM, one could read / write arbitrary sensors without needing to re-flash the device, just send a new SC description. The last part would require a system to create the states/transitions, but wouldn't be too tricky with MessagePack and some fiddly. But can't think of how to do that without some sort of event data. I'll keep pondering that and post any ideas if they come up. |
I've been reviewing the uSCXML library, which emits a C code based SCXML state machine. It's mostly similar to uFSM but targets SCXML and has support for event data. It mainly uses It'd be possible to tweak uFSM to be able to support user defined data models, at least for single-threaded uses by modifying a few things. First would be passing That can all be done using global variables (at least single threaded) but would be much cleaner and stable if added to the state machine context. BTW, is there currently a way for do_actions to get the current event safely? Between that and figuring out how to get a state to run to completion or add new events, I would be able to to add some sort of SCXML support! I'll try and add at least some sort of basic support for data contexts in the next week or so on my fork and see how it turns out... |
I think understand what you are trying to do. I do belive that this will quickly become quite complicated. Getting pointers to various things through to the event handlers is probably easy, it's also easy But... How would this work if you queue more than one event of the same type but with different data/parameters? Smells like you would need dynamic memory to do this and also the concurrency problem you have noted, which again could be solved by At the moment there is no safe way to extract the current state, this would not be so difficult to add using a locking mechanism for the transition algorithm. But I do wonder when you would need this, if you actually need current state information outside of the actual state machine, doesn't that imply that your are building logic and making descisions that Also, another note on the current state, this could be several states, sometimes because states can be nested in composit states and sometimes because of orthogonal regions in a composite state (or combinations). This can of course be extracted but could be a computationally intense operation if there are deeply nested regions. A final comment on your SPI example, if we're taking micro seconds and sampling of sensors we likely have hard, real time, By the way, what tools are you using to draw SCXML? so far I've only found / tested the SCXML -tool built into "QT desiger" |
Good comments! I went over and modified the code last night to pass the state / transition pointers. That was pretty easy. But handling the data and the events is where it got interesting... I'm mulling over how to handle data. Especially the benefit / complexity tradeoffs. I like the library because it's simple and flexible, adding complexity would be counter productive. For locking the data, it looks like there's already Personally, I'm planning on using dynamic allocation external to the library for loading. If the main library can have "slots" for user data with void pointers, it'd let the end user decide if they want it all static (say a static queue) of dynamic, or pools, etc. My current use case is building on the BeagleBone PRU's. They're single threaded and no interrupts. However, they're very deterministic which is what I'm going for, so having allocations during execution wouldn't work. I'd allocate any data upon loading the SM, but otherwise it should be static while running. |
About needing the state machine contexts external to the SM (in the guards, actions, etc).. I looked through it a bit more. the uSCXML library had them so I'd assumed they're necessary for some SCXML support. I'm not so sure after looking through the SCXML spec (grepping for "must" is pretty handy). The only thing I could find was related to parallel (orthogonal in xmi (?)) states where a transition can be conditional based on the sibling states <parallel id="oven">
...
<state id="engine">
...
<state id="cooking">
<transition cond="In('open')" target="idle"/>
<!-- a 'time' event is seen once a second -->
<transition event="time">
<assign location="timer" expr="timer + 1"/>
</transition>
</state>
</state>
</state>
<!-- this region tracks the microwave door state -->
<state id="door">
...
<state id="closed">
<transition event="door.open" target="open"/>
</state>
...
</state>
</parallel> Though having a way to support something like Currently I'm writing SCXML just by hand. I find it pretty easy to read & write (at least for smaller examples). Much easier than the XMI format. If I need to move toward graphical editing I'd like to take up creating a vuejs or web-component renderer / editor. I'd experimented with the idea before, and it'd pretty easy using web-components to create components which map to SCXML elements with a simple prefix/name mapping. There's some pure JS libraries out there that handle SCXML as well. |
Storing <int, void*> pair's on the queue would be straight forward. That
way it would also be possible to preserve the way the API is today. I'm
thinking that we could add another queue_put -function.
The 'ufsm_queue_put(struct ufsm_queue *q, uint32_t ev)' is kept as is but
we add another function 'ufsm_queue_put2(struct ufsm_queue *q, uint32_t ev,
void *data)'. The original function would just add the event and NULL
pointers.
I started a small branch to test some of this stuff:
https://github.com/jonpe960/ufsm/tree/queuetest
One thing that occurred to me while doing this was how to manage the life
cycle of the allocated event data. Let's imagine the following scenario:
1) Allocate some data and post event to queue
2a) Event could not be processed -> Callback to de-allocate data
2b) Transition reaches a pseudo state (Entry, Exit or connection point
reference), here we likely would want to copy the data pointer over to the
"other side". This makes it tricky to understand when the transition has
completed and when it is OK to de-allocate data
2c) Reaches a state with a DO activity -> de-allocate callback when state
completes or callback when state is exited while the DO acitivity is still
running
2d) Reaches a history state, if the history state has a transition carrying
data to another state this data would have to be preserved as well, even
when the owning state is not active. De-allocation could only happen when
the history state is not pointing to a state that does not expect any event
data.
//Jonas
…On Thu, Jun 14, 2018 at 12:14 AM Jaremy Creechley ***@***.***> wrote:
About needing the state machine contexts external to the SM (in the
guards, actions, etc).. I looked through it a bit more. the uSCXML library
had them so I'd assumed they're necessary for some SCXML support.
I'm not so sure after looking through the SCXML spec (grepping for "must"
is pretty handy). The only thing I could find was related to parallel
states where a transition can be conditional based on the sibling states <transition
cond="In('open')" target="idle"/> where the In('open') is a predicate for
detecting the current state. It could potentially be handled internally to
the SM or via the data model purely by users.
<parallel id="oven">
...
<state id="engine">
...
<state id="cooking">
<transition cond="In('open')" target="idle"/>
<!-- a 'time' event is seen once a second -->
<transition event="time">
<assign location="timer" expr="timer + 1"/>
</transition>
</state>
</state>
</state>
<!-- this region tracks the microwave door state -->
<state id="door">
...
<state id="closed">
<transition event="door.open" target="open"/>
</state>
...
</state>
</parallel>
Though having a way to support something like <assign location="timer"
expr="timer + 1"/> would be great, I'll probably start with just pre-set
setters (like you do in the guard examples and setting flags). But even
then the transition / entries / exits would only need access to the current
data model.
Currently I'm writing SCXML just by hand. I find it pretty easy to read &
write (at least for smaller examples). Much easier than the XMI format. If
I need to move toward graphical editing I'd like to take up creating a
vuejs or web-component renderer / editor. I'd experimented with the idea
before, and it'd pretty easy using web-components to create components
which map to SCXML elements with a simple prefix/name mapping. There's some
pure JS libraries out there that handle SCXML as well.
—
You are receiving this because you commented.
Reply to this email directly, view it on GitHub
<#5 (comment)>, or mute
the thread
<https://github.com/notifications/unsubscribe-auth/ANUXFdFPXJNscGB8PFrexrOGf55itMWdks5t8Y62gaJpZM4UhChH>
.
|
That's looking promising. I'll try it out with some simple data models. Wonder if I could get a "count down" working... |
Made a simple test for the equivalent of an SCXML |
Great little project! I've been reading through it and think I'll be able to use for an upcoming project. Ideally I'd like to try adding some support for SCXML. Looks like almost everything needed is present aside from the variables in events.
However, I haven't been able to understand how the queue interacts with the events. Is it all supposed to be driven from the outside, like in the
test_process
function? In thesimple
example you just callprocess_event
.Ideally, I'd like to be able to have a state add an event to do a self-transitions or triggers. It looks like it'd be possible, but I'm not sure if it'd be safe/possible to enqueue an event from an entry/exit action.
The text was updated successfully, but these errors were encountered: