aboutsummaryrefslogtreecommitdiff
path: root/examples/rp
diff options
context:
space:
mode:
authorrafael <[email protected]>2024-07-28 00:19:54 +0200
committerrafael <[email protected]>2024-07-28 00:19:54 +0200
commite05e5d33f0ab832b2ea1c48674c99b41581118be (patch)
treebcc0313aa9c58b8e587d1460d892a3402283204b /examples/rp
parent5d46b694ca60ee69d83eb2bd53f592b2c5b03d28 (diff)
review comments
Diffstat (limited to 'examples/rp')
-rw-r--r--examples/rp/src/bin/orchestrate_tasks.rs66
1 files changed, 59 insertions, 7 deletions
diff --git a/examples/rp/src/bin/orchestrate_tasks.rs b/examples/rp/src/bin/orchestrate_tasks.rs
index b3c37de4c..0e21d5833 100644
--- a/examples/rp/src/bin/orchestrate_tasks.rs
+++ b/examples/rp/src/bin/orchestrate_tasks.rs
@@ -14,6 +14,7 @@
14//! - a task that generates random numbers in intervals of 90s 14//! - a task that generates random numbers in intervals of 90s
15//! - a task that notifies about being attached/disattached from usb power 15//! - a task that notifies about being attached/disattached from usb power
16//! - a task that measures vsys voltage in intervals of 30s 16//! - a task that measures vsys voltage in intervals of 30s
17//! - a task that consumes the state information and reacts to it
17 18
18#![no_std] 19#![no_std]
19#![no_main] 20#![no_main]
@@ -57,6 +58,7 @@ enum Events {
57 FirstRandomSeed(u32), 58 FirstRandomSeed(u32),
58 SecondRandomSeed(u32), 59 SecondRandomSeed(u32),
59 ThirdRandomSeed(u32), 60 ThirdRandomSeed(u32),
61 ResetFirstRandomSeed,
60} 62}
61 63
62/// This is the type of Commands that we will send from the orchestrating task to the worker tasks. 64/// This is the type of Commands that we will send from the orchestrating task to the worker tasks.
@@ -103,6 +105,11 @@ static EVENT_CHANNEL: channel::Channel<CriticalSectionRawMutex, Events, 10> = ch
103/// Signal for stopping the first random signal task. We use a signal here, because we need no queue. It is suffiient to have one signal active. 105/// Signal for stopping the first random signal task. We use a signal here, because we need no queue. It is suffiient to have one signal active.
104static STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new(); 106static STOP_FIRST_RANDOM_SIGNAL: signal::Signal<CriticalSectionRawMutex, Commands> = signal::Signal::new();
105 107
108/// Channel for the state that we want the consumer task to react to. We use a channel here, because we want to have a queue of state changes, although
109/// we want the queue to be of size 1, because we want to finish rwacting to the state change before the next one comes in. This is just a design choice
110/// and depends on your use case.
111static CONSUMER_CHANNEL: channel::Channel<CriticalSectionRawMutex, State, 1> = channel::Channel::new();
112
106// And now we can put all this into use 113// And now we can put all this into use
107 114
108/// This is the main task, that will not do very much besides spawning the other tasks. This is a design choice, you could do the 115/// This is the main task, that will not do very much besides spawning the other tasks. This is a design choice, you could do the
@@ -116,11 +123,11 @@ async fn main(spawner: Spawner) {
116 123
117 // spawn the tasks 124 // spawn the tasks
118 spawner.spawn(orchestrate(spawner)).unwrap(); 125 spawner.spawn(orchestrate(spawner)).unwrap();
119 spawner.spawn(random_30s(spawner)).unwrap();
120 spawner.spawn(random_60s(spawner)).unwrap(); 126 spawner.spawn(random_60s(spawner)).unwrap();
121 spawner.spawn(random_90s(spawner)).unwrap(); 127 spawner.spawn(random_90s(spawner)).unwrap();
122 spawner.spawn(usb_power(spawner, r.vbus)).unwrap(); 128 spawner.spawn(usb_power(spawner, r.vbus)).unwrap();
123 spawner.spawn(vsys_voltage(spawner, r.vsys)).unwrap(); 129 spawner.spawn(vsys_voltage(spawner, r.vsys)).unwrap();
130 spawner.spawn(consumer(spawner)).unwrap();
124} 131}
125 132
126/// This is the task handling the system state and orchestrating the other tasks. WEe can regard this as the "main loop" of the system. 133/// This is the task handling the system state and orchestrating the other tasks. WEe can regard this as the "main loop" of the system.
@@ -131,6 +138,9 @@ async fn orchestrate(_spawner: Spawner) {
131 // we need to have a receiver for the events 138 // we need to have a receiver for the events
132 let receiver = EVENT_CHANNEL.receiver(); 139 let receiver = EVENT_CHANNEL.receiver();
133 140
141 // and we need a sender for the consumer task
142 let state_sender = CONSUMER_CHANNEL.sender();
143
134 loop { 144 loop {
135 // we await on the receiver, this will block until a new event is available 145 // we await on the receiver, this will block until a new event is available
136 // as an alternative to this, we could also await on multiple channels, this would block until at least one of the channels has an event 146 // as an alternative to this, we could also await on multiple channels, this would block until at least one of the channels has an event
@@ -172,23 +182,65 @@ async fn orchestrate(_spawner: Spawner) {
172 state.third_random_seed = seed; 182 state.third_random_seed = seed;
173 info!("Third random seed: {}", seed); 183 info!("Third random seed: {}", seed);
174 } 184 }
185 Events::ResetFirstRandomSeed => {
186 // update the state and/or react to the event here
187 state.times_we_got_first_random_seed = 0;
188 state.first_random_seed = 0;
189 info!("Resetting the first random seed counter");
190 }
175 } 191 }
176 // we now have an altered state 192 // we now have an altered state
177 // there is a crate for detecting field changes on crates.io (https://crates.io/crates/fieldset) that might be useful here 193 // there is a crate for detecting field changes on crates.io (https://crates.io/crates/fieldset) that might be useful here
178 // for now we just keep it simple 194 // for now we just keep it simple
179 info!("State: {:?}", &state);
180 195
181 // here we react to the state, in this case here we want to stop the first random seed task after we got it a defined number of times 196 // we send the state to the consumer task
182 if state.times_we_got_first_random_seed == state.maximum_times_we_want_first_random_seed { 197 // since the channel has a size of 1, this will block until the consumer task has received the state, which is what we want here in this example
183 info!("Stopping the first random signal task"); 198 // **Note:** It is bad design to send too much data between tasks, with no clear definition of what "too much" is. In this example we send the
184 // we send a command to the task 199 // whole state, in a real world application you might want to send only the data, that is relevant to the consumer task AND only when it has changed.
185 STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop); 200 // We keep it simple here.
201 state_sender.send(state.clone()).await;
202 }
203}
204
205/// This task will consume the state information and react to it. This is a simple example, in a real world application this would be more complex
206/// and we could have multiple consumer tasks, each reacting to different parts of the state.
207#[embassy_executor::task]
208async fn consumer(spawner: Spawner) {
209 // we need to have a receiver for the state
210 let receiver = CONSUMER_CHANNEL.receiver();
211 let sender = EVENT_CHANNEL.sender();
212 loop {
213 // we await on the receiver, this will block until a new state is available
214 let state = receiver.receive().await;
215 // react to the state, in this case here we just log it
216 info!("The consumer has reveived this state: {:?}", &state);
217
218 // here we react to the state, in this case here we want to start or stop the first random signal task depending on the state of the system
219 match state.times_we_got_first_random_seed {
220 max if max == state.maximum_times_we_want_first_random_seed => {
221 info!("Stopping the first random signal task");
222 // we send a command to the task
223 STOP_FIRST_RANDOM_SIGNAL.signal(Commands::Stop);
224 // we notify the orchestrator that we have sent the command
225 sender.send(Events::ResetFirstRandomSeed).await;
226 }
227 0 => {
228 // we start the task, which presents us with an interesting problem, because we may return here before the task has started
229 // here we just try and log if the task has started, in a real world application you might want to handle this more gracefully
230 info!("Starting the first random signal task");
231 match spawner.spawn(random_30s(spawner)) {
232 Ok(_) => info!("Successfully spawned random_30s task"),
233 Err(e) => info!("Failed to spawn random_30s task: {:?}", e),
234 }
235 }
236 _ => {}
186 } 237 }
187 } 238 }
188} 239}
189 240
190/// This task will generate random numbers in intervals of 30s 241/// This task will generate random numbers in intervals of 30s
191/// The task will terminate after it has received a command signal to stop, see the orchestrate task for that. 242/// The task will terminate after it has received a command signal to stop, see the orchestrate task for that.
243/// Note that we are not spawning this task from main, as we will show how such a task can be spawned and closed dynamically.
192#[embassy_executor::task] 244#[embassy_executor::task]
193async fn random_30s(_spawner: Spawner) { 245async fn random_30s(_spawner: Spawner) {
194 let mut rng = RoscRng; 246 let mut rng = RoscRng;