5#include <block/iblock.h>
6#include <utils/is_number.h>
7#include <utils/logging.h>
9blocks::IBlock::IBlock(IBlockHAL *hardware) : FunctionBlock(
"I", hardware), hardware(hardware), outputs{0} {
10 classifier.class_enum = CLASS_;
13blocks::IBlock::IBlock() : IBlock(new IBlockHALDummy(
bus::NULL_ADDRESS)) {}
15FLASHMEM metadata::eui_t blocks::IBlock::get_entity_eui()
const {
17 return hardware->get_entity_eui();
21FLASHMEM utils::status blocks::IBlock::write_to_hardware() {
22 return utils::status(hardware->write_upscaling(scaling_factors) and hardware->write_outputs(outputs));
25FLASHMEM
bool blocks::IBlock::init() {
26 LOG(ANABRID_DEBUG_INIT, __PRETTY_FUNCTION__);
27 if (!FunctionBlock::init()) {
31 if (!write_to_hardware())
36FLASHMEM
bool blocks::IBlock::_is_connected(uint8_t input, uint8_t output)
const {
37 return outputs[output] & INPUT_BITMASK(input);
40FLASHMEM
bool blocks::IBlock::_is_output_connected(uint8_t output)
const {
return outputs[output]; }
42FLASHMEM
bool blocks::IBlock::is_connected(uint8_t input, uint8_t output)
const {
43 if (output >= NUM_OUTPUTS or input >= NUM_INPUTS)
45 return _is_connected(input, output);
48FLASHMEM
bool blocks::IBlock::is_anything_connected()
const {
49 for (
auto &output : outputs)
50 if (_is_output_connected(output))
55FLASHMEM
bool blocks::IBlock::connect(uint8_t input, uint8_t output,
bool exclusive,
56 bool allow_input_splitting) {
57 if (output >= NUM_OUTPUTS or input >= NUM_INPUTS)
61 if (!allow_input_splitting) {
62 for (
size_t other_output_idx = 0; other_output_idx < outputs.size(); other_output_idx++) {
63 if (output == other_output_idx)
65 if (is_connected(input, other_output_idx)) {
72 outputs[output] = INPUT_BITMASK(input);
74 outputs[output] |= INPUT_BITMASK(input);
78FLASHMEM
void blocks::IBlock::reset_outputs() {
79 for (
auto &output : outputs) {
84FLASHMEM
void blocks::IBlock::reset(entities::ResetAction action) {
85 FunctionBlock::reset(action);
87 if (action.has(entities::ResetAction::CIRCUIT_RESET)) {
93FLASHMEM utils::status blocks::IBlock::config_self_from_json(JsonObjectConst cfg) {
94#ifdef ANABRID_DEBUG_ENTITY_CONFIG
95 Serial.println(__PRETTY_FUNCTION__);
97 for (
auto cfgItr = cfg.begin(); cfgItr != cfg.end(); ++cfgItr) {
98 if (cfgItr->key() ==
"outputs") {
99 auto res = _config_outputs_from_json(cfgItr->value());
102 }
else if (cfgItr->key() ==
"upscaling") {
103 auto res = _config_upscaling_from_json(cfgItr->value());
107 return utils::status(
"IBlock: Unknown configuration key");
110 return utils::status::success();
113FLASHMEM utils::status blocks::IBlock::_config_outputs_from_json(
const JsonVariantConst &cfg) {
116 if (cfg.is<JsonObjectConst>()) {
117 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
121 std::string output_str = keyval.key().c_str();
122 if (!utils::is_number(output_str.begin(), output_str.end()))
123 return utils::status(
"IBlock: Expected number but key is '%s'", output_str.c_str());
125 auto output = std::stoul(output_str);
127 if (!disconnect(output))
128 return utils::status(
"IBlock: Could not disconnect output '%d', probably out of range", output);
130 auto res = _connect_from_json(keyval.value(), output);
134 return utils::status::success();
138 else if (cfg.is<JsonArrayConst>()) {
139 auto outputs_json = cfg.as<JsonArrayConst>();
140 if (outputs_json.size() != NUM_OUTPUTS)
141 return utils::status(
"IBlock: Given array size %d does not meet expected %d", outputs_json.size(),
145 for (JsonVariantConst input_spec : outputs_json) {
147 auto res = _connect_from_json(input_spec, idx++);
151 return utils::status::success();
153 return utils::status(
"IBlock: Expected either array or object as configuration");
156FLASHMEM utils::status blocks::IBlock::_config_upscaling_from_json(
const JsonVariantConst &cfg) {
157 if (cfg.is<JsonObjectConst>()) {
158 for (JsonPairConst keyval : cfg.as<JsonObjectConst>()) {
159 if (!keyval.value().is<
bool>())
160 return utils::status(
"IBlock upscaling must be boolean");
162 auto input = std::stoul(keyval.key().c_str());
163 if (input > NUM_INPUTS)
164 return utils::status(
"IBlock upscaling too many values");
166 set_upscaling(input, keyval.value());
167 return utils::status::success();
169 }
else if (cfg.is<JsonArrayConst>()) {
170 auto array = cfg.as<JsonArrayConst>();
171 if (array.size() != NUM_INPUTS)
172 return utils::status(
"IBlock upscaling wrong number of inputs");
174 for (uint8_t input = 0; input < NUM_INPUTS; input++) {
175 if (!array[input].is<bool>())
176 return utils::status(
"IBlock upscaling expecting boolean");
177 set_upscaling(input, array[input]);
179 return utils::status::success();
181 return utils::status(
"IBlock upscaling: Either provide list or object");
184FLASHMEM utils::status blocks::IBlock::_connect_from_json(
const JsonVariantConst &input_spec, uint8_t output) {
185 if (input_spec.isNull()) {
187 }
else if (input_spec.is<JsonArrayConst>()) {
188 for (
auto input : input_spec.as<JsonArrayConst>()) {
189 if (!input.is<uint8_t>())
190 return utils::status(
"IBlock: input is not an integer");
191 if (!connect(input, output))
192 return utils::status(
"IBlock: Could not connect %d -> %d, probably out of bounds", input, outputs);
194 }
else if (input_spec.is<uint8_t>()) {
195 if (!connect(input_spec, output))
196 return utils::status(
"IBlock: Could not connect %d -> %d.", input_spec, output);
198 return utils::status(
"IBlock: Illegal data type in input specification");
200 return utils::status::success();
203FLASHMEM
bool blocks::IBlock::disconnect(uint8_t input, uint8_t output) {
205 if (!is_connected(input, output))
207 outputs[output] &= ~INPUT_BITMASK(input);
211FLASHMEM
bool blocks::IBlock::disconnect(uint8_t output) {
212 if (output >= NUM_OUTPUTS)
218FLASHMEM
bool blocks::IBlock::set_upscaling(uint8_t input,
bool upscale) {
219 if (input >= NUM_INPUTS)
221 scaling_factors[input] = upscale;
225FLASHMEM
void blocks::IBlock::set_upscaling(std::bitset<NUM_INPUTS> scales) { scaling_factors = scales; }
227FLASHMEM
void blocks::IBlock::reset_upscaling() { scaling_factors.reset(); }
229FLASHMEM
bool blocks::IBlock::get_upscaling(uint8_t output)
const {
233 return scaling_factors[output];
236FLASHMEM
void blocks::IBlock::config_self_to_json(JsonObject &cfg) {
237 Entity::config_self_to_json(cfg);
239 auto outputs_cfg = cfg.createNestedArray(
"outputs");
240 for (uint8_t output = 0; output < NUM_OUTPUTS; output++) {
241 if (outputs[output]) {
242 auto inputs_cfg = outputs_cfg.createNestedArray();
243 for (uint8_t input = 0; input < NUM_INPUTS; input++)
244 if (_is_connected(input, output))
245 inputs_cfg.add(input);
247 outputs_cfg.add(
nullptr);
251 auto upscaling_cfg = cfg.createNestedArray(
"upscaling");
252 for (uint8_t input = 0; input < NUM_INPUTS; input++) {
253 if (scaling_factors[input])
254 upscaling_cfg.add(
true);
256 upscaling_cfg.add(
false);
260FLASHMEM
const std::array<uint32_t, blocks::IBlock::NUM_OUTPUTS> &blocks::IBlock::get_outputs()
const {
264FLASHMEM
void blocks::IBlock::set_outputs(
const std::array<uint32_t, NUM_OUTPUTS> &outputs_) {