REDAC HybridController
Firmware for LUCIDAC/REDAC Teensy
Loading...
Searching...
No Matches
ublock.cpp
Go to the documentation of this file.
1// Copyright (c) 2024 anabrid GmbH
2// Contact: https://www.anabrid.com/licensing/
3// SPDX-License-Identifier: MIT OR GPL-2.0-or-later
4
5#include <algorithm>
6
7#include <block/ublock.h>
8#include <utils/logging.h>
9
10FLASHMEM void utils::shift_5_left(uint8_t *buffer, size_t size) {
11 for (size_t idx = 0; idx < size - 1; idx++) {
12 auto next = buffer[idx + 1];
13 // Shift by 5, leaving zeros at 0-4
14 buffer[idx] <<= 5;
15 buffer[idx] |= (next >> 3) & 0x1F;
16 }
17 buffer[size - 1] <<= 5;
18}
19
20FLASHMEM blocks::UBlock::UBlock(UBlockHAL *hardware)
21 : FunctionBlock("U", hardware), hardware(hardware), output_input_map{} {
22 classifier.class_enum = CLASS_;
23 reset_connections();
24}
25
26FLASHMEM metadata::eui_t blocks::UBlock::get_entity_eui() const {
27 if (hardware)
28 return hardware->get_entity_eui();
29 return {};
30}
31
32FLASHMEM bool blocks::UBlock::_i_sanity_check(const uint8_t input) { return input < NUM_OF_INPUTS; }
33
34FLASHMEM bool blocks::UBlock::_o_sanity_check(const uint8_t output) { return output < NUM_OF_OUTPUTS; }
35
36FLASHMEM bool blocks::UBlock::_io_sanity_check(const uint8_t input, const uint8_t output) {
37 return _i_sanity_check(input) && _o_sanity_check(output);
38}
39
40FLASHMEM void blocks::UBlock::_connect(const uint8_t input, const uint8_t output) {
41 output_input_map[output] = input;
42}
43
44FLASHMEM bool blocks::UBlock::connect(const uint8_t input, const uint8_t output, bool force) {
45 if (!_io_sanity_check(input, output))
46 return false;
47
48 // Check for other connections on the same output, unless we don't care if we overwrite them
49 if (!force and _is_output_connected(output))
50 return false;
51
52 // Check if the transmission mode makes the specified connection impossible and if the mode can or should be
53 // chnged
54 if (a_side_mode != Transmission_Mode::ANALOG_INPUT &&
55 ((output < 16 && input != 15) || (output >= 16 && input != 14))) {
56 if (!force)
57 if (is_input_connected(input))
58 return false;
59 change_a_side_transmission_mode(Transmission_Mode::ANALOG_INPUT);
60 }
61
62 if (b_side_mode != Transmission_Mode::ANALOG_INPUT &&
63 ((output < 16 && input == 15) || (output >= 16 && input == 14))) {
64 if (!force)
65 if (is_input_connected(input))
66 return false;
67 change_b_side_transmission_mode(Transmission_Mode::ANALOG_INPUT);
68 }
69
70 _connect(input, output);
71 return true;
72}
73
74FLASHMEM bool blocks::UBlock::connect_alternative(Transmission_Mode signal_mode, const uint8_t output,
75 bool force, bool use_a_side) {
76 if (!_o_sanity_check(output))
77 return false;
78
79 // Do not consider Transmission_Mode::ANALOG_INPUT a valid mode
80 if (signal_mode == Transmission_Mode::ANALOG_INPUT)
81 return false;
82
83 // When we want to connect a reference signal, we expect the reference magnitude to be one
84 if ((signal_mode == Transmission_Mode::POS_REF or signal_mode == Transmission_Mode::NEG_REF) and
85 ref_magnitude != Reference_Magnitude::ONE and !force)
86 return false;
87
88 // Check for other connections on the same output, unless we don't care if we overwrite them
89 if (!force and _is_output_connected(output))
90 return false;
91
92 // Check if the transmission mode makes the specified connection impossible and if the mode can or should be
93 // chnged
94 if (a_side_mode != signal_mode && use_a_side) {
95 // TODO: force for a-side could be added, but since there wont be much use for an a-side
96 // alternative signal, you have to use force or set the correct signal mode
97 if (!force)
98 return false;
99 change_a_side_transmission_mode(signal_mode);
100 }
101
102 if (b_side_mode != signal_mode && !use_a_side) {
103 if (!force)
104 if (is_input_connected(14) || is_input_connected(15))
105 return false;
106 change_b_side_transmission_mode(signal_mode);
108 }
109
110 // Make a one to one connection if possible. Uses Input zero as fallback
111 uint8_t input;
112 if (use_a_side) {
113 if (output < 15)
114 input = output;
115 else if (output == 15)
116 input = 0;
117 else if (output == 30)
118 input = 0;
119 else
120 input = output - 16;
121 } else {
122 if (output < 16)
123 input = 15;
124 else
125 input = 14;
126 }
127
128 _connect(input, output);
129 return true;
130}
131
132FLASHMEM void blocks::UBlock::_disconnect(const uint8_t output) { output_input_map[output] = -1; }
133
134FLASHMEM bool blocks::UBlock::disconnect(const uint8_t input, const uint8_t output) {
135 if (!_io_sanity_check(input, output))
136 return false;
137
138 if (_is_connected(input, output)) {
139 _disconnect(output);
140 return true;
141 } else {
142 return false;
143 }
144}
145
146FLASHMEM bool blocks::UBlock::disconnect(const uint8_t output) {
147 if (!_o_sanity_check(output))
148 return false;
149 _disconnect(output);
150 return true;
151}
152
153FLASHMEM bool blocks::UBlock::_is_connected(const uint8_t input, const uint8_t output) const {
154 return output_input_map[output] == input;
155}
156
157FLASHMEM bool blocks::UBlock::is_connected(const uint8_t input, const uint8_t output) const {
158 if (!_io_sanity_check(input, output))
159 return false;
160
161 return _is_connected(input, output);
162}
163
164FLASHMEM bool blocks::UBlock::_is_output_connected(const uint8_t output) const {
165 return output_input_map[output] >= 0;
166}
167
168FLASHMEM bool blocks::UBlock::is_output_connected(const uint8_t output) const {
169 if (!_o_sanity_check(output))
170 return false;
171
172 return _is_output_connected(output);
173}
174
175FLASHMEM bool blocks::UBlock::_is_input_connected(const uint8_t input) const {
176 for (const auto &output : output_input_map)
177 if (output == input)
178 return true;
179 return false;
180}
181
182FLASHMEM bool blocks::UBlock::is_input_connected(const uint8_t input) const {
183 if (!_i_sanity_check(input))
184 return false;
185 return _is_input_connected(input);
186}
187
188FLASHMEM bool blocks::UBlock::is_anything_connected() const {
189 for (auto output : OUTPUT_IDX_RANGE())
190 if (_is_output_connected(output))
191 return true;
192 return false;
193}
194
195FLASHMEM void blocks::UBlock::change_a_side_transmission_mode(const Transmission_Mode mode) {
196 a_side_mode = mode;
197}
198
199FLASHMEM void blocks::UBlock::change_b_side_transmission_mode(const Transmission_Mode mode) {
200 b_side_mode = mode;
201}
202
203FLASHMEM void blocks::UBlock::change_all_transmission_modes(const Transmission_Mode mode) {
204 change_a_side_transmission_mode(mode);
205 change_b_side_transmission_mode(mode);
206}
207
208FLASHMEM void
209blocks::UBlock::change_all_transmission_modes(const std::pair<Transmission_Mode, Transmission_Mode> modes) {
210 change_a_side_transmission_mode(modes.first);
211 change_b_side_transmission_mode(modes.second);
212}
213
214FLASHMEM std::pair<blocks::UBlock::Transmission_Mode, blocks::UBlock::Transmission_Mode>
215blocks::UBlock::get_all_transmission_modes() const {
216 return std::make_pair(a_side_mode, b_side_mode);
217}
218
219FLASHMEM blocks::UBlock::Reference_Magnitude blocks::UBlock::get_reference_magnitude() {
220 return ref_magnitude;
221}
222
223FLASHMEM void blocks::UBlock::change_reference_magnitude(blocks::UBlock::Reference_Magnitude ref) {
224 ref_magnitude = ref;
225}
226
227FLASHMEM utils::status blocks::UBlock::write_to_hardware() {
228 if (!hardware->write_outputs(output_input_map) or
229 !hardware->write_transmission_modes_and_ref({a_side_mode, b_side_mode}, ref_magnitude)) {
230 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
231 return utils::status::failure();
232 }
233 return utils::status::success();
234}
235
236FLASHMEM void blocks::UBlock::reset_connections() {
237 std::fill(begin(output_input_map), end(output_input_map), -1);
238}
239
240FLASHMEM void blocks::UBlock::reset(entities::ResetAction action) {
241 FunctionBlock::reset(action);
242
243 if (action.has(entities::ResetAction::CIRCUIT_RESET)) {
244 change_all_transmission_modes(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
245 reset_connections();
246 reset_reference_magnitude();
247 }
248}
249
250FLASHMEM
251utils::status blocks::UBlock::config_self_from_json(JsonObjectConst cfg) {
252#ifdef ANABRID_DEBUG_ENTITY_CONFIG
253 Serial.println(__PRETTY_FUNCTION__);
254#endif
255 for (auto cfgItr = cfg.begin(); cfgItr != cfg.end(); ++cfgItr) {
256 if (cfgItr->key() == "outputs") {
257 auto ret = _config_outputs_from_json(cfgItr->value());
258 if (!ret)
259 return ret;
260 } else if (cfgItr->key() == "constant") {
261 auto ret = _config_constants_from_json(cfgItr->value());
262 if (!ret)
263 return ret;
264 } else {
265 return utils::status::failure();
266
267 return utils::status("UBlock: Unknown configuration key");
268 }
269 }
270 return utils::status::success();
271}
272
273FLASHMEM
274utils::status blocks::UBlock::_config_outputs_from_json(const JsonVariantConst &cfg) {
275 // Handle an array of outputs
276 // In this case, all outputs are reset
277 if (cfg.is<JsonArrayConst>()) {
278 auto outputs = cfg.as<JsonArrayConst>();
279 if (outputs.size() != NUM_OF_OUTPUTS)
280 return utils::status("Nblock: Wrong number of configuration elements. Given %d, expected %d",
281 outputs.size(), NUM_OF_OUTPUTS);
282 reset_connections();
283 uint8_t idx = 0;
284 for (JsonVariantConst input : outputs) {
285 if (input.isNull()) {
286 idx++;
287 continue;
288 } else if (!input.is<uint8_t>()) {
289 return utils::status("UBlock: Input is not an integer");
290 }
291 if (!connect(input.as<uint8_t>(), idx++))
292 return utils::status("UBlock: Cannot connect input at idx");
293 }
294 return utils::status::success();
295 }
296 // Handle a mapping of outputs
297 // In this case, only explicitly passed outputs are changed
298 // To allow reconnections (swapping), we first clear all outputs and set them afterwards.
299 else if (cfg.is<JsonObjectConst>()) {
300 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
301 // Sanity check that any input passed is a number
302 if (!(keyval.value().is<uint8_t>() or keyval.value().isNull()))
303 return utils::status("UBlock: Given input is not a number '%s'", keyval.value().as<const char *>());
304 // TODO: Check conversion from string to number
305 auto output = std::stoul(keyval.key().c_str());
306 !disconnect(output);
307 }
308 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
309 // TODO: Check conversion from string to number
310 auto output = std::stoul(keyval.key().c_str());
311 // Type is checked above
312 if (keyval.value().isNull())
313 continue;
314 auto input = keyval.value().as<uint8_t>();
315 if (!connect(input, output))
316 return utils::status("UBlock: Could not connect %d -> %d", input, output);
317 }
318 return utils::status::success();
319 }
320 return utils::status("UBlock either requires array or object configuration");
321}
322
323FLASHMEM
324utils::status blocks::UBlock::_config_constants_from_json(const JsonVariantConst &cfg) {
325 float constant = cfg.as<float>();
326 if (cfg.is<bool>())
327 constant = cfg.as<bool>() ? 1 : 0;
328 if (cfg.isNull())
329 constant = 0;
330 if (constant == 0) {
331 // Turn OFF constant usage. We don't use Transmission_Mode::GROUND because
332 // that's really pointless, we then also can just not connect the relevant UBlock switch.
333 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
334 reset_reference_magnitude();
335 utils::status::success();
336 }
337 if (constant > 0)
338 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::POS_REF);
339 if (constant < 0)
340 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::NEG_REF);
341 if (abs(constant) == 0.1f)
342 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE_TENTH);
343 else if (abs(constant) == 1.0f)
344 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE);
345 else
346 return utils::status("UBlock::config_self_from_json: Pass +-0.1 or +-1.");
347 return utils::status::success();
348}
349
350FLASHMEM void blocks::UBlock::config_self_to_json(JsonObject &cfg) {
351 Entity::config_self_to_json(cfg);
352 // Save outputs into cfg
353 auto outputs_cfg = cfg.createNestedArray("outputs");
354 for (const auto &output : output_input_map) {
355 if (output >= 0)
356 outputs_cfg.add(output);
357 else
358 outputs_cfg.add(nullptr);
359 }
360 // Constant setting to JSON
361 if (b_side_mode == blocks::UBlock::Transmission_Mode::ANALOG_INPUT) {
362 cfg["constant"] = false;
363 } else {
364 float val = -123;
365 // we support here much more then at the setter side, because the firmware
366 // side has so much more states that simply have no representation at client side.
367 switch (b_side_mode) {
368 case blocks::UBlock::Transmission_Mode::POS_REF:
369 val = +1.0;
370 break;
371 case blocks::UBlock::Transmission_Mode::NEG_REF:
372 val = -1.0;
373 break;
374 case blocks::UBlock::Transmission_Mode::GROUND:
375 val = 0;
376 break;
377 default:
378 break;
379 }
380 if (ref_magnitude == blocks::UBlock::Reference_Magnitude::ONE_TENTH) {
381 val *= 0.1;
382 }
383 cfg["constant"] = val;
384 }
385}
386
387FLASHMEM void blocks::UBlock::reset_reference_magnitude() { ref_magnitude = Reference_Magnitude::ONE; }
388
389FLASHMEM bool blocks::UBlockHAL_Dummy::write_outputs(std::array<int8_t, 32> outputs) { return true; }
390
391FLASHMEM bool blocks::UBlockHAL_Dummy::write_transmission_modes_and_ref(
392 std::pair<Transmission_Mode, Transmission_Mode> modes, blocks::UBlockHAL::Reference_Magnitude ref) {
393 return true;
394}
395
396FLASHMEM void blocks::UBlockHAL_Dummy::reset_transmission_modes_and_ref() {}
uint32_t uint32_t size
Definition flasher.cpp:63
Definition mode.h:14