feat(power): Power1 стаб + dev-mock fake-ACC (feature dev-mocks)
PowerService (Power1, стаб running) + PowerMock (dev.PowerMock1, разделяет State через Arc<Mutex>). dev-mocks — default-фича; прод (--no-default-features) mock не регистрирует. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Signed-off-by: Alexander <akotenev2003@gmail.com>
This commit is contained in:
Generated
+14
@@ -800,6 +800,20 @@ dependencies = [
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shturman-power"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"anyhow",
|
||||
"shturman-common",
|
||||
"shturman-ipc",
|
||||
"shturman-sdk",
|
||||
"tempfile",
|
||||
"tokio",
|
||||
"tracing",
|
||||
"zbus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "shturman-sdk"
|
||||
version = "0.0.0"
|
||||
|
||||
@@ -8,6 +8,7 @@ members = [
|
||||
"crates/shturman-sdk",
|
||||
"crates/core/shturman-firstboot",
|
||||
"crates/core/shturman-settings",
|
||||
"crates/core/shturman-power",
|
||||
]
|
||||
|
||||
[workspace.package]
|
||||
|
||||
@@ -0,0 +1,22 @@
|
||||
[package]
|
||||
name = "shturman-power"
|
||||
version = "0.0.0"
|
||||
edition.workspace = true
|
||||
license.workspace = true
|
||||
|
||||
[features]
|
||||
# dev-mocks — вкл. в dev (fake-ACC для тестов/v0.3); прод выключает `--no-default-features`.
|
||||
default = ["dev-mocks"]
|
||||
dev-mocks = []
|
||||
|
||||
[dependencies]
|
||||
shturman-ipc = { path = "../../shturman-ipc" }
|
||||
shturman-common = { path = "../../shturman-common" }
|
||||
zbus.workspace = true
|
||||
tokio.workspace = true
|
||||
anyhow.workspace = true
|
||||
tracing.workspace = true
|
||||
|
||||
[dev-dependencies]
|
||||
tempfile.workspace = true
|
||||
shturman-sdk = { path = "../../shturman-sdk" }
|
||||
@@ -0,0 +1,6 @@
|
||||
//! `ru.shturman.Power1` — стаб питания/жизненного цикла (домен B).
|
||||
//! v0: статичное состояние `running`, мутируется только dev-mock (fake-ACC). Полная FSM/секвенсинг — v0.3.
|
||||
|
||||
pub mod service;
|
||||
|
||||
pub use service::PowerService;
|
||||
@@ -0,0 +1,22 @@
|
||||
//! `ru.shturman.Power1` — сервис. На шину выводит systemd (План 5). Полная FSM/секвенсинг — v0.3.
|
||||
//! В dev-сборке дополнительно регистрирует `ru.shturman.dev.PowerMock1` (fake-ACC) на том же пути.
|
||||
|
||||
use shturman_common::init_tracing;
|
||||
use shturman_ipc::{connect, names};
|
||||
use shturman_power::PowerService;
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() -> anyhow::Result<()> {
|
||||
init_tracing("shturman-power");
|
||||
let conn = connect().await?;
|
||||
let svc = PowerService::new();
|
||||
#[cfg(feature = "dev-mocks")]
|
||||
let mock = svc.mock();
|
||||
conn.object_server().at(names::power::PATH, svc).await?;
|
||||
#[cfg(feature = "dev-mocks")]
|
||||
conn.object_server().at(names::power::PATH, mock).await?;
|
||||
conn.request_name(names::power::NAME).await?;
|
||||
tracing::info!("ru.shturman.Power1 на шине");
|
||||
std::future::pending::<()>().await;
|
||||
Ok(())
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
//! Server-стаб `ru.shturman.Power1` + (feature `dev-mocks`) `ru.shturman.dev.PowerMock1` (fake-ACC).
|
||||
//! zbus 4: несколько интерфейсов на одном объекте — это РАЗНЫЕ типы на одном пути, разделяющие
|
||||
//! состояние через `Arc<Mutex<State>>` (а не два `#[interface]` на одном типе).
|
||||
|
||||
use shturman_common::monotonic_secs;
|
||||
use shturman_ipc::types::{IgnitionState, PowerSource, PowerState};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use zbus::interface;
|
||||
use zbus::object_server::SignalContext;
|
||||
|
||||
struct State {
|
||||
power: PowerState,
|
||||
ignition: IgnitionState,
|
||||
source: PowerSource,
|
||||
}
|
||||
|
||||
impl Default for State {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
power: PowerState::Running,
|
||||
ignition: IgnitionState::Running,
|
||||
source: PowerSource::Vehicle12v,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Стаб питания (`Power1`). В v0 стартует в `running`; запись/actuator отсутствуют (#2).
|
||||
pub struct PowerService {
|
||||
state: Arc<Mutex<State>>,
|
||||
}
|
||||
|
||||
impl Default for PowerService {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
state: Arc::new(Mutex::new(State::default())),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PowerService {
|
||||
pub fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
|
||||
// Inherent-аксессоры (тесты + источник для interface-методов).
|
||||
pub fn power_state(&self) -> PowerState {
|
||||
self.state.lock().unwrap().power
|
||||
}
|
||||
pub fn ignition(&self) -> IgnitionState {
|
||||
self.state.lock().unwrap().ignition
|
||||
}
|
||||
pub fn source(&self) -> PowerSource {
|
||||
self.state.lock().unwrap().source
|
||||
}
|
||||
|
||||
/// dev-mock «fake-ACC», разделяющий состояние (только в dev-сборке).
|
||||
#[cfg(feature = "dev-mocks")]
|
||||
pub fn mock(&self) -> PowerMock {
|
||||
PowerMock {
|
||||
state: Arc::clone(&self.state),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[interface(name = "ru.shturman.Power1")]
|
||||
impl PowerService {
|
||||
async fn get_power_state(&self) -> String {
|
||||
self.power_state().as_str().to_string()
|
||||
}
|
||||
|
||||
/// Внутренний; в v0-стабе — no-op (полная sleep/wake — v1/v2, B §7).
|
||||
async fn request_sleep(&self) {}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn ignition_state(&self) -> String {
|
||||
self.ignition().as_str().to_string()
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn uptime(&self) -> u64 {
|
||||
monotonic_secs()
|
||||
}
|
||||
|
||||
#[zbus(property)]
|
||||
async fn power_source(&self) -> String {
|
||||
self.source().as_str().to_string()
|
||||
}
|
||||
|
||||
#[zbus(signal)]
|
||||
async fn acc_changed(ctx: &SignalContext<'_>, on: bool) -> zbus::Result<()>;
|
||||
#[zbus(signal)]
|
||||
async fn shutdown_imminent(
|
||||
ctx: &SignalContext<'_>,
|
||||
seconds: u32,
|
||||
reason: &str,
|
||||
) -> zbus::Result<()>;
|
||||
#[zbus(signal)]
|
||||
async fn shutdown_aborted(ctx: &SignalContext<'_>) -> zbus::Result<()>;
|
||||
#[zbus(signal)]
|
||||
async fn sleep(ctx: &SignalContext<'_>) -> zbus::Result<()>;
|
||||
#[zbus(signal)]
|
||||
async fn wake(ctx: &SignalContext<'_>) -> zbus::Result<()>;
|
||||
}
|
||||
|
||||
/// dev-mock «fake-ACC» — отдельный тип на том же пути. Прод (`--no-default-features`) его НЕ регистрирует.
|
||||
/// Методы возвращают `()` (ошибку эмита сигнала игнорируем — мок не отвечает D-Bus-ошибкой).
|
||||
#[cfg(feature = "dev-mocks")]
|
||||
pub struct PowerMock {
|
||||
state: Arc<Mutex<State>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "dev-mocks")]
|
||||
#[interface(name = "ru.shturman.dev.PowerMock1")]
|
||||
impl PowerMock {
|
||||
async fn set_acc(&self, on: bool, #[zbus(signal_context)] ctx: SignalContext<'_>) {
|
||||
{
|
||||
let mut st = self.state.lock().unwrap();
|
||||
st.ignition = if on {
|
||||
IgnitionState::Running
|
||||
} else {
|
||||
IgnitionState::Off
|
||||
};
|
||||
st.power = if on {
|
||||
PowerState::Running
|
||||
} else {
|
||||
PowerState::Off
|
||||
};
|
||||
}
|
||||
// Эмитим Power1-сигнал (тот же путь; имя интерфейса добавляет сама acc_changed).
|
||||
let _ = PowerService::acc_changed(&ctx, on).await;
|
||||
}
|
||||
|
||||
async fn set_ignition(&self, state: String) {
|
||||
if let Ok(ig) = state.parse::<IgnitionState>() {
|
||||
self.state.lock().unwrap().ignition = ig;
|
||||
}
|
||||
}
|
||||
|
||||
async fn trigger_shutdown(
|
||||
&self,
|
||||
seconds: u32,
|
||||
reason: String,
|
||||
#[zbus(signal_context)] ctx: SignalContext<'_>,
|
||||
) {
|
||||
let _ = PowerService::shutdown_imminent(&ctx, seconds, &reason).await;
|
||||
}
|
||||
|
||||
async fn abort_shutdown(&self, #[zbus(signal_context)] ctx: SignalContext<'_>) {
|
||||
let _ = PowerService::shutdown_aborted(&ctx).await;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn defaults_running() {
|
||||
let svc = PowerService::new();
|
||||
assert_eq!(svc.power_state(), PowerState::Running);
|
||||
assert_eq!(svc.ignition(), IgnitionState::Running);
|
||||
assert_eq!(svc.source(), PowerSource::Vehicle12v);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user