ProtoCore v0.0.2
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
fanuc_j519.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file fanuc_j519.cpp
6 * @brief FANUC Stream Motion (J519) UDP codec - builders + parsers (see fanuc_j519.h).
7 *
8 * Straight-line field packing at fixed offsets: every packet is a constant size, so each builder
9 * checks @p cap once up front and each parser requires the exact length. All integers little-endian
10 * through shared_primitives/endian.h; floats are IEEE-754 binary32 moved through a uint32_t with
11 * memcpy (no type punning, no strict-aliasing UB) - the same approach as the OPC UA Variant codec.
12 */
13
15
16#if PC_ENABLE_FANUC_J519
17
19#include <string.h> // memcpy (string.h is allowed; the no-stdlib rule is about stdlib.h/malloc)
20
21namespace
22{
23// --- float <-> little-endian binary32 --------------------------------------------------------
24size_t wr_f32le(uint8_t *p, float v)
25{
26 uint32_t u;
27 memcpy(&u, &v, 4);
28 return pc_wr32le(p, u);
29}
30float rd_f32le(const uint8_t *p)
31{
32 uint32_t u = pc_rd32le(p);
33 float v;
34 memcpy(&v, &u, 4);
35 return v;
36}
37
38// Pack/unpack a run of @p n consecutive binary32 at @p p.
39void wr_f32_block(uint8_t *p, const float *src, size_t n)
40{
41 for (size_t k = 0; k < n; k++)
42 {
43 wr_f32le(p + 4 * k, src[k]);
44 }
45}
46void rd_f32_block(const uint8_t *p, float *dst, size_t n)
47{
48 for (size_t k = 0; k < n; k++)
49 {
50 dst[k] = rd_f32le(p + 4 * k);
51 }
52}
53
54// Every packet opens with { type, version } - written once here so no builder repeats it.
55void wr_header(uint8_t *buf, J519Type type, uint32_t version_no)
56{
57 pc_wr32le(buf + 0, (uint32_t)type);
58 pc_wr32le(buf + 4, version_no);
59}
60
61// A parser accepts only its own exact length and type code (the type space is shared per direction,
62// so length is what actually separates Start from Status and Request from Ack).
63bool hdr_ok(const uint8_t *buf, size_t len, size_t want_len, J519Type want_type)
64{
65 return buf && len == want_len && pc_rd32le(buf + 0) == (uint32_t)want_type;
66}
67} // namespace
68
69// --- header ---------------------------------------------------------------------------------------
70
71bool pc_j519_peek(const uint8_t *buf, size_t len, uint32_t *type, uint32_t *version_no)
72{
73 if (!buf || len < 8)
74 {
75 return false;
76 }
77 if (type)
78 {
79 *type = pc_rd32le(buf + 0);
80 }
81 if (version_no)
82 {
83 *version_no = pc_rd32le(buf + 4);
84 }
85 return true;
86}
87
88// --- PC -> robot: build ---------------------------------------------------------------------------
89
90size_t pc_j519_build_start(uint8_t *buf, size_t cap, uint32_t version_no)
91{
92 if (!buf || cap < PC_J519_LEN_START)
93 {
94 return 0;
95 }
96 wr_header(buf, J519Type::J519_START_OR_STATUS, version_no);
97 return PC_J519_LEN_START;
98}
99
100size_t pc_j519_build_stop(uint8_t *buf, size_t cap, uint32_t version_no)
101{
102 if (!buf || cap < PC_J519_LEN_STOP)
103 {
104 return 0;
105 }
106 wr_header(buf, J519Type::J519_STOP, version_no);
107 return PC_J519_LEN_STOP;
108}
109
110size_t pc_j519_build_motion(uint8_t *buf, size_t cap, const J519MotionCommand *cmd)
111{
112 if (!buf || !cmd || cap < PC_J519_LEN_MOTION)
113 {
114 return 0;
115 }
116 wr_header(buf, J519Type::J519_MOTION, cmd->version_no);
117 pc_wr32le(buf + 8, cmd->sequence_no);
118 buf[12] = cmd->last_data;
119 buf[13] = cmd->read_io_type;
120 pc_wr16le(buf + 14, cmd->read_io_index);
121 pc_wr16le(buf + 16, cmd->read_io_mask);
122 buf[18] = cmd->data_style;
123 buf[19] = cmd->write_io_type;
124 pc_wr16le(buf + 20, cmd->write_io_index);
125 pc_wr16le(buf + 22, cmd->write_io_mask);
126 pc_wr16le(buf + 24, cmd->write_io_value);
127 pc_wr16le(buf + 26, 0); // unused
128 wr_f32_block(buf + 28, cmd->joint_data, PC_J519_AXES);
129 return PC_J519_LEN_MOTION;
130}
131
132size_t pc_j519_build_request(uint8_t *buf, size_t cap, const J519Request *req)
133{
134 if (!buf || !req || cap < PC_J519_LEN_REQUEST)
135 {
136 return 0;
137 }
138 wr_header(buf, J519Type::J519_REQUEST_OR_ACK, req->version_no);
139 pc_wr32le(buf + 8, req->axis_no);
140 pc_wr32le(buf + 12, req->threshold_type);
141 return PC_J519_LEN_REQUEST;
142}
143
144// --- PC -> robot: parse ---------------------------------------------------------------------------
145
146bool pc_j519_parse_motion(const uint8_t *buf, size_t len, J519MotionCommand *out)
147{
148 if (!out || !hdr_ok(buf, len, PC_J519_LEN_MOTION, J519Type::J519_MOTION))
149 {
150 return false;
151 }
152 out->version_no = pc_rd32le(buf + 4);
153 out->sequence_no = pc_rd32le(buf + 8);
154 out->last_data = buf[12];
155 out->read_io_type = buf[13];
156 out->read_io_index = pc_rd16le(buf + 14);
157 out->read_io_mask = pc_rd16le(buf + 16);
158 out->data_style = buf[18];
159 out->write_io_type = buf[19];
160 out->write_io_index = pc_rd16le(buf + 20);
161 out->write_io_mask = pc_rd16le(buf + 22);
162 out->write_io_value = pc_rd16le(buf + 24);
163 // octets 26..27 are unused padding - not surfaced
164 rd_f32_block(buf + 28, out->joint_data, PC_J519_AXES);
165 return true;
166}
167
168bool pc_j519_parse_request(const uint8_t *buf, size_t len, J519Request *out)
169{
170 if (!out || !hdr_ok(buf, len, PC_J519_LEN_REQUEST, J519Type::J519_REQUEST_OR_ACK))
171 {
172 return false;
173 }
174 out->version_no = pc_rd32le(buf + 4);
175 out->axis_no = pc_rd32le(buf + 8);
176 out->threshold_type = pc_rd32le(buf + 12);
177 return true;
178}
179
180// --- robot -> PC: build ---------------------------------------------------------------------------
181
182size_t pc_j519_build_status(uint8_t *buf, size_t cap, const J519RobotStatus *st)
183{
184 if (!buf || !st || cap < PC_J519_LEN_STATUS)
185 {
186 return 0;
187 }
188 wr_header(buf, J519Type::J519_START_OR_STATUS, st->version_no);
189 pc_wr32le(buf + 8, st->sequence_no);
190 buf[12] = st->status;
191 buf[13] = st->read_io_type;
192 pc_wr16le(buf + 14, st->read_io_index);
193 pc_wr16le(buf + 16, st->read_io_mask);
194 pc_wr16le(buf + 18, st->read_io_value);
195 pc_wr32le(buf + 20, st->time_stamp);
196 wr_f32_block(buf + 24, st->cartesian_pose, PC_J519_AXES);
197 wr_f32_block(buf + 60, st->joint_pose, PC_J519_AXES);
198 wr_f32_block(buf + 96, st->motor_current, PC_J519_AXES);
199 return PC_J519_LEN_STATUS;
200}
201
202size_t pc_j519_build_ack(uint8_t *buf, size_t cap, const J519Ack *ack)
203{
204 if (!buf || !ack || cap < PC_J519_LEN_ACK)
205 {
206 return 0;
207 }
208 wr_header(buf, J519Type::J519_REQUEST_OR_ACK, ack->version_no);
209 pc_wr32le(buf + 8, ack->axis_no);
210 pc_wr32le(buf + 12, ack->threshold_type);
211 pc_wr32le(buf + 16, ack->max_cart_speed);
212 pc_wr32le(buf + 20, ack->unknown0);
213 wr_f32_block(buf + 24, ack->threshold_no_load, PC_J519_THRESHOLDS);
214 wr_f32_block(buf + 104, ack->threshold_max_load, PC_J519_THRESHOLDS);
215 return PC_J519_LEN_ACK;
216}
217
218// --- robot -> PC: parse ---------------------------------------------------------------------------
219
220bool pc_j519_parse_status(const uint8_t *buf, size_t len, J519RobotStatus *out)
221{
222 if (!out || !hdr_ok(buf, len, PC_J519_LEN_STATUS, J519Type::J519_START_OR_STATUS))
223 {
224 return false;
225 }
226 out->version_no = pc_rd32le(buf + 4);
227 out->sequence_no = pc_rd32le(buf + 8);
228 out->status = buf[12];
229 out->read_io_type = buf[13];
230 out->read_io_index = pc_rd16le(buf + 14);
231 out->read_io_mask = pc_rd16le(buf + 16);
232 out->read_io_value = pc_rd16le(buf + 18);
233 out->time_stamp = pc_rd32le(buf + 20);
234 rd_f32_block(buf + 24, out->cartesian_pose, PC_J519_AXES);
235 rd_f32_block(buf + 60, out->joint_pose, PC_J519_AXES);
236 rd_f32_block(buf + 96, out->motor_current, PC_J519_AXES);
237 return true;
238}
239
240bool pc_j519_parse_ack(const uint8_t *buf, size_t len, J519Ack *out)
241{
242 if (!out || !hdr_ok(buf, len, PC_J519_LEN_ACK, J519Type::J519_REQUEST_OR_ACK))
243 {
244 return false;
245 }
246 out->version_no = pc_rd32le(buf + 4);
247 out->axis_no = pc_rd32le(buf + 8);
248 out->threshold_type = pc_rd32le(buf + 12);
249 out->max_cart_speed = pc_rd32le(buf + 16);
250 out->unknown0 = pc_rd32le(buf + 20);
251 rd_f32_block(buf + 24, out->threshold_no_load, PC_J519_THRESHOLDS);
252 rd_f32_block(buf + 104, out->threshold_max_load, PC_J519_THRESHOLDS);
253 return true;
254}
255
256#endif // PC_ENABLE_FANUC_J519
Fixed-width integer serializers into a raw uint8_t* buffer - one source of truth.
uint32_t pc_rd32le(const uint8_t *p)
Read a little-endian u32 at p.
Definition endian.h:66
size_t pc_wr32le(uint8_t *p, uint32_t v)
Write v little-endian at p.
Definition endian.h:40
uint16_t pc_rd16le(const uint8_t *p)
Read a little-endian u16 at p.
Definition endian.h:60
size_t pc_wr16le(uint8_t *p, uint16_t v)
Write v little-endian at p.
Definition endian.h:32
FANUC Stream Motion (option J519) UDP codec (PC_ENABLE_FANUC_J519) - the robot counterpart to the shi...