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