diff --git a/src/generated/models/resource.rs b/src/generated/models/resource.rs index 65eda4a..2e4aa2d 100644 --- a/src/generated/models/resource.rs +++ b/src/generated/models/resource.rs @@ -8,9 +8,10 @@ * Generated by: https://openapi-generator.tech */ -use crate::wire::values_map::ValuesMap; use serde::{Deserialize, Serialize}; +use crate::wire::values_map::ValuesMap; + /// Resource : A resource is an energy device or system subject to control by a VEN. #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] diff --git a/src/generated/models/subscription.rs b/src/generated/models/subscription.rs index de16af1..95eaf51 100644 --- a/src/generated/models/subscription.rs +++ b/src/generated/models/subscription.rs @@ -8,9 +8,10 @@ * Generated by: https://openapi-generator.tech */ +use serde::{Deserialize, Serialize}; + use crate::generated::models::SubscriptionObjectOperationsInner; use crate::wire::values_map::ValuesMap; -use serde::{Deserialize, Serialize}; /// Subscription : An object created by a client to receive notification of operations on objects. Clients may subscribe to be notified when a type of object is created, updated, or deleted. diff --git a/src/generated/models/ven.rs b/src/generated/models/ven.rs index efd9707..05c1ded 100644 --- a/src/generated/models/ven.rs +++ b/src/generated/models/ven.rs @@ -8,10 +8,12 @@ * Generated by: https://openapi-generator.tech */ -use super::Resource; -use crate::wire::values_map::ValuesMap; use serde::{Deserialize, Serialize}; +use crate::wire::values_map::ValuesMap; + +use super::Resource; + /// Ven : Ven represents a client with the ven role. #[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] diff --git a/src/lib.rs b/src/lib.rs index c25c949..c6c7882 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,96 +3,6 @@ use serde::{Deserialize, Serialize}; pub mod generated; pub mod wire; -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Event { - Simple, - Price, - ChargeStateSetpoint, - DispatchSetpoint, - DispatchSetpointRelative, - ControlSetpoint, - ExportPrice, - #[serde(rename = "GHG")] - GHG, - Curve, - #[serde(rename = "OLS")] - OLS, - ImportCapacitySubscription, - ImportCapacityReservation, - ImportCapacityReservationFee, - ImportCapacityAvailable, - ImportCapacityAvailablePrice, - ExportCapacitySubscription, - ExportCapacityReservation, - ExportCapacityReservationFee, - ExportCapacityAvailable, - ExportCapacityAvailablePrice, - ImportCapacityLimit, - ExportCapacityLimit, - AlertGridEmergency, - AlertBlackStart, - AlertPossibleOutage, - AlertFlexAlert, - AlertFire, - AlertFreezing, - AlertWind, - AlertTsunami, - AlertAirQuality, - AlertOther, - #[serde(rename = "CTA2045_REBOOT")] - CTA2045Reboot, - #[serde(rename = "CTA2045_SET_OVERRIDE_STATUS")] - CTA2045SetOverrideStatus, - #[serde(untagged)] - Private(String), -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ReportType { - Reading, - Usage, - Demand, - Setpoint, - DeltaUsage, - Baseline, - OperatingState, - UpRegulationAvailable, - DownRegulationAvailable, - RegulationSetpoint, - StorageUsableCapacity, - StorageChargeLevel, - StorageMaxDischargePower, - StorageMaxChargePower, - SimpleLevel, - UsageForecast, - StorageDispatchForecast, - LoadShedDeltaAvailable, - GenerationDeltaAvailable, - DataQuality, - ImportReservationCapacity, - ImportReservationFee, - ExportReservationCapacity, - ExportReservationFee, - #[serde(untagged)] - Private(String), -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ReadingType { - DirectRead, - Estimated, - Summed, - Mean, - Peak, - Forecast, - Average, - #[serde(untagged)] - Private(String), -} - #[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum OperatingState { @@ -112,15 +22,7 @@ pub enum OperatingState { Private(String), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ResourceName { - AggregatedReport, - #[serde(untagged)] - Private(String), -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum DataQuality { /// No known reasons to doubt the data. @@ -136,32 +38,7 @@ pub enum DataQuality { Private(String), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum Target { - /// A Power Service Location is a utility named specific location in - /// geography or the distribution system, usually the point of service to a - /// customer site. - PowerServiceLocation, - /// A Service Area is a utility named geographic region. - ServiceArea, - /// Targeting a specific group (string). - Group, - /// Targeting a specific resource (string). - ResourceName, - /// Targeting a specific VEN (string). - #[serde(rename = "VEN_NAME")] - VENName, - /// Targeting a specific event (string). - EventName, - /// Targeting a specific program (string). - ProgramName, - /// An application specific privately defined target. - #[serde(untagged)] - Private(String), -} - -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum Attribute { /// Describes a single geographic point. Values contains 2 floats, generally @@ -183,7 +60,7 @@ pub enum Attribute { Private(String), } -#[derive(Serialize, Deserialize, PartialEq, Eq, Debug)] +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] #[serde(rename_all = "SCREAMING_SNAKE_CASE")] pub enum Unit { /// Kilowatt-hours (kWh) @@ -248,74 +125,6 @@ impl Client { mod tests { use super::*; - #[test] - fn test_event_serialization() { - assert_eq!( - serde_json::to_string(&Event::Simple).unwrap(), - r#""SIMPLE""# - ); - assert_eq!( - serde_json::to_string(&Event::CTA2045Reboot).unwrap(), - r#""CTA2045_REBOOT""# - ); - assert_eq!( - serde_json::from_str::(r#""GHG""#).unwrap(), - Event::GHG - ); - assert_eq!( - serde_json::from_str::(r#""something else""#).unwrap(), - Event::Private(String::from("something else")) - ); - } - - #[test] - fn test_report_type_serialization() { - assert_eq!( - serde_json::to_string(&ReportType::Baseline).unwrap(), - r#""BASELINE""# - ); - assert_eq!( - serde_json::to_string(&ReportType::RegulationSetpoint).unwrap(), - r#""REGULATION_SETPOINT""# - ); - assert_eq!( - serde_json::to_string(&ReportType::Private(String::from("something else"))).unwrap(), - r#""something else""# - ); - assert_eq!( - serde_json::from_str::(r#""DEMAND""#).unwrap(), - ReportType::Demand - ); - assert_eq!( - serde_json::from_str::(r#""EXPORT_RESERVATION_FEE""#).unwrap(), - ReportType::ExportReservationFee - ); - assert_eq!( - serde_json::from_str::(r#""something else""#).unwrap(), - ReportType::Private(String::from("something else")) - ); - } - - #[test] - fn test_reading_type_serialization() { - assert_eq!( - serde_json::to_string(&ReadingType::DirectRead).unwrap(), - r#""DIRECT_READ""# - ); - assert_eq!( - serde_json::to_string(&ReadingType::Private(String::from("something else"))).unwrap(), - r#""something else""# - ); - assert_eq!( - serde_json::from_str::(r#""AVERAGE""#).unwrap(), - ReadingType::Average - ); - assert_eq!( - serde_json::from_str::(r#""something else""#).unwrap(), - ReadingType::Private(String::from("something else")) - ); - } - #[test] fn test_operating_state_serialization() { assert_eq!( @@ -341,26 +150,6 @@ mod tests { ); } - #[test] - fn test_resource_name_serialization() { - assert_eq!( - serde_json::to_string(&ResourceName::AggregatedReport).unwrap(), - r#""AGGREGATED_REPORT""# - ); - assert_eq!( - serde_json::to_string(&ResourceName::Private(String::from("something else"))).unwrap(), - r#""something else""# - ); - assert_eq!( - serde_json::from_str::(r#""AGGREGATED_REPORT""#).unwrap(), - ResourceName::AggregatedReport - ); - assert_eq!( - serde_json::from_str::(r#""something else""#).unwrap(), - ResourceName::Private(String::from("something else")) - ); - } - #[test] fn test_data_quality_serialization() { assert_eq!(serde_json::to_string(&DataQuality::Ok).unwrap(), r#""OK""#); @@ -378,26 +167,6 @@ mod tests { ); } - #[test] - fn test_target_serialization() { - assert_eq!( - serde_json::to_string(&Target::EventName).unwrap(), - r#""EVENT_NAME""# - ); - assert_eq!( - serde_json::to_string(&Target::Private(String::from("something else"))).unwrap(), - r#""something else""# - ); - assert_eq!( - serde_json::from_str::(r#""VEN_NAME""#).unwrap(), - Target::VENName - ); - assert_eq!( - serde_json::from_str::(r#""something else""#).unwrap(), - Target::Private(String::from("something else")) - ); - } - #[test] fn test_attribute_serialization() { assert_eq!( diff --git a/src/wire/event.rs b/src/wire/event.rs index 4a90902..390fba8 100644 --- a/src/wire/event.rs +++ b/src/wire/event.rs @@ -1,12 +1,13 @@ //! Types used for the event/ endpoint -use serde::{Deserialize, Serialize}; - -use crate::wire::interval::{Interval, IntervalPeriod}; +use crate::wire::interval::IntervalPeriod; use crate::wire::program::ProgramId; use crate::wire::report::ReportDescriptor; -use crate::wire::values_map::ValuesMap; -use crate::wire::{Currency, DateTime, PayloadType, Unit}; +use crate::wire::target::TargetMap; +use crate::wire::values_map::Value; +use crate::wire::{Currency, DateTime}; +use crate::Unit; +use serde::{Deserialize, Serialize}; /// Event object to communicate a Demand Response request to VEN. If intervalPeriod is present, sets /// start time and duration of intervals. @@ -37,7 +38,7 @@ pub struct Event { pub priority: Option, /// A list of valuesMap objects. #[serde(skip_serializing_if = "Option::is_none")] - pub targets: Option>, + pub targets: Option, /// A list of reportDescriptor objects. Used to request reports from VEN. #[serde(skip_serializing_if = "Option::is_none")] pub report_descriptors: Option>, @@ -48,12 +49,12 @@ pub struct Event { #[serde(skip_serializing_if = "Option::is_none")] pub interval_period: Option, /// A list of interval objects. - pub intervals: Vec, + pub intervals: Vec, } impl Event { /// Event object to communicate a Demand Response request to VEN. If intervalPeriod is present, sets start time and duration of intervals. - pub fn new(program_id: ProgramId, intervals: Vec) -> Event { + pub fn new(program_id: ProgramId, intervals: Vec) -> Event { Event { id: None, created_date_time: None, @@ -62,7 +63,7 @@ impl Event { program_id, event_name: None, priority: None, - targets: None, + targets: Default::default(), report_descriptors: None, payload_descriptors: None, interval_period: None, @@ -99,7 +100,7 @@ pub enum EventObjectType { #[serde(rename_all = "camelCase")] pub struct EventPayloadDescriptor { /// Enumerated or private string signifying the nature of values. - pub payload_type: PayloadType, + pub payload_type: EventType, /// Units of measure. #[serde(skip_serializing_if = "Option::is_none")] pub units: Option, @@ -109,7 +110,7 @@ pub struct EventPayloadDescriptor { } impl EventPayloadDescriptor { - pub fn new(payload_type: PayloadType) -> Self { + pub fn new(payload_type: EventType) -> Self { Self { payload_type, units: None, @@ -118,13 +119,68 @@ impl EventPayloadDescriptor { } } +/// An object defining a temporal window and a list of valuesMaps. if intervalPeriod present may set +/// temporal aspects of interval or override event.intervalPeriod. +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct EventInterval { + /// A client generated number assigned an interval object. Not a sequence number. + pub id: i32, + /// Defines default start and durations of intervals. + #[serde(skip_serializing_if = "Option::is_none")] + pub interval_period: Option, + /// A list of valuesMap objects. + pub payloads: Vec, +} + +impl EventInterval { + pub fn new(id: i32, payloads: Vec) -> Self { + Self { + id, + interval_period: None, + payloads, + } + } +} + +/// Represents one or more values associated with a type. E.g. a type of PRICE contains a single float value. +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct EventValuesMap { + /// Enumerated or private string signifying the nature of values. E.G. \"PRICE\" indicates value is to be interpreted as a currency. + #[serde(rename = "type")] + pub value_type: EventType, + /// A list of data points. Most often a singular value such as a price. + // TODO: The type of Value is actually defined by value_type + pub values: Vec, +} + #[cfg(test)] mod tests { - use crate::wire::values_map::{Value, ValueType}; + use crate::wire::values_map::Value; use crate::wire::Duration; use super::*; + #[test] + fn test_event_serialization() { + assert_eq!( + serde_json::to_string(&EventType::Simple).unwrap(), + r#""SIMPLE""# + ); + assert_eq!( + serde_json::to_string(&EventType::CTA2045Reboot).unwrap(), + r#""CTA2045_REBOOT""# + ); + assert_eq!( + serde_json::from_str::(r#""GHG""#).unwrap(), + EventType::GHG + ); + assert_eq!( + serde_json::from_str::(r#""something else""#).unwrap(), + EventType::Private(String::from("something else")) + ); + } + #[test] fn parse_minimal() { let example = r#"{"programID":"foo","intervals":[]}"#; @@ -180,7 +236,7 @@ mod tests { program_id: ProgramId("object-999".into()), event_name: Some("price event 11-18-2022".into()), priority: Some(0), - targets: None, + targets: Default::default(), report_descriptors: None, payload_descriptors: None, interval_period: Some(IntervalPeriod { @@ -188,15 +244,15 @@ mod tests { duration: Some(Duration("PT1H".into())), randomize_start: Some(Duration("PT1H".into())), }), - intervals: vec![Interval { + intervals: vec![EventInterval { id: 0, interval_period: Some(IntervalPeriod { start: DateTime("2023-06-15T09:30:00Z".into()), duration: Some(Duration("PT1H".into())), randomize_start: Some(Duration("PT1H".into())), }), - payloads: vec![ValuesMap { - value_type: ValueType("PRICE".into()), + payloads: vec![EventValuesMap { + value_type: EventType::Price, values: vec![Value::Number(0.17)], }], }], @@ -208,3 +264,48 @@ mod tests { ); } } + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum EventType { + Simple, + Price, + ChargeStateSetpoint, + DispatchSetpoint, + DispatchSetpointRelative, + ControlSetpoint, + ExportPrice, + #[serde(rename = "GHG")] + GHG, + Curve, + #[serde(rename = "OLS")] + OLS, + ImportCapacitySubscription, + ImportCapacityReservation, + ImportCapacityReservationFee, + ImportCapacityAvailable, + ImportCapacityAvailablePrice, + ExportCapacitySubscription, + ExportCapacityReservation, + ExportCapacityReservationFee, + ExportCapacityAvailable, + ExportCapacityAvailablePrice, + ImportCapacityLimit, + ExportCapacityLimit, + AlertGridEmergency, + AlertBlackStart, + AlertPossibleOutage, + AlertFlexAlert, + AlertFire, + AlertFreezing, + AlertWind, + AlertTsunami, + AlertAirQuality, + AlertOther, + #[serde(rename = "CTA2045_REBOOT")] + CTA2045Reboot, + #[serde(rename = "CTA2045_SET_OVERRIDE_STATUS")] + CTA2045SetOverrideStatus, + #[serde(untagged)] + Private(String), +} diff --git a/src/wire/mod.rs b/src/wire/mod.rs index d269e21..ffb62b2 100644 --- a/src/wire/mod.rs +++ b/src/wire/mod.rs @@ -9,6 +9,7 @@ pub mod event; pub mod interval; pub mod program; pub mod report; +pub mod target; pub mod values_map; // TODO: Replace with real ISO8601 type @@ -21,20 +22,9 @@ pub struct DateTime(String); #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub struct Duration(String); -// TODO: Replace with values from spec -/// A physical unit as described in Table 9 of the OpenADR Definition -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub enum Unit { - Todo, -} - // TODO: Find a nice ISO 4217 crate /// A currency described as listed in ISO 4217 #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] pub enum Currency { Todo, } - -// TODO figure out what this is... -#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct PayloadType(String); diff --git a/src/wire/program.rs b/src/wire/program.rs index e1fddb5..f14fbc0 100644 --- a/src/wire/program.rs +++ b/src/wire/program.rs @@ -5,7 +5,7 @@ use serde::{Deserialize, Serialize}; use crate::wire::event::EventPayloadDescriptor; use crate::wire::interval::IntervalPeriod; use crate::wire::report::ReportPayloadDescriptor; -use crate::wire::values_map::ValuesMap; +use crate::wire::target::TargetMap; use crate::wire::{DateTime, Duration}; pub type Programs = Vec; @@ -78,7 +78,7 @@ pub struct Program { pub payload_descriptors: Option>, /// A list of valuesMap objects. #[serde(skip_serializing_if = "Option::is_none")] - pub targets: Option>, + pub targets: Option, } impl Program { diff --git a/src/wire/report.rs b/src/wire/report.rs index 1ca3a6c..045bdc4 100644 --- a/src/wire/report.rs +++ b/src/wire/report.rs @@ -1,12 +1,14 @@ //! Types used for the report/ endpoint +use crate::Unit; use serde::{Deserialize, Serialize}; use crate::wire::event::EventId; use crate::wire::interval::{Interval, IntervalPeriod}; use crate::wire::program::ProgramId; -use crate::wire::values_map::ValuesMap; -use crate::wire::{DateTime, PayloadType, Unit}; +use crate::wire::target::TargetMap; +use crate::wire::values_map::Value; +use crate::wire::DateTime; /// report object. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] @@ -90,13 +92,13 @@ pub enum ObjectType { } /// Report data associated with a resource. -#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] pub struct Resource { /// User generated identifier. A value of AGGREGATED_REPORT indicates an aggregation of more /// that one resource's data // TODO: handle special name and length validation - pub resource_name: String, + pub resource_name: ResourceName, /// Defines default start and durations of intervals. #[serde(skip_serializing_if = "Option::is_none")] pub interval_period: Option, @@ -106,7 +108,7 @@ pub struct Resource { impl Resource { /// Report data associated with a resource. - pub fn new(resource_name: String, intervals: Vec) -> Resource { + pub fn new(resource_name: ResourceName, intervals: Vec) -> Resource { Resource { resource_name, interval_period: None, @@ -122,16 +124,16 @@ impl Resource { #[serde(rename_all = "camelCase")] pub struct ReportDescriptor { /// Enumerated or private string signifying the nature of values. - pub payload_type: PayloadType, + pub payload_type: ReportType, /// Enumerated or private string signifying the type of reading. - #[serde(skip_serializing_if = "Option::is_none")] - pub reading_type: Option, + #[serde(skip_serializing_if = "ReadingType::is_default", default)] + pub reading_type: ReadingType, /// Units of measure. #[serde(skip_serializing_if = "Option::is_none")] pub units: Option, /// A list of valuesMap objects. #[serde(skip_serializing_if = "Option::is_none")] - pub targets: Option>, + pub targets: Option, /// True if report should aggregate results from all targeted resources. False if report includes results for each resource. #[serde(default = "bool_false")] pub aggregate: bool, @@ -154,10 +156,10 @@ pub struct ReportDescriptor { impl ReportDescriptor { /// An object that may be used to request a report from a VEN. See OpenADR REST User Guide for detailed description of how configure a report request. - pub fn new(payload_type: PayloadType) -> Self { + pub fn new(payload_type: ReportType) -> Self { Self { payload_type, - reading_type: None, + reading_type: ReadingType::default(), units: None, targets: None, aggregate: false, @@ -193,10 +195,10 @@ fn pos_one() -> i32 { #[serde(rename_all = "camelCase")] pub struct ReportPayloadDescriptor { /// Enumerated or private string signifying the nature of values. - pub payload_type: PayloadType, + pub payload_type: ReportType, /// Enumerated or private string signifying the type of reading. - #[serde(skip_serializing_if = "Option::is_none")] - pub reading_type: Option, + #[serde(skip_serializing_if = "ReadingType::is_default", default)] + pub reading_type: ReadingType, /// Units of measure. #[serde(skip_serializing_if = "Option::is_none")] pub units: Option, @@ -209,10 +211,10 @@ pub struct ReportPayloadDescriptor { } impl ReportPayloadDescriptor { - pub fn new(payload_type: PayloadType) -> Self { + pub fn new(payload_type: ReportType) -> Self { Self { payload_type, - reading_type: None, + reading_type: Default::default(), units: None, accuracy: None, confidence: None, @@ -220,28 +222,105 @@ impl ReportPayloadDescriptor { } } +// TODO: Add range checks #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "SCREAMING_SNAKE_CASE")] -pub enum ReadingType { - DirectRead, - Todo, +pub struct Confidence(u8); + +/// An object defining a temporal window and a list of valuesMaps. if intervalPeriod present may set +/// temporal aspects of interval or override event.intervalPeriod. +#[derive(Clone, Debug, PartialEq, Default, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct ReportInterval { + /// A client generated number assigned an interval object. Not a sequence number. + pub id: i32, + /// Defines default start and durations of intervals. + #[serde(skip_serializing_if = "Option::is_none")] + pub interval_period: Option, + /// A list of valuesMap objects. + pub payloads: Vec, } -// TODO: Add range checks +impl ReportInterval { + pub fn new(id: i32, payloads: Vec) -> Self { + Self { + id, + interval_period: None, + payloads, + } + } +} + +/// Represents one or more values associated with a type. E.g. a type of PRICE contains a single float value. #[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] -pub struct Confidence(u8); +pub struct ReportValuesMap { + /// Enumerated or private string signifying the nature of values. E.G. \"PRICE\" indicates value is to be interpreted as a currency. + #[serde(rename = "type")] + pub value_type: ReportType, + /// A list of data points. Most often a singular value such as a price. + // TODO: The type of Value is actually defined by value_type + pub values: Vec, +} #[cfg(test)] mod tests { - use crate::wire::values_map::{Value, ValueType}; + use crate::wire::values_map::{Value, ValueType, ValuesMap}; use crate::wire::Duration; use super::*; + #[test] + fn test_report_type_serialization() { + assert_eq!( + serde_json::to_string(&ReportType::Baseline).unwrap(), + r#""BASELINE""# + ); + assert_eq!( + serde_json::to_string(&ReportType::RegulationSetpoint).unwrap(), + r#""REGULATION_SETPOINT""# + ); + assert_eq!( + serde_json::to_string(&ReportType::Private(String::from("something else"))).unwrap(), + r#""something else""# + ); + assert_eq!( + serde_json::from_str::(r#""DEMAND""#).unwrap(), + ReportType::Demand + ); + assert_eq!( + serde_json::from_str::(r#""EXPORT_RESERVATION_FEE""#).unwrap(), + ReportType::ExportReservationFee + ); + assert_eq!( + serde_json::from_str::(r#""something else""#).unwrap(), + ReportType::Private(String::from("something else")) + ); + } + + #[test] + fn test_reading_type_serialization() { + assert_eq!( + serde_json::to_string(&ReadingType::DirectRead).unwrap(), + r#""DIRECT_READ""# + ); + assert_eq!( + serde_json::to_string(&ReadingType::Private(String::from("something else"))).unwrap(), + r#""something else""# + ); + assert_eq!( + serde_json::from_str::(r#""AVERAGE""#).unwrap(), + ReadingType::Average + ); + assert_eq!( + serde_json::from_str::(r#""something else""#).unwrap(), + ReadingType::Private(String::from("something else")) + ); + } + #[test] fn descriptor_parses_minimal() { let json = r#"{"payloadType":"hello"}"#; - let expected = ReportDescriptor::new(PayloadType("hello".into())); + let expected = + ReportDescriptor::new(crate::wire::report::ReportType::Private("hello".into())); assert_eq!( serde_json::from_str::(json).unwrap(), @@ -262,6 +341,26 @@ mod tests { assert_eq!(serde_json::from_str::(example).unwrap(), expected); } + #[test] + fn test_resource_name_serialization() { + assert_eq!( + serde_json::to_string(&ResourceName::AggregatedReport).unwrap(), + r#""AGGREGATED_REPORT""# + ); + assert_eq!( + serde_json::to_string(&ResourceName::Private(String::from("something else"))).unwrap(), + r#""something else""# + ); + assert_eq!( + serde_json::from_str::(r#""AGGREGATED_REPORT""#).unwrap(), + ResourceName::AggregatedReport + ); + assert_eq!( + serde_json::from_str::(r#""something else""#).unwrap(), + ResourceName::Private(String::from("something else")) + ); + } + #[test] fn parses_example() { let example = r#"[{ @@ -313,7 +412,7 @@ mod tests { report_name: Some("Battery_usage_04112023".into()), payload_descriptors: None, resources: vec![Resource { - resource_name: "RESOURCE-999".into(), + resource_name: ResourceName::Private("RESOURCE-999".into()), interval_period: Some(IntervalPeriod { start: DateTime("2023-06-15T09:30:00Z".into()), duration: Some(Duration("PT1H".into())), @@ -340,3 +439,63 @@ mod tests { ); } } + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ReportType { + Reading, + Usage, + Demand, + Setpoint, + DeltaUsage, + Baseline, + OperatingState, + UpRegulationAvailable, + DownRegulationAvailable, + RegulationSetpoint, + StorageUsableCapacity, + StorageChargeLevel, + StorageMaxDischargePower, + StorageMaxChargePower, + SimpleLevel, + UsageForecast, + StorageDispatchForecast, + LoadShedDeltaAvailable, + GenerationDeltaAvailable, + DataQuality, + ImportReservationCapacity, + ImportReservationFee, + ExportReservationCapacity, + ExportReservationFee, + #[serde(untagged)] + Private(String), +} + +#[derive(Clone, Default, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ReadingType { + #[default] + DirectRead, + Estimated, + Summed, + Mean, + Peak, + Forecast, + Average, + #[serde(untagged)] + Private(String), +} + +impl ReadingType { + fn is_default(&self) -> bool { + *self == Self::default() + } +} + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum ResourceName { + AggregatedReport, + #[serde(untagged)] + Private(String), +} diff --git a/src/wire/target.rs b/src/wire/target.rs new file mode 100644 index 0000000..d1eb26e --- /dev/null +++ b/src/wire/target.rs @@ -0,0 +1,62 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)] +pub struct TargetMap(Vec); + +// TODO: Handle strong typing of values +#[derive(Clone, Debug, PartialEq, Serialize, Deserialize)] +pub struct TargetEntry { + #[serde(rename = "type")] + label: TargetLabel, + values: [String; 1], +} + +#[derive(Clone, Serialize, Deserialize, PartialEq, Eq, Debug)] +#[serde(rename_all = "SCREAMING_SNAKE_CASE")] +pub enum TargetLabel { + /// A Power Service Location is a utility named specific location in + /// geography or the distribution system, usually the point of service to a + /// customer site. + PowerServiceLocation, + /// A Service Area is a utility named geographic region. + ServiceArea, + /// Targeting a specific group (string). + Group, + /// Targeting a specific resource (string). + ResourceName, + /// Targeting a specific VEN (string). + #[serde(rename = "VEN_NAME")] + VENName, + /// Targeting a specific event (string). + EventName, + /// Targeting a specific program (string). + ProgramName, + /// An application specific privately defined target. + #[serde(untagged)] + Private(String), +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_target_serialization() { + assert_eq!( + serde_json::to_string(&TargetLabel::EventName).unwrap(), + r#""EVENT_NAME""# + ); + assert_eq!( + serde_json::to_string(&TargetLabel::Private(String::from("something else"))).unwrap(), + r#""something else""# + ); + assert_eq!( + serde_json::from_str::(r#""VEN_NAME""#).unwrap(), + TargetLabel::VENName + ); + assert_eq!( + serde_json::from_str::(r#""something else""#).unwrap(), + TargetLabel::Private(String::from("something else")) + ); + } +}