diff --git a/chirpstack/migrations_postgres/2025-01-13-152218_refactor_device_profile_fields/up.sql b/chirpstack/migrations_postgres/2025-01-13-152218_refactor_device_profile_fields/up.sql index 9a8f477c..78a5677e 100644 --- a/chirpstack/migrations_postgres/2025-01-13-152218_refactor_device_profile_fields/up.sql +++ b/chirpstack/migrations_postgres/2025-01-13-152218_refactor_device_profile_fields/up.sql @@ -1,6 +1,7 @@ alter table device_profile add column abp_params jsonb null, - add column class_b_params jsonb null; + add column class_b_params jsonb null, + add column class_c_params jsonb null; update device_profile set abp_params = json_build_object( @@ -16,7 +17,13 @@ update device_profile 'ping_slot_nb_k', class_b_ping_slot_nb_k, 'ping_slot_dr', class_b_ping_slot_dr, 'ping_slot_freq', class_b_ping_slot_freq) - where supports_class_b = false; + where supports_class_b = true; + +update device_profile + set class_c_params = json_build_object( + 'timeout', class_c_timeout) + where + supports_class_c = true; alter table device_profile drop column abp_rx1_delay, @@ -26,5 +33,6 @@ alter table device_profile drop column class_b_timeout, drop column class_b_ping_slot_nb_k, drop column class_b_ping_slot_dr, - drop column class_b_ping_slot_freq; + drop column class_b_ping_slot_freq, + drop column class_c_timeout; diff --git a/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/down.sql b/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/down.sql index 1b11d46d..b14daf94 100644 --- a/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/down.sql +++ b/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/down.sql @@ -6,6 +6,7 @@ alter table device_profile add column class_b_timeout integer not null default 0 alter table device_profile add column class_b_ping_slot_nb_k integer not null default 0; alter table device_profile add column class_b_ping_slot_dr smallint not null default 0; alter table device_profile add column class_b_ping_slot_freq bigint not null default 0; +alter table device_profile add column class_c_timeout integer not null default 0; update device_profile set @@ -25,6 +26,13 @@ update device_profile where class_b_params is not null; +update device_profile + set + class_c_timeout = class_c_params->'timeout' + where + class_c_params is not null; + alter table device_profile drop column abp_params; alter table device_profile drop column class_b_params; +alter table device_profile drop column class_c_params; diff --git a/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/up.sql b/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/up.sql index fcf92d9c..3fb6dc57 100644 --- a/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/up.sql +++ b/chirpstack/migrations_sqlite/2025-01-13-163304_refactor_device_profile_fields/up.sql @@ -1,5 +1,6 @@ alter table device_profile add column abp_params text null; alter table device_profile add column class_b_params text null; +alter table device_profile add column class_c_params text null; update device_profile set abp_params = json_object( @@ -15,7 +16,12 @@ update device_profile 'ping_slot_nb_k', class_b_ping_slot_nb_k, 'ping_slot_dr', class_b_ping_slot_dr, 'ping_slot_freq', class_b_ping_slot_freq) - where supports_class_b = false; + where supports_class_b = true; + +update device_profile + set class_c_params = json_object( + 'timeout', class_c_timeout) + where supports_class_c = true; alter table device_profile drop column abp_rx1_delay; alter table device_profile drop column abp_rx1_dr_offset; @@ -27,3 +33,4 @@ alter table device_profile drop column class_b_ping_slot_nb_k; alter table device_profile drop column class_b_ping_slot_dr; alter table device_profile drop column class_b_ping_slot_freq; +alter table device_profile drop column class_c_timeout; diff --git a/chirpstack/src/api/device_profile.rs b/chirpstack/src/api/device_profile.rs index 9d68eeaf..1abf8aec 100644 --- a/chirpstack/src/api/device_profile.rs +++ b/chirpstack/src/api/device_profile.rs @@ -60,7 +60,6 @@ impl DeviceProfileService for DeviceProfile { supports_otaa: req_dp.supports_otaa, supports_class_b: req_dp.supports_class_b, supports_class_c: req_dp.supports_class_c, - class_c_timeout: req_dp.class_c_timeout as i32, tags: fields::KeyValue::new(req_dp.tags.clone()), measurements: fields::Measurements::new( req_dp @@ -126,6 +125,13 @@ impl DeviceProfileService for DeviceProfile { } else { None }, + class_c_params: if req_dp.supports_class_c { + Some(fields::ClassCParams { + timeout: req_dp.class_c_timeout as u16, + }) + } else { + None + }, ..Default::default() }; @@ -159,6 +165,7 @@ impl DeviceProfileService for DeviceProfile { let dp = device_profile::get(&dp_id).await.map_err(|e| e.status())?; let abp_params = dp.abp_params.clone().unwrap_or_default(); let class_b_params = dp.class_b_params.clone().unwrap_or_default(); + let class_c_params = dp.class_c_params.clone().unwrap_or_default(); let mut resp = Response::new(api::GetDeviceProfileResponse { device_profile: Some(api::DeviceProfile { @@ -182,7 +189,7 @@ impl DeviceProfileService for DeviceProfile { class_b_ping_slot_nb_k: class_b_params.ping_slot_nb_k as u32, class_b_ping_slot_dr: class_b_params.ping_slot_dr as u32, class_b_ping_slot_freq: class_b_params.ping_slot_freq as u32, - class_c_timeout: dp.class_c_timeout as u32, + class_c_timeout: class_c_params.timeout as u32, abp_rx1_delay: abp_params.rx1_delay as u32, abp_rx1_dr_offset: abp_params.rx1_dr_offset as u32, abp_rx2_dr: abp_params.rx2_dr as u32, @@ -276,7 +283,6 @@ impl DeviceProfileService for DeviceProfile { supports_otaa: req_dp.supports_otaa, supports_class_b: req_dp.supports_class_b, supports_class_c: req_dp.supports_class_c, - class_c_timeout: req_dp.class_c_timeout as i32, tags: fields::KeyValue::new(req_dp.tags.clone()), measurements: fields::Measurements::new( req_dp @@ -342,6 +348,13 @@ impl DeviceProfileService for DeviceProfile { } else { None }, + class_c_params: if req_dp.supports_class_c { + Some(fields::ClassCParams { + timeout: req_dp.class_c_timeout as u16, + }) + } else { + None + }, ..Default::default() }) .await diff --git a/chirpstack/src/downlink/tx_ack.rs b/chirpstack/src/downlink/tx_ack.rs index 0ea6f838..17039b9c 100644 --- a/chirpstack/src/downlink/tx_ack.rs +++ b/chirpstack/src/downlink/tx_ack.rs @@ -282,8 +282,12 @@ impl TxAck { qi.is_pending = true; if dev.enabled_class == DeviceClass::C { - let timeout = - Utc::now() + Duration::try_seconds(dp.class_c_timeout as i64).unwrap_or_default(); + let timeout_sec = dp + .class_c_params + .as_ref() + .map(|v| v.timeout) + .unwrap_or_default() as i64; + let timeout = Utc::now() + Duration::try_seconds(timeout_sec).unwrap_or_default(); qi.timeout_after = Some(timeout); } diff --git a/chirpstack/src/storage/device_profile.rs b/chirpstack/src/storage/device_profile.rs index 0bf0b80d..2edd3798 100644 --- a/chirpstack/src/storage/device_profile.rs +++ b/chirpstack/src/storage/device_profile.rs @@ -34,7 +34,6 @@ pub struct DeviceProfile { pub supports_otaa: bool, pub supports_class_b: bool, pub supports_class_c: bool, - pub class_c_timeout: i32, pub tags: fields::KeyValue, pub payload_codec_script: String, pub flush_queue_on_activate: bool, @@ -68,6 +67,7 @@ pub struct DeviceProfile { pub rx1_delay: i16, pub abp_params: Option, pub class_b_params: Option, + pub class_c_params: Option, } impl DeviceProfile { @@ -107,7 +107,6 @@ impl Default for DeviceProfile { supports_otaa: false, supports_class_b: false, supports_class_c: false, - class_c_timeout: 0, tags: fields::KeyValue::new(HashMap::new()), measurements: fields::Measurements::new(HashMap::new()), auto_detect_measurements: false, @@ -138,6 +137,7 @@ impl Default for DeviceProfile { rx1_delay: 0, abp_params: None, class_b_params: None, + class_c_params: None, } } } @@ -238,7 +238,6 @@ pub async fn update(dp: DeviceProfile) -> Result { device_profile::supports_otaa.eq(&dp.supports_otaa), device_profile::supports_class_b.eq(&dp.supports_class_b), device_profile::supports_class_c.eq(&dp.supports_class_c), - device_profile::class_c_timeout.eq(&dp.class_c_timeout), device_profile::tags.eq(&dp.tags), device_profile::measurements.eq(&dp.measurements), device_profile::auto_detect_measurements.eq(&dp.auto_detect_measurements), @@ -275,6 +274,7 @@ pub async fn update(dp: DeviceProfile) -> Result { device_profile::rx1_delay.eq(&dp.rx1_delay), device_profile::abp_params.eq(&dp.abp_params), device_profile::class_b_params.eq(&dp.class_b_params), + device_profile::class_c_params.eq(&dp.class_c_params), )) .get_result(&mut get_async_db_conn().await?) .await diff --git a/chirpstack/src/storage/fields/device_profile.rs b/chirpstack/src/storage/fields/device_profile.rs index 2753e857..d703b513 100644 --- a/chirpstack/src/storage/fields/device_profile.rs +++ b/chirpstack/src/storage/fields/device_profile.rs @@ -101,3 +101,48 @@ impl serialize::ToSql for ClassBParams { Ok(serialize::IsNull::No) } } + +#[derive( + Default, Debug, Clone, PartialEq, Eq, Deserialize, Serialize, AsExpression, FromSqlRow, +)] +#[cfg_attr(feature = "postgres", diesel(sql_type = Jsonb))] +#[cfg_attr(feature = "sqlite", diesel(sql_type = Text))] +pub struct ClassCParams { + pub timeout: u16, +} + +#[cfg(feature = "postgres")] +impl deserialize::FromSql for ClassCParams { + fn from_sql(value: ::RawValue<'_>) -> deserialize::Result { + let value = >::from_sql(value)?; + Ok(serde_json::from_value(value)?) + } +} + +#[cfg(feature = "postgres")] +impl serialize::ToSql for ClassCParams { + fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Pg>) -> serialize::Result { + let value = serde_json::to_value(&self)?; + >::to_sql(&value, &mut out.reborrow()) + } +} + +#[cfg(feature = "sqlite")] +impl deserialize::FromSql for ClassCParams +where + *const str: deserialize::FromSql, +{ + fn from_sql(value: ::RawValue<'_>) -> deserialize::Result { + let s = + <*const str as deserialize::FromSql>::from_sql(value)?; + Ok(serde_json::from_str(unsafe { &*s })?) + } +} + +#[cfg(feature = "sqlite")] +impl serialize::ToSql for ClassCParams { + fn to_sql<'b>(&'b self, out: &mut serialize::Output<'b, '_, Sqlite>) -> serialize::Result { + out.set_value(serde_json::to_string(&self)?); + Ok(serialize::IsNull::No) + } +} diff --git a/chirpstack/src/storage/fields/mod.rs b/chirpstack/src/storage/fields/mod.rs index e2654d11..b58d045a 100644 --- a/chirpstack/src/storage/fields/mod.rs +++ b/chirpstack/src/storage/fields/mod.rs @@ -9,7 +9,7 @@ mod uuid; pub use big_decimal::BigDecimal; pub use dev_nonces::DevNonces; -pub use device_profile::{AbpParams, ClassBParams}; +pub use device_profile::{AbpParams, ClassBParams, ClassCParams}; pub use device_session::DeviceSession; pub use key_value::KeyValue; pub use measurements::*; diff --git a/chirpstack/src/storage/schema_postgres.rs b/chirpstack/src/storage/schema_postgres.rs index ec098a99..300a47e2 100644 --- a/chirpstack/src/storage/schema_postgres.rs +++ b/chirpstack/src/storage/schema_postgres.rs @@ -103,7 +103,6 @@ diesel::table! { supports_otaa -> Bool, supports_class_b -> Bool, supports_class_c -> Bool, - class_c_timeout -> Int4, tags -> Jsonb, payload_codec_script -> Text, flush_queue_on_activate -> Bool, @@ -138,6 +137,7 @@ diesel::table! { rx1_delay -> Int2, abp_params -> Nullable, class_b_params -> Nullable, + class_c_params -> Nullable, } } diff --git a/chirpstack/src/storage/schema_sqlite.rs b/chirpstack/src/storage/schema_sqlite.rs index 66ee85b0..1fca2d14 100644 --- a/chirpstack/src/storage/schema_sqlite.rs +++ b/chirpstack/src/storage/schema_sqlite.rs @@ -92,7 +92,6 @@ diesel::table! { supports_otaa -> Bool, supports_class_b -> Bool, supports_class_c -> Bool, - class_c_timeout -> Integer, tags -> Text, payload_codec_script -> Text, flush_queue_on_activate -> Bool, @@ -126,6 +125,7 @@ diesel::table! { rx1_delay -> SmallInt, abp_params -> Nullable, class_b_params -> Nullable, + class_c_params -> Nullable, } }