1use ifc_model::{Entity, EntityId, Transaction, Value};
4
5use crate::error::{TabularError, TabularResult};
6
7const REGULAR: &str = "IFCREGULARTIMESERIES";
8const IRREGULAR: &str = "IFCIRREGULARTIMESERIES";
9const VALUE: &str = "IFCTIMESERIESVALUE";
10const IRREGULAR_VALUE: &str = "IFCIRREGULARTIMESERIESVALUE";
11
12pub fn add_time_series_value(tx: &mut Transaction, values: Vec<Value>) -> TabularResult<EntityId> {
18 if values.is_empty() {
19 return Err(TabularError::EmptyList {
20 entity: VALUE,
21 attribute: "ListValues",
22 });
23 }
24 Ok(tx.create(Entity::new(VALUE, vec![Value::List(values)])))
25}
26
27pub fn add_irregular_value(
36 tx: &mut Transaction,
37 timestamp: &str,
38 values: Vec<Value>,
39) -> TabularResult<EntityId> {
40 if timestamp.trim().is_empty() {
41 return Err(TabularError::BlankRequired {
42 entity: IRREGULAR_VALUE,
43 attribute: "TimeStamp",
44 });
45 }
46 if values.is_empty() {
47 return Err(TabularError::EmptyList {
48 entity: IRREGULAR_VALUE,
49 attribute: "ListValues",
50 });
51 }
52 let attributes = vec![Value::Text(timestamp.into()), Value::List(values)];
53 Ok(tx.create(Entity::new(IRREGULAR_VALUE, attributes)))
54}
55
56#[derive(Debug, Clone, Copy, Default)]
58pub struct SeriesDraft<'a> {
59 pub name: &'a str,
61 pub description: Option<&'a str>,
63 pub start_time: &'a str,
65 pub end_time: &'a str,
67 pub data_type: &'a str,
69 pub data_origin: &'a str,
71 pub user_defined_data_origin: Option<&'a str>,
73 pub unit: Option<EntityId>,
75}
76
77fn supertype_slots(entity: &'static str, draft: SeriesDraft<'_>) -> TabularResult<Vec<Value>> {
78 for (attribute, text) in [
79 ("Name", draft.name),
80 ("StartTime", draft.start_time),
81 ("EndTime", draft.end_time),
82 ("TimeSeriesDataType", draft.data_type),
83 ("DataOrigin", draft.data_origin),
84 ] {
85 if text.trim().is_empty() {
86 return Err(TabularError::BlankRequired { entity, attribute });
87 }
88 }
89 Ok(vec![
90 Value::Text(draft.name.into()),
91 draft
92 .description
93 .map_or(Value::Null, |text| Value::Text(text.into())),
94 Value::Text(draft.start_time.into()),
95 Value::Text(draft.end_time.into()),
96 Value::Enum(draft.data_type.into()),
97 Value::Enum(draft.data_origin.into()),
98 draft
99 .user_defined_data_origin
100 .map_or(Value::Null, |text| Value::Text(text.into())),
101 draft.unit.map_or(Value::Null, Value::Ref),
102 ])
103}
104
105pub fn add_regular_time_series(
115 tx: &mut Transaction,
116 draft: SeriesDraft<'_>,
117 time_step: f64,
118 values: &[EntityId],
119) -> TabularResult<EntityId> {
120 let mut attributes = supertype_slots(REGULAR, draft)?;
121 if !time_step.is_finite() || time_step <= 0.0 {
122 return Err(TabularError::NotFinite {
123 entity: REGULAR,
124 attribute: "TimeStep",
125 });
126 }
127 if values.is_empty() {
128 return Err(TabularError::EmptyList {
129 entity: REGULAR,
130 attribute: "Values",
131 });
132 }
133 attributes.push(Value::Real(time_step));
134 attributes.push(Value::List(
135 values.iter().copied().map(Value::Ref).collect(),
136 ));
137 Ok(tx.create(Entity::new(REGULAR, attributes)))
138}
139
140pub fn add_irregular_time_series(
146 tx: &mut Transaction,
147 draft: SeriesDraft<'_>,
148 values: &[EntityId],
149) -> TabularResult<EntityId> {
150 let mut attributes = supertype_slots(IRREGULAR, draft)?;
151 if values.is_empty() {
152 return Err(TabularError::EmptyList {
153 entity: IRREGULAR,
154 attribute: "Values",
155 });
156 }
157 attributes.push(Value::List(
158 values.iter().copied().map(Value::Ref).collect(),
159 ));
160 Ok(tx.create(Entity::new(IRREGULAR, attributes)))
161}