use crate::templates::template::{Template, TemplateType};
use crate::utils::{read_u16_from_bytes, read_u32_from_bytes};
use chrono::{DateTime, Utc};
use super::product_template::ProductTemplate;
use super::tables::{FixedSurfaceType, GeneratingProcess, TimeUnit, ProbabilityType};
pub struct ProbabilityHorizontalForecastTemplate {
data: Vec<u8>,
discipline: u8,
}
impl Template for ProbabilityHorizontalForecastTemplate {
fn data(&self) -> &[u8] {
&self.data
}
fn template_number(&self) -> u16 {
5
}
fn template_type(&self) -> TemplateType {
TemplateType::Product
}
fn template_name(&self) -> &str {
"Probability forecast at a horizontal level or in a horizontal layer at a point in time"
}
}
impl ProbabilityHorizontalForecastTemplate {
pub fn new(data: Vec<u8>, discipline: u8) -> Self {
ProbabilityHorizontalForecastTemplate { data, discipline }
}
pub fn category_value(&self) -> u8 {
self.data[9]
}
pub fn parameter_value(&self) -> u8 {
self.data[10]
}
pub fn generating_process(&self) -> GeneratingProcess {
self.data[11].into()
}
pub fn observation_cutoff_hours_after_reference_time(&self) -> u16 {
read_u16_from_bytes(&self.data, 14).unwrap_or(0)
}
pub fn observation_cutoff_minutes_after_cutoff_time(&self) -> u8 {
self.data[16]
}
pub fn first_fixed_surface_scale_factor(&self) -> i8 {
as_signed!(self.data[23], 8, i8)
}
pub fn first_fixed_surface_scaled_value(&self) -> i32 {
as_signed!(read_u32_from_bytes(&self.data, 24).unwrap_or(0), 32, i32)
}
pub fn second_fixed_surface_scale_factor(&self) -> i8 {
as_signed!(self.data[29], 8, i8)
}
pub fn second_fixed_surface_scaled_value(&self) -> i32 {
as_signed!(read_u32_from_bytes(&self.data, 30).unwrap_or(0), 32, i32)
}
pub fn forecast_probability_number(&self) -> u8 {
self.data.get(34).copied().unwrap_or(0)
}
pub fn total_number_of_forecast_probabilities(&self) -> u8 {
self.data.get(35).copied().unwrap_or(0)
}
pub fn probability_type(&self) -> ProbabilityType {
self.data.get(36).copied().unwrap_or(0).into()
}
pub fn lower_limit_scale_factor(&self) -> i8 {
as_signed!(self.data.get(37).copied().unwrap_or(0), 8, i8)
}
pub fn lower_limit_scaled_value(&self) -> i32 {
as_signed!(read_u32_from_bytes(&self.data, 38).unwrap_or(0), 32, i32)
}
pub fn upper_limit_scale_factor(&self) -> i8 {
as_signed!(self.data.get(42).copied().unwrap_or(0), 8, i8)
}
pub fn upper_limit_scaled_value(&self) -> i32 {
as_signed!(read_u32_from_bytes(&self.data, 43).unwrap_or(0), 32, i32)
}
pub fn array_index(&self) -> Option<usize> {
match self.first_fixed_surface_type() {
FixedSurfaceType::OrderedSequence => {
Some(self.first_fixed_surface_scaled_value() as usize)
}
_ => None,
}
}
pub fn scale_value(factor: i8, scaled_value: i32) -> Option<f64> {
let factor = if factor == i8::MIN + 1 {
0
} else {
factor as i32
};
let scale_factor = 10_f64.powi(-factor);
if scaled_value == i32::MIN + 1 {
None
} else {
Some(scaled_value as f64 * scale_factor)
}
}
pub fn lower_limit(&self) -> Option<f64> {
Self::scale_value(
self.lower_limit_scale_factor(),
self.lower_limit_scaled_value(),
)
}
pub fn upper_limit(&self) -> Option<f64> {
Self::scale_value(
self.upper_limit_scale_factor(),
self.upper_limit_scaled_value(),
)
}
}
impl ProductTemplate for ProbabilityHorizontalForecastTemplate {
fn discipline(&self) -> u8 {
self.discipline
}
fn category_value(&self) -> u8 {
self.data[9]
}
fn parameter_value(&self) -> u8 {
self.data[10]
}
fn generating_process(&self) -> GeneratingProcess {
self.data[11].into()
}
fn time_unit(&self) -> TimeUnit {
self.data[17].into()
}
fn time_increment_unit(&self) -> Option<TimeUnit> {
None
}
fn time_interval(&self) -> u32 {
read_u32_from_bytes(&self.data, 18).unwrap_or(0)
}
fn time_increment_interval(&self) -> Option<u32> {
None
}
fn forecast_datetime(&self, reference_date: DateTime<Utc>) -> DateTime<Utc> {
let offset_duration = self.time_interval_duration();
reference_date + offset_duration
}
fn forecast_end_datetime(&self, _reference_date: DateTime<Utc>) -> Option<DateTime<Utc>> {
None
}
fn first_fixed_surface_type(&self) -> FixedSurfaceType {
self.data[22].into()
}
fn first_fixed_surface_value(&self) -> Option<f64> {
ProbabilityHorizontalForecastTemplate::scale_value(
self.first_fixed_surface_scale_factor(),
self.first_fixed_surface_scaled_value(),
)
}
fn second_fixed_surface_type(&self) -> FixedSurfaceType {
self.data[28].into()
}
fn second_fixed_surface_value(&self) -> Option<f64> {
ProbabilityHorizontalForecastTemplate::scale_value(
self.second_fixed_surface_scale_factor(),
self.second_fixed_surface_scaled_value(),
)
}
fn derived_forecast_type(&self) -> Option<super::tables::DerivedForecastType> {
None
}
fn statistical_process_type(&self) -> Option<super::tables::TypeOfStatisticalProcessing> {
None
}
}