use anyhow::Context;
use bitflags::bitflags;
use std::fmt::Display;
use std::str::FromStr;
use yaserde::{DefaultYaSerde, HexBinaryYaSerde, PrimitiveYaSerde, YaDeserialize, YaSerialize};
use crate::traits::Validate;
use super::primitives::{
HexBinary128, Int32, Int48, Int64, String32, String42, Uint16, Uint32, Uint48,
};
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "AccumulationBehaviourType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum AccumulationBehaviourType {
#[default]
NotApplicable = 0,
Cumulative = 3,
DeltaData = 4,
Indicating = 6,
Summation = 9,
Instantaneous = 12,
}
impl Validate for AccumulationBehaviourType {}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, YaSerialize, YaDeserialize)]
#[yaserde(rename = "ApplianceLoadReductionType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum ApplianceLoadReductionType {
#[default]
DelayApplianceLoad = 0,
TemporaryApplianceLoadReduction = 1,
}
impl Validate for ApplianceLoadReductionType {}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "CommodityType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub enum CommodityType {
#[default]
NotApplicable = 0, ElectricitySecondaryMetered = 1, ElectricityPrimaryMetered = 2, Air = 4,
NaturalGas = 7,
Propane = 8,
PotableWater = 9,
Steam = 10,
WasteWater = 11,
HeatingFluid = 12,
CoolingFluid = 13,
}
impl Validate for CommodityType {}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "ConsumptionBlockType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum ConsumptionBlockType {
#[default]
NotApplicable = 0,
Block1 = 1,
Block2 = 2,
Block3 = 3,
Block4 = 4,
Block5 = 5,
Block6 = 6,
Block7 = 7,
Block8 = 8,
Block9 = 9,
Block10 = 10,
Block11 = 11,
Block12 = 12,
Block13 = 13,
Block14 = 14,
Block15 = 15,
Block16 = 16,
}
impl Validate for ConsumptionBlockType {}
pub type CurrencyCode = Uint16;
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "DataQualifierType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum DataQualifierType {
#[default]
NotApplicable = 0,
Average = 2,
Maximum = 8,
Minimum = 9,
Normal = 12,
StandardDeviationOfPopulation = 29,
StandardDeviationOfSample = 30,
}
impl Validate for DataQualifierType {}
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "DateTimeInterval")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub struct DateTimeInterval {
#[yaserde(rename = "duration")]
pub duration: Uint32,
#[yaserde(rename = "start")]
pub start: TimeType,
}
impl Validate for DateTimeInterval {}
bitflags! {
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, HexBinaryYaSerde)]
pub struct DeviceCategoryType: u32 { const ProgrammableCommunicatingThermostat = 1;
const StripHeaters = 2;
const WaterHeater = 8;
const PoolPump = 16;
const Sauna = 32;
const HotTub = 64;
const SmartAppliance = 128;
const IrrigationPump = 256;
const ManagedCommercialAndIndustrialLoads = 512;
const SimpleMiscLoads = 1024;
const ExteriorLighting = 2048;
const InteriorLighting = 4096;
const LoadControlSwitch = 8192;
const EnergyManagementSystem = 16384;
const SmartEnergyModule = 32768;
const ElectricVehicle = 65536;
const EVSE = 131072;
const VirutalOrMixedDer = 262144;
const ReciprocatingEngine = 524288;
const PhotovoltaicSystem = 2097152;
const CombinedPvAndStorage = 8388608;
const OtherGenerationSystem = 16777216;
const OtherStorageSystem = 33554432;
}
}
impl Validate for DeviceCategoryType {}
bitflags! {
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, HexBinaryYaSerde)]
pub struct QualityFlags: u16 { const Valid = 1;
const ManuallyEdited = 2;
const EstimatedRef = 8;
const EstimatedLinear = 16;
const Questionable = 32;
const Derived = 64;
const Projected = 128;
}
}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, PrimitiveYaSerde)]
pub struct DstRuleType(u32);
impl Validate for DstRuleType {}
impl DstRuleType {
const SECONDS_SHIFT: u32 = 0;
const HOURS_SHIFT: u32 = 12;
const DAY_OF_WEEK_SHIFT: u32 = 17;
const DAY_OF_MONTH_SHIFT: u32 = 20;
const OPERATOR_SHIFT: u32 = 25;
const MONTH_SHIFT: u32 = 28;
const SECONDS_MASK: u32 = 0xFFF << Self::SECONDS_SHIFT; const HOURS_MASK: u32 = 0x1F << Self::HOURS_SHIFT; const DAY_OF_WEEK_MASK: u32 = 0x7 << Self::DAY_OF_WEEK_SHIFT; const DAY_OF_MONTH_MASK: u32 = 0x1F << Self::DAY_OF_MONTH_SHIFT; const OPERATOR_MASK: u32 = 0x7 << Self::OPERATOR_SHIFT; const MONTH_MASK: u32 = 0xF << Self::MONTH_SHIFT; pub fn new(
seconds: u32,
hours: u32,
day_of_week: u32,
day_of_month: u32,
operator: u32,
month: u32,
) -> Self {
let mut out = DstRuleType(0);
out.set_seconds(seconds);
out.set_hours(hours);
out.set_day_of_week(day_of_week);
out.set_day_of_month(day_of_month);
out.set_operator(operator);
out.set_month(month);
out
}
pub fn seconds(&self) -> u32 {
self.0 & Self::SECONDS_MASK
}
pub fn set_seconds(&mut self, seconds: u32) {
self.0 = (self.0 & !Self::SECONDS_MASK) | (seconds & Self::SECONDS_MASK);
}
pub fn hours(&self) -> u32 {
(self.0 & Self::HOURS_MASK) >> Self::HOURS_SHIFT
}
pub fn set_hours(&mut self, hours: u32) {
self.0 = (self.0 & !Self::HOURS_MASK) | ((hours << Self::HOURS_SHIFT) & Self::HOURS_MASK);
}
pub fn day_of_week(&self) -> u32 {
(self.0 & Self::DAY_OF_WEEK_MASK) >> Self::DAY_OF_WEEK_SHIFT
}
pub fn set_day_of_week(&mut self, day_of_week: u32) {
self.0 = (self.0 & !Self::DAY_OF_WEEK_MASK)
| ((day_of_week << Self::DAY_OF_WEEK_SHIFT) & Self::DAY_OF_WEEK_MASK);
}
pub fn day_of_month(&self) -> u32 {
(self.0 & Self::DAY_OF_MONTH_MASK) >> Self::DAY_OF_MONTH_SHIFT
}
pub fn set_day_of_month(&mut self, day_of_month: u32) {
self.0 = (self.0 & !Self::DAY_OF_MONTH_MASK)
| ((day_of_month << Self::DAY_OF_MONTH_SHIFT) & Self::DAY_OF_MONTH_MASK);
}
pub fn operator(&self) -> u32 {
(self.0 & Self::OPERATOR_MASK) >> Self::OPERATOR_SHIFT
}
pub fn set_operator(&mut self, operator: u32) {
self.0 = (self.0 & !Self::OPERATOR_MASK)
| ((operator << Self::OPERATOR_SHIFT) & Self::OPERATOR_MASK);
}
pub fn month(&self) -> u32 {
(self.0 & Self::MONTH_MASK) >> Self::MONTH_SHIFT
}
pub fn set_month(&mut self, month: u32) {
self.0 = (self.0 & !Self::MONTH_MASK) | ((month << Self::MONTH_SHIFT) & Self::MONTH_MASK);
}
}
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "FlowDirectionType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum FlowDirectionType {
#[default]
NotApplicable = 0,
Forward = 1,
Reverse = 19,
}
impl Validate for FlowDirectionType {}
#[derive(Default, PartialEq, Eq, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "GPSLocationType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub struct GPSLocationType {
#[yaserde(rename = "lat")]
pub lat: String32,
#[yaserde(rename = "lon")]
pub lon: String32,
}
impl Validate for GPSLocationType {}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "KindType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum KindType {
#[default]
NotApplicable = 0,
Currency = 3,
Demand = 8,
Energy = 12,
Power = 37,
}
impl Validate for KindType {}
pub type LocaleType = String42;
pub type MRIDType = HexBinary128;
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, DefaultYaSerde)]
pub struct OneHourRangeType(i16);
impl Validate for OneHourRangeType {}
impl OneHourRangeType {
pub fn new(val: i16) -> Option<OneHourRangeType> {
if !(-3600..=3600).contains(&val) {
None
} else {
Some(OneHourRangeType(val))
}
}
pub fn get(&self) -> i16 {
self.0
}
}
impl FromStr for OneHourRangeType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse()
.ok()
.and_then(OneHourRangeType::new)
.context("OneHourRangeType value must be between -3600 and 3600")
}
}
impl Display for OneHourRangeType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
pub type PENType = Uint32;
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, DefaultYaSerde)]
pub struct Percent(u16);
impl Validate for Percent {}
impl Percent {
pub fn new(val: u16) -> Option<Percent> {
if val > 10_000 {
None
} else {
Some(Percent(val))
}
}
pub fn get(&self) -> u16 {
self.0
}
}
impl FromStr for Percent {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse()
.ok()
.and_then(Percent::new)
.context("Percent value must be between 0 and 10000")
}
}
impl Display for Percent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "PhaseCode")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum PhaseCode {
#[default]
NotApplicable = 0,
PhaseC = 32, PhaseCN = 33, PhaseCA = 40,
PhaseB = 64,
PhaseBN = 65,
PhaseBC = 66,
PhaseA = 128, PhaseAN = 129, PhaseAB = 132,
PhaseABC = 224,
}
impl Validate for PhaseCode {}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, DefaultYaSerde)]
pub struct PINType(u32);
impl Validate for PINType {}
impl PINType {
pub fn new(val: u32) -> Option<PINType> {
if val > 999_999 {
None
} else {
Some(PINType(val))
}
}
pub fn get(&self) -> u32 {
self.0
}
}
impl FromStr for PINType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse()
.ok()
.and_then(PINType::new)
.context("PINType value must be between 0 and 999,999")
}
}
impl Display for PINType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "PowerOfTenMultiplierType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(i8)]
pub enum PowerOfTenMultiplierType {
Nano = -9,
NegativeEight = -8,
NegativeSeven = -7,
Micro = -6,
NegativeFive = -5,
NegativeFour = -4,
Milli = -3,
Centi = -2,
Deci = -1,
#[default]
None = 0,
Deca = 1,
Hecto = 2,
Kilo = 3,
Four = 4,
Five = 5,
Mega = 6,
Seven = 7,
Eight = 8,
Giga = 9,
}
impl Validate for PowerOfTenMultiplierType {}
#[derive(
Default, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Debug, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "PrimacyType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum PrimacyType {
#[default]
InHomeEnergyManagementSystem = 0,
ContractedPremisesServiceProvider = 1,
NonContractualServiceProvider = 2,
}
impl Validate for PrimacyType {}
#[derive(Default, PartialEq, Eq, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "RealEnergy")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub struct RealEnergy {
#[yaserde(rename = "multiplier")]
pub multiplier: PowerOfTenMultiplierType,
#[yaserde(rename = "value")]
pub value: Uint48,
}
impl Validate for RealEnergy {}
bitflags! {
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Clone, Copy, Debug, HexBinaryYaSerde)]
pub struct RoleFlagsType: u16 { const IsMirror = 1;
const IsPremiseAggregationPoint = 2;
const IsPEV = 4;
const IsDER = 8;
const IsRevenueQuality = 16;
const IsDC = 32;
const IsSubmeter = 64;
}
}
impl Validate for RoleFlagsType {}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, YaSerialize, YaDeserialize)]
#[yaserde(rename = "ServiceKind")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum ServiceKind {
#[default]
Electricity = 0,
Gas = 1,
Water = 2,
Time = 3,
Pressure = 4,
Heat = 5,
Cooling = 6,
}
impl Validate for ServiceKind {}
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, DefaultYaSerde)]
pub struct SFDIType(u64);
impl Validate for SFDIType {}
impl SFDIType {
pub fn new(val: u64) -> Option<SFDIType> {
if val > 1_099_511_627_775 {
None
} else {
Some(SFDIType(val))
}
}
pub fn get(&self) -> u64 {
self.0
}
}
impl FromStr for SFDIType {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse()
.ok()
.and_then(SFDIType::new)
.context("SFDIType value must be between -10,000 and 10,000")
}
}
impl Display for SFDIType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, DefaultYaSerde)]
pub struct SignedPercent(i16);
impl Validate for SignedPercent {}
impl SignedPercent {
pub fn new(val: i16) -> Option<SignedPercent> {
if !(-10_000..=10_000).contains(&val) {
None
} else {
Some(SignedPercent(val))
}
}
pub fn get(&self) -> i16 {
self.0
}
}
impl FromStr for SignedPercent {
type Err = anyhow::Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
s.parse()
.ok()
.and_then(SignedPercent::new)
.context("SignedPercent value must be between -10,000 and 10,000")
}
}
impl Display for SignedPercent {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
#[derive(Default, PartialEq, Eq, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "SignedRealEnergy")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub struct SignedRealEnergy {
#[yaserde(rename = "multiplier")]
pub multiplier: PowerOfTenMultiplierType,
#[yaserde(rename = "value")]
pub value: Int48,
}
impl Validate for SignedRealEnergy {}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, YaSerialize, YaDeserialize)]
#[repr(u8)]
pub enum SubscribableType {
#[default]
NoSubscriptionsSupported = 0,
NonConditionalSubscriptions = 1,
ConditionalSubscriptions = 2,
AllSubscriptions = 3,
}
impl Validate for SubscribableType {}
pub type TimeOffsetType = Int32;
pub type TimeType = Int64;
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "TOUType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum TOUType {
#[default]
NotApplicable = 0,
TouA = 1,
TouB = 2,
TouC = 3,
TouD = 4,
TouE = 5,
TouF = 6,
TouG = 7,
TouH = 8,
TouI = 9,
TouJ = 10,
TouK = 11,
TouL = 12,
TouM = 13,
TouN = 14,
TouO = 15,
}
impl Validate for TOUType {}
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, YaSerialize, YaDeserialize)]
#[yaserde(rename = "UnitType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
#[allow(non_camel_case_types)]
pub enum UnitType {
#[default]
kWh = 0,
kW = 1,
Watts = 2,
CubicMeters = 3,
CubicFeet = 4,
USGallons = 5,
ImperialGallons = 6,
BTU = 7,
Liters = 8,
kPAGauge = 9,
kPAAbsolute = 10,
Megajoule = 11,
Unitless = 12,
}
impl Validate for UnitType {}
#[derive(Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, YaSerialize, YaDeserialize)]
#[yaserde(rename = "UnitValueType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
pub struct UnitValueType {
#[yaserde(rename = "multiplier")]
pub multiplier: PowerOfTenMultiplierType,
#[yaserde(rename = "unit")]
pub unit: UomType,
#[yaserde(rename = "value")]
pub value: Int32,
}
impl Validate for UnitValueType {}
#[derive(
Default, PartialEq, PartialOrd, Eq, Ord, Debug, Clone, Copy, YaSerialize, YaDeserialize,
)]
#[yaserde(rename = "UomType")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum UomType {
#[default]
NotApplicable = 0,
Amperes = 5,
Kelvin = 6,
DegreesCelsius = 23,
Voltage = 29,
Joule = 31,
Hz = 33,
W = 38,
MtrCubed = 42,
VA = 61,
VAr = 63,
CosTheta = 65,
VSquared = 67,
ASquared = 69,
VAh = 71,
Wh = 72,
VArh = 73,
Ah = 106,
FtCubed = 119,
FtCubedPerHour = 122,
MCubedPerHour = 125,
USGallons = 128,
UGGallonsPerHour = 129,
ImperialGallons = 130,
ImperialGallonsPerHour = 131,
BTU = 132,
BTUPerHour = 133,
Liter = 134,
LiterPerHour = 137,
PAGauge = 140,
PAAbsolute = 155,
Therm = 169,
}
impl Validate for UomType {}
pub type VersionType = Uint16;
#[derive(Default, PartialEq, Eq, Debug, Clone, Copy, YaSerialize, YaDeserialize)]
#[yaserde(rename = "status")]
#[yaserde(namespace = "urn:ieee:std:2030.5:ns")]
#[repr(u8)]
pub enum UsagePointStatus {
#[default]
Off,
On,
}
#[test]
fn basic_dstruletype() {
let rule = DstRuleType::new(2700, 1, 5, 0, 4, 3);
assert_eq!(rule.seconds(), 2700);
assert_eq!(rule.hours(), 1);
assert_eq!(rule.day_of_week(), 5);
assert_eq!(rule.day_of_month(), 0);
assert_eq!(rule.operator(), 4);
assert_eq!(rule.month(), 3);
}