gmt_dos-clients_servos 3.2.0

GMT DOS Servo-Mechanisms Client
use gmt_dos_actors::{
    actor::{PlainActor, Terminator},
    framework::{
        model::FlowChart,
        network::{AddActorOutput, AddOuput, TryIntoInputs},
    },
    prelude::{Actor, IntoLogs, IntoLogsN},
    system::{Sys, System, SystemError},
};
use gmt_dos_clients_arrow::Arrow;
use gmt_dos_clients_fem::DiscreteModalSolver;
#[cfg(topend = "ASM")]
use gmt_dos_clients_io::gmt_m2::asm::{
    M2ASMFluidDampingForces, M2ASMVoiceCoilsForces, M2ASMVoiceCoilsMotion,
};
#[cfg(topend = "FSM")]
use gmt_dos_clients_io::gmt_m2::fsm::{M2FSMPiezoForces, M2FSMPiezoNodes};
use gmt_dos_clients_io::{
    cfd_wind_loads::{CFDM1WindLoads, CFDM2WindLoads, CFDMountWindLoads},
    gmt_m1::assembly,
    gmt_m2::{M2PositionerForces, M2PositionerNodes},
    mount::{MountEncoders, MountTorques},
};
use gmt_dos_clients_m2_ctrl::Positioners;
use gmt_dos_clients_mount::Mount;
use gmt_dos_clients_windloads::system::{
    M1 as WindOnM1, M2 as WindOnM2, Mount as WindOnMount, SigmoidCfdLoads,
};
use gmt_dos_systems_m1::assembly::M1;
use gmt_dos_systems_m2::M2;

use interface::Flatten;
use serde::{Deserialize, Serialize};

use crate::FemSolver;

pub mod io;
pub mod traits;

#[derive(Clone, Serialize, Deserialize)]
pub struct GmtServoMechanisms<const M1_RATE: usize, const M2_RATE: usize = 1> {
    pub(crate) fem: Actor<DiscreteModalSolver<FemSolver>>,
    pub(crate) mount: Actor<Mount>,
    pub(crate) m1: Sys<M1<M1_RATE>>,
    pub(crate) m2_positioners: Actor<Positioners>,
    pub(crate) m2: Sys<M2<1>>,
    #[serde(skip)]
    pub(crate) telemetry: Option<Terminator<Arrow>>,
    pub(crate) wind_loads: Option<Sys<SigmoidCfdLoads>>,
}

impl<const M1_RATE: usize, const M2_RATE: usize> System for GmtServoMechanisms<M1_RATE, M2_RATE> {
    fn name(&self) -> String {
        format!("GMT Servo-Mechanisms")
    }
    fn build(&mut self) -> Result<&mut Self, SystemError> {
        log::debug!("building GmtServoMechanisms System");
        self.mount
            .add_output()
            .build::<MountTorques>()
            .into_input(&mut self.fem)?;
        self.fem
            .add_output()
            .bootstrap()
            .build::<MountEncoders>()
            .into_input(&mut self.mount)?;

        #[cfg(not(m1_hp_force_extension))]
        {
            self.m1
                .add_output()
                .build::<assembly::M1HardpointsForces>()
                .into_input(&mut self.fem)?;
            self.fem
                .add_output()
                .bootstrap()
                .build::<assembly::M1HardpointsMotion>()
                .into_input(&mut self.m1)?;
        }
        #[cfg(m1_hp_force_extension)]
        {
            self.m1
                .add_output()
                .build::<assembly::M1HardpointsMotion>()
                .into_input(&mut self.fem)?;
            self.fem
                .add_output()
                .bootstrap()
                .build::<assembly::M1HardpointsForces>()
                .into_input(&mut self.m1)?;
        }

        self.m1
            .add_output()
            .build::<assembly::M1ActuatorAppliedForces>()
            .into_input(&mut self.fem)?;

        self.m2_positioners
            .add_output()
            .build::<M2PositionerForces>()
            .into_input(&mut self.fem)?;
        self.fem
            .add_output()
            .bootstrap()
            .build::<M2PositionerNodes>()
            .into_input(&mut self.m2_positioners)?;

        if let Some(telemetry) = &mut self.telemetry {
            self.mount
                .add_output()
                .unbounded()
                .build::<MountTorques>()
                .log(telemetry)?;
            #[cfg(not(m1_hp_force_extension))]
            self.m1
                .add_output()
                .unbounded()
                .build::<Flatten<assembly::M1HardpointsForces>>()
                .log(telemetry)?;
            #[cfg(m1_hp_force_extension)]
            self.m1
                .add_output()
                .unbounded()
                .build::<Flatten<assembly::M1HardpointsMotion>>()
                .log(telemetry)?;
            self.m1
                .add_output()
                .unbounded()
                .build::<Flatten<assembly::M1ActuatorAppliedForces>>()
                .log(telemetry)?;
            self.m2_positioners
                .add_output()
                .unbounded()
                .build::<M2PositionerForces>()
                .logn(telemetry, 42)?;
        }

        #[cfg(topend = "ASM")]
        {
            self.m2
                .add_output()
                .build::<M2ASMVoiceCoilsForces>()
                .into_input(&mut self.fem)?;
            self.m2
                .add_output()
                .build::<M2ASMFluidDampingForces>()
                .into_input(&mut self.fem)?;
            self.fem
                .add_output()
                .bootstrap()
                .build::<M2ASMVoiceCoilsMotion>()
                .into_input(&mut self.m2)?;
            if let Some(telemetry) = &mut self.telemetry {
                self.m2
                    .add_output()
                    .unbounded()
                    .build::<Flatten<M2ASMVoiceCoilsForces>>()
                    .log(telemetry)?;
                self.m2
                    .add_output()
                    .unbounded()
                    .build::<Flatten<M2ASMFluidDampingForces>>()
                    .log(telemetry)?;
            }
        }

        #[cfg(topend = "FSM")]
        {
            self.m2
                .add_output()
                .build::<M2FSMPiezoForces>()
                .into_input(&mut self.fem)?;
            self.fem
                .add_output()
                .bootstrap()
                .build::<M2FSMPiezoNodes>()
                .into_input(&mut self.m2)?;
            if let Some(telemetry) = &mut self.telemetry {
                self.m2
                    .add_output()
                    .unbounded()
                    .build::<M2FSMPiezoForces>()
                    .log(telemetry)?;
            }
        }

        if let Some(wind_loads) = self.wind_loads.as_mut() {
            <Sys<SigmoidCfdLoads> as AddActorOutput<'_, WindOnMount, 1, 1>>::add_output(wind_loads)
                .build::<CFDMountWindLoads>()
                .into_input(&mut self.fem)?;
            <Sys<SigmoidCfdLoads> as AddActorOutput<'_, WindOnM1, 1, 1>>::add_output(wind_loads)
                .build::<CFDM1WindLoads>()
                .into_input(&mut self.fem)?;
            <Sys<SigmoidCfdLoads> as AddActorOutput<'_, WindOnM2, 1, 1>>::add_output(wind_loads)
                .build::<CFDM2WindLoads>()
                .into_input(&mut self.fem)?;
        }

        Ok(self)
    }
    fn plain(&self) -> gmt_dos_actors::actor::PlainActor {
        PlainActor::new(self.name())
            .inputs(
                PlainActor::from(&self.fem)
                    .filter_inputs_by_name(&[
                        "MountTorques",
                        "M1HardpointsMotion",
                        "M1HardpointsForces",
                        "M1ActuatorAppliedForces",
                        "M2PositionerForces",
                        "M2ASMVoiceCoilsForces",
                        "M2FSMPiezoForces",
                        "M2ASMFluidDampingForces",
                        "CFDMountWindLoads",
                        "CFDM1WindLoads",
                        "CFDM2WindLoads",
                    ])
                    .zip(PlainActor::from(&self.mount).filter_inputs_by_name(&["MountEncoders"]))
                    .zip(
                        PlainActor::from(&self.m2_positioners)
                            .filter_inputs_by_name(&["M2PositionerNodes"]),
                    )
                    .zip(
                        PlainActor::from(&self.m1.dispatch_in).filter_inputs_by_name(
                            if cfg!(m1_hp_force_extension) {
                                &["M1HardpointsMotion", "M1HardpointsForces"]
                            } else {
                                &["M1HardpointsMotion"]
                            },
                        ),
                    )
                    .zip(
                        PlainActor::from(&self.m2.dispatch_in)
                            .filter_inputs_by_name(&["M2ASMVoiceCoilsMotion", "M2FSMPiezoNodes"]),
                    )
                    .map(|((((mut fem, mount), m2_pos), m1), m2)| {
                        fem.extend(mount);
                        fem.extend(m2_pos);
                        fem.extend(m1);
                        fem.extend(m2);
                        fem
                    })
                    .unwrap(),
            )
            .outputs(
                PlainActor::from(&self.fem)
                    .filter_outputs_by_name(&[
                        "MountEncoders",
                        "M1HardpointsMotion",
                        "M1HardpointsForces",
                        "M2PositionerNodes",
                        "M2FSMPiezoNodes",
                        "M2ASMVoiceCoilsMotion",
                    ])
                    .unwrap(),
            )
            .image("gmt-servos.png".to_string())
            .graph(self.graph())
            .build()
    }
}