sys/
sysmod.rs

1//! システムモジュール関連。
2
3pub mod camera;
4pub mod discord;
5pub mod health;
6pub mod http;
7pub mod line;
8pub mod openai;
9pub mod sysinfo;
10pub mod twitter;
11
12use self::{
13    camera::Camera, discord::Discord, health::Health, http::HttpServer, openai::OpenAi,
14    sysinfo::SystemInfo, twitter::Twitter,
15};
16use crate::{rpienv, sysmod::line::Line, taskserver::Control};
17use anyhow::Result;
18use chrono::NaiveTime;
19use log::info;
20use std::sync::Arc;
21use tokio::sync::Mutex as TokioMutex;
22
23/// システムモジュールが実装するトレイト。
24pub trait SystemModule: Sync + Send {
25    /// [SystemModule] の初期化時には [crate::sys::taskserver::TaskServer] がまだ存在しないので
26    /// タスクの登録はこのタイミングまで遅延させる。
27    fn on_start(&mut self, _ctrl: &Control) {}
28}
29
30/// [SystemModules] 内の [SystemModule] はマルチスレッドにアクセスされるため、
31/// ロックが必要かつ await 可能。
32type SysModArc<T> = Arc<TokioMutex<T>>;
33
34/// タスクのエントリポイントに渡される引数からアクセス可能な [SystemModule] のリスト。
35/// デッドロックに注意。
36///
37/// ## デッドロックについて
38/// それぞれの [SystemModule] はアクセスする前にロックを取得する必要があるが、
39/// 複数同時にロックする場合、その順番に気を付けないと
40/// デッドロックを引き起こす可能性がある。
41pub struct SystemModules {
42    pub sysinfo: SysModArc<sysinfo::SystemInfo>,
43    pub health: SysModArc<health::Health>,
44    pub camera: SysModArc<camera::Camera>,
45    pub twitter: SysModArc<twitter::Twitter>,
46    pub discord: SysModArc<discord::Discord>,
47    pub line: SysModArc<line::Line>,
48    pub openai: SysModArc<openai::OpenAi>,
49    pub http: SysModArc<http::HttpServer>,
50
51    /// 全 [SystemModule] にイベントを配送するための参照のリストを作る。
52    event_target_list: Vec<SysModArc<dyn SystemModule>>,
53}
54
55impl SystemModules {
56    pub fn new() -> Result<SystemModules> {
57        info!("initialize system modules...");
58
59        info!("get Raspberry Pi info...");
60        let rpienv = rpienv::raspi_env();
61        info!("{}", rpienv);
62
63        let wakeup_health_ck: Vec<_> = (0..24)
64            .flat_map(|hour| (0..60).map(move |min| NaiveTime::from_hms_opt(hour, min, 0).unwrap()))
65            .collect();
66        let wakeup_health_tw = vec![
67            NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
68            NaiveTime::from_hms_opt(6, 0, 0).unwrap(),
69            NaiveTime::from_hms_opt(12, 0, 0).unwrap(),
70            NaiveTime::from_hms_opt(18, 0, 0).unwrap(),
71        ];
72
73        let wakeup_camera = vec![
74            NaiveTime::from_hms_opt(0, 0, 0).unwrap(),
75            NaiveTime::from_hms_opt(3, 0, 0).unwrap(),
76            NaiveTime::from_hms_opt(6, 0, 0).unwrap(),
77            NaiveTime::from_hms_opt(9, 0, 0).unwrap(),
78            NaiveTime::from_hms_opt(12, 0, 0).unwrap(),
79            NaiveTime::from_hms_opt(15, 0, 0).unwrap(),
80            NaiveTime::from_hms_opt(18, 0, 0).unwrap(),
81            NaiveTime::from_hms_opt(21, 0, 0).unwrap(),
82        ];
83
84        let wakeup_twiter: Vec<_> = (0..24)
85            .flat_map(|hour| {
86                (0..60)
87                    .step_by(5)
88                    .map(move |min| NaiveTime::from_hms_opt(hour, min, 0).unwrap())
89            })
90            .collect();
91
92        let wakeup_discord: Vec<_> = (0..24)
93            .flat_map(|hour| {
94                (0..60)
95                    .step_by(10)
96                    .map(move |min| NaiveTime::from_hms_opt(hour, min, 0).unwrap())
97            })
98            .collect();
99
100        let mut event_target_list: Vec<SysModArc<dyn SystemModule>> = vec![];
101
102        let sysinfo = Arc::new(TokioMutex::new(SystemInfo::new()));
103        let health = Arc::new(TokioMutex::new(Health::new(
104            wakeup_health_ck,
105            wakeup_health_tw,
106        )?));
107        let camera = Arc::new(TokioMutex::new(Camera::new(wakeup_camera)?));
108        let twitter = Arc::new(TokioMutex::new(Twitter::new(wakeup_twiter)?));
109        let discord = Arc::new(TokioMutex::new(Discord::new(wakeup_discord)?));
110        let line = Arc::new(TokioMutex::new(Line::new()?));
111        let openai = Arc::new(TokioMutex::new(OpenAi::new()?));
112        let http = Arc::new(TokioMutex::new(HttpServer::new()?));
113
114        event_target_list.push(sysinfo.clone());
115        event_target_list.push(health.clone());
116        event_target_list.push(camera.clone());
117        event_target_list.push(twitter.clone());
118        event_target_list.push(discord.clone());
119        event_target_list.push(line.clone());
120        event_target_list.push(openai.clone());
121        event_target_list.push(http.clone());
122
123        info!("OK: initialize system modules");
124        Ok(Self {
125            sysinfo,
126            health,
127            camera,
128            twitter,
129            discord,
130            line,
131            openai,
132            http,
133            event_target_list,
134        })
135    }
136
137    pub async fn on_start(&self, ctrl: &Control) {
138        info!("invoke on_start for system modules...");
139        for sysmod in self.event_target_list.iter() {
140            sysmod.lock().await.on_start(ctrl);
141        }
142        info!("OK: invoke on_start for system modules");
143    }
144}