ProtoCore v1.0.16
Deterministic, zero-heap network stack for embedded targets
Loading...
Searching...
No Matches
lsv2.h
Go to the documentation of this file.
1// ProtoCore v1.0.16 - Copyright (C) 2026 Douglas Quigg (dstroy0) <dquigg123@gmail.com>
2// SPDX-License-Identifier: AGPL-3.0-or-later
3
4/**
5 * @file lsv2.h
6 * @brief Heidenhain LSV/2 telegram codec (PROTOCORE_ENABLE_LSV2) - a zero-heap codec for the LSV/2 protocol
7 * Heidenhain TNC controls (iTNC 530, TNC 320/620/640, ...) speak for DNC and data access over a
8 * serial link or, as implemented here, LSV/2-over-TCP (default port 19000). A CNC-native
9 * southbound source for the common European control, alongside `services/focas` (Fanuc) and
10 * `services/haas_mdc` (Haas).
11 *
12 * Wire framing (byte-exact, both directions): a telegram is a 4-byte big-endian payload-length prefix,
13 * then a 4-character ASCII command / response mnemonic, then `payload-length` payload bytes. The length
14 * counts ONLY the payload - the mnemonic is not included - so a telegram with no payload is exactly 8
15 * bytes on the wire (e.g. a bare `T_OK` acknowledgement is `00 00 00 00 'T' '_' 'O' 'K'`). The command
16 * mnemonics group into login / logout (`A_LG` / `A_LO`), file system (`C_*` write, `R_*` read), and
17 * run / status inspection (`R_RI` / `R_ST` / `R_VR`); the control answers with a response mnemonic
18 * (`T_OK` done, `T_FD` file-data finished, `S_*` a data reply, or `T_ER` / `T_BD` a two-byte
19 * error-class + error-code).
20 *
21 * This codec owns the framing and the request builders: @ref protocore_lsv2_build frames an arbitrary
22 * mnemonic + payload, @ref protocore_lsv2_parse slices one complete telegram off a byte stream (reporting how
23 * many bytes it consumed so a caller can re-frame the rest), and the typed helpers build the common
24 * requests (@ref protocore_lsv2_build_login, @ref protocore_lsv2_build_filename, @ref protocore_lsv2_build_run_info)
25 * and read the response (@ref protocore_lsv2_is_ok, @ref protocore_lsv2_error). The TCP / serial link to the control is
26 * the application's.
27 *
28 * Reference: the LSV/2 telegram framing + the command / response mnemonic set, cross-checked
29 * byte-for-byte against the `pyLSV2` reference implementation (drunsinn/pyLSV2, the widely-used open
30 * Python LSV/2 client) - the `!L` big-endian length prefix, the 4-character mnemonic, and the `!H`
31 * big-endian run-info selector.
32 *
33 * @author Douglas Quigg (dstroy0)
34 * @date 2026
35 */
36
37#ifndef PROTOCORE_LSV2_H
38#define PROTOCORE_LSV2_H
39
40#include "protocore_config.h" // the entry point: protocore_types.h for the widths
41
42#if PROTOCORE_ENABLE_LSV2
43
45
46/** @brief Default LSV/2-over-TCP port a Heidenhain control listens on. */
47#define PROTOCORE_LSV2_TCP_PORT 19000
48/** @brief Every LSV/2 command / response mnemonic is exactly four ASCII characters. */
49#define PROTOCORE_LSV2_MNEMONIC_LEN 4
50/** @brief Fixed telegram header: a 4-byte length prefix + the 4-byte mnemonic (payload follows). */
51#define PROTOCORE_LSV2_HEADER_LEN 8
52
53// ── command mnemonics (CP -> control) ──────────────────────────────────────────────────────────
54#define PROTOCORE_LSV2_CMD_LOGIN "A_LG" ///< gain access to a privilege group (payload: login[+password])
55#define PROTOCORE_LSV2_CMD_LOGOUT "A_LO" ///< drop a privilege group (payload: optional login)
56#define PROTOCORE_LSV2_CMD_FILE_LOAD "R_FL" ///< read a file from the control (payload: filename)
57#define PROTOCORE_LSV2_CMD_FILE_SEND "C_FL" ///< send a file to the control (payload: filename)
58#define PROTOCORE_LSV2_CMD_FILE_DELETE "C_FD" ///< delete a file (payload: filename)
59#define PROTOCORE_LSV2_CMD_FILE_INFO "R_FI" ///< read info about a file (payload: filename)
60#define PROTOCORE_LSV2_CMD_DIR_CHANGE "C_DC" ///< change the working directory (payload: path)
61#define PROTOCORE_LSV2_CMD_DIR_MAKE "C_DM" ///< create a directory (payload: path)
62#define PROTOCORE_LSV2_CMD_DIR_DELETE "C_DD" ///< delete a directory (payload: path)
63#define PROTOCORE_LSV2_CMD_DIR_READ "R_DR" ///< read directory contents
64#define PROTOCORE_LSV2_CMD_DIR_INFO "R_DI" ///< read info about the selected directory
65#define PROTOCORE_LSV2_CMD_RUN_INFO "R_RI" ///< read run info (payload: 2-byte big-endian selector)
66#define PROTOCORE_LSV2_CMD_STATUS "R_ST" ///< request remote status
67#define PROTOCORE_LSV2_CMD_VERSION "R_VR" ///< read control version / identity info
68#define PROTOCORE_LSV2_CMD_PARAM "R_PR" ///< read a parameter from the control
69
70// ── response mnemonics (control -> CP) ─────────────────────────────────────────────────────────
71#define PROTOCORE_LSV2_RSP_OK "T_OK" ///< last transaction completed
72#define PROTOCORE_LSV2_RSP_ERROR "T_ER" ///< transaction error (payload: error-class, error-code)
73#define PROTOCORE_LSV2_RSP_FIN "T_FD" ///< all file data sent, transfer finished
74#define PROTOCORE_LSV2_RSP_XFER_ERR "T_BD" ///< file-transfer error (payload: error-class, error-code)
75#define PROTOCORE_LSV2_RSP_LONG "M_CC" ///< a long-running operation completed
76#define PROTOCORE_LSV2_RSP_RUN_INFO "S_RI" ///< run-info data reply
77#define PROTOCORE_LSV2_RSP_STATUS "S_ST" ///< status data reply
78#define PROTOCORE_LSV2_RSP_VERSION "S_VR" ///< version data reply
79#define PROTOCORE_LSV2_RSP_FILE "S_FL" ///< file-content data reply
80#define PROTOCORE_LSV2_RSP_DIR "S_DR" ///< directory-content data reply
81
82// ── login privilege groups (payload for A_LG) ──────────────────────────────────────────────────
83#define PROTOCORE_LSV2_LOGIN_INSPECT "INSPECT" ///< read machine state / parameters
84#define PROTOCORE_LSV2_LOGIN_FILE "FILE" ///< file-system access
85#define PROTOCORE_LSV2_LOGIN_DNC "DNC" ///< DNC functions
86#define PROTOCORE_LSV2_LOGIN_MONITOR "MONITOR" ///< screen / keypad monitoring
87#define PROTOCORE_LSV2_LOGIN_DIAG "DIAGNOSTICS" ///< diagnostics
88#define PROTOCORE_LSV2_LOGIN_PLCDEBUG "PLCDEBUG" ///< PLC debug
89
90/** @brief Run-info selectors for @ref protocore_lsv2_build_run_info (the 2-byte `R_RI` argument). */
91enum Lsv2RunInfo : uint16_t // NOSONAR(cpp:S3642): unscoped so a selector converts to the 2-byte R_RI
92 // argument without a cast; no other enum in this header shares its value space
93{
94 LSV2_RI_EXEC_STATE = 23, ///< program execution state (running / stopped / ...)
95 LSV2_RI_SELECTED_PGM = 24, ///< currently selected program
96 LSV2_RI_OVERRIDE = 25, ///< feed / rapid / spindle override values
97 LSV2_RI_PGM_STATE = 26, ///< program status
98};
99
100/** @brief One parsed telegram: the 4-char mnemonic (NOT null-terminated) plus the payload slice, which
101 * points INTO the caller's buffer (zero-copy; the buffer must outlive the struct). */
102typedef struct
103{
104 char mnemonic[PROTOCORE_LSV2_MNEMONIC_LEN];
105 const uint8_t *payload;
106 size_t payload_len;
107} Lsv2Telegram;
108
109/**
110 * @brief Frame a telegram: a 4-byte big-endian @p payload_len, the 4-character @p mnemonic, then the
111 * payload. @p mnemonic must point at (at least) four characters; exactly four are copied.
112 * @return total bytes written (`PROTOCORE_LSV2_HEADER_LEN + payload_len`), or 0 on overflow / bad input.
113 */
114size_t protocore_lsv2_build(uint8_t *buf, size_t cap, const char *mnemonic, const uint8_t *payload, size_t payload_len);
115
116/**
117 * @brief Slice one complete telegram off @p buf: read the big-endian length prefix, and if the whole
118 * telegram (header + payload) is present, fill @p out and report the byte count via @p consumed
119 * (so the caller can advance past it and re-parse the remainder).
120 * @return true if a complete telegram was found; false if fewer than `8 + payload_len` bytes are
121 * available yet (the caller should accumulate more).
122 */
123proto_bool protocore_lsv2_parse(const uint8_t *buf, size_t len, Lsv2Telegram *out, size_t *consumed);
124
125/**
126 * @brief True if the parsed telegram's mnemonic equals the four characters at @p mnemonic4.
127 */
128proto_bool protocore_lsv2_is(const Lsv2Telegram *t, const char *mnemonic4);
129
130/**
131 * @brief Build a login telegram (`A_LG`): payload is the NUL-terminated privilege group (one of the
132 * `PROTOCORE_LSV2_LOGIN_*` strings), optionally followed by a NUL-terminated @p password.
133 * @param password pass nullptr for a group that needs no password.
134 * @return total bytes written, or 0 on overflow / bad input.
135 */
136size_t protocore_lsv2_build_login(uint8_t *buf, size_t cap, const char *login, const char *password);
137
138/**
139 * @brief Build a logout telegram (`A_LO`): payload is the NUL-terminated privilege group to drop, or
140 * empty when @p login is nullptr (log out of everything).
141 * @return total bytes written, or 0 on overflow / bad input.
142 */
143size_t protocore_lsv2_build_logout(uint8_t *buf, size_t cap, const char *login);
144
145/**
146 * @brief Build a filename-argument command: the 4-char @p mnemonic (`R_FL` / `C_FL` / `C_FD` / `C_DC` /
147 * `C_DM` / `C_DD` / `R_FI`) followed by the NUL-terminated @p filename as the payload.
148 * @return total bytes written, or 0 on overflow / bad input.
149 */
150size_t protocore_lsv2_build_filename(uint8_t *buf, size_t cap, const char *mnemonic, const char *filename);
151
152/**
153 * @brief Build a run-info request (`R_RI`) with @p info_code (an @ref Lsv2RunInfo selector) as a 2-byte
154 * big-endian payload.
155 * @return total bytes written (always `PROTOCORE_LSV2_HEADER_LEN + 2` on success), or 0 on overflow.
156 */
157size_t protocore_lsv2_build_run_info(uint8_t *buf, size_t cap, uint16_t info_code);
158
159/**
160 * @brief True if the response is `T_OK` (transaction completed).
161 */
162proto_bool protocore_lsv2_is_ok(const Lsv2Telegram *t);
163
164/**
165 * @brief True if the response is an error mnemonic (`T_ER` transaction error or `T_BD` transfer error).
166 */
167proto_bool protocore_lsv2_is_error(const Lsv2Telegram *t);
168
169/**
170 * @brief Decode an error response's two-byte payload into @p err_class and @p err_code.
171 * @return true on an error mnemonic (`T_ER` / `T_BD`) carrying exactly two payload bytes.
172 */
173proto_bool protocore_lsv2_error(const Lsv2Telegram *t, uint8_t *err_class, uint8_t *err_code);
174
176
177#endif // PROTOCORE_ENABLE_LSV2
178
179#endif // PROTOCORE_LSV2_H
#define PROTOCORE_BEGIN_DECLS
Give a header's declarations C linkage, so their symbol names carry no parameter types.
Definition types.h:96
_Bool proto_bool
The truth value.
Definition types.h:64
#define PROTOCORE_END_DECLS
Definition types.h:97