2025-10-02 18:39:32 +02:00
|
|
|
use std::fmt;
|
2025-10-11 10:08:28 +02:00
|
|
|
use std::sync::Arc;
|
2025-10-02 18:39:32 +02:00
|
|
|
|
2025-10-11 10:08:28 +02:00
|
|
|
use bevy_reflect::Reflect;
|
2025-09-16 21:07:44 +02:00
|
|
|
use chrono::{DateTime, Utc};
|
2025-10-03 21:19:14 +02:00
|
|
|
use std::sync::RwLock;
|
2025-09-16 21:07:44 +02:00
|
|
|
use xmltree::Element;
|
|
|
|
|
|
|
|
|
|
use crate::{
|
2025-10-03 21:19:14 +02:00
|
|
|
object_trait::{UpnpInstance, UpnpObject},
|
|
|
|
|
state_variables::{StateVarInstance, StateVariable, UpnpVariable},
|
|
|
|
|
variable_types::{StateValue, StateValueError, UpnpVarType},
|
|
|
|
|
UpnpObjectType, UpnpTyped, UpnpTypedInstance
|
2025-09-16 21:07:44 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
impl UpnpVariable for StateVarInstance {
|
|
|
|
|
fn get_definition(&self) -> &StateVariable {
|
2025-10-02 18:39:32 +02:00
|
|
|
return &self.model;
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
impl UpnpObject for StateVarInstance {
|
2025-10-03 21:19:14 +02:00
|
|
|
fn to_xml_element(&self) -> Element {
|
|
|
|
|
self.get_definition().to_xml_element()
|
2025-10-01 15:44:52 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
impl UpnpVarType for StateVarInstance {
|
|
|
|
|
fn as_state_var_type(&self) -> crate::variable_types::StateVarType {
|
|
|
|
|
self.get_definition().as_state_var_type()
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
impl UpnpInstance for StateVarInstance {
|
|
|
|
|
type Model = StateVariable;
|
|
|
|
|
|
|
|
|
|
fn new(from: &StateVariable) -> Self {
|
2025-09-16 21:07:44 +02:00
|
|
|
Self {
|
|
|
|
|
object: UpnpObjectType {
|
|
|
|
|
name: from.object.name.clone(),
|
|
|
|
|
object_type: "StateVarInstance".to_string(),
|
|
|
|
|
},
|
2025-10-02 18:39:32 +02:00
|
|
|
model: from.clone(),
|
|
|
|
|
value: RwLock::new(from.get_default()),
|
|
|
|
|
old_value: RwLock::new(from.get_default()),
|
|
|
|
|
last_modified: RwLock::new(Utc::now()),
|
|
|
|
|
last_notification: RwLock::new(Utc::now()),
|
2025-10-10 23:49:59 +02:00
|
|
|
service: RwLock::new(None),
|
2025-10-11 10:08:28 +02:00
|
|
|
reflexive_cache: RwLock::new(None),
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UpnpTyped for StateVarInstance {
|
|
|
|
|
fn as_upnp_object_type(&self) -> &UpnpObjectType {
|
|
|
|
|
return &self.object;
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
2025-10-02 18:39:32 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl UpnpTypedInstance for StateVarInstance {
|
2025-09-16 21:07:44 +02:00
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
fn get_model(&self) -> &Self::Model {
|
|
|
|
|
&self.model
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl fmt::Debug for StateVarInstance {
|
|
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
|
|
|
|
f.debug_struct("StateVarInstance")
|
|
|
|
|
.field("object", &self.object)
|
|
|
|
|
.field("model", &self.model)
|
|
|
|
|
.field("value", &self.value)
|
|
|
|
|
.field("old_value", &self.old_value)
|
|
|
|
|
.field("last_modified", &self.last_modified)
|
|
|
|
|
.field("last_notification", &self.last_notification)
|
|
|
|
|
.finish()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl Clone for StateVarInstance {
|
|
|
|
|
fn clone(&self) -> Self {
|
|
|
|
|
Self {
|
|
|
|
|
object: self.object.clone(),
|
|
|
|
|
model: self.model.clone(),
|
2025-10-03 21:19:14 +02:00
|
|
|
value: RwLock::new(self.value.read().unwrap().clone()),
|
|
|
|
|
old_value: RwLock::new(self.old_value.read().unwrap().clone()),
|
|
|
|
|
last_modified: RwLock::new(self.last_modified.read().unwrap().clone()),
|
|
|
|
|
last_notification: RwLock::new(self.last_notification.read().unwrap().clone()),
|
2025-10-10 23:49:59 +02:00
|
|
|
service: RwLock::new(self.service.read().unwrap().clone()),
|
2025-10-11 10:08:28 +02:00
|
|
|
reflexive_cache: RwLock::new(None), // Le cache n'est pas cloné, il sera recalculé si nécessaire
|
2025-10-02 18:39:32 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
impl StateVarInstance {
|
2025-10-10 23:49:59 +02:00
|
|
|
/// Enregistre le service parent pour cette variable.
|
|
|
|
|
///
|
|
|
|
|
/// Cette méthode doit être appelée depuis `ServiceInstance::new()` pour
|
|
|
|
|
/// permettre à la variable de notifier le service lorsqu'elle change.
|
|
|
|
|
///
|
|
|
|
|
/// # Arguments
|
|
|
|
|
///
|
|
|
|
|
/// * `service` - Arc vers le ServiceInstance parent
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// # use pmoupnp::services::ServiceInstance;
|
|
|
|
|
/// # use pmoupnp::state_variables::StateVarInstance;
|
|
|
|
|
/// # use std::sync::Arc;
|
|
|
|
|
/// let service_instance = Arc::new(ServiceInstance::new(&service));
|
|
|
|
|
/// let var_instance = Arc::new(StateVarInstance::new(&variable));
|
|
|
|
|
/// var_instance.register_service(Arc::downgrade(&service_instance));
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn register_service(&self, service: std::sync::Weak<crate::services::ServiceInstance>) {
|
|
|
|
|
let mut svc = self.service.write().unwrap();
|
|
|
|
|
*svc = Some(service);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
pub async fn set_value(&self, new_value: StateValue) -> Result<(), StateValueError> {
|
|
|
|
|
// Validation du type
|
|
|
|
|
if self.as_state_var_type() != new_value.as_state_var_type() {
|
|
|
|
|
return Err(StateValueError::TypeError(
|
|
|
|
|
"Value type mismatch".to_string()
|
|
|
|
|
));
|
|
|
|
|
}
|
2025-10-10 23:49:59 +02:00
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
// Mise à jour avec les locks
|
2025-10-03 21:19:14 +02:00
|
|
|
let mut old_val = self.old_value.write().unwrap();
|
|
|
|
|
let mut val = self.value.write().unwrap();
|
|
|
|
|
let mut modified = self.last_modified.write().unwrap();
|
2025-10-10 23:49:59 +02:00
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
*old_val = val.clone();
|
2025-10-10 23:49:59 +02:00
|
|
|
*val = new_value.clone();
|
2025-10-02 18:39:32 +02:00
|
|
|
*modified = Utc::now();
|
2025-10-10 23:49:59 +02:00
|
|
|
|
2025-10-11 10:08:28 +02:00
|
|
|
// Invalider le cache réflexif
|
|
|
|
|
{
|
|
|
|
|
let mut cache = self.reflexive_cache.write().unwrap();
|
|
|
|
|
*cache = None;
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-10 23:49:59 +02:00
|
|
|
// Notifier le service parent si la variable envoie des événements
|
|
|
|
|
if self.is_sending_notification() {
|
|
|
|
|
// Relâcher les locks avant d'appeler le service
|
|
|
|
|
drop(val);
|
|
|
|
|
drop(old_val);
|
|
|
|
|
drop(modified);
|
|
|
|
|
|
|
|
|
|
if let Some(weak_service) = self.service.read().unwrap().as_ref() {
|
|
|
|
|
if let Some(service) = weak_service.upgrade() {
|
2025-10-11 10:08:28 +02:00
|
|
|
// Obtenir la valeur réflexive (sans propager l'erreur car on est dans une notification)
|
|
|
|
|
if let Ok(reflected_value) = self.reflexive_value() {
|
|
|
|
|
service.event_to_be_sent(self.get_name().to_string(), reflected_value);
|
|
|
|
|
}
|
2025-10-10 23:49:59 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-02 18:39:32 +02:00
|
|
|
Ok(())
|
|
|
|
|
}
|
2025-09-16 21:07:44 +02:00
|
|
|
/// Accès à la valeur
|
2025-10-02 18:39:32 +02:00
|
|
|
pub fn value(&self) -> StateValue {
|
2025-10-03 21:19:14 +02:00
|
|
|
self.value.read().unwrap().clone()
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/// Accès au timestamp
|
|
|
|
|
pub fn last_modified(&self) -> DateTime<Utc> {
|
2025-10-03 21:19:14 +02:00
|
|
|
self.last_modified.read().unwrap().clone()
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|
2025-10-11 10:08:28 +02:00
|
|
|
|
|
|
|
|
/// Retourne la valeur sous forme réflexive (Reflect).
|
|
|
|
|
///
|
|
|
|
|
/// Cette méthode utilise un cache pour optimiser les performances lorsqu'un parser
|
|
|
|
|
/// est défini. Si la variable a un parser, la valeur String sera parsée et le résultat
|
|
|
|
|
/// sera mis en cache. Sinon, la StateValue brute est retournée directement comme Reflect.
|
|
|
|
|
///
|
|
|
|
|
/// Le cache est invalidé automatiquement lors de `set_value()`.
|
|
|
|
|
///
|
|
|
|
|
/// # Returns
|
|
|
|
|
///
|
|
|
|
|
/// Un `Arc<dyn Reflect>` contenant soit:
|
|
|
|
|
/// - La valeur parsée (si un parser est défini)
|
|
|
|
|
/// - La StateValue brute (sinon)
|
|
|
|
|
///
|
|
|
|
|
/// # Examples
|
|
|
|
|
///
|
|
|
|
|
/// ```rust,ignore
|
|
|
|
|
/// let var = StateVarInstance::new(&variable);
|
|
|
|
|
/// let reflected = var.reflexive_value();
|
|
|
|
|
/// // reflected peut maintenant être inspecté avec l'API Reflect
|
|
|
|
|
/// ```
|
|
|
|
|
pub fn reflexive_value(&self) -> Result<Arc<dyn Reflect>, crate::state_variables::StateVariableError> {
|
|
|
|
|
// Vérifier si on a un cache valide
|
|
|
|
|
{
|
|
|
|
|
let cache = self.reflexive_cache.read().unwrap();
|
|
|
|
|
if let Some(cached) = cache.as_ref() {
|
|
|
|
|
return Ok(Arc::clone(cached));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pas de cache, il faut calculer la valeur
|
|
|
|
|
let value = self.value.read().unwrap().clone();
|
|
|
|
|
|
|
|
|
|
// Si la variable a un parser, l'utiliser
|
|
|
|
|
if let Some(parser) = &self.model.parse {
|
|
|
|
|
// La valeur doit être une String pour être parsée
|
|
|
|
|
if let crate::variable_types::StateValue::String(s) = &value {
|
|
|
|
|
match parser(s) {
|
|
|
|
|
Ok(parsed) => {
|
|
|
|
|
// Convertir Box<dyn Reflect> en Arc<dyn Reflect>
|
|
|
|
|
let arc_reflect: Arc<dyn Reflect> = Arc::from(parsed);
|
|
|
|
|
|
|
|
|
|
// Mettre en cache
|
|
|
|
|
let mut cache = self.reflexive_cache.write().unwrap();
|
|
|
|
|
*cache = Some(Arc::clone(&arc_reflect));
|
|
|
|
|
|
|
|
|
|
return Ok(arc_reflect);
|
|
|
|
|
}
|
|
|
|
|
Err(e) => return Err(e),
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Pas de parser ou la valeur n'est pas une String: convertir la StateValue en Reflect
|
|
|
|
|
let reflected = value.to_reflect();
|
|
|
|
|
let arc_reflect: Arc<dyn Reflect> = Arc::from(reflected);
|
|
|
|
|
|
|
|
|
|
// Mettre en cache
|
|
|
|
|
let mut cache = self.reflexive_cache.write().unwrap();
|
|
|
|
|
*cache = Some(Arc::clone(&arc_reflect));
|
|
|
|
|
|
|
|
|
|
Ok(arc_reflect)
|
|
|
|
|
}
|
2025-09-16 21:07:44 +02:00
|
|
|
}
|