Skip to content

Commit 3c77b07

Browse files
authored
Merge pull request #850 from HaoboGu/feat/rynk-pr2-service-core
Add Rynk core service struct
2 parents 8252052 + 2700e72 commit 3c77b07

13 files changed

Lines changed: 623 additions & 199 deletions

File tree

rmk-config/src/default_config/event_default.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -27,17 +27,17 @@ subs = 1
2727
[event.wpm_update]
2828
channel_size = 1
2929
pubs = 1
30-
subs = 1
30+
subs = 2
3131

3232
[event.led_indicator]
3333
channel_size = 2
3434
pubs = 2
35-
subs = 3
35+
subs = 4
3636

3737
[event.sleep_state]
3838
channel_size = 1
3939
pubs = 1
40-
subs = 1
40+
subs = 2
4141

4242
# Power events
4343
[event.battery_status]

rmk-types/src/protocol/rynk/buffer.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ pub const RYNK_MAX_PAYLOAD: usize = {
114114
m
115115
};
116116

117-
/// Minimum buffer size required to hold any single Rynk frame
117+
/// Minimum buffer size required to hold any single Rynk message
118118
/// (header + max-payload). User-configured `RYNK_BUFFER_SIZE` must
119119
/// not be smaller than this.
120120
pub const RYNK_MIN_BUFFER_SIZE: usize = RYNK_HEADER_SIZE + RYNK_MAX_PAYLOAD;

rmk-types/src/protocol/rynk/cmd.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,10 @@
2323
2424
use strum::FromRepr;
2525

26-
/// Command tag carried in the [`Header`](super::Header) CMD field.
26+
/// Command tag carried in the header CMD field.
2727
///
28-
/// The wire encoding is a plain `u16 LE` written by `Header::encode_into`
29-
/// Cmd is never postcard-encoded, so no `Serialize`/`Deserialize`/`MaxSize`
28+
/// The wire encoding is a plain `u16 LE` written by [`RynkMessage::set_cmd`](super::RynkMessage::set_cmd)
29+
/// `Cmd` is never postcard-encoded, so no `Serialize`/`Deserialize`/`MaxSize`
3030
/// derives are needed here.
3131
#[repr(u16)]
3232
#[derive(Copy, Clone, PartialEq, Eq, Debug, Hash, FromRepr)]
@@ -82,7 +82,6 @@ pub enum Cmd {
8282

8383
// ── Connection (0x07xx) ──
8484
GetConnectionType = 0x0701,
85-
SetConnectionType = 0x0702,
8685
#[cfg(feature = "_ble")]
8786
GetBleStatus = 0x0703,
8887
#[cfg(feature = "_ble")]
@@ -97,6 +96,12 @@ pub enum Cmd {
9796
GetBatteryStatus = 0x0803,
9897
#[cfg(all(feature = "_ble", feature = "split"))]
9998
GetPeripheralStatus = 0x0804,
99+
/// Latest WPM, sourced from the `WpmUpdate` topic snapshot.
100+
GetWpm = 0x0805,
101+
/// Latest sleep flag, sourced from the `SleepState` topic snapshot.
102+
GetSleepState = 0x0806,
103+
/// Latest HID LED bitmap, sourced from the `LedIndicator` topic snapshot.
104+
GetLedIndicator = 0x0807,
100105

101106
// ── Topics (0x80xx, server → host push) ──
102107
LayerChange = 0x8001,

rmk-types/src/protocol/rynk/header.rs

Lines changed: 0 additions & 128 deletions
This file was deleted.
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
//! Rynk wire-format message view.
2+
//!
3+
//! Fixed 5-byte header followed by a postcard-encoded payload:
4+
//!
5+
//! ```text
6+
//! ┌──────────────┬───────┬──────────────┐
7+
//! │ CMD u16 LE │SEQ u8 │ LEN u16 LE │ ← 5-byte header
8+
//! ├──────────────┴───────┴──────────────┤
9+
//! │ payload bytes ... │
10+
//! └─────────────────────────────────────┘
11+
//! ```
12+
13+
use super::RynkError;
14+
use super::cmd::Cmd;
15+
16+
/// Size in bytes of the fixed Rynk header.
17+
pub const RYNK_HEADER_SIZE: usize = 5;
18+
19+
/// Message operations for Rynk
20+
pub trait RynkMessage {
21+
fn cmd(&self) -> Result<Cmd, RynkError>;
22+
fn seq(&self) -> Result<u8, RynkError>;
23+
fn payload_len(&self) -> Result<u16, RynkError>;
24+
fn payload(&self) -> Result<&[u8], RynkError>;
25+
fn payload_mut(&mut self) -> Result<&mut [u8], RynkError>;
26+
27+
fn set_cmd(&mut self, cmd: Cmd) -> Result<(), RynkError>;
28+
fn set_seq(&mut self, seq: u8) -> Result<(), RynkError>;
29+
fn set_payload_len(&mut self, len: u16) -> Result<(), RynkError>;
30+
}
31+
32+
impl RynkMessage for [u8] {
33+
fn cmd(&self) -> Result<Cmd, RynkError> {
34+
if self.len() < RYNK_HEADER_SIZE {
35+
return Err(RynkError::InvalidRequest);
36+
}
37+
Cmd::from_repr(u16::from_le_bytes([self[0], self[1]])).ok_or(RynkError::InvalidRequest)
38+
}
39+
40+
fn seq(&self) -> Result<u8, RynkError> {
41+
if self.len() < RYNK_HEADER_SIZE {
42+
return Err(RynkError::InvalidRequest);
43+
}
44+
Ok(self[2])
45+
}
46+
47+
fn payload_len(&self) -> Result<u16, RynkError> {
48+
if self.len() < RYNK_HEADER_SIZE {
49+
return Err(RynkError::InvalidRequest);
50+
}
51+
Ok(u16::from_le_bytes([self[3], self[4]]))
52+
}
53+
54+
fn payload(&self) -> Result<&[u8], RynkError> {
55+
if self.len() < RYNK_HEADER_SIZE {
56+
return Err(RynkError::InvalidRequest);
57+
}
58+
Ok(&self[RYNK_HEADER_SIZE..])
59+
}
60+
61+
fn payload_mut(&mut self) -> Result<&mut [u8], RynkError> {
62+
if self.len() < RYNK_HEADER_SIZE {
63+
return Err(RynkError::InvalidRequest);
64+
}
65+
Ok(&mut self[RYNK_HEADER_SIZE..])
66+
}
67+
68+
fn set_cmd(&mut self, cmd: Cmd) -> Result<(), RynkError> {
69+
if self.len() < RYNK_HEADER_SIZE {
70+
return Err(RynkError::InvalidRequest);
71+
}
72+
self[0..2].copy_from_slice(&(cmd as u16).to_le_bytes());
73+
Ok(())
74+
}
75+
76+
fn set_seq(&mut self, seq: u8) -> Result<(), RynkError> {
77+
if self.len() < RYNK_HEADER_SIZE {
78+
return Err(RynkError::InvalidRequest);
79+
}
80+
self[2] = seq;
81+
Ok(())
82+
}
83+
84+
fn set_payload_len(&mut self, len: u16) -> Result<(), RynkError> {
85+
if self.len() < RYNK_HEADER_SIZE {
86+
return Err(RynkError::InvalidRequest);
87+
}
88+
self[3..5].copy_from_slice(&len.to_le_bytes());
89+
Ok(())
90+
}
91+
}
92+
93+
#[cfg(test)]
94+
mod tests {
95+
use super::*;
96+
97+
#[test]
98+
fn round_trip_header_fields() {
99+
let mut buf = [0u8; RYNK_HEADER_SIZE + 7];
100+
buf.set_cmd(Cmd::GetVersion).unwrap();
101+
buf.set_seq(0x42).unwrap();
102+
buf.set_payload_len(7).unwrap();
103+
buf.payload_mut().unwrap()[..7].copy_from_slice(&[1, 2, 3, 4, 5, 6, 7]);
104+
105+
assert_eq!(buf.cmd().unwrap(), Cmd::GetVersion);
106+
assert_eq!(buf.seq().unwrap(), 0x42);
107+
assert_eq!(buf.payload_len().unwrap(), 7);
108+
assert_eq!(buf.payload().unwrap(), &[1, 2, 3, 4, 5, 6, 7]);
109+
}
110+
111+
#[test]
112+
fn cmd_rejects_short_buffer() {
113+
let buf = [0u8; RYNK_HEADER_SIZE - 1];
114+
assert_eq!(buf.cmd(), Err(RynkError::InvalidRequest));
115+
}
116+
117+
#[test]
118+
fn cmd_rejects_unknown_discriminant() {
119+
let mut buf = [0u8; RYNK_HEADER_SIZE];
120+
buf[0..2].copy_from_slice(&0xFFFFu16.to_le_bytes());
121+
assert_eq!(buf.cmd(), Err(RynkError::InvalidRequest));
122+
}
123+
124+
#[test]
125+
fn every_accessor_rejects_short_buffer() {
126+
let mut buf = [0u8; RYNK_HEADER_SIZE - 1];
127+
assert_eq!(buf.seq(), Err(RynkError::InvalidRequest));
128+
assert_eq!(buf.payload_len(), Err(RynkError::InvalidRequest));
129+
assert!(buf.payload().is_err());
130+
assert!(buf.payload_mut().is_err());
131+
assert_eq!(buf.set_cmd(Cmd::GetVersion), Err(RynkError::InvalidRequest));
132+
assert_eq!(buf.set_seq(0), Err(RynkError::InvalidRequest));
133+
assert_eq!(buf.set_payload_len(0), Err(RynkError::InvalidRequest));
134+
}
135+
}

0 commit comments

Comments
 (0)