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 <proto/main.pb.h>
6
7#include <algorithm>
8
9#include <block/ublock.h>
10#include <entity/visitor.h>
11#include <utils/logging.h>
12
13void utils::shift_5_left(uint8_t *buffer, size_t size) {
14 for (size_t idx = 0; idx < size - 1; idx++) {
15 auto next = buffer[idx + 1];
16 // Shift by 5, leaving zeros at 0-4
17 buffer[idx] <<= 5;
18 buffer[idx] |= (next >> 3) & 0x1F;
19 }
20 buffer[size - 1] <<= 5;
21}
22
23blocks::UBlock::UBlock(UBlockHAL *hardware)
24 : FunctionBlock("U", hardware), hardware(hardware), output_input_map{} {
25 classifier.class_enum = CLASS_;
26 reset_connections();
27}
28
29metadata::eui_t blocks::UBlock::get_entity_eui() const {
30 if (hardware)
31 return hardware->get_entity_eui();
32 return {};
33}
34
35bool blocks::UBlock::_i_sanity_check(const uint8_t input) { return input < NUM_OF_INPUTS; }
36
37bool blocks::UBlock::_o_sanity_check(const uint8_t output) { return output < NUM_OF_OUTPUTS; }
38
39bool blocks::UBlock::_io_sanity_check(const uint8_t input, const uint8_t output) {
40 return _i_sanity_check(input) && _o_sanity_check(output);
41}
42
43bool blocks::UBlock::connect(const uint8_t input, const uint8_t output, bool force) {
44 if (!_io_sanity_check(input, output))
45 return false;
46
47 output_input_map[output] = input;
48 return true;
49}
50
51bool blocks::UBlock::connect_alternative(Transmission_Mode signal_mode, const uint8_t output,
52 bool force, bool use_a_side) {
53 if (!_o_sanity_check(output))
54 return false;
55
56 // Do not consider Transmission_Mode::ANALOG_INPUT a valid mode
57 if (signal_mode == Transmission_Mode::ANALOG_INPUT)
58 return false;
59
60 // Check for other connections on the same output, unless we don't care if we overwrite them
61 if (!force and _is_output_connected(output))
62 return false;
63
64 // Check if the transmission mode makes the specified connection impossible and if the mode can or should be
65 // chnged
66 if (a_side_mode != signal_mode && use_a_side) {
67 // TODO: force for a-side could be added, but since there wont be much use for an a-side
68 // alternative signal, you have to use force or set the correct signal mode
69 if (!force)
70 return false;
71 change_a_side_transmission_mode(signal_mode);
72 }
73
74 if (b_side_mode != signal_mode && !use_a_side) {
75 if (!force)
76 if (is_input_connected(14) || is_input_connected(15))
77 return false;
78 change_b_side_transmission_mode(signal_mode);
80 }
81
82 // Make a one to one connection if possible. Uses Input zero as fallback
83 uint8_t input;
84 if (use_a_side) {
85 if (output < 15)
86 input = output;
87 else if (output == 15)
88 input = 0;
89 else if (output == 30)
90 input = 0;
91 else
92 input = output - 16;
93 } else {
94 if (output < 16)
95 input = 15;
96 else
97 input = 14;
98 }
99
100 connect(input, output);
101 return true;
102}
103
104void blocks::UBlock::_disconnect(const uint8_t output) { output_input_map[output] = -1; }
105
106bool blocks::UBlock::disconnect(const uint8_t input, const uint8_t output) {
107 if (!_io_sanity_check(input, output))
108 return false;
109
110 if (_is_connected(input, output)) {
111 _disconnect(output);
112 return true;
113 } else {
114 return false;
115 }
116}
117
118bool blocks::UBlock::disconnect(const uint8_t output) {
119 if (!_o_sanity_check(output))
120 return false;
121 _disconnect(output);
122 return true;
123}
124
125bool blocks::UBlock::_is_connected(const uint8_t input, const uint8_t output) const {
126 return output_input_map[output] == input;
127}
128
129bool blocks::UBlock::is_connected(const uint8_t input, const uint8_t output) const {
130 if (!_io_sanity_check(input, output))
131 return false;
132
133 return _is_connected(input, output);
134}
135
136bool blocks::UBlock::_is_output_connected(const uint8_t output) const {
137 return output_input_map[output] >= 0;
138}
139
140bool blocks::UBlock::is_output_connected(const uint8_t output) const {
141 if (!_o_sanity_check(output))
142 return false;
143
144 return _is_output_connected(output);
145}
146
147bool blocks::UBlock::_is_input_connected(const uint8_t input) const {
148 for (const auto &output : output_input_map)
149 if (output == input)
150 return true;
151 return false;
152}
153
154bool blocks::UBlock::is_input_connected(const uint8_t input) const {
155 if (!_i_sanity_check(input))
156 return false;
157 return _is_input_connected(input);
158}
159
160bool blocks::UBlock::is_anything_connected() const {
161 for (auto output : OUTPUT_IDX_RANGE())
162 if (_is_output_connected(output))
163 return true;
164 return false;
165}
166
167void blocks::UBlock::change_a_side_transmission_mode(const Transmission_Mode mode) {
168 a_side_mode = mode;
169}
170
171void blocks::UBlock::change_b_side_transmission_mode(const Transmission_Mode mode) {
172 b_side_mode = mode;
173}
174
175void blocks::UBlock::change_all_transmission_modes(const Transmission_Mode mode) {
176 change_a_side_transmission_mode(mode);
177 change_b_side_transmission_mode(mode);
178}
179
180void
181blocks::UBlock::change_all_transmission_modes(const std::pair<Transmission_Mode, Transmission_Mode> modes) {
182 change_a_side_transmission_mode(modes.first);
183 change_b_side_transmission_mode(modes.second);
184}
185
186std::pair<blocks::UBlock::Transmission_Mode, blocks::UBlock::Transmission_Mode>
187blocks::UBlock::get_all_transmission_modes() const {
188 return std::make_pair(a_side_mode, b_side_mode);
189}
190
191blocks::UBlock::Reference_Magnitude blocks::UBlock::get_reference_magnitude() {
192 return ref_magnitude;
193}
194
195void blocks::UBlock::change_reference_magnitude(blocks::UBlock::Reference_Magnitude ref) {
196 ref_magnitude = ref;
197}
198
199utils::status blocks::UBlock::write_to_hardware() {
200 if (!hardware->write_outputs(output_input_map) or
201 !hardware->write_transmission_modes_and_ref({a_side_mode, b_side_mode}, ref_magnitude)) {
202 LOG(ANABRID_PEDANTIC, __PRETTY_FUNCTION__);
203 return utils::status::failure();
204 }
205 return utils::status::success();
206}
207
208void blocks::UBlock::reset_connections() {
209 std::fill(begin(output_input_map), end(output_input_map), -1);
210}
211
212void blocks::UBlock::reset(entities::ResetAction action) {
213 FunctionBlock::reset(action);
214
215 if (action.has(entities::ResetAction::CIRCUIT_RESET)) {
216 change_all_transmission_modes(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
217 reset_connections();
218 reset_reference_magnitude();
219 }
220}
221
222ConfigResult blocks::UBlock::config(const pb_Config &cfg) {
223#ifdef ANABRID_DEBUG_ENTITY_CONFIG
224 Serial.println(__PRETTY_FUNCTION__);
225#endif
226 if (cfg.which_kind != pb_Config_select_config_tag)
227 return ConfigResult::err("expected select block config");
228
229 auto& select_config = cfg.kind.select_config;
230
231 auto res = apply_outputs(select_config);
232 if (!res)
233 return res;
234
235 res = apply_constants(select_config);
236 if (!res)
237 return res;
238
239 return ConfigResult::ok(true);
240}
241
242ConfigResult blocks::UBlock::apply_outputs(const pb_SelectConfig &cfg) {
243 // Handle an array of outputs
244 // In this case, all outputs are reset
245 reset_connections();
246 for (size_t idx = 0; idx < cfg.connections_count; ++idx) {
247 auto& connection = cfg.connections[idx];
248 if (!connect(connection.input, connection.output))
249 return ConfigResult::err("UBlock: Cannot connect input at idx");
250 }
251 return ConfigResult::ok(true);
252}
253
254ConfigResult blocks::UBlock::apply_constants(const pb_SelectConfig &cfg) {
255 if (cfg.constant == pb_SelectConfig_ConstantConfig_GROUND) {
256 // Turn OFF constant usage. We don't use Transmission_Mode::GROUND because
257 // that's really pointless, we then also can just not connect the relevant UBlock switch.
258 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::ANALOG_INPUT);
259 reset_reference_magnitude();
260 return ConfigResult::ok(true);
261 }else if (cfg.constant == pb_SelectConfig_ConstantConfig_POS_REF) {
262 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::POS_REF);
263 }
264 else if (cfg.constant == pb_SelectConfig_ConstantConfig_NEG_REF) {
265 change_b_side_transmission_mode(blocks::UBlock::Transmission_Mode::NEG_REF);
266 }
267 else
268 return ConfigResult::err_fmt("UBlock::config_self_from_pb: Ground, pos_ref or neg_ref");
269
270 if (cfg.magnitude == pb_SelectConfig_Magnitude_ONE_TENTH)
271 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE_TENTH);
272 else if (cfg.magnitude == pb_SelectConfig_Magnitude_ONE) {
273 change_reference_magnitude(blocks::UBlock::Reference_Magnitude::ONE);
274 }
275 else
276 return ConfigResult::err_fmt("UBlock::config_self_from_pb: Pass +-0.1 or +-1.");
277
278
279 return ConfigResult::ok(true);
280}
281
282void blocks::UBlock::extract(entities::ExtractVisitor &collector) {
283 Entity::extract(collector);
284 auto& cfg = collector.create(pb_Config_select_config_tag);
285
286 // Save outputs into cfg
287 auto& select_config = cfg.kind.select_config = pb_SelectConfig_init_default;
288 for (uint32_t idx = 0; idx < NUM_OF_OUTPUTS; ++idx) {
289 auto input = output_input_map[idx];
290 if (input < 0)
291 continue;
292
293 select_config.connections[select_config.connections_count++] = {
294 .input= static_cast<uint32_t>(output_input_map[idx]),
295 .output = idx
296 };
297 }
298
299 // Constant setting to protobuf
300 // we support here much more then at the setter side, because the firmware
301 // side has so much more states that simply have no representation at client side.
302 switch (b_side_mode) {
303 case Transmission_Mode::POS_REF:
304 select_config.constant = pb_SelectConfig_ConstantConfig_POS_REF;
305 break;
306 case Transmission_Mode::NEG_REF:
307 select_config.constant = pb_SelectConfig_ConstantConfig_NEG_REF;
308 break;
309 default:
310 select_config.constant = pb_SelectConfig_ConstantConfig_GROUND;
311 break;
312 }
313
314 if (ref_magnitude == Reference_Magnitude::ONE_TENTH) {
315 select_config.magnitude = pb_SelectConfig_Magnitude_ONE_TENTH;
316 }else {
317 select_config.magnitude = pb_SelectConfig_Magnitude_ONE;
318 }
319}
320
321void blocks::UBlock::reset_reference_magnitude() { ref_magnitude = Reference_Magnitude::ONE; }
322
323bool blocks::UBlockHAL_Dummy::write_outputs(std::array<int8_t, 32> outputs) { return true; }
324
325bool blocks::UBlockHAL_Dummy::write_transmission_modes_and_ref(
326 std::pair<Transmission_Mode, Transmission_Mode> modes, blocks::UBlockHAL::Reference_Magnitude ref) {
327 return true;
328}
329
330void blocks::UBlockHAL_Dummy::reset_transmission_modes_and_ref() {}
uint32_t
Definition flasher.cpp:195
uint32_t uint32_t size
Definition flasher.cpp:63
Definition mode.h:14