Skip to content

Commit f2d640e

Browse files
committed
editoast: use uom to type units
Signed-off-by: Tristram Gräbener <[email protected]>
1 parent 4634d1b commit f2d640e

File tree

23 files changed

+305
-157
lines changed

23 files changed

+305
-157
lines changed

editoast/Cargo.lock

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

editoast/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,7 @@ tracing-opentelemetry = { version = "0.28.0", default-features = false, features
185185
"tracing-log",
186186
] }
187187
tracing-subscriber = { version = "0.3.19", features = ["env-filter"] }
188+
uom.workspace = true
188189
url.workspace = true
189190
utoipa.workspace = true
190191
uuid.workspace = true

editoast/editoast_common/src/hash_rounded_float.rs

+24
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,35 @@
11
use std::hash::{Hash, Hasher};
2+
use uom::si::{
3+
acceleration::meter_per_second_squared,
4+
f64::{Acceleration, Length, Velocity},
5+
length::meter,
6+
velocity::meter_per_second,
7+
};
28

39
/// Hash a float through a rounded integer value
410
/// `hash_float<3,_>` means that the value is rounded to the nearest thousandth
511
pub fn hash_float<const T: u8, H: Hasher>(value: &f64, state: &mut H) {
612
((value * 10i64.pow(T as u32) as f64).round() as i64).hash(state);
713
}
814

15+
/// Hash a length through a rounded integer value
16+
/// `hash_length<3,_>` means that the value is rounded to the nearest thousandth
17+
pub fn hash_length<const T: u8, H: Hasher>(value: &Length, state: &mut H) {
18+
hash_float::<T, H>(&value.get::<meter>(), state);
19+
}
20+
21+
/// Hash a acceleration through a rounded integer value
22+
/// `hash_acceleration<3,_>` means that the value is rounded to the nearest thousandth
23+
pub fn hash_acceleration<const T: u8, H: Hasher>(value: &Acceleration, state: &mut H) {
24+
hash_float::<T, H>(&value.get::<meter_per_second_squared>(), state);
25+
}
26+
27+
/// Hash a velocity through a rounded integer value
28+
/// `hash_velocity<3,_>` means that the value is rounded to the nearest thousandth
29+
pub fn hash_velocity<const T: u8, H: Hasher>(value: &Velocity, state: &mut H) {
30+
hash_float::<T, H>(&value.get::<meter_per_second>(), state);
31+
}
32+
933
/// Hash a list of floats through a list of rounded integer value
1034
/// `hash_float_slice<3,_>` means that the values are rounded to the nearest thousandth
1135
pub fn hash_float_slice<const T: u8, H: Hasher>(value: &[f64], state: &mut H) {

editoast/editoast_common/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,11 @@ pub mod rangemap_utils;
44
pub mod schemas;
55
pub mod units;
66

7+
pub use hash_rounded_float::hash_acceleration;
78
pub use hash_rounded_float::hash_float;
89
pub use hash_rounded_float::hash_float_slice;
10+
pub use hash_rounded_float::hash_length;
11+
pub use hash_rounded_float::hash_velocity;
912

1013
schemas! {
1114
geometry::schemas(),

editoast/editoast_common/src/units.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ macro_rules! define_unit {
7575
$quantity::new::<Unit>(value)
7676
}
7777

78-
pub fn from(qty: $quantity) -> f64 {
78+
pub fn from(qty: &$quantity) -> f64 {
7979
qty.get::<Unit>()
8080
}
8181

editoast/editoast_derive/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -218,6 +218,7 @@ pub fn search_config_store(input: proc_macro::TokenStream) -> proc_macro::TokenS
218218
/// * `#[model(to_string)]`: calls `to_string()` before writing the field to the database and calls `String::from` after reading (diesel column type: String)
219219
/// * `#[model(to_enum)]`: is converted as `u8` before writing the field to the database and calls `FromRepr::from_repr` after reading (diesel column type: TinyInt)
220220
/// * `#[model(remote = "T")]`: calls `Into::<T>::into` before writing the field to the database and calls `T::from` after reading (diesel column type: T)
221+
/// * `#[model(uom_unit = "T")]`: the field is an uom quantity and stored in the database in the unit `T`, e.g. `"uom::si::length::meter"`
221222
/// * `#[model(geo)]` **TODO**: TBD
222223
///
223224
/// #### A note on identifiers

editoast/editoast_derive/src/model/args.rs

+2
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ pub(super) struct ModelFieldArgs {
7979
pub(super) to_enum: bool,
8080
#[darling(default)]
8181
pub(super) remote: Option<syn::Type>,
82+
#[darling(default)]
83+
pub(super) uom_unit: Option<syn::Type>,
8284
}
8385

8486
impl GeneratedTypeArgs {

editoast/editoast_derive/src/model/config.rs

+7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ pub(crate) enum FieldTransformation {
4545
Geo,
4646
ToString,
4747
ToEnum(syn::Type),
48+
UomUnit(syn::Type),
4849
}
4950

5051
#[derive(Debug, PartialEq)]
@@ -132,12 +133,14 @@ impl ModelField {
132133
Some(FieldTransformation::ToEnum(_)) => {
133134
parse_quote! { #expr as i16 }
134135
}
136+
Some(FieldTransformation::UomUnit(ref unit)) => parse_quote! { #expr.get::<#unit>() },
135137
None => parse_quote! { #expr },
136138
}
137139
}
138140

139141
#[allow(clippy::wrong_self_convention)]
140142
pub(crate) fn from_transformed(&self, expr: syn::Expr) -> syn::Expr {
143+
let ty = &self.ty;
141144
match self.transform {
142145
Some(FieldTransformation::Remote(_)) => parse_quote! { #expr.into() },
143146
Some(FieldTransformation::Json) => parse_quote! { #expr.0 },
@@ -146,6 +149,9 @@ impl ModelField {
146149
Some(FieldTransformation::ToEnum(ref ty)) => {
147150
parse_quote! { #ty::from_repr(#expr as usize).expect("Invalid variant repr") }
148151
}
152+
Some(FieldTransformation::UomUnit(ref unit)) => {
153+
parse_quote! { #ty::new::<#unit>(#expr) }
154+
}
149155
None => parse_quote! { #expr },
150156
}
151157
}
@@ -158,6 +164,7 @@ impl ModelField {
158164
Some(FieldTransformation::Geo) => unimplemented!("to be designed"),
159165
Some(FieldTransformation::ToString) => parse_quote! { String },
160166
Some(FieldTransformation::ToEnum(_)) => parse_quote! { i16 },
167+
Some(FieldTransformation::UomUnit(_)) => parse_quote! { f64 },
161168
None => ty.clone(),
162169
}
163170
}

editoast/editoast_derive/src/model/parsing.rs

+11-8
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,7 @@ impl ModelField {
165165
value.geo,
166166
value.to_string,
167167
to_enum,
168+
value.uom_unit,
168169
)
169170
.map_err(|e| e.with_span(&ident))?;
170171
Ok(Self {
@@ -188,16 +189,18 @@ impl FieldTransformation {
188189
geo: bool,
189190
to_string: bool,
190191
to_enum: Option<syn::Type>,
192+
uom_unit: Option<syn::Type>,
191193
) -> darling::Result<Option<Self>> {
192-
match (remote, json, geo, to_string, to_enum) {
193-
(Some(ty), false, false, false, None) => Ok(Some(Self::Remote(ty))),
194-
(None, true, false, false, None) => Ok(Some(Self::Json)),
195-
(None, false, true, false, None) => Ok(Some(Self::Geo)),
196-
(None, false, false, true, None) => Ok(Some(Self::ToString)),
197-
(None, false, false, false, Some(ty)) => Ok(Some(Self::ToEnum(ty))),
198-
(None, false, false, false, None) => Ok(None),
194+
match (remote, json, geo, to_string, to_enum, uom_unit) {
195+
(Some(ty), false, false, false, None, None) => Ok(Some(Self::Remote(ty))),
196+
(None, true, false, false, None, None) => Ok(Some(Self::Json)),
197+
(None, false, true, false, None, None) => Ok(Some(Self::Geo)),
198+
(None, false, false, true, None, None) => Ok(Some(Self::ToString)),
199+
(None, false, false, false, Some(ty), None) => Ok(Some(Self::ToEnum(ty))),
200+
(None, false, false, false, None, Some(ty)) => Ok(Some(Self::UomUnit(ty))),
201+
(None, false, false, false, None, None) => Ok(None),
199202
_ => Err(Error::custom(
200-
"Model: remote, json, geo, to_string and to_enum attributes are mutually exclusive",
203+
"Model: remote, json, geo, to_string, to_enum and uom_unit attributes are mutually exclusive",
201204
)),
202205
}
203206
}

editoast/editoast_schemas/Cargo.toml

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ serde.workspace = true
1616
serde_json.workspace = true
1717
strum.workspace = true
1818
thiserror.workspace = true
19+
uom.workspace = true
1920
utoipa.workspace = true
2021
uuid.workspace = true
2122

editoast/editoast_schemas/src/rolling_stock.rs

+10-9
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ pub use rolling_stock_livery::RollingStockLiveryMetadata;
3131
mod towed_rolling_stock;
3232
pub use towed_rolling_stock::TowedRollingStock;
3333

34+
use editoast_common::units::*;
3435
use serde::Deserialize;
3536
use serde::Serialize;
3637
use std::collections::HashMap;
@@ -53,15 +54,15 @@ pub struct RollingStock {
5354
pub locked: bool,
5455
pub effort_curves: EffortCurves,
5556
pub base_power_class: Option<String>,
56-
/// In m
57-
pub length: f64,
58-
/// In m/s
59-
pub max_speed: f64,
60-
pub startup_time: f64,
61-
/// In m/s²
62-
pub startup_acceleration: f64,
63-
/// In m/s²
64-
pub comfort_acceleration: f64,
57+
#[serde(with = "meter")]
58+
pub length: Length,
59+
#[serde(with = "meter_per_second")]
60+
pub max_speed: Velocity,
61+
pub startup_time: f64, // What unit ?
62+
#[serde(with = "meter_per_second_squared")]
63+
pub startup_acceleration: Acceleration,
64+
#[serde(with = "meter_per_second_squared")]
65+
pub comfort_acceleration: Acceleration,
6566
// The constant gamma braking coefficient used when NOT circulating
6667
// under ETCS/ERTMS signaling system in m/s^2
6768
pub const_gamma: f64,
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::RollingResistancePerWeight;
2+
use editoast_common::units::*;
23

34
#[derive(Debug, Clone, PartialEq, serde::Deserialize, serde::Serialize)]
45
pub struct TowedRollingStock {
@@ -7,16 +8,17 @@ pub struct TowedRollingStock {
78
pub railjson_version: String,
89
/// In kg
910
pub mass: f64,
10-
/// In m
11-
pub length: f64,
12-
/// In m/s²
13-
pub comfort_acceleration: f64,
14-
/// In m/s²
15-
pub startup_acceleration: f64,
11+
#[serde(with = "meter")]
12+
pub length: Length,
13+
#[serde(with = "meter_per_second_squared")]
14+
pub comfort_acceleration: Acceleration,
15+
#[serde(with = "meter_per_second_squared")]
16+
pub startup_acceleration: Acceleration,
1617
pub inertia_coefficient: f64,
1718
pub rolling_resistance: RollingResistancePerWeight,
1819
/// The constant gamma braking coefficient used when NOT circulating
1920
/// under ETCS/ERTMS signaling system in m/s^2
2021
pub const_gamma: f64,
21-
pub max_speed: Option<f64>,
22+
#[serde(with = "meter_per_second::option")]
23+
pub max_speed: Option<Velocity>,
2224
}

editoast/openapi.yaml

+21-4
Original file line numberDiff line numberDiff line change
@@ -2573,7 +2573,9 @@ paths:
25732573
max_speed:
25742574
type: number
25752575
format: double
2576-
description: Maximum speed of the consist in km/h
2576+
description: |-
2577+
Maximum speed of the consist in km/h
2578+
Velocity in km·h⁻¹
25772579
nullable: true
25782580
maximum_departure_delay:
25792581
type: integer
@@ -2632,7 +2634,9 @@ paths:
26322634
total_length:
26332635
type: number
26342636
format: double
2635-
description: Total length of the consist in meters
2637+
description: |-
2638+
Total length of the consist in meters
2639+
Length in m
26362640
nullable: true
26372641
total_mass:
26382642
type: number
@@ -6927,6 +6931,7 @@ components:
69276931
comfort_acceleration:
69286932
type: number
69296933
format: double
6934+
description: Acceleration in m·s⁻²
69306935
const_gamma:
69316936
type: number
69326937
format: double
@@ -6945,6 +6950,7 @@ components:
69456950
length:
69466951
type: number
69476952
format: double
6953+
description: Length in m
69486954
loading_gauge:
69496955
$ref: '#/components/schemas/LoadingGaugeType'
69506956
locked:
@@ -6955,6 +6961,7 @@ components:
69556961
max_speed:
69566962
type: number
69576963
format: double
6964+
description: Velocity in m·s⁻¹
69586965
metadata:
69596966
allOf:
69606967
- $ref: '#/components/schemas/RollingStockMetadata'
@@ -6972,6 +6979,7 @@ components:
69726979
startup_acceleration:
69736980
type: number
69746981
format: double
6982+
description: Acceleration in m·s⁻²
69756983
startup_time:
69766984
type: number
69776985
format: double
@@ -8552,7 +8560,9 @@ components:
85528560
max_speed:
85538561
type: number
85548562
format: double
8555-
description: Maximum speed of the consist in km/h
8563+
description: |-
8564+
Maximum speed of the consist in km/h
8565+
Velocity in km·h⁻¹
85568566
nullable: true
85578567
maximum_departure_delay:
85588568
type: integer
@@ -8611,7 +8621,9 @@ components:
86118621
total_length:
86128622
type: number
86138623
format: double
8614-
description: Total length of the consist in meters
8624+
description: |-
8625+
Total length of the consist in meters
8626+
Length in m
86158627
nullable: true
86168628
total_mass:
86178629
type: number
@@ -8789,6 +8801,7 @@ components:
87898801
length:
87908802
type: number
87918803
format: double
8804+
description: Length in m
87928805
loading_gauge:
87938806
$ref: '#/components/schemas/LoadingGaugeType'
87948807
locked:
@@ -8918,6 +8931,7 @@ components:
89188931
comfort_acceleration:
89198932
type: number
89208933
format: double
8934+
description: Acceleration in m·s⁻²
89218935
const_gamma:
89228936
type: number
89238937
format: double
@@ -8939,6 +8953,7 @@ components:
89398953
length:
89408954
type: number
89418955
format: double
8956+
description: Length in m
89428957
loading_gauge:
89438958
$ref: '#/components/schemas/LoadingGaugeType'
89448959
locked:
@@ -8950,6 +8965,7 @@ components:
89508965
max_speed:
89518966
type: number
89528967
format: double
8968+
description: Velocity in m·s⁻¹
89538969
metadata:
89548970
allOf:
89558971
- $ref: '#/components/schemas/RollingStockMetadata'
@@ -8972,6 +8988,7 @@ components:
89728988
startup_acceleration:
89738989
type: number
89748990
format: double
8991+
description: Acceleration in m·s⁻²
89758992
startup_time:
89768993
type: number
89778994
format: double

0 commit comments

Comments
 (0)