Skip to content

Commit e9027f2

Browse files
lxsaahCopilot
andauthored
54 implement knxip connector for building automation (#55)
* initial implementation knx connector for tokio * initial implementation embassy knx connector * format and rename demo * update Makefile * fix clippy errors and warnings * update devcontainer CI workflow for improved efficiency * persist watch receiver to avoid infinite subscription loop * fix group adress parsing and refactor documentation for clarity * remove conflicting memory.x from knx-pico to ensure correct STM32 memory layout * implement outbound publishing for the tokio connector * fix clippy * implement outbound publishers for embassy * update embassy * fix clippy * feat: Enhance KNX connector with ACK handling and timeout detection - Added production status documentation in lib.rs. - Refactored tokio_client.rs to implement pending ACK tracking for outbound telegrams. - Introduced timeout detection for ACKs, with logging for timed-out sequences. - Updated group address parsing in examples to handle various data formats. - Added comprehensive integration tests for connection state management and frame building. - Implemented unit tests for group address parsing and formatting. * add knx-pico submodule * update submodules * Update Ethernet device type and configuration in KNX and MQTT demos * fix: Update knx-pico dependency path to avoid version conflicts * update knx-pico * update knx pico * reexport dpt from knx-pico crate * use dpt en/decoding from knx-connector crate * Refactor KNX connector to utilize knx-pico for type-safe group address handling and protocol parsing * update embassy * update README's * Update examples/tokio-knx-connector-demo/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Update examples/tokio-knx-connector-demo/README.md Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 23b543b commit e9027f2

30 files changed

Lines changed: 4574 additions & 34 deletions

File tree

.github/workflows/devcontainer.yml

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,13 @@ on:
44
push:
55
branches: [ main, develop ]
66
paths:
7-
- '.devcontainer/Dockerfile'
8-
- '.devcontainer/devcontainer.json'
7+
- '.devcontainer/**'
98
- '.github/workflows/devcontainer.yml'
109
pull_request:
11-
branches: [ main ]
10+
branches: [ main, develop ]
1211
paths:
13-
- '.devcontainer/Dockerfile'
14-
- '.devcontainer/devcontainer.json'
12+
- '.devcontainer/**'
1513
- '.github/workflows/devcontainer.yml'
16-
schedule:
17-
# Test weekly to catch upstream image changes
18-
- cron: '0 6 * * 1' # Every Monday at 6 AM UTC
1914
workflow_dispatch:
2015

2116
env:
@@ -168,6 +163,19 @@ jobs:
168163
with:
169164
submodules: recursive
170165

166+
- name: Free up disk space
167+
run: |
168+
echo "Disk space before cleanup:"
169+
df -h
170+
sudo rm -rf /usr/share/dotnet
171+
sudo rm -rf /usr/local/lib/android
172+
sudo rm -rf /opt/ghc
173+
sudo rm -rf /opt/hostedtoolcache/CodeQL
174+
sudo docker image prune -af
175+
sudo apt-get clean
176+
echo "Disk space after cleanup:"
177+
df -h
178+
171179
- name: Set up Docker Buildx
172180
uses: docker/setup-buildx-action@v3
173181

@@ -186,6 +194,7 @@ jobs:
186194
image-ref: aimdb-devcontainer:scan
187195
format: 'sarif'
188196
output: 'trivy-results.sarif'
197+
skip-dirs: '/usr/share/dotnet,/usr/local/lib/android'
189198

190199
- name: Upload Trivy scan results to GitHub Security tab
191200
uses: github/codeql-action/upload-sarif@v3

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,3 +4,6 @@
44
[submodule "_external/mountain-mqtt"]
55
path = _external/mountain-mqtt
66
url = https://github.com/aimdb-dev/mountain-mqtt.git
7+
[submodule "_external/knx-pico"]
8+
path = _external/knx-pico
9+
url = https://github.com/aimdb-dev/knx-pico.git

Cargo.lock

Lines changed: 83 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,13 @@ members = [
77
"aimdb-tokio-adapter",
88
"aimdb-sync",
99
"aimdb-mqtt-connector",
10+
"aimdb-knx-connector",
1011
"tools/aimdb-cli",
1112
"tools/aimdb-mcp",
1213
"examples/tokio-mqtt-connector-demo",
14+
"examples/tokio-knx-connector-demo",
1315
"examples/embassy-mqtt-connector-demo",
16+
"examples/embassy-knx-connector-demo",
1417
"examples/sync-api-demo",
1518
"examples/remote-access-demo",
1619
]

Makefile

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ build:
5656
cargo build --package aimdb-cli
5757
@printf "$(YELLOW) → Building MCP server$(NC)\n"
5858
cargo build --package aimdb-mcp
59+
@printf "$(YELLOW) → Building KNX connector$(NC)\n"
60+
cargo build --package aimdb-knx-connector --features "std,tokio-runtime"
5961

6062
test:
6163
@printf "$(GREEN)Running all tests (valid combinations)...$(NC)\n"
@@ -73,10 +75,12 @@ test:
7375
cargo test --package aimdb-cli
7476
@printf "$(YELLOW) → Testing MCP server$(NC)\n"
7577
cargo test --package aimdb-mcp
78+
@printf "$(YELLOW) → Testing KNX connector$(NC)\n"
79+
cargo test --package aimdb-knx-connector --features "std,tokio-runtime"
7680

7781
fmt:
7882
@printf "$(GREEN)Formatting code (workspace members only)...$(NC)\n"
79-
@for pkg in aimdb-executor aimdb-core aimdb-client aimdb-embassy-adapter aimdb-tokio-adapter aimdb-sync aimdb-mqtt-connector aimdb-cli aimdb-mcp sync-api-demo tokio-mqtt-connector-demo embassy-mqtt-connector-demo; do \
83+
@for pkg in aimdb-executor aimdb-core aimdb-client aimdb-embassy-adapter aimdb-tokio-adapter aimdb-sync aimdb-mqtt-connector aimdb-knx-connector aimdb-cli aimdb-mcp sync-api-demo tokio-mqtt-connector-demo embassy-mqtt-connector-demo tokio-knx-connector-demo embassy-knx-connector-demo; do \
8084
printf "$(YELLOW) → Formatting $$pkg$(NC)\n"; \
8185
cargo fmt -p $$pkg 2>/dev/null || true; \
8286
done
@@ -85,7 +89,7 @@ fmt:
8589
fmt-check:
8690
@printf "$(GREEN)Checking code formatting (workspace members only)...$(NC)\n"
8791
@FAILED=0; \
88-
for pkg in aimdb-executor aimdb-core aimdb-client aimdb-embassy-adapter aimdb-tokio-adapter aimdb-sync aimdb-mqtt-connector aimdb-cli aimdb-mcp sync-api-demo tokio-mqtt-connector-demo embassy-mqtt-connector-demo; do \
92+
for pkg in aimdb-executor aimdb-core aimdb-client aimdb-embassy-adapter aimdb-tokio-adapter aimdb-sync aimdb-mqtt-connector aimdb-knx-connector aimdb-cli aimdb-mcp sync-api-demo tokio-mqtt-connector-demo embassy-mqtt-connector-demo tokio-knx-connector-demo embassy-knx-connector-demo; do \
8993
printf "$(YELLOW) → Checking $$pkg$(NC)\n"; \
9094
if ! cargo fmt -p $$pkg -- --check 2>&1; then \
9195
printf "$(RED)❌ Formatting check failed for $$pkg$(NC)\n"; \
@@ -118,6 +122,10 @@ clippy:
118122
cargo clippy --package aimdb-cli --all-targets -- -D warnings
119123
@printf "$(YELLOW) → Clippy on MCP server$(NC)\n"
120124
cargo clippy --package aimdb-mcp --all-targets -- -D warnings
125+
@printf "$(YELLOW) → Clippy on KNX connector (std)$(NC)\n"
126+
cargo clippy --package aimdb-knx-connector --features "std,tokio-runtime" --all-targets -- -D warnings
127+
@printf "$(YELLOW) → Clippy on KNX connector (embassy)$(NC)\n"
128+
cargo clippy --package aimdb-knx-connector --target thumbv7em-none-eabihf --no-default-features --features "embassy-runtime" -- -D warnings
121129

122130
doc:
123131
@printf "$(GREEN)Generating dual-platform documentation...$(NC)\n"
@@ -129,13 +137,15 @@ doc:
129137
cargo doc --package aimdb-tokio-adapter --features "tokio-runtime,tracing,metrics" --no-deps
130138
cargo doc --package aimdb-sync --no-deps
131139
cargo doc --package aimdb-mqtt-connector --features "std,tokio-runtime" --no-deps
140+
cargo doc --package aimdb-knx-connector --features "std,tokio-runtime" --no-deps
132141
cargo doc --package aimdb-cli --no-deps
133142
cargo doc --package aimdb-mcp --no-deps
134143
@cp -r target/doc/* target/doc-final/cloud/
135144
@printf "$(YELLOW) → Building embedded documentation$(NC)\n"
136145
cargo doc --package aimdb-core --no-default-features --no-deps
137146
cargo doc --package aimdb-embassy-adapter --features "embassy-runtime" --no-deps
138147
cargo doc --package aimdb-mqtt-connector --no-default-features --features "embassy-runtime" --no-deps
148+
cargo doc --package aimdb-knx-connector --no-default-features --features "embassy-runtime" --no-deps
139149
@cp -r target/doc/* target/doc-final/embedded/
140150
@printf "$(YELLOW) → Creating main index page$(NC)\n"
141151
@cp docs/index.html target/doc-final/index.html
@@ -158,6 +168,8 @@ test-embedded:
158168
cargo check --package aimdb-embassy-adapter --target thumbv7em-none-eabihf --no-default-features --features "embassy-runtime,embassy-net-support"
159169
@printf "$(YELLOW) → Checking aimdb-mqtt-connector (Embassy) on thumbv7em-none-eabihf target$(NC)\n"
160170
cargo check --package aimdb-mqtt-connector --target thumbv7em-none-eabihf --no-default-features --features "embassy-runtime"
171+
@printf "$(YELLOW) → Checking aimdb-knx-connector (Embassy) on thumbv7em-none-eabihf target$(NC)\n"
172+
cargo check --package aimdb-knx-connector --target thumbv7em-none-eabihf --no-default-features --features "embassy-runtime"
161173

162174
## Example projects
163175
examples:
@@ -168,6 +180,10 @@ examples:
168180
cargo build --package tokio-mqtt-connector-demo
169181
@printf "$(YELLOW) → Building embassy-mqtt-connector-demo (embedded, embassy runtime)$(NC)\n"
170182
cargo build --package embassy-mqtt-connector-demo --target thumbv7em-none-eabihf
183+
@printf "$(YELLOW) → Building tokio-knx-connector-demo (native, tokio runtime)$(NC)\n"
184+
cargo build --package tokio-knx-connector-demo
185+
@printf "$(YELLOW) → Building embassy-knx-connector-demo (embedded, embassy runtime)$(NC)\n"
186+
cargo build --package embassy-knx-connector-demo --target thumbv7em-none-eabihf
171187
@printf "$(GREEN)All examples built successfully!$(NC)\n"
172188

173189
## Security & Quality commands

_external/embassy

Submodule embassy updated 67 files

_external/knx-pico

Submodule knx-pico added at e60ca5b

aimdb-embassy-adapter/src/buffer.rs

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,7 @@ impl<
195195
// Clone the Arc for the reader
196196
EmbassyBufferReader {
197197
buffer: Arc::clone(&self.inner),
198+
watch_receiver: None, // Will be initialized on first recv() for Watch buffers
198199
}
199200
}
200201
}
@@ -264,8 +265,8 @@ impl<
264265

265266
/// Reader for Embassy buffers
266267
///
267-
/// Holds an Arc reference to the buffer. Each recv() call creates a temporary
268-
/// subscription to read one value.
268+
/// Holds persistent subscription state for each buffer type.
269+
/// For Watch buffers, stores a persistent Receiver to track which value has been seen.
269270
pub struct EmbassyBufferReader<
270271
T: Clone + Send + 'static,
271272
const CAP: usize,
@@ -274,6 +275,9 @@ pub struct EmbassyBufferReader<
274275
const WATCH_N: usize,
275276
> {
276277
buffer: Arc<EmbassyBufferInner<T, CAP, SUBS, PUBS, WATCH_N>>,
278+
/// Persistent Watch receiver. The 'static lifetime is safe because the Arc keeps the Watch alive.
279+
watch_receiver:
280+
Option<embassy_sync::watch::Receiver<'static, CriticalSectionRawMutex, T, WATCH_N>>,
277281
}
278282

279283
impl<
@@ -289,8 +293,6 @@ impl<
289293
) -> core::pin::Pin<Box<dyn core::future::Future<Output = Result<T, DbError>> + Send + '_>>
290294
{
291295
Box::pin(async move {
292-
// Create a temporary subscription for this recv() call
293-
// This works because the Arc keeps the buffer alive
294296
match &*self.buffer {
295297
EmbassyBufferInner::SpmcRing(channel) => match channel.subscriber() {
296298
Ok(mut sub) => match sub.next_message().await {
@@ -302,10 +304,32 @@ impl<
302304
},
303305
Err(_) => Err(DbError::BufferClosed { _buffer_name: () }),
304306
},
305-
EmbassyBufferInner::Watch(watch) => match watch.receiver() {
306-
Some(mut rx) => Ok(rx.changed().await),
307-
None => Err(DbError::BufferClosed { _buffer_name: () }),
308-
},
307+
EmbassyBufferInner::Watch(watch) => {
308+
// Watch requires a persistent receiver to track seen values.
309+
// Creating a new receiver each time causes infinite loops (always returns current value).
310+
if self.watch_receiver.is_none() {
311+
// SAFETY: The Arc in self.buffer keeps the Watch alive for this reader's lifetime.
312+
// We extend the lifetime to 'static to store the receiver, which is safe because
313+
// the receiver is just (&Watch, u64 counter) and will be dropped with the reader.
314+
let watch_static: &'static embassy_sync::watch::Watch<
315+
CriticalSectionRawMutex,
316+
T,
317+
WATCH_N,
318+
> = unsafe { &*(watch as *const _) };
319+
320+
self.watch_receiver = watch_static.receiver();
321+
if self.watch_receiver.is_none() {
322+
return Err(DbError::BufferClosed { _buffer_name: () });
323+
}
324+
}
325+
326+
// Use the persistent receiver to detect changes
327+
if let Some(ref mut rx) = self.watch_receiver {
328+
Ok(rx.changed().await)
329+
} else {
330+
Err(DbError::BufferClosed { _buffer_name: () })
331+
}
332+
}
309333
EmbassyBufferInner::Mailbox(channel) => {
310334
let rx = channel.receiver();
311335
Ok(rx.receive().await)

0 commit comments

Comments
 (0)