gled-core 2.30.2

The engine of gled without a user interface: inputs, timing, rendering, Art-Net/DMX output and storage
Documentation
//! The MIDI tools interfaces use while configuring controllers: the monitor
//! of incoming traffic, MIDI learn and test outputs. The engine streams
//! traffic only while an interface shows the monitor, and captures learn
//! input only while one waits for it.

use std::{collections::BTreeSet, sync::Arc};

use gled_protocol::midi::{MidiMonitorEvent, TestCommand};
use kanal::{Receiver, Sender};
use tracing::warn;

use super::{ClientId, LearnLog, MidiLog};
use crate::midi::{learn, monitor, runtime};

#[derive(Debug)]
pub(super) struct MidiTools {
    monitor: Receiver<MidiMonitorEvent>,
    learn: Receiver<[u8; 3]>,
    test: Sender<TestCommand>,
    watchers: BTreeSet<ClientId>,
    learners: BTreeSet<ClientId>,
    pub(super) monitor_log: Arc<MidiLog>,
    pub(super) learn_log: Arc<LearnLog>,
}

impl MidiTools {
    pub(super) fn new(
        monitor: Receiver<MidiMonitorEvent>,
        learn: Receiver<[u8; 3]>,
        test: Sender<TestCommand>,
    ) -> Self {
        Self {
            monitor,
            learn,
            test,
            watchers: BTreeSet::new(),
            learners: BTreeSet::new(),
            monitor_log: Arc::default(),
            learn_log: Arc::default(),
        }
    }

    pub(super) fn watch(&mut self, id: ClientId, watch: bool) {
        set(&mut self.watchers, id, watch);
        self.update();
    }

    pub(super) fn learning(&mut self, id: ClientId, learning: bool) {
        set(&mut self.learners, id, learning);
        self.update();
    }

    pub(super) fn disconnect(&mut self, id: ClientId) {
        self.watchers.remove(&id);
        self.learners.remove(&id);
        self.update();
    }

    fn update(&self) {
        let watched = !self.watchers.is_empty();
        monitor::set_streaming_enabled(watched);
        learn::set_streaming_enabled(watched);
        learn::set_learn_active(watched && !self.learners.is_empty());
    }

    pub(super) fn test(&self, command: TestCommand) {
        if command == TestCommand::ClearAllTestStates {
            runtime::clear_all_test_states();
            return;
        }
        if let Err(err) = self.test.send(command) {
            warn!("The MIDI runtime does not take test commands: {err}");
        }
    }

    /// Moves what arrived into the logs interfaces read.
    pub(super) fn tick(&mut self) {
        while let Ok(Some(event)) = self.monitor.try_recv() {
            Arc::make_mut(&mut self.monitor_log).push(event);
        }
        while let Ok(Some(message)) = self.learn.try_recv() {
            Arc::make_mut(&mut self.learn_log).push(message);
        }
    }
}

fn set(clients: &mut BTreeSet<ClientId>, id: ClientId, member: bool) {
    if member {
        clients.insert(id);
    } else {
        clients.remove(&id);
    }
}