MC Protocol Serial C++ 0.2.3
MC protocol serial library for MCU-oriented environments
Loading...
Searching...
No Matches
qualified_buffer.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <array>
4#include <cstddef>
5#include <cstdint>
6
11
12namespace mcprotocol::serial {
13
16 G,
17 HG
18};
19
26
28[[nodiscard]] constexpr const char* qualified_buffer_kind_name(
29 QualifiedBufferDeviceKind kind) noexcept {
30 return kind == QualifiedBufferDeviceKind::HG ? "HG" : "G";
31}
32
35 std::uint32_t word_address) noexcept {
36 return word_address * 2U;
37}
38
39namespace detail {
40
41[[nodiscard]] constexpr char ascii_upper(char value) noexcept {
42 return (value >= 'a' && value <= 'z') ? static_cast<char>(value - ('a' - 'A')) : value;
43}
44
45[[nodiscard]] constexpr bool is_separator(char value) noexcept {
46 return value == '\\' || value == '/';
47}
48
49[[nodiscard]] inline bool parse_u32_chars(
51 int base,
52 std::uint32_t& out_value) noexcept {
53 if (text.empty()) {
54 return false;
55 }
56
57 std::uint32_t value = 0U;
58 for (char ch : text) {
59 std::uint32_t digit = 0U;
60 if (ch >= '0' && ch <= '9') {
61 digit = static_cast<std::uint32_t>(ch - '0');
62 } else if (base == 16 && ch >= 'A' && ch <= 'F') {
63 digit = static_cast<std::uint32_t>(ch - 'A' + 10);
64 } else if (base == 16 && ch >= 'a' && ch <= 'f') {
65 digit = static_cast<std::uint32_t>(ch - 'a' + 10);
66 } else {
67 return false;
68 }
69
70 if (digit >= static_cast<std::uint32_t>(base)) {
71 return false;
72 }
73
74 value = static_cast<std::uint32_t>(value * static_cast<std::uint32_t>(base) + digit);
75 }
76
77 out_value = value;
78 return true;
79}
80
81[[nodiscard]] inline bool parse_u32_auto(
83 std::uint32_t& out_value) noexcept {
84 if (text.size() > 2U && text[0] == '0' &&
85 (text[1] == 'x' || text[1] == 'X')) {
86 return parse_u32_chars(text.substr(2U), 16, out_value);
87 }
88 return parse_u32_chars(text, 10, out_value);
89}
90
91} // namespace detail
92
96 QualifiedBufferWordDevice& out_device) noexcept {
97 if (text.size() < 4U || detail::ascii_upper(text.front()) != 'U') {
98 return make_status(
100 "Qualified buffer device must begin with U");
101 }
102
104 for (std::size_t index = 1U; index < text.size(); ++index) {
105 if (detail::is_separator(text[index])) {
106 separator = index;
107 break;
108 }
109 }
110 if (separator == std::string_view::npos || separator <= 1U ||
111 separator >= (text.size() - 1U)) {
112 return make_status(
114 "Qualified buffer device must look like U3E0\\G0 or U3E0\\HG0");
115 }
116
117 std::uint32_t module_number = 0;
118 if (!detail::parse_u32_chars(text.substr(1U, separator - 1U), 16, module_number) ||
119 module_number > 0xFFFFU) {
120 return make_status(
122 "Qualified buffer module number must be a 16-bit hexadecimal value");
123 }
124
125 const std::string_view suffix = text.substr(separator + 1U);
127 std::size_t prefix_length = 0U;
128 if (suffix.size() >= 2U &&
129 detail::ascii_upper(suffix[0]) == 'H' &&
130 detail::ascii_upper(suffix[1]) == 'G') {
132 prefix_length = 2U;
133 } else if (!suffix.empty() && detail::ascii_upper(suffix[0]) == 'G') {
135 prefix_length = 1U;
136 } else {
137 return make_status(
139 "Qualified buffer device suffix must start with G or HG");
140 }
141
142 if (suffix.size() <= prefix_length) {
143 return make_status(
145 "Qualified buffer device must include a word address");
146 }
147
148 std::uint32_t word_address = 0;
149 if (!detail::parse_u32_auto(suffix.substr(prefix_length), word_address)) {
150 return make_status(
152 "Qualified buffer device word address is invalid");
153 }
154
155 out_device = QualifiedBufferWordDevice {
156 .kind = kind,
157 .module_number = static_cast<std::uint16_t>(module_number),
158 .word_address = word_address,
159 };
160 return ok_status();
161}
162
165 const QualifiedBufferWordDevice& device,
166 std::uint16_t word_length,
167 ModuleBufferReadRequest& out_request) noexcept {
168 if (word_length == 0U) {
169 return make_status(
171 "Qualified buffer read length must be at least 1 word");
172 }
173 if (word_length > 960U) {
174 return make_status(
176 "Qualified buffer read length must be in range 1..960 words");
177 }
178
179 out_request = ModuleBufferReadRequest {
181 .bytes = static_cast<std::uint16_t>(word_length * 2U),
182 .module_number = device.module_number,
183 };
184 return ok_status();
185}
186
190 std::span<std::byte> out_bytes,
191 std::size_t& out_size) noexcept {
192 out_size = 0U;
193 if (words.empty()) {
194 return make_status(
196 "Qualified buffer write values must not be empty");
197 }
198 const std::size_t required = words.size() * 2U;
199 if (out_bytes.size() < required) {
200 return make_status(
202 "Qualified buffer byte buffer is too small");
203 }
204
205 for (std::size_t index = 0; index < words.size(); ++index) {
206 const std::uint16_t value = words[index];
207 out_bytes[index * 2U] = static_cast<std::byte>(value & 0xFFU);
208 out_bytes[index * 2U + 1U] = static_cast<std::byte>((value >> 8U) & 0xFFU);
209 }
210 out_size = required;
211 return ok_status();
212}
213
216 const QualifiedBufferWordDevice& device,
218 std::span<std::byte> byte_storage,
219 ModuleBufferWriteRequest& out_request,
220 std::size_t& out_byte_count) noexcept {
221 out_byte_count = 0U;
222 if (words.empty()) {
223 return make_status(
225 "Qualified buffer write values must not be empty");
226 }
227 if (words.size() > 960U) {
228 return make_status(
230 "Qualified buffer write length must be in range 1..960 words");
231 }
232
233 const Status encode_status =
234 encode_qualified_buffer_word_values(words, byte_storage, out_byte_count);
235 if (!encode_status.ok()) {
236 return encode_status;
237 }
238
239 out_request = ModuleBufferWriteRequest {
241 .module_number = device.module_number,
242 .bytes = byte_storage.first(out_byte_count),
243 };
244 return ok_status();
245}
246
250 std::span<std::uint16_t> out_words) noexcept {
251 if ((bytes.size() % 2U) != 0U) {
252 return make_status(
254 "Qualified buffer byte payload must contain an even number of bytes");
255 }
256 const std::size_t word_count = bytes.size() / 2U;
257 if (out_words.size() < word_count) {
258 return make_status(
260 "Qualified buffer word output buffer is too small");
261 }
262
263 for (std::size_t index = 0; index < word_count; ++index) {
264 const auto low = static_cast<std::uint8_t>(bytes[index * 2U]);
265 const auto high = static_cast<std::uint8_t>(bytes[index * 2U + 1U]);
266 out_words[index] = static_cast<std::uint16_t>(low | (high << 8U));
267 }
268 return ok_status();
269}
270
271} // namespace mcprotocol::serial
constexpr size_type size() const noexcept
constexpr span first(size_type count) const noexcept
constexpr string_view substr(size_type pos, size_type count=npos) const noexcept
static constexpr size_type npos
constexpr size_type size() const noexcept
constexpr bool empty() const noexcept
bool parse_u32_chars(std::string_view text, int base, std::uint32_t &out_value) noexcept
constexpr char ascii_upper(char value) noexcept
constexpr bool is_separator(char value) noexcept
bool parse_u32_auto(std::string_view text, std::uint32_t &out_value) noexcept
constexpr Status make_status(StatusCode code, const char *message, std::uint16_t plc_error_code=0) noexcept
Builds a status value with an optional PLC end code.
Definition status.hpp:42
Status parse_qualified_buffer_word_device(std::string_view text, QualifiedBufferWordDevice &out_device) noexcept
Parses a helper qualified device string such as U3E0\\G10 or U3E0\\HG20.
Status encode_qualified_buffer_word_values(std::span< const std::uint16_t > words, std::span< std::byte > out_bytes, std::size_t &out_size) noexcept
Encodes helper qualified word values into little-endian module-buffer bytes.
Status make_qualified_buffer_read_words_request(const QualifiedBufferWordDevice &device, std::uint16_t word_length, ModuleBufferReadRequest &out_request) noexcept
Builds a module-buffer read request for a helper qualified word range.
constexpr Status ok_status() noexcept
Returns the default success status.
Definition status.hpp:37
QualifiedBufferDeviceKind
Qualified buffer-memory family used by helper U... accessors.
constexpr const char * qualified_buffer_kind_name(QualifiedBufferDeviceKind kind) noexcept
Returns "G" or "HG" for the helper device kind.
Status make_qualified_buffer_write_words_request(const QualifiedBufferWordDevice &device, std::span< const std::uint16_t > words, std::span< std::byte > byte_storage, ModuleBufferWriteRequest &out_request, std::size_t &out_byte_count) noexcept
Builds a module-buffer write request for helper qualified word access.
constexpr std::uint32_t qualified_buffer_word_to_byte_address(std::uint32_t word_address) noexcept
Converts a qualified word address to the corresponding module-buffer byte address.
Status decode_qualified_buffer_word_values(std::span< const std::byte > bytes, std::span< std::uint16_t > out_words) noexcept
Decodes little-endian module-buffer bytes into helper qualified word values.
Module-buffer byte read request (0601 helper path).
Definition types.hpp:608
std::uint32_t start_address
Starting module-buffer byte address.
Definition types.hpp:610
Module-buffer byte write request (1601 helper path).
Definition types.hpp:618
std::uint32_t start_address
Starting module-buffer byte address.
Definition types.hpp:620
Parsed U...\\G... or U...\\HG... qualified word device.
Result object returned by most public APIs.
Definition status.hpp:26
constexpr bool ok() const noexcept
Definition status.hpp:31
Public request, response, configuration, and callback types for the serial MC protocol library.