-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.cursorrules
More file actions
294 lines (227 loc) · 7.6 KB
/
.cursorrules
File metadata and controls
294 lines (227 loc) · 7.6 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
# LoRaWAN Payload Schema Development Rules
## Project Context
This is the reference implementation of the LoRaWAN Payload Schema specification.
The goal is to provide production-ready interpreters and code generators for
declarative device payload definitions.
## Schema Language Quick Reference
See `docs/SCHEMA-LANGUAGE-REFERENCE.md` for complete reference.
```
TYPES: u8 u16 u24 u32 u64 | s8 s16 s24 s32 s64 | f16 f32 f64 | bool
ascii hex bytes base64 | number string | skip enum
STRUCTURES: object | repeat | byte_group | tlv
MODIFIERS: add mult div | lookup | polynomial | compute | guard | transform
CONDITIONALS: match (value) | flagged (bitmask) | tlv (tag dispatch)
TRANSFORMS: sqrt abs pow floor ceiling clamp log10 log
COMPUTE OPS: add sub mul div mod idiv
GUARD OPS: gt gte lt lte eq ne
ENCODINGS: sign_magnitude bcd gray
REFERENCES: $field_name | var: name | use: definition
```
Example schema:
```yaml
name: sensor
version: 1
fields:
- name: temperature
type: s16
div: 10
unit: "°C"
- name: humidity
type: u8
test_vectors:
- name: basic
payload: "00E7 32"
expected:
temperature: 23.1
humidity: 50
```
## Code Style
### C Code (Production/Embedded)
#### Types
- Use fixed-width types from rt.h: u1_t, u2_t, u4_t, u8_t, s1_t, s2_t, s4_t, s8_t
- Use str_t for const char* strings
- Use ustime_t for microsecond timestamps
#### Naming
- Functions: lowercase_with_underscores()
- Types: lowercase_with_t suffix (message_t, header_t)
- Constants: UPPERCASE_WITH_UNDERSCORES
- Enums: ENUM_PREFIX_VALUE (MTYPE_CONFIRMED_UP)
- Static/private functions: prefix with module name or underscore
#### Memory
- Prefer static allocation over dynamic (malloc/free)
- Use fixed-size buffers with explicit size constants
- Define MAX_* constants for pool sizes
#### Portability
- Target: Linux, Zephyr RTOS, FreeRTOS
- Use sys.h abstraction for OS services (time, logging, random)
- Avoid GNU-specific extensions in core protocol code
- No POSIX-specific calls in portable modules
#### Byte Order
- LoRaWAN uses little-endian
- Use read_u2_le(), write_u4_le() helpers
- Never assume host byte order
### Python Code (Testing/Simulation)
- Python 3.8+ minimum
- Type hints for all function signatures
- Async/await for all network operations
- Use dataclasses for message structures
- Use pytest for testing with @pytest.mark.asyncio
## Testing Philosophy
### Rule: Tests First, Not Code Archaeology
When debugging:
1. Form hypothesis from symptoms
2. Write minimal test exercising that code path
3. Test proves/disproves hypothesis
4. Same test validates fix and prevents regression
### C Self-Tests
```c
// Use TCHECK() for assertions
TCHECK(result == expected);
TCHECK(buffer[0] == 0x40);
// Use TFAIL() for explicit failures
if (error_condition) {
TFAIL("Unexpected error condition");
}
// Register tests in selftests.c
extern void selftest_mymodule(void);
```
### Python Tests
```python
@pytest.mark.asyncio
async def test_requirement_name(mock_lns, gateway_sim):
"""
Test: Brief description
Spec: Section X.X requirement text
Given: Initial conditions
When: Action taken
Then: Expected result
"""
# Test implementation
```
## Specification Compliance
### Comments
- Reference spec sections: `/* Per spec section 5.2.1 */`
- Quote normative text for critical requirements
- Note RFU handling: `/* RFU - set to 0, ignore on receive */`
### Normative Keyword Mapping
| Spec Keyword | Code Behavior |
|--------------|---------------|
| MUST, SHALL | Return error / assert on violation |
| MUST NOT, SHALL NOT | Return error / assert if present |
| SHOULD | Log warning if not followed |
| MAY | Document as optional, test both paths |
### RFU Fields
- Encode: Always set RFU bits/fields to 0
- Decode: Silently ignore RFU bits/fields
- Comment: `/* RFU - Reserved for Future Use */`
## Protocol Buffers (nanopb)
When working with .proto files:
```bash
# Regenerate after .proto changes
nanopb_generator -I . -D . messages.proto
```
- Define .options file for static allocation sizes
- Always regenerate both .pb.c and .pb.h together
- Clean object files after protobuf changes
## Logging
```c
// Levels: LOG_DEBUG, LOG_INFO, LOG_WARN, LOG_ERROR
// Modules: MOD_CODEC, MOD_PROTO, MOD_SYS, etc.
LOG(LOG_DEBUG, MOD_CODEC, "Decoding message: len=%d", len);
LOG(LOG_ERROR, MOD_PROTO, "Invalid MType: 0x%02x", mtype);
```
## Building
```bash
# Linux debug build
make platform=linux variant=debug
# Run self-tests
./build-linux-debug/bin/selftest
# Python tests
pytest tests/ -v
# Simulation
python simulation/run_simulation.py
```
## Common Patterns
### Message Encoding
```c
int encode_message(u1_t* buf, size_t bufsize, const message_t* msg) {
if (bufsize < MESSAGE_MIN_SIZE) {
return -1; // Buffer too small
}
int pos = 0;
buf[pos++] = msg->mtype;
write_u4_le(&buf[pos], msg->devaddr);
pos += 4;
/* ... */
return pos; // Return bytes written
}
```
### Message Decoding
```c
int decode_message(message_t* msg, const u1_t* buf, size_t len) {
if (len < MESSAGE_MIN_SIZE) {
return -1; // Message too short
}
int pos = 0;
msg->mtype = buf[pos++];
msg->devaddr = read_u4_le(&buf[pos]);
pos += 4;
/* ... */
return 0; // Success
}
```
### Async Mock Server (Python)
```python
@mock_server.on_message('updf')
async def handle_uplink(msg):
# Process uplink
return {'msgtype': 'dntxed', 'diid': msg['diid']}
```
## File Organization
```
src/ - C implementation (portable)
src-linux/ - Linux-specific C code
include/ - Public headers
python/ - Python package
tests/ - pytest tests
simulation/ - Mock servers and scenarios
proto/ - Protocol buffer definitions
```
## Don't
- Don't use malloc() in embedded-targeted code
- Don't assume host byte order
- Don't use printf() - use LOG() macro
- Don't skip error handling
- Don't merge without tests passing
- Don't hardcode IP addresses or ports (use config)
## Schema Development (Converting Existing Codecs)
See `docs/SCHEMA-DEVELOPMENT-GUIDE.md` for complete guide.
### Required Process
1. **Analyze original codec** - Extract ALL message types and code paths
2. **Generate test vectors** - Run original codec, capture exact output
3. **Write schema incrementally** - Validate after each change
4. **Document deviations** - Note bugs in original, scope limitations
### Minimum Test Vector Coverage
- One test per message type
- Boundary tests: all zeros, all 0xFF
- Edge cases: division by zero, negative values
- Flag combinations
### Tools
```bash
# Extract test vectors from existing JS codec
node tools/analyze_codec.js vendor/codec.js --output vectors.yaml
# Validate schema and test vectors
python3 tools/validate_schema.py schema.yaml
# Compare generated vs original codec
node -e "..." # inline comparison script
```
### Common Issues
- **Rounding**: Use `transform: [{op: round, decimals: 2}]`
- **Booleans**: Schema returns 0/1, JS returns true/false (document)
- **Missing types**: Use `analyze_codec.js` to find ALL message types
## Automation and Context Limits
- For mundane tasks (bulk search/replace, repetitive edits, large scans), prefer build scripts or automation, and explicitly consider context window limits before proceeding
- For wide changes, use repo-wide search tools and document assumptions before editing
- If a task spans many files, work in batches and re-run consistency checks after each batch
- Prefer `rg`/`Glob`-style searches over manual scanning, especially in large repos
- For large files, read only the needed sections (use offsets/limits) to avoid context overload