Using postcard

This commit is contained in:
2026-07-28 22:20:50 +02:00
parent 92fe70def2
commit 7968ae14af
12 changed files with 166 additions and 153 deletions
Generated
+16
View File
@@ -2264,6 +2264,18 @@ dependencies = [
"zeroize", "zeroize",
] ]
[[package]]
name = "embedded-io"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ef1a6892d9eef45c8fa6b9e0086428a2cca8491aca8f787c534a3d6d0bcb3ced"
[[package]]
name = "embedded-io"
version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "edd0f118536f44f5ccd48bcb8b111bdc3de888b58c74639dfb034a357d0f206d"
[[package]] [[package]]
name = "encoding_rs" name = "encoding_rs"
version = "0.8.35" version = "0.8.35"
@@ -3740,6 +3752,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24" checksum = "6764c3b5dd454e283a30e6dfe78e9b31096d9e32036b5d1eaac7a6119ccb9a24"
dependencies = [ dependencies = [
"cobs", "cobs",
"embedded-io 0.4.0",
"embedded-io 0.6.1",
"heapless", "heapless",
"serde", "serde",
] ]
@@ -3958,7 +3972,9 @@ name = "pulse-wire"
version = "0.1.0-alpha.0" version = "0.1.0-alpha.0"
dependencies = [ dependencies = [
"hypersdk", "hypersdk",
"postcard",
"serde", "serde",
"tokio",
] ]
[[package]] [[package]]
+1 -1
View File
@@ -29,7 +29,7 @@ toml = "1.1.3"
members = ["pulse-ui", "pulse-wire"] members = ["pulse-ui", "pulse-wire"]
[workspace.dependencies] [workspace.dependencies]
postcard = "1.1.3" postcard = { version = "1.1.3", features = ["alloc"]}
pulse-ui = { path = "pulse-ui", version = "0.1.0-alpha.0" } pulse-ui = { path = "pulse-ui", version = "0.1.0-alpha.0" }
pulse-wire = { path = "pulse-wire", version = "0.1.0-alpha.0" } pulse-wire = { path = "pulse-wire", version = "0.1.0-alpha.0" }
hypersdk = "0.2.14" hypersdk = "0.2.14"
+2
View File
@@ -6,3 +6,5 @@ edition = "2024"
[dependencies] [dependencies]
serde = { workspace = true } serde = { workspace = true }
hypersdk = { workspace = true } hypersdk = { workspace = true }
postcard = { workspace = true }
tokio = { workspace = true }
+5 -5
View File
@@ -1,13 +1,13 @@
use crate::units::{Direction, Symbol, USD}; use crate::units::{Direction, Symbol, USD};
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum MarketTrend { pub enum MarketTrend {
Bullish, Bullish,
Bearish, Bearish,
Neutral, Neutral,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum LogKind { pub enum LogKind {
Info, Info,
Warn, Warn,
@@ -15,7 +15,7 @@ pub enum LogKind {
Debug, Debug,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Signal { pub struct Signal {
pub symbol: String, pub symbol: String,
pub kind: Direction, pub kind: Direction,
@@ -26,14 +26,14 @@ pub struct Signal {
pub stop_loss: USD, pub stop_loss: USD,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct EventLog { pub struct EventLog {
pub kind: LogKind, pub kind: LogKind,
pub name: String, pub name: String,
pub message: String, pub message: String,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Position { pub struct Position {
pub symbol: Symbol, pub symbol: Symbol,
pub size: f64, pub size: f64,
+4
View File
@@ -19,3 +19,7 @@ pub mod prelude {
pub fn server_path() -> PathBuf { pub fn server_path() -> PathBuf {
PathBuf::from("/tmp/pulse-engine.sock") PathBuf::from("/tmp/pulse-engine.sock")
} }
pub fn map_postcard_err<T>(res: postcard::Result<T>) -> tokio::io::Result<T> {
res.map_err(|e| tokio::io::Error::new(std::io::ErrorKind::Other, e))
}
+17 -21
View File
@@ -1,34 +1,30 @@
use crate::{ use crate::{
PulseWire,
general::{EventLog, Signal}, general::{EventLog, Signal},
terminal::MarketItem, terminal::MarketItem,
units::Direction, units::Direction,
}; };
use hypersdk::hypercore::{Candle, CandleInterval, Subscription}; use hypersdk::hypercore::{Candle, CandleInterval, Subscription};
use pulse_macros::pwp;
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(serde::Deserialize, serde::Serialize)]
pub struct StrategyManifest { pub struct StrategyManifest {
name: String, pub name: String,
description: String, pub description: String,
author: String, pub author: String,
version: String, pub version: String,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
#[derive(serde::Deserialize, serde::Serialize)]
pub struct RiskManifest { pub struct RiskManifest {
name: String, pub name: String,
description: String, pub description: String,
author: String, pub author: String,
version: String, pub version: String,
max_loss: u8, pub max_loss: u8,
cooldown: CandleInterval, pub cooldown: CandleInterval,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum StrategyMessage { pub enum StrategyMessage {
Log(EventLog), Log(EventLog),
@@ -49,7 +45,7 @@ pub enum StrategyMessage {
Signal(StrategySignal), Signal(StrategySignal),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum RiskMessage { pub enum RiskMessage {
Log(EventLog), Log(EventLog),
@@ -60,7 +56,7 @@ pub enum RiskMessage {
Reject { reason: String }, Reject { reason: String },
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum StrategyEngineMessage { pub enum StrategyEngineMessage {
Initialize, Initialize,
@@ -84,7 +80,7 @@ pub enum StrategyEngineMessage {
}, },
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum RiskEngineMessage { pub enum RiskEngineMessage {
Initialize, Initialize,
@@ -95,7 +91,7 @@ pub enum RiskEngineMessage {
Signal(StrategySignal), Signal(StrategySignal),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct StrategySignal { pub struct StrategySignal {
pub symbol: String, pub symbol: String,
pub side: Direction, pub side: Direction,
+24 -26
View File
@@ -1,13 +1,11 @@
use crate::{ use crate::{
PulseWire,
general::{EventLog, MarketTrend, Position, Signal}, general::{EventLog, MarketTrend, Position, Signal},
plugin::{RiskManifest, StrategyManifest}, plugin::{RiskManifest, StrategyManifest},
units::{Symbol, USD, Volatility}, units::{Symbol, USD, Volatility},
}; };
use hypersdk::hypercore::CandleInterval; use hypersdk::hypercore::CandleInterval;
use pulse_macros::pwp;
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum TerminalServerMessage { pub enum TerminalServerMessage {
// WatchList // WatchList
WatchListUpdated(Vec<MarketItem>), WatchListUpdated(Vec<MarketItem>),
@@ -32,12 +30,12 @@ pub enum TerminalServerMessage {
AddLog(EventLog), AddLog(EventLog),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum TerminalClientMessage { pub enum TerminalClientMessage {
ExecuteCommand(String), ExecuteCommand(String),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MarketItem { pub struct MarketItem {
pub symbol: Symbol, pub symbol: Symbol,
pub price: USD, pub price: USD,
@@ -45,7 +43,7 @@ pub struct MarketItem {
pub volume_24h: USD, pub volume_24h: USD,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct MarketOverview { pub struct MarketOverview {
pub trend: MarketTrend, pub trend: MarketTrend,
pub volatility: Volatility, pub volatility: Volatility,
@@ -53,32 +51,32 @@ pub struct MarketOverview {
pub alerts: Vec<Alert>, pub alerts: Vec<Alert>,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum AlertLevel { pub enum AlertLevel {
High, High,
Medium, Medium,
Low, Low,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Alert { pub struct Alert {
level: AlertLevel, pub level: AlertLevel,
message: String, pub message: String,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Balance { pub struct Balance {
pub asset: String, pub asset: String,
pub amount: f64, pub amount: f64,
pub value: f64, pub value: f64,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum InspectTarget { pub enum InspectTarget {
None, None,
Some(Vec<InspectItem>), Some(Vec<InspectItem>),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum InspectItem { pub enum InspectItem {
String(String), String(String),
Symbol(String), Symbol(String),
@@ -86,35 +84,35 @@ pub enum InspectItem {
F64(f64), F64(f64),
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Status { pub struct Status {
feed: Mode, pub feed: Mode,
exchange: String, pub exchange: String,
dex: String, pub dex: String,
latency: u16, pub latency: u16,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Mode { pub enum Mode {
Auto, Auto,
Manual, Manual,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum ItemState { pub enum ItemState {
Running, Running,
Stopped, Stopped,
Error, Error,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct Strategy { pub struct Strategy {
strategy: StrategyManifest, pub strategy: StrategyManifest,
risk: RiskManifest, pub risk: RiskManifest,
mode: Mode, pub mode: Mode,
state: ItemState, pub state: ItemState,
cooldown: CandleInterval, pub cooldown: CandleInterval,
} }
impl std::fmt::Display for AlertLevel { impl std::fmt::Display for AlertLevel {
+4 -28
View File
@@ -1,20 +1,16 @@
use pulse_macros::pwp; #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
use crate::PulseWire;
#[derive(Debug, Clone)]
pub struct Symbol(pub String); pub struct Symbol(pub String);
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy, serde::Serialize, serde::Deserialize)]
pub struct USD(pub f64); pub struct USD(pub f64);
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Direction { pub enum Direction {
Buy, Buy,
Sell, Sell,
} }
#[pwp] #[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub enum Volatility { pub enum Volatility {
Low, Low,
Medium, Medium,
@@ -37,26 +33,6 @@ impl std::fmt::Display for USD {
} }
} }
impl PulseWire for Symbol {
fn from_com(com: &mut Vec<u8>) -> Self {
Self(String::from_com(com))
}
fn to_com(&self) -> Vec<u8> {
self.0.to_com()
}
}
impl PulseWire for USD {
fn from_com(com: &mut Vec<u8>) -> Self {
Self(f64::from_com(com))
}
fn to_com(&self) -> Vec<u8> {
self.0.to_com()
}
}
pub fn format_f64(value: f64) -> String { pub fn format_f64(value: f64) -> String {
let abs = value.abs(); let abs = value.abs();
+5 -5
View File
@@ -166,11 +166,11 @@ impl StrategyEngine {
None => {} None => {}
Some(RiskMessage::GetWatchList) => { Some(RiskMessage::GetWatchList) => {
let mut v = vec![1]; self.risk
.send(&&RiskEngineMessage::WatchList(
v.extend(engine.config.lock().await.watchlist.to_com()); engine.watch_list.lock().await.clone(),
))
self.risk.send_raw(&v).await?; .await?;
} }
Some(RiskMessage::Log(mut log)) => { Some(RiskMessage::Log(mut log)) => {
+7 -8
View File
@@ -1,16 +1,14 @@
use std::marker::PhantomData; use std::marker::PhantomData;
use pulse_wire::PulseWire; use pulse_wire::map_postcard_err;
use serde::{Deserialize, Serialize};
use serde::Deserialize;
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
process::{Child, ChildStdout}, process::{Child, ChildStdout},
sync::Mutex, sync::Mutex,
}; };
#[derive(Debug)] #[derive(Debug)]
pub struct Plugin<S: PulseWire, R: PulseWire, M: for<'de> Deserialize<'de>> { pub struct Plugin<S: Serialize, R: for<'de> Deserialize<'de>, M: for<'de> Deserialize<'de>> {
pub manifest: Mutex<M>, pub manifest: Mutex<M>,
pub stdout: Mutex<ChildStdout>, pub stdout: Mutex<ChildStdout>,
pub process: Mutex<Child>, pub process: Mutex<Child>,
@@ -18,7 +16,7 @@ pub struct Plugin<S: PulseWire, R: PulseWire, M: for<'de> Deserialize<'de>> {
pub _p: (PhantomData<S>, PhantomData<R>), pub _p: (PhantomData<S>, PhantomData<R>),
} }
impl<S: PulseWire, R: PulseWire, M: for<'de> Deserialize<'de>> Plugin<S, R, M> { impl<S: Serialize, R: for<'de> Deserialize<'de>, M: for<'de> Deserialize<'de>> Plugin<S, R, M> {
pub fn new(mut child: Child, manifest: M) -> Self { pub fn new(mut child: Child, manifest: M) -> Self {
Self { Self {
stdout: Mutex::new(child.stdout.take().expect("Failed to obtain child stdout")), stdout: Mutex::new(child.stdout.take().expect("Failed to obtain child stdout")),
@@ -45,11 +43,12 @@ impl<S: PulseWire, R: PulseWire, M: for<'de> Deserialize<'de>> Plugin<S, R, M> {
stdout.read_exact(&mut buffer).await?; stdout.read_exact(&mut buffer).await?;
Ok(Some(R::from_com(&mut buffer))) Ok(Some(map_postcard_err(postcard::from_bytes(&buffer))?))
} }
pub async fn send(&self, msg: &S) -> tokio::io::Result<()> { pub async fn send(&self, msg: &S) -> tokio::io::Result<()> {
self.send_raw(&msg.to_com()).await self.send_raw(&map_postcard_err(postcard::to_allocvec(msg))?)
.await
} }
pub async fn send_raw(&self, msg: &[u8]) -> tokio::io::Result<()> { pub async fn send_raw(&self, msg: &[u8]) -> tokio::io::Result<()> {
+4 -4
View File
@@ -1,5 +1,5 @@
use crate::engine::Engine; use crate::engine::Engine;
use pulse_wire::prelude::*; use pulse_wire::{map_postcard_err, prelude::*};
use std::{ use std::{
collections::HashMap, collections::HashMap,
sync::{Arc, Weak}, sync::{Arc, Weak},
@@ -104,7 +104,7 @@ impl TerminalServer {
reader.read_exact(&mut buffer).await?; reader.read_exact(&mut buffer).await?;
match TerminalClientMessage::from_com(&mut buffer) { match map_postcard_err(postcard::from_bytes(&buffer))? {
TerminalClientMessage::ExecuteCommand(command) => { TerminalClientMessage::ExecuteCommand(command) => {
let command = command.as_str(); let command = command.as_str();
@@ -126,7 +126,7 @@ impl TerminalServer {
self: &Arc<Self>, self: &Arc<Self>,
message: pulse_wire::terminal::TerminalServerMessage, message: pulse_wire::terminal::TerminalServerMessage,
) -> tokio::io::Result<()> { ) -> tokio::io::Result<()> {
let msg = message.to_com(); let msg = map_postcard_err(postcard::to_allocvec(&message))?;
let mut clients = self.clients.lock().await; let mut clients = self.clients.lock().await;
@@ -158,7 +158,7 @@ impl TerminalServer {
format!("Client({id}) does not exist"), format!("Client({id}) does not exist"),
) )
})?, })?,
&message.to_com(), &map_postcard_err(postcard::to_allocvec(&message))?,
) )
.await .await
} }
+33 -11
View File
@@ -1,4 +1,5 @@
use pulse_wire::prelude::*; use pulse_ui::state::State;
use pulse_wire::{map_postcard_err, prelude::*};
use tokio::{ use tokio::{
io::{AsyncReadExt, AsyncWriteExt}, io::{AsyncReadExt, AsyncWriteExt},
@@ -29,7 +30,7 @@ impl TerminalClient {
&mut self, &mut self,
message: pulse_wire::terminal::TerminalClientMessage, message: pulse_wire::terminal::TerminalClientMessage,
) -> tokio::io::Result<()> { ) -> tokio::io::Result<()> {
let msg = message.to_com(); let msg = map_postcard_err(postcard::to_allocvec(&message))?;
self.writer.write(&msg.len().to_le_bytes()).await?; self.writer.write(&msg.len().to_le_bytes()).await?;
self.writer.write(&msg).await?; self.writer.write(&msg).await?;
self.writer.flush().await?; self.writer.flush().await?;
@@ -42,7 +43,7 @@ impl TerminalClient {
std::mem::swap(&mut self.reader, &mut reader); std::mem::swap(&mut self.reader, &mut reader);
let mut reader = reader.expect("Reader failed to swap"); let reader = reader.expect("Reader failed to swap");
let watch_list = app.watch_list.clone(); let watch_list = app.watch_list.clone();
let active_positions = app.active_positions.clone(); let active_positions = app.active_positions.clone();
@@ -52,8 +53,33 @@ impl TerminalClient {
let status = app.status.clone(); let status = app.status.clone();
let inspect = app.inspect.clone(); let inspect = app.inspect.clone();
tokio::spawn(async move { tokio::spawn(Self::run_client(
loop { reader,
watch_list,
active_positions,
logs,
signals,
market_overview,
status,
inspect,
));
app.sock = Some(self);
app
}
pub async fn run_client(
mut reader: OwnedReadHalf,
watch_list: State<Vec<MarketItem>>,
active_positions: State<Vec<Position>>,
logs: State<Vec<EventLog>>,
signals: State<Vec<Signal>>,
market_overview: State<Option<Strategy>>,
status: State<Option<Status>>,
inspect: State<InspectTarget>,
) -> tokio::io::Result<()> {
let mut len_buf = [0u8; size_of::<usize>()]; let mut len_buf = [0u8; size_of::<usize>()];
reader reader
.read_exact(&mut len_buf) .read_exact(&mut len_buf)
@@ -69,7 +95,7 @@ impl TerminalClient {
.await .await
.expect("Failed to read socket"); .expect("Failed to read socket");
match TerminalServerMessage::from_com(&mut buffer) { match map_postcard_err(postcard::from_bytes(&buffer))? {
TerminalServerMessage::WatchListUpdated(v) => { TerminalServerMessage::WatchListUpdated(v) => {
*watch_list.lock().await = v; *watch_list.lock().await = v;
} }
@@ -102,11 +128,7 @@ impl TerminalClient {
logs.lock().await.push(v); logs.lock().await.push(v);
} }
} }
}
});
app.sock = Some(self); Ok(())
app
} }
} }