Skip to content

Commit bb9f16c

Browse files
committed
Launch firmware download in the background
Signed-off-by: Didier Wenzek <didier.wenzek@free.fr>
1 parent 1d8ca94 commit bb9f16c

File tree

3 files changed

+61
-20
lines changed

3 files changed

+61
-20
lines changed

crates/extensions/c8y_firmware_manager/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ tedge_mqtt_ext = { workspace = true }
2727
tedge_timer_ext = { workspace = true }
2828
tedge_utils = { workspace = true }
2929
thiserror = { workspace = true }
30+
tokio = { workspace = true }
3031

3132
[dev-dependencies]
3233
assert-json-diff = { workspace = true }

crates/extensions/c8y_firmware_manager/src/actor.rs

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,14 @@ pub type OperationTimeout = Timeout<OperationKey>;
5555
pub type IdDownloadResult = (String, DownloadResult);
5656
pub type IdDownloadRequest = (String, DownloadRequest);
5757

58-
fan_in_message_type!(FirmwareInput[MqttMessage, OperationTimeout] : Debug);
58+
#[derive(Debug)]
59+
pub struct RequestForwardOutcome {
60+
child_id: String,
61+
operation_id: String,
62+
result: Result<(), FirmwareManagementError>,
63+
}
64+
65+
fan_in_message_type!(FirmwareInput[MqttMessage, OperationTimeout, RequestForwardOutcome] : Debug);
5966

6067
pub struct FirmwareManagerActor {
6168
input_receiver: LoggingReceiver<FirmwareInput>,
@@ -69,11 +76,11 @@ impl Actor for FirmwareManagerActor {
6976
"FirmwareManager"
7077
}
7178

72-
// This actor handles 2 kinds of messages from its peer actors:
79+
// This actor handles 3 kinds of messages from its peer actors:
7380
//
7481
// 1. MQTT messages from the MqttActor for firmware update requests from the cloud and firmware update responses from the child devices
7582
// 2. Operation timeouts from the TimerActor for requests for which the child devices don't respond within the timeout window
76-
83+
// 3. RequestForwardOutcome sent back by the background workers once the firmware request has been forwarded to the child device
7784
async fn run(mut self) -> Result<(), RuntimeError> {
7885
self.resend_operations_to_child_device().await?;
7986
// TODO: We need a dedicated actor to publish 500 later.
@@ -88,6 +95,18 @@ impl Actor for FirmwareManagerActor {
8895
FirmwareInput::OperationTimeout(timeout) => {
8996
self.process_operation_timeout(timeout).await?;
9097
}
98+
FirmwareInput::RequestForwardOutcome(outcome) => {
99+
if let Err(err) = outcome.result {
100+
self.fail_operation_in_cloud(
101+
&outcome.child_id,
102+
Some(&outcome.operation_id),
103+
&err.to_string(),
104+
)
105+
.await?;
106+
}
107+
// The firmware has been downloaded and the request forwarded to the child device.
108+
// Simply waits for a response from the child device (over MQTT) or a timeout.
109+
}
91110
}
92111
}
93112
Ok(())
@@ -102,6 +121,7 @@ impl FirmwareManagerActor {
102121
jwt_retriever: JwtRetriever,
103122
timer_sender: DynSender<SetTimeout<OperationKey>>,
104123
download_sender: ClientMessageBox<IdDownloadRequest, IdDownloadResult>,
124+
progress_sender: DynSender<RequestForwardOutcome>,
105125
) -> Self {
106126
Self {
107127
input_receiver,
@@ -111,6 +131,7 @@ impl FirmwareManagerActor {
111131
jwt_retriever,
112132
download_sender,
113133
timer_sender,
134+
progress_sender,
114135
},
115136
active_child_ops: HashMap::new(),
116137
}
@@ -187,7 +208,7 @@ impl FirmwareManagerActor {
187208
return Ok(());
188209
}
189210

190-
let child_id = smartrest_request.device.as_str();
211+
let child_id = smartrest_request.device.clone();
191212

192213
if let Err(err) = self
193214
.validate_same_request_in_progress(smartrest_request.clone())
@@ -199,7 +220,7 @@ impl FirmwareManagerActor {
199220
Ok(())
200221
}
201222
_ => {
202-
self.fail_operation_in_cloud(child_id, None, &err.to_string())
223+
self.fail_operation_in_cloud(&child_id, None, &err.to_string())
203224
.await?;
204225
Err(err)
205226
}
@@ -208,19 +229,29 @@ impl FirmwareManagerActor {
208229

209230
// Addressing the new firmware operation to further step.
210231
let op_id = nanoid!();
211-
if let Err(err) = self
212-
.worker
213-
.handle_firmware_download_request_child_device(
214-
smartrest_request.clone(),
215-
op_id.as_str(),
216-
)
217-
.await
218-
{
219-
self.fail_operation_in_cloud(child_id, Some(&op_id), &err.to_string())
220-
.await?;
221-
}
232+
let operation_key = OperationKey::new(&child_id, &op_id);
233+
234+
let mut worker = self.worker.clone();
235+
tokio::spawn(async move {
236+
let result = worker
237+
.handle_firmware_download_request_child_device(
238+
smartrest_request.clone(),
239+
op_id.as_str(),
240+
)
241+
.await;
242+
if let Err(err) = worker
243+
.progress_sender
244+
.send(RequestForwardOutcome {
245+
child_id,
246+
operation_id: op_id,
247+
result,
248+
})
249+
.await
250+
{
251+
error!("Fail to forward operation progress due to: {err}");
252+
}
253+
});
222254

223-
let operation_key = OperationKey::new(child_id, &op_id);
224255
self.active_child_ops
225256
.insert(operation_key, ActiveOperationState::Pending);
226257

@@ -231,8 +262,10 @@ impl FirmwareManagerActor {
231262
impl FirmwareManagerWorker {
232263
// Check if the firmware file is already in cache.
233264
// If yes, publish a firmware request to child device with that firmware in the cache.
234-
// Otherwise, send a download request to the DownloaderActor and return immediately without waiting for the download to complete so that other requests/responses can be processed while the download is in progress.
235-
// The download will be performed by the DownloaderActor asynchronously and the response will be processed by this actor later on, in the `run` method.
265+
// Otherwise, send a download request to the DownloaderActor awaiting for the download to complete.
266+
//
267+
// This method has to be spawn in a task
268+
// so other requests/responses can be processed while the download is in progress.
236269
async fn handle_firmware_download_request_child_device(
237270
&mut self,
238271
smartrest_request: SmartRestFirmwareRequest,
@@ -290,7 +323,7 @@ impl FirmwareManagerWorker {
290323
Ok(())
291324
}
292325

293-
// This function is called on receiving a DownloadResult from the DownloaderActor or when the firmware file is already available in the cache.
326+
// This function is called on receiving a DownloadResult from the DownloaderActor.
294327
// If the download is successful, publish a firmware request to child device with it
295328
// Otherwise, fail the operation in the cloud
296329
async fn process_downloaded_firmware(
@@ -772,6 +805,7 @@ struct FirmwareManagerWorker {
772805
jwt_retriever: JwtRetriever,
773806
download_sender: ClientMessageBox<IdDownloadRequest, IdDownloadResult>,
774807
timer_sender: DynSender<SetTimeout<OperationKey>>,
808+
progress_sender: DynSender<RequestForwardOutcome>,
775809
}
776810

777811
impl Clone for FirmwareManagerWorker {
@@ -782,6 +816,7 @@ impl Clone for FirmwareManagerWorker {
782816
jwt_retriever: self.jwt_retriever.clone(),
783817
download_sender: self.download_sender.clone(),
784818
timer_sender: self.timer_sender.sender_clone(),
819+
progress_sender: self.progress_sender.sender_clone(),
785820
}
786821
}
787822
}

crates/extensions/c8y_firmware_manager/src/lib.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::actor::IdDownloadRequest;
1010
use crate::actor::IdDownloadResult;
1111
use crate::actor::OperationSetTimeout;
1212
use crate::actor::OperationTimeout;
13+
use crate::actor::RequestForwardOutcome;
1314
use crate::operation::OperationKey;
1415
use actor::FirmwareInput;
1516
use actor::FirmwareManagerActor;
@@ -41,6 +42,7 @@ pub struct FirmwareManagerBuilder {
4142
jwt_retriever: JwtRetriever,
4243
timer_sender: DynSender<SetTimeout<OperationKey>>,
4344
download_sender: ClientMessageBox<IdDownloadRequest, IdDownloadResult>,
45+
progress_sender: DynSender<RequestForwardOutcome>,
4446
signal_sender: mpsc::Sender<RuntimeRequest>,
4547
}
4648

@@ -67,13 +69,15 @@ impl FirmwareManagerBuilder {
6769
let jwt_retriever = JwtRetriever::new(jwt_actor);
6870
let timer_sender = timer_actor.connect_consumer(NoConfig, input_sender.clone().into());
6971
let download_sender = ClientMessageBox::new(downloader_actor);
72+
let progress_sender = input_sender.into();
7073
Ok(Self {
7174
config,
7275
input_receiver,
7376
mqtt_publisher,
7477
jwt_retriever,
7578
timer_sender,
7679
download_sender,
80+
progress_sender,
7781
signal_sender,
7882
})
7983
}
@@ -109,6 +113,7 @@ impl Builder<FirmwareManagerActor> for FirmwareManagerBuilder {
109113
self.jwt_retriever,
110114
self.timer_sender,
111115
self.download_sender,
116+
self.progress_sender,
112117
))
113118
}
114119
}

0 commit comments

Comments
 (0)