plc_control/src/telemetry.rs

155 lines
4.7 KiB
Rust

use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::model::ScanMode;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum ValueType {
Null,
Bool,
Int,
UInt,
Float,
Text,
Bytes,
Array,
Object,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum PointQuality {
Good,
Bad,
Uncertain,
Unknown,
}
impl PointQuality {
pub fn from_status_code(status: &opcua::types::StatusCode) -> Self {
if status.is_good() {
Self::Good
} else if status.is_bad() {
Self::Bad
} else if status.is_uncertain() {
Self::Uncertain
} else {
Self::Unknown
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(untagged)]
pub enum DataValue {
Null,
Bool(bool),
Int(i64),
UInt(u64),
Float(f64),
Text(String),
Bytes(Vec<u8>),
Array(Vec<DataValue>),
Object(serde_json::Value),
}
impl DataValue {
pub fn to_json_value(&self) -> serde_json::Value {
match self {
DataValue::Null => serde_json::Value::Null,
DataValue::Bool(v) => serde_json::Value::Bool(*v),
DataValue::Int(v) => serde_json::json!(*v),
DataValue::UInt(v) => serde_json::json!(*v),
DataValue::Float(v) => serde_json::json!(*v),
DataValue::Text(v) => serde_json::json!(v),
DataValue::Bytes(v) => serde_json::json!(v),
DataValue::Array(v) => {
serde_json::Value::Array(v.iter().map(DataValue::to_json_value).collect())
}
DataValue::Object(v) => v.clone(),
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PointMonitorInfo {
pub protocol: String,
pub source_id: Uuid,
pub point_id: Uuid,
pub client_handle: u32,
pub scan_mode: ScanMode,
#[serde(serialize_with = "crate::util::datetime::option_utc_to_local_str")]
pub timestamp: Option<DateTime<Utc>>,
pub quality: PointQuality,
pub value: Option<DataValue>,
pub value_type: Option<ValueType>,
pub value_text: Option<String>,
pub old_value: Option<DataValue>,
#[serde(serialize_with = "crate::util::datetime::option_utc_to_local_str")]
pub old_timestamp: Option<DateTime<Utc>>,
pub value_changed: bool,
}
impl PointMonitorInfo {
pub fn value_as_json(&self) -> Option<serde_json::Value> {
self.value.as_ref().map(DataValue::to_json_value)
}
}
#[derive(Debug, Clone)]
pub struct PointNewValue {
pub source_id: Uuid,
pub point_id: Option<Uuid>,
pub client_handle: u32,
pub value: Option<DataValue>,
pub value_type: Option<ValueType>,
pub value_text: Option<String>,
pub quality: PointQuality,
pub protocol: String,
pub timestamp: Option<DateTime<Utc>>,
pub scan_mode: ScanMode,
}
pub fn opcua_variant_to_data(value: &opcua::types::Variant) -> DataValue {
use opcua::types::Variant;
match value {
Variant::Empty => DataValue::Null,
Variant::Boolean(v) => DataValue::Bool(*v),
Variant::SByte(v) => DataValue::Int(*v as i64),
Variant::Byte(v) => DataValue::UInt(*v as u64),
Variant::Int16(v) => DataValue::Int(*v as i64),
Variant::UInt16(v) => DataValue::UInt(*v as u64),
Variant::Int32(v) => DataValue::Int(*v as i64),
Variant::UInt32(v) => DataValue::UInt(*v as u64),
Variant::Int64(v) => DataValue::Int(*v),
Variant::UInt64(v) => DataValue::UInt(*v),
Variant::Float(v) => DataValue::Float(*v as f64),
Variant::Double(v) => DataValue::Float(*v),
Variant::String(v) => DataValue::Text(v.to_string()),
Variant::ByteString(v) => DataValue::Bytes(v.value.clone().unwrap_or_default()),
Variant::Array(v) => {
DataValue::Array(v.values.iter().map(opcua_variant_to_data).collect())
}
_ => DataValue::Text(value.to_string()),
}
}
pub fn opcua_variant_type(value: &opcua::types::Variant) -> ValueType {
use opcua::types::Variant;
match value {
Variant::Empty => ValueType::Null,
Variant::Boolean(_) => ValueType::Bool,
Variant::SByte(_) | Variant::Int16(_) | Variant::Int32(_) | Variant::Int64(_) => ValueType::Int,
Variant::Byte(_) | Variant::UInt16(_) | Variant::UInt32(_) | Variant::UInt64(_) => ValueType::UInt,
Variant::Float(_) | Variant::Double(_) => ValueType::Float,
Variant::String(_) => ValueType::Text,
Variant::ByteString(_) => ValueType::Bytes,
Variant::Array(_) => ValueType::Array,
_ => ValueType::Text,
}
}