diff options
-rw-r--r-- | crates/core/c8y_translator/src/serializer.rs | 4 | ||||
-rw-r--r-- | crates/core/tedge/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/core/tedge/src/cli/connect/command.rs | 4 | ||||
-rw-r--r-- | crates/core/tedge_mapper/Cargo.toml | 2 | ||||
-rw-r--r-- | crates/core/tedge_mapper/src/c8y/converter.rs | 27 | ||||
-rw-r--r-- | crates/core/tedge_mapper/src/c8y/tests.rs | 52 | ||||
-rw-r--r-- | crates/core/tedge_watchdog/src/dummy_watchdog.rs | 3 | ||||
-rw-r--r-- | crates/core/tedge_watchdog/src/error.rs | 4 | ||||
-rw-r--r-- | crates/core/tedge_watchdog/src/main.rs | 13 | ||||
-rw-r--r-- | docs/src/howto-guides/002_installation.md | 9 | ||||
-rw-r--r-- | docs/src/tutorials/raise-alarm.md | 8 | ||||
-rw-r--r-- | docs/src/tutorials/send-events.md | 13 | ||||
-rw-r--r-- | plugins/c8y_configuration_plugin/src/main.rs | 4 | ||||
-rwxr-xr-x | uninstall-thin-edge_io.sh | 2 |
14 files changed, 93 insertions, 54 deletions
diff --git a/crates/core/c8y_translator/src/serializer.rs b/crates/core/c8y_translator/src/serializer.rs index e268235a..fb2d788a 100644 --- a/crates/core/c8y_translator/src/serializer.rs +++ b/crates/core/c8y_translator/src/serializer.rs @@ -48,12 +48,12 @@ impl C8yJsonSerializer { // object referenced by "externalId", instead of root device object // referenced by MQTT client's Device ID. let _ = json.write_key("externalSource"); - let _ = json.write_open_obj(); + json.write_open_obj(); let _ = json.write_key("externalId"); let _ = json.write_str(child_id); let _ = json.write_key("type"); let _ = json.write_str("c8y_Serial"); - let _ = json.write_close_obj(); + json.write_close_obj(); } Self { diff --git a/crates/core/tedge/Cargo.toml b/crates/core/tedge/Cargo.toml index c2589b8a..5f09f9b3 100644 --- a/crates/core/tedge/Cargo.toml +++ b/crates/core/tedge/Cargo.toml @@ -7,6 +7,8 @@ rust-version = "1.58.1" license = "Apache-2.0" readme = "README.md" description = "tedge is the cli tool for thin-edge.io" +homepage = "https://thin-edge.io" +repository = "https://github.com/thin-edge/thin-edge.io" [package.metadata.deb] recommends = "mosquitto" diff --git a/crates/core/tedge/src/cli/connect/command.rs b/crates/core/tedge/src/cli/connect/command.rs index faed5fde..8714bdd7 100644 --- a/crates/core/tedge/src/cli/connect/command.rs +++ b/crates/core/tedge/src/cli/connect/command.rs @@ -489,7 +489,7 @@ fn clean_up( bridge_config: &BridgeConfig, ) -> Result<(), ConnectError> { let path = get_bridge_config_file_path(config_location, bridge_config); - let _ = std::fs::remove_file(&path).or_else(ok_if_not_found)?; + std::fs::remove_file(&path).or_else(ok_if_not_found)?; Ok(()) } @@ -516,7 +516,7 @@ fn write_bridge_config_to_file( .join(TEDGE_BRIDGE_CONF_DIR_PATH); // This will forcefully create directory structure if it doesn't exist, we should find better way to do it, maybe config should deal with it? - let _ = create_directories(&dir_path)?; + create_directories(&dir_path)?; let common_config_path = get_common_mosquitto_config_file_path(config_location, common_mosquitto_config); diff --git a/crates/core/tedge_mapper/Cargo.toml b/crates/core/tedge_mapper/Cargo.toml index 009d3cc6..2032a2e6 100644 --- a/crates/core/tedge_mapper/Cargo.toml +++ b/crates/core/tedge_mapper/Cargo.toml @@ -6,6 +6,8 @@ edition = "2021" rust-version = "1.58.1" license = "Apache-2.0" description = "tedge_mapper is the mapper that translates thin-edge.io data model to c8y/az data model." +homepage = "https://thin-edge.io" +repository = "https://github.com/thin-edge/thin-edge.io" [package.metadata.deb] pre-depends = "tedge" diff --git a/crates/core/tedge_mapper/src/c8y/converter.rs b/crates/core/tedge_mapper/src/c8y/converter.rs index 12f3ec3c..03123697 100644 --- a/crates/core/tedge_mapper/src/c8y/converter.rs +++ b/crates/core/tedge_mapper/src/c8y/converter.rs @@ -274,20 +274,23 @@ where ) -> Result<Vec<Message>, ConversionError> { if topic.name.starts_with("tedge/alarms") { let mut mqtt_messages: Vec<Message> = Vec::new(); - let topic_split: Vec<&str> = topic.name.split('/').collect(); - if topic_split.len() == 5 { - let child_id = topic_split[4]; - // Create a child device, if it does not exists already - if !child_id.is_empty() && !self.children.contains(child_id) { - self.children.insert(child_id.to_string()); - mqtt_messages.push(Message::new( - &Topic::new_unchecked(SMARTREST_PUBLISH_TOPIC), - format!("101,{child_id},{child_id},thin-edge.io-child"), - )); - } - } self.size_threshold.validate(message)?; let mut messages = self.alarm_converter.try_convert_alarm(message)?; + if !messages.is_empty() { + // When there is some messages to be sent on behalf of a child device, + // this child device must be declared first, if not done yet + let topic_split: Vec<&str> = topic.name.split('/').collect(); + if topic_split.len() == 5 { + let child_id = topic_split[4]; + if !child_id.is_empty() && !self.children.contains(child_id) { + self.children.insert(child_id.to_string()); + mqtt_messages.push(Message::new( + &Topic::new_unchecked(SMARTREST_PUBLISH_TOPIC), + format!("101,{child_id},{child_id},thin-edge.io-child"), + )); + } + } + } mqtt_messages.append(&mut messages); Ok(mqtt_messages) } else if topic.name.starts_with(INTERNAL_ALARMS_TOPIC) { diff --git a/crates/core/tedge_mapper/src/c8y/tests.rs b/crates/core/tedge_mapper/src/c8y/tests.rs index 0413c33a..e72f9eed 100644 --- a/crates/core/tedge_mapper/src/c8y/tests.rs +++ b/crates/core/tedge_mapper/src/c8y/tests.rs @@ -76,8 +76,8 @@ async fn mapper_publishes_software_update_request() { let (_tmp_dir, sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); // Prepare and publish a software update smartrest request on `c8y/s/ds`. let smartrest = r#"528,external_id,nodered,1.0.0::debian,,install"#; - let _ = broker.publish("c8y/s/ds", smartrest).await.unwrap(); - let _ = publish_a_fake_jwt_token(broker).await; + broker.publish("c8y/s/ds", smartrest).await.unwrap(); + publish_a_fake_jwt_token(broker).await; let expected_update_list = r#" "updateList": [ @@ -114,7 +114,7 @@ async fn mapper_publishes_software_update_status_onto_c8y_topic() { // Start SM Mapper let (_tmp_dir, sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); - let _ = publish_a_fake_jwt_token(broker).await; + publish_a_fake_jwt_token(broker).await; // Prepare and publish a software update status response message `executing` on `tedge/commands/res/software/update`. let json_response = r#"{ @@ -122,7 +122,7 @@ async fn mapper_publishes_software_update_status_onto_c8y_topic() { "status": "executing" }"#; - let _ = broker + broker .publish("tedge/commands/res/software/update", json_response) .await .unwrap(); @@ -145,7 +145,7 @@ async fn mapper_publishes_software_update_status_onto_c8y_topic() { ]} ]}"#; - let _ = broker + broker .publish("tedge/commands/res/software/update", json_response) .await .unwrap(); @@ -169,7 +169,7 @@ async fn mapper_publishes_software_update_failed_status_onto_c8y_topic() { // Start SM Mapper let (_tmp_dir, sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); - let _ = publish_a_fake_jwt_token(broker).await; + publish_a_fake_jwt_token(broker).await; // The agent publish an error let json_response = r#" @@ -191,7 +191,7 @@ async fn mapper_publishes_software_update_failed_status_onto_c8y_topic() { "failures":[] }"#; - let _ = broker + broker .publish("tedge/commands/res/software/update", json_response) .await .unwrap(); @@ -235,8 +235,8 @@ async fn mapper_fails_during_sw_update_recovers_and_process_response() -> Result // Prepare and publish a software update smartrest request on `c8y/s/ds`. let smartrest = r#"528,external_id,nodered,1.0.0::debian,,install"#; - let _ = broker.publish("c8y/s/ds", smartrest).await.unwrap(); - let _ = publish_a_fake_jwt_token(broker).await; + broker.publish("c8y/s/ds", smartrest).await.unwrap(); + publish_a_fake_jwt_token(broker).await; let expected_update_list = r#" "updateList": [ @@ -278,7 +278,7 @@ async fn mapper_fails_during_sw_update_recovers_and_process_response() -> Result ] } ]}"#; - let _ = broker + broker .publish( "tedge/commands/res/software/update", &remove_whitespace(json_response), @@ -319,7 +319,7 @@ async fn mapper_publishes_software_update_request_with_wrong_action() { let (_tmp_dir, _sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); // Prepare and publish a c8y_SoftwareUpdate smartrest request on `c8y/s/ds` that contains a wrong action `remove`, that is not known by c8y. let smartrest = r#"528,external_id,nodered,1.0.0::debian,,remove"#; - let _ = broker.publish("c8y/s/ds", smartrest).await.unwrap(); + broker.publish("c8y/s/ds", smartrest).await.unwrap(); // Expect a 501 (executing) followed by a 502 (failed) mqtt_tests::assert_received_all_expected( @@ -341,7 +341,7 @@ async fn c8y_mapper_alarm_mapping_to_smartrest() { // Start the C8Y Mapper let (_tmp_dir, sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); - let _ = broker + broker .publish_with_opts( "tedge/alarms/major/temperature_alarm", r#"{ "text": "Temperature high" }"#, @@ -360,7 +360,7 @@ async fn c8y_mapper_alarm_mapping_to_smartrest() { .await; //Clear the previously published alarm - let _ = broker + broker .publish_with_opts( "tedge/alarms/major/temperature_alarm", "", @@ -385,7 +385,7 @@ async fn c8y_mapper_child_alarm_mapping_to_smartrest() { // Start the C8Y Mapper let (_tmp_dir, sm_mapper) = start_c8y_mapper(broker.port).await.unwrap(); - let _ = broker + broker .publish_with_opts( "tedge/alarms/minor/temperature_high/external_sensor", r#"{ "text": "Temperature high" }"#, @@ -395,7 +395,7 @@ async fn c8y_mapper_child_alarm_mapping_to_smartrest() { .await .unwrap(); - let _ = broker + broker .publish_with_opts( "tedge/alarms/minor/temperature_high/external_sensor", r#"{ "text": "Temperature high" }"#, @@ -414,7 +414,7 @@ async fn c8y_mapper_child_alarm_mapping_to_smartrest() { .await; //Clear the previously published alarm - let _ = broker + broker .publish_with_opts( "tedge/alarms/minor/temperature_high/external_sensor", "", @@ -441,7 +441,7 @@ async fn c8y_mapper_syncs_pending_alarms_on_startup() { .messages_published_on("c8y-internal/alarms/critical/temperature_alarm") .await; - let _ = broker + broker .publish_with_opts( "tedge/alarms/critical/temperature_alarm", r#"{ "text": "Temperature very high" }"#, @@ -471,7 +471,7 @@ async fn c8y_mapper_syncs_pending_alarms_on_startup() { sm_mapper.abort(); //Publish a new alarm while the mapper is down - let _ = broker + broker .publish_with_opts( "tedge/alarms/critical/pressure_alarm", r#"{ "text": "Pressure very high" }"#, @@ -483,8 +483,7 @@ async fn c8y_mapper_syncs_pending_alarms_on_startup() { // Ignored until the rumqttd broker bug that doesn't handle empty retained messages //Clear the existing alarm while the mapper is down - // let _ = broker - // .publish_with_opts( + // broker.publish_with_opts( // "tedge/alarms/critical/temperature_alarm", // "", // mqtt_channel::QoS::AtLeastOnce, @@ -533,7 +532,7 @@ async fn c8y_mapper_syncs_pending_child_alarms_on_startup() { .messages_published_on("c8y-internal/alarms/critical/temperature_alarm/external_sensor") .await; - let _ = broker + broker .publish_with_opts( "tedge/alarms/critical/temperature_alarm/external_sensor", r#"{ "text": "Temperature very high" }"#, @@ -563,7 +562,7 @@ async fn c8y_mapper_syncs_pending_child_alarms_on_startup() { sm_mapper.abort(); //Publish a new alarm while the mapper is down - let _ = broker + broker .publish_with_opts( "tedge/alarms/critical/pressure_alarm/external_sensor", r#"{ "text": "Pressure very high" }"#, @@ -573,7 +572,7 @@ async fn c8y_mapper_syncs_pending_child_alarms_on_startup() { .await .unwrap(); - let _ = broker + broker .publish_with_opts( "tedge/alarms/critical/pressure_alarm/external_sensor", r#"{ "text": "Pressure very high" }"#, @@ -585,8 +584,7 @@ async fn c8y_mapper_syncs_pending_child_alarms_on_startup() { // Ignored until the rumqttd broker bug that doesn't handle empty retained messages //Clear the existing alarm while the mapper is down - // let _ = broker - // .publish_with_opts( + // broker.publish_with_opts( // "tedge/alarms/critical/temperature_alarm/external_sensor", // "", // mqtt_channel::QoS::AtLeastOnce, @@ -683,7 +681,7 @@ async fn test_sync_child_alarms() { let alarm_message = Message::new(&Topic::new_unchecked(alarm_topic), alarm_payload); // During the sync phase, alarms are not converted immediately, but only cached to be synced later - assert_eq!(converter.convert(&alarm_message).await.len(), 1); + assert!(converter.convert(&alarm_message).await.is_empty()); let non_alarm_topic = "tedge/measurements/external_sensor"; let non_alarm_payload = r#"{"temp": 1}"#; @@ -1169,5 +1167,5 @@ fn remove_whitespace(s: &str) -> String { } async fn publish_a_fake_jwt_token(broker: &MqttProcessHandler) { - let _ = broker.publish("c8y/s/dat", "71,1111").await.unwrap(); + broker.publish("c8y/s/dat", "71,1111").await.unwrap(); } diff --git a/crates/core/tedge_watchdog/src/dummy_watchdog.rs b/crates/core/tedge_watchdog/src/dummy_watchdog.rs new file mode 100644 index 00000000..f701ffbe --- /dev/null +++ b/crates/core/tedge_watchdog/src/dummy_watchdog.rs @@ -0,0 +1,3 @@ +pub async fn start_watchdog(_config_dir: PathBuf) -> Result<(), anyhow::Error> { + anyhow::Error::from(crate::error::WatchdogError::WatchdogNotAvailable) +} diff --git a/crates/core/tedge_watchdog/src/error.rs b/crates/core/tedge_watchdog/src/error.rs index f9763798..27a9a1ed 100644 --- a/crates/core/tedge_watchdog/src/error.rs +++ b/crates/core/tedge_watchdog/src/error.rs @@ -4,6 +4,10 @@ use tedge_config::{ConfigSettingError, TEdgeConfigError}; #[derive(Debug, thiserror::Error)] pub enum WatchdogError { + #[cfg(not(target_os = "linux"))] + #[error("The watchdog is not available on this platform")] + WatchdogNotAvailable, + #[error("Fail to run `{cmd}`: {from}")] CommandExecError { cmd: String, from: std::io::Error }, diff --git a/crates/core/tedge_watchdog/src/main.rs b/crates/core/tedge_watchdog/src/main.rs index e4478b2f..8f0c2b4b 100644 --- a/crates/core/tedge_watchdog/src/main.rs +++ b/crates/core/tedge_watchdog/src/main.rs @@ -3,7 +3,18 @@ use std::path::PathBuf; use tedge_config::DEFAULT_TEDGE_CONFIG_PATH; mod error; + +// on linux, we use systemd +#[cfg(target_os = "linux")] mod systemd_watchdog; +#[cfg(target_os = "linux")] +use systemd_watchdog as watchdog; + +// on non-linux, we do nothing for now +#[cfg(not(target_os = "linux"))] +mod dummy_watchdog; +#[cfg(not(target_os = "linux"))] +use dummy_watchdog as watchdog; #[derive(Debug, clap::Parser)] #[clap( @@ -31,5 +42,5 @@ async fn main() -> Result<(), anyhow::Error> { let watchdog_opt = WatchdogOpt::parse(); tedge_utils::logging::initialise_tracing_subscriber(watchdog_opt.debug); - systemd_watchdog::start_watchdog(watchdog_opt.config_dir).await + watchdog::start_watchdog(watchdog_opt.config_dir).await } diff --git a/docs/src/howto-guides/002_installation.md b/docs/src/howto-guides/002_installation.md index b4d3549a..a64e849c 100644 --- a/docs/src/howto-guides/002_installation.md +++ b/docs/src/howto-guides/002_installation.md @@ -137,12 +137,13 @@ from below mentioned location. ```shell wget https://raw.githubusercontent.com/thin-edge/thin-edge.io/main/uninstall-thin-edge_io.sh +chmod a+x uninstall-thin-edge_io.sh ``` The uninstall script provides options as shown below. ```shell -USAGE: - delete-thin-edge_io [COMMAND] +USAGE: + ./uninstall-thin-edge.io.sh [COMMAND] COMMANDS: remove Uninstall thin-edge.io with keeping configuration files @@ -157,7 +158,7 @@ COMMANDS: Use uninstall script as shown below just to `remove` the `thin-edge.io` packages. ```shell -uninstall-thin-edge.io.sh remove +./uninstall-thin-edge.io.sh remove ``` > Note: Removes just the thin-edge.io packages and does not remove the `configuration` files. @@ -166,7 +167,7 @@ Use uninstall script as shown below to remove the thin-edge.io as well as to rem associated with these thin-edge.io packages. ```shell -uninstall-thin-edge.io.sh purge +./uninstall-thin-edge.io.sh purge ``` ## Next steps diff --git a/docs/src/tutorials/raise-alarm.md b/docs/src/tutorials/raise-alarm.md index 026edb33..742397aa 100644 --- a/docs/src/tutorials/raise-alarm.md +++ b/docs/src/tutorials/raise-alarm.md @@ -72,6 +72,12 @@ An already raised alarm can be cleared by sending an empty message with retained If alarms of different severities exist for a given alarm type, they must all be cleared separately as they're all treated as independent alarms. +### Raising alarms from child devices + +Alarms for child devices can be raised by publishing the alarm payload to `tedge/alarms/<severity>/<alarm-type>/<child-device-id>` topic, +where the `child-device-id` is the unique device id of the child device. +The alarm payload structure is the same, as described in the previous section. + ## Cloud data mapping If the device is connected to some supported IoT cloud platform, any alarms raised locally on thin-edge.io will be forwarded to the connected cloud platform as well. @@ -92,6 +98,8 @@ For example the `temperature_high` alarm with `critical` severity described in t ... and is published to `c8y/s/us` topic which will get forwarded to the connected Cumulocity cloud instance. +If the alarm is raised from a child device, the payload is published to `c8y/s/us/<child-device-id>` topic instead. + Find more information about SmartREST representations for alarms in Cumulocity [here](https://cumulocity.com/guides/10.11.0/reference/smartrest-two/#alarm-templates) Find more information about alarms data model in Cumulocity [here](https://cumulocity.com/guides/concepts/domain-model/#events) diff --git a/docs/src/tutorials/send-events.md b/docs/src/tutorials/send-events.md index 5a658539..1e5bdc20 100644 --- a/docs/src/tutorials/send-events.md +++ b/docs/src/tutorials/send-events.md @@ -47,6 +47,12 @@ When the `time` field is not provided, thin-edge.io will use the current system When you want to skip both fields, use an empty payload to indicate the same. There are no such restrictions on the `<event-type>` value. +### Sending events from child devices + +Events for child devices can be sent by publishing the event payload to `tedge/events/<event-type>/<child-device-id>` topic, +where the `child-device-id` is the unique device id of the child device. +The event payload structure is the same, as described in the previous section. + ## Cloud data mapping If the device is connected to some supported IoT cloud platform, an event that is triggered locally on thin-edge.io will be forwarded to the connected cloud platform as well. @@ -76,9 +82,10 @@ The Cumulocity JSON mapping of the same event would be as follows: "type":"login_event", "text":"A user just logged in", "time":"2021-01-01T05:30:45+00:00", - "source": { - "id":"<c8y-device-id>" - } + "externalSource":{ + "externalId":"<child-device-id>", + "type":"c8y_Serial" + } } ``` diff --git a/plugins/c8y_configuration_plugin/src/main.rs b/plugins/c8y_configuration_plugin/src/main.rs index 6f9ddf7b..2e1f620e 100644 --- a/plugins/c8y_configuration_plugin/src/main.rs +++ b/plugins/c8y_configuration_plugin/src/main.rs @@ -64,7 +64,7 @@ pub struct ConfigPluginOpt { async fn create_mqtt_client(mqtt_port: u16) -> Result<mqtt_channel::Connection, anyhow::Error> { let mut topic_filter = mqtt_channel::TopicFilter::new_unchecked(C8yTopic::SmartRestRequest.as_str()); - let _ = topic_filter.add_all(health_check_topics("c8y-configuration-plugin")); + topic_filter.add_all(health_check_topics("c8y-configuration-plugin")); let mqtt_config = mqtt_channel::Config::default() .with_port(mqtt_port) @@ -360,7 +360,7 @@ mod tests { .await; // Send a software upload request to the plugin - let _ = broker + broker .publish( "c8y/s/ds", format!("526,tedge-device,{test_config_type}").as_str(), diff --git a/uninstall-thin-edge_io.sh b/uninstall-thin-edge_io.sh index c1e955d8..99f19a4a 100755 --- a/uninstall-thin-edge_io.sh +++ b/uninstall-thin-edge_io.sh @@ -13,7 +13,7 @@ clouds=("c8y" "az") usage() { cat <<EOF USAGE: - delete-thin-edge_io [COMMAND] + uninstall-thin-edge_io.sh [COMMAND] COMMANDS: remove Uninstall thin-edge.io with keeping configuration files |