Skip to content

Commit c66b713

Browse files
authored
Added status field to field access messages (#7)
This PR fixes the issue that we previously did not define what the server should respond, when it does not have corresponding field registered. To fix this I added a status field to the response messages Additionally i've added a ci script, so that you can run and fix formatting/linting issues locally. See: ci-rust.sh
1 parent 08d193d commit c66b713

10 files changed

Lines changed: 263 additions & 30 deletions

File tree

.github/workflows/rust.yml

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,14 @@ jobs:
1717
steps:
1818
- uses: actions/checkout@v4
1919
- name: Build
20-
working-directory: liquidcan_rust
21-
run: cargo build --verbose
20+
run: ./ci-rust.sh build
2221
- name: Run tests
23-
working-directory: liquidcan_rust
24-
run: cargo test --verbose
22+
run: ./ci-rust.sh test
2523
- name: Run macro tests
26-
working-directory: liquidcan_rust/liquidcan_rust_macros
27-
run: cargo test --verbose
24+
run: ./ci-rust.sh test-macros
2825
- name: Check formatting
29-
working-directory: liquidcan_rust
30-
run: cargo fmt --all -- --check
26+
run: ./ci-rust.sh fmt
3127
- name: Run clippy
32-
working-directory: liquidcan_rust
33-
run: cargo clippy --all-targets --all-features -- -D warnings
28+
run: ./ci-rust.sh clippy
3429
- name: Run clippy on macros
35-
working-directory: liquidcan_rust/liquidcan_rust_macros
36-
run: cargo clippy --all-targets --all-features -- -D warnings
30+
run: ./ci-rust.sh clippy-macros

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -343,3 +343,4 @@ TSWLatexianTemp*
343343

344344
# MSVC Windows builds of rustc generate these, which store debugging information
345345
*.pdb
346+
**/.idea

LiquidCAN.pdf

3.74 KB
Binary file not shown.

README.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,28 @@ When creating issues or pull requests with protocol changes, generate a PDF diff
2424

2525
```bash
2626
latexdiff-vc --git --flatten --pdf -r <old-commit> LiquidCAN.tex
27-
```
27+
```
28+
29+
## Development
30+
31+
### Running CI Checks
32+
33+
The repository includes a CI script (`ci-rust.sh`) that runs all quality checks on the Rust implementation. This script is used both locally and in GitHub Actions
34+
35+
**Run all checks:**
36+
```bash
37+
./ci-rust.sh
38+
# or explicitly
39+
./ci-rust.sh all
40+
```
41+
42+
**Run individual checks:**
43+
```bash
44+
./ci-rust.sh build # Build the project
45+
./ci-rust.sh test # Run tests
46+
./ci-rust.sh test-macros # Run macro tests
47+
./ci-rust.sh fmt # Check code formatting
48+
./ci-rust.sh clippy # Run clippy linter
49+
./ci-rust.sh clippy-macros # Run clippy on macros
50+
```
51+
You can fix formatting or linter issues by adding the -fix suffix to the command. e.g: `./ci-rust.sh clippy-fix`

ci-rust.sh

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
#!/usr/bin/env bash
2+
3+
# CI script for Rust LiquidCAN project
4+
# This script runs all the checks that are performed in the GitHub Actions workflow
5+
6+
set -e # Exit on error
7+
8+
# Colors for output
9+
RED='\033[0;31m'
10+
GREEN='\033[0;32m'
11+
YELLOW='\033[1;33m'
12+
NC='\033[0m' # No Color
13+
14+
# Function to print step headers
15+
print_step() {
16+
echo -e "\n${YELLOW}==== $1 ====${NC}\n"
17+
}
18+
19+
print_success() {
20+
echo -e "${GREEN}$1${NC}"
21+
}
22+
23+
print_error() {
24+
echo -e "${RED}$1${NC}"
25+
}
26+
27+
# Get the script directory
28+
SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
29+
LIQUIDCAN_RUST_DIR="$SCRIPT_DIR/liquidcan_rust"
30+
MACROS_DIR="$LIQUIDCAN_RUST_DIR/liquidcan_rust_macros"
31+
32+
# Export environment variable
33+
export CARGO_TERM_COLOR=always
34+
35+
# Function to run a specific step or all steps
36+
run_step() {
37+
local step=$1
38+
39+
case $step in
40+
build)
41+
print_step "Build"
42+
cd "$LIQUIDCAN_RUST_DIR"
43+
cargo build --verbose || { print_error "Build failed"; return 1; }
44+
print_success "Build completed"
45+
;;
46+
47+
test)
48+
print_step "Run tests"
49+
cd "$LIQUIDCAN_RUST_DIR"
50+
cargo test --verbose || { print_error "Tests failed"; return 1; }
51+
print_success "Tests passed"
52+
;;
53+
54+
test-macros)
55+
print_step "Run macro tests"
56+
cd "$MACROS_DIR"
57+
cargo test --verbose || { print_error "Macro tests failed"; return 1; }
58+
print_success "Macro tests passed"
59+
;;
60+
61+
fmt)
62+
print_step "Check formatting"
63+
cd "$LIQUIDCAN_RUST_DIR"
64+
cargo fmt --all -- --check || { print_error "Formatting check failed"; return 1; }
65+
print_success "Formatting check passed"
66+
;;
67+
fmt-fix)
68+
print_step "Fix formatting"
69+
cd "$LIQUIDCAN_RUST_DIR"
70+
cargo fmt --all || { print_error "Formatting fix failed"; return 1; }
71+
print_success "Formatting fixed"
72+
;;
73+
74+
clippy)
75+
print_step "Run clippy"
76+
cd "$LIQUIDCAN_RUST_DIR"
77+
cargo clippy --all-targets --all-features -- -D warnings || { print_error "Clippy check failed"; return 1; }
78+
print_success "Clippy check passed"
79+
;;
80+
clippy-fix)
81+
print_step "Run clippy with fix"
82+
cd "$LIQUIDCAN_RUST_DIR"
83+
cargo clippy --all-targets --all-features --fix -- -D warnings || { print_error "Clippy fix failed"; return 1; }
84+
print_success "Clippy fix passed"
85+
;;
86+
clippy-macros)
87+
print_step "Run clippy on macros"
88+
cd "$MACROS_DIR"
89+
cargo clippy --all-targets --all-features -- -D warnings || { print_error "Clippy check on macros failed"; return 1; }
90+
print_success "Clippy check on macros passed"
91+
;;
92+
clippy-macros-fix)
93+
print_step "Run clippy on macros with fix"
94+
cd "$MACROS_DIR"
95+
cargo clippy --all-targets --all-features --fix -- -D warnings || { print_error "Clippy fix on macros failed"; return 1; }
96+
print_success "Clippy fix on macros passed"
97+
;;
98+
99+
100+
all)
101+
run_step build || return 1
102+
run_step test || return 1
103+
run_step test-macros || return 1
104+
run_step fmt || return 1
105+
run_step clippy || return 1
106+
run_step clippy-macros || return 1
107+
;;
108+
109+
*)
110+
echo "Usage: $0 [build|test|test-macros|fmt|clippy|clippy-macros|all]"
111+
echo ""
112+
echo "Steps:"
113+
echo " build - Build the project"
114+
echo " test - Run tests"
115+
echo " test-macros - Run macro tests"
116+
echo " fmt - Check formatting"
117+
echo " clippy - Run clippy linter"
118+
echo " clippy-macros - Run clippy on macros"
119+
echo " all - Run all steps (default)"
120+
exit 1
121+
;;
122+
esac
123+
}
124+
125+
# Main execution
126+
STEP=${1:-all}
127+
echo "Running Rust CI checks..."
128+
echo "Step: $STEP"
129+
130+
if run_step "$STEP"; then
131+
echo -e "\n${GREEN}All checks passed successfully!${NC}"
132+
exit 0
133+
else
134+
echo -e "\n${RED}Checks failed!${NC}"
135+
exit 1
136+
fi

liquidcan_rust/src/can_message.rs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -74,9 +74,9 @@ pub enum CanMessage {
7474
ParameterSetLockReq {
7575
payload: payloads::ParameterSetLockPayload,
7676
} = 52,
77-
#[pad(61)]
77+
#[pad(60)]
7878
ParameterSetLockConfirmation {
79-
payload: payloads::ParameterSetLockPayload,
79+
payload: payloads::ParameterSetLockConfirmationPayload,
8080
} = 53,
8181

8282
// Field Access
@@ -92,7 +92,7 @@ pub enum CanMessage {
9292
FieldIDLookupReq {
9393
payload: payloads::FieldIDLookupReqPayload,
9494
} = 62,
95-
#[pad(61)]
95+
#[pad(60)]
9696
FieldIDLookupRes {
9797
payload: payloads::FieldIDLookupResPayload,
9898
} = 63,

liquidcan_rust/src/message_conversion.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ mod tests {
3333
use crate::CanMessageFrame;
3434
use crate::can_message::CanMessage;
3535
use crate::payloads;
36+
use crate::payloads::FieldStatus;
3637
use zerocopy::FromZeros;
3738

3839
fn test_round_trip(msg: CanMessage) {
@@ -172,9 +173,10 @@ mod tests {
172173

173174
#[test]
174175
fn test_parameter_set_lock_confirmation() {
175-
let payload = payloads::ParameterSetLockPayload {
176+
let payload = payloads::ParameterSetLockConfirmationPayload {
176177
parameter_id: 13,
177178
parameter_lock: payloads::ParameterLockStatus::Unlocked,
179+
field_status: FieldStatus::Ok,
178180
};
179181
let msg = CanMessage::ParameterSetLockConfirmation { payload };
180182
test_round_trip(msg);
@@ -191,7 +193,8 @@ mod tests {
191193
fn test_field_get_res() {
192194
let payload = payloads::FieldGetResPayload {
193195
field_id: 21,
194-
value: [0xCC; 62],
196+
field_status: FieldStatus::Ok,
197+
value: [0xCC; 61],
195198
};
196199
let msg = CanMessage::FieldGetRes { payload };
197200
test_round_trip(msg);
@@ -210,6 +213,7 @@ mod tests {
210213
fn test_field_id_lookup_res() {
211214
let payload = payloads::FieldIDLookupResPayload {
212215
field_id: 22,
216+
field_status: FieldStatus::Ok,
213217
field_type: payloads::CanDataType::Float32,
214218
};
215219
let msg = CanMessage::FieldIDLookupRes { payload };

liquidcan_rust/src/payloads.rs

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -90,18 +90,25 @@ pub struct ParameterSetConfirmationPayload {
9090
pub status: ParameterSetStatus, // Status code
9191
pub value: [u8; 61], // Confirmed value after set operation
9292
}
93+
#[derive(Specifier, Debug, Copy, Clone, PartialEq, Eq, Immutable, TryFromBytes, IntoBytes)]
94+
#[repr(u8)]
95+
pub enum FieldStatus {
96+
Ok = 0,
97+
NotFound = 1,
98+
}
9399

94100
#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable, PartialEq)]
95101
#[repr(C, packed)]
96102
pub struct FieldGetReqPayload {
97103
pub field_id: u8, // Field identifier
98104
}
99105

100-
#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable, PartialEq)]
106+
#[derive(Debug, Clone, TryFromBytes, IntoBytes, Immutable, PartialEq)]
101107
#[repr(C, packed)]
102108
pub struct FieldGetResPayload {
103-
pub field_id: u8, // Field identifier
104-
pub value: [u8; 62], // Field value
109+
pub field_id: u8, // Field identifier
110+
pub field_status: FieldStatus, // Status of the get operation
111+
pub value: [u8; 61], // Field value
105112
}
106113

107114
#[derive(Debug, Clone, FromBytes, IntoBytes, Immutable, PartialEq)]
@@ -114,8 +121,9 @@ pub struct FieldIDLookupReqPayload {
114121
#[derive(Debug, Clone, TryFromBytes, IntoBytes, Immutable, PartialEq)]
115122
#[repr(C, packed)]
116123
pub struct FieldIDLookupResPayload {
117-
pub field_id: u8, // Field ID
118-
pub field_type: CanDataType, // Field Datatype
124+
pub field_id: u8, // Field ID
125+
pub field_status: FieldStatus, // Status of the lookup operation
126+
pub field_type: CanDataType, // Field Datatype
119127
}
120128

121129
// Important: only derives TryFromBytes because bool doesn't derive FromBytes
@@ -125,6 +133,14 @@ pub struct ParameterSetLockPayload {
125133
pub parameter_id: u8, // Parameter identifier to lock
126134
pub parameter_lock: ParameterLockStatus, // Lock status (0=unlocked, 1=locked)
127135
}
136+
// Important: only derives TryFromBytes because bool doesn't derive FromBytes
137+
#[derive(Debug, Clone, TryFromBytes, IntoBytes, Immutable, PartialEq)]
138+
#[repr(C, packed)]
139+
pub struct ParameterSetLockConfirmationPayload {
140+
pub parameter_id: u8, // Parameter identifier to lock
141+
pub parameter_lock: ParameterLockStatus, // Lock status (0=unlocked, 1=locked)
142+
pub field_status: FieldStatus, // Status of the parameter
143+
}
128144

129145
static_assertions::const_assert_eq!(size_of::<NodeInfoResPayload>(), 63);
130146
static_assertions::const_assert_eq!(size_of::<StatusPayload>(), 63);
@@ -137,5 +153,10 @@ static_assertions::const_assert_eq!(size_of::<ParameterSetConfirmationPayload>()
137153
static_assertions::const_assert_eq!(size_of::<FieldGetReqPayload>(), 1);
138154
static_assertions::const_assert_eq!(size_of::<FieldGetResPayload>(), 63);
139155
static_assertions::const_assert_eq!(size_of::<FieldIDLookupReqPayload>(), 61);
140-
static_assertions::const_assert_eq!(size_of::<FieldIDLookupResPayload>(), 2);
156+
static_assertions::const_assert_eq!(size_of::<FieldIDLookupResPayload>(), 3);
157+
static_assertions::const_assert_eq!(size_of::<ParameterSetLockConfirmationPayload>(), 3);
141158
static_assertions::const_assert_eq!(size_of::<ParameterSetLockPayload>(), 2);
159+
static_assertions::const_assert_eq!(size_of::<FieldStatus>(), 1);
160+
static_assertions::const_assert_eq!(size_of::<CanDataType>(), 1);
161+
static_assertions::const_assert_eq!(size_of::<ParameterSetStatus>(), 1);
162+
static_assertions::const_assert_eq!(size_of::<ParameterLockStatus>(), 1);

sections/05_field_management.tex

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -81,18 +81,19 @@ \subsubsection{Parameter Updates}\label{subsubsec:parameter-updates}
8181

8282
\subsubsection{Parameter Setting}\label{subsubsec:parameter-setting}
8383
Other bus members can send a \texttt{parameter\_set\_req} message, which includes the field ID of the parameter and the new value.
84-
Once the node receives the request, it attempts to set the internal parameter value and responds with a \texttt{parameter\_set\_confirmation} message containing a status code, the parameter ID, and the actual value. The status field indicates whether the operation was successful or provides an error code (see ParameterSetStatus enum in \ref{subsec:ParameterSetStatus}). The value field should contain the actual value read back from the parameter, not simply the value that was received in the request.
84+
Once the node receives the request, it attempts to set the internal parameter value and responds with a \texttt{parameter\_set\_confirmation} message containing a status code, a field status, the parameter ID, and the actual value. The status field indicates whether the operation was successful or provides an error code (see ParameterSetStatus enum in \ref{subsec:ParameterSetStatus}). The \texttt{field\_status} indicates whether the parameter exists. If the parameter was not found (field\_status = NotFound) the status field should return InvalidParameterID. Otherwise, the value field should contain the actual value read back from the parameter and not simply the value that was received in the request.
85+
8586

8687
When a parameter is internally modified through some automated system, the updated value must be sent as a \texttt{parameter\_set\_confirmation} message to the server with a \texttt{NodeToNodeModification} status code.
8788
\subsubsection{Parameter Locking}\label{subsubsec:parameter-locking}
8889

8990
A parameter can optionally be locked through a \texttt{parameter\_set\_lock\_req} message .
9091
After a parameter has been locked, it cannot be modified by an external node.
91-
A parameter can only be unlocked by the locking node or the server.To lock a parameter, a node sends a \texttt{parameter\_set\_lock\_req} with the fieldID and the locking status (0=unlocked, 1=locked). The recieving node responds with a \texttt{parameter\_set\_lock\_confirmation}, confirming the sent fieldID and the locking status. The receiving node also sends a \texttt{parameter\_set\_lock\_confirmation} to the server to update it on the locking status of the parameter.
92+
A parameter can only be unlocked by the locking node or the server.To lock a parameter, a node sends a \texttt{parameter\_set\_lock\_req} with the fieldID and the locking status (0=unlocked, 1=locked). The recieving node responds with a \texttt{parameter\_set\_lock\_confirmation}, confirming the sent fieldID, the locking status, and a field status (see FieldStatus enum in \ref{subsec:FieldStatus}). If the parameter is not found the node should respond with a not found status(field\_status = NotFound) and the erroneous field id. The receiving node also sends a \texttt{parameter\_set\_lock\_confirmation} to the server to update it on the locking status of the parameter. When a parameter was not found, it does not communicate this with the server.
9293
\paragraph{}
9394
\subsubsection{Requesting Field Data}\label{subsubsec:requesting-field-data}
9495
A field can be accessed through a \texttt{field\_get\_req} message, which contains the field ID.
95-
Nodes respond with a \texttt{field\_get\_res} message, containing the field ID and the value of the field.
96+
Nodes respond with a \texttt{field\_get\_res} message, containing the field ID, a field status (see FieldStatus enum in \ref{subsec:FieldStatus}), and the value of the field. If the field is not found (field\_status = NotFound), the field\_id should still contain the requested field ID from the request message, and the value field content is undefined.
9697

9798
\subsubsection{Field name Lookup}\label{subsubsec:field-name-lookup}
98-
The field name lookup covers the case where nodes need to access fields from other nodes. Since they don't recieve the \texttt{field\_registration} messages, they don't know the fieldIDs of the named fields they want to access. \texttt{field\_id\_lookup\_req} messages contain the remote field name. The Node responds with a \texttt{field\_id\_lookup\_res} message, containing the fielID and the datatype of the field. A fieldID of 0 indicates that the field was not found.
99+
The field name lookup covers the case where nodes need to access fields from other nodes. Since they don't recieve the \texttt{field\_registration} messages, they don't know the fieldIDs of the named fields they want to access. \texttt{field\_id\_lookup\_req} messages contain the remote field name. The Node responds with a \texttt{field\_id\_lookup\_res} message, containing the fieldID, the datatype of the field, and a field status (see FieldStatus enum in \ref{subsec:FieldStatus}). If the field is not found (field\_status = NotFound), the field\_id should still contain the requested field ID from the request message.

0 commit comments

Comments
 (0)