You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

331 lines
10 KiB

/*
* EEZ Modular Firmware
* Copyright (C) 2021-present, Envox d.o.o.
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <stdio.h>
#include <eez/core/util.h>
#include <eez/core/os.h>
#include <eez/gui/gui.h>
#include <eez/gui/keypad.h>
#include <eez/gui/widgets/input.h>
#include <eez/gui/widgets/containers/layout_view.h>
#include <eez/flow/flow.h>
#include <eez/flow/components.h>
#include <eez/flow/queue.h>
#include <eez/flow/flow_defs_v3.h>
#include <eez/flow/debugger.h>
#include <eez/flow/hooks.h>
using namespace eez::gui;
namespace eez {
namespace flow {
#if defined(__EMSCRIPTEN__)
uint32_t g_wasmModuleId = 0;
#endif
static const uint32_t FLOW_TICK_MAX_DURATION_MS = 5;
int g_selectedLanguage = 0;
FlowState *g_firstFlowState;
FlowState *g_lastFlowState;
static bool g_isStopped = true;
////////////////////////////////////////////////////////////////////////////////
unsigned start(Assets *assets) {
auto flowDefinition = static_cast<FlowDefinition *>(assets->flowDefinition);
if (flowDefinition->flows.count == 0) {
return 0;
}
g_isStopped = false;
queueReset();
scpiComponentInitHook();
onStarted(assets);
return 1;
}
void tick() {
if (!isFlowRunningHook()) {
return;
}
uint32_t startTickCount = millis();
while (true) {
FlowState *flowState;
unsigned componentIndex;
bool continuousTask;
if (!peekNextTaskFromQueue(flowState, componentIndex, continuousTask)) {
break;
}
if (!continuousTask && !canExecuteStep(flowState, componentIndex)) {
break;
}
removeNextTaskFromQueue();
executeComponent(flowState, componentIndex);
if (isFlowStopped()) {
break;
}
auto component = flowState->flow->components[componentIndex];
for (uint32_t i = 0; i < component->inputs.count; i++) {
auto inputIndex = component->inputs[i];
if (flowState->flow->componentInputs[inputIndex] & COMPONENT_INPUT_FLAG_IS_SEQ_INPUT) {
auto pValue = &flowState->values[inputIndex];
*pValue = Value();
onValueChanged(pValue);
}
}
if (canFreeFlowState(flowState)) {
freeFlowState(flowState);
}
if (millis() - startTickCount >= FLOW_TICK_MAX_DURATION_MS) {
break;
}
}
finishToDebuggerMessageHook();
}
void freeAllChildrenFlowStates(FlowState *firstChildFlowState) {
auto flowState = firstChildFlowState;
while (flowState != nullptr) {
auto nextFlowState = flowState->nextSibling;
freeAllChildrenFlowStates(flowState->firstChild);
freeFlowState(flowState);
flowState = nextFlowState;
}
}
void stop() {
freeAllChildrenFlowStates(g_firstFlowState);
g_firstFlowState = nullptr;
g_lastFlowState = nullptr;
g_isStopped = true;
queueReset();
onStopped();
}
bool isFlowStopped() {
return g_isStopped;
}
FlowState *getFlowState(Assets *assets, int flowStateIndex) {
return (FlowState *)(ALLOC_BUFFER + flowStateIndex);
}
FlowState *getFlowState(Assets *assets, int16_t pageIndex, const WidgetCursor &widgetCursor) {
if (!assets->flowDefinition) {
return nullptr;
}
if (!isFlowRunningHook()) {
return nullptr;
}
if (widgetCursor.widget && widgetCursor.widget->type == WIDGET_TYPE_LAYOUT_VIEW) {
if (widgetCursor.flowState) {
auto layoutViewWidget = (LayoutViewWidget *)widgetCursor.widget;
auto flowState = widgetCursor.flowState;
auto layoutViewWidgetComponentIndex = layoutViewWidget->componentIndex;
return getLayoutViewFlowState(flowState, layoutViewWidgetComponentIndex, pageIndex);
}
} else {
auto page = assets->pages[pageIndex];
if (!(page->flags & PAGE_IS_USED_AS_CUSTOM_WIDGET)) {
FlowState *flowState;
for (flowState = g_firstFlowState; flowState; flowState = flowState->nextSibling) {
if (flowState->flowIndex == pageIndex) {
break;
}
}
if (!flowState) {
flowState = initPageFlowState(assets, pageIndex, nullptr, 0);
}
return flowState;
}
}
return nullptr;
}
int getPageIndex(FlowState *flowState) {
return flowState->flowIndex;
}
void executeFlowAction(const gui::WidgetCursor &widgetCursor, int16_t actionId, void *param) {
if (!isFlowRunningHook()) {
return;
}
auto flowState = widgetCursor.flowState;
actionId = -actionId - 1;
auto flow = flowState->flow;
if (actionId >= 0 && actionId < (int16_t)flow->widgetActions.count) {
auto componentOutput = flow->widgetActions[actionId];
if (componentOutput->componentIndex != -1 && componentOutput->componentOutputIndex != -1) {
if (widgetCursor.widget->type == WIDGET_TYPE_DROP_DOWN_LIST) {
auto params = Value::makeArrayRef(defs_v3::SYSTEM_STRUCTURE_DROP_DOWN_LIST_ACTION_PARAMS_NUM_FIELDS, defs_v3::SYSTEM_STRUCTURE_DROP_DOWN_LIST_ACTION_PARAMS, 0x53e3b30b);
// index
((ArrayValueRef *)params.refValue)->arrayValue.values[defs_v3::SYSTEM_STRUCTURE_DROP_DOWN_LIST_ACTION_PARAMS_FIELD_INDEX] = widgetCursor.iterators[0];
// indexes
auto indexes = Value::makeArrayRef(MAX_ITERATORS, defs_v3::ARRAY_TYPE_INTEGER, 0xb1f68ef8);
for (size_t i = 0; i < MAX_ITERATORS; i++) {
((ArrayValueRef *)indexes.refValue)->arrayValue.values[i] = (int)widgetCursor.iterators[i];
}
((ArrayValueRef *)params.refValue)->arrayValue.values[defs_v3::SYSTEM_STRUCTURE_DROP_DOWN_LIST_ACTION_PARAMS_FIELD_INDEXES] = indexes;
// selectedIndex
((ArrayValueRef *)params.refValue)->arrayValue.values[defs_v3::SYSTEM_STRUCTURE_DROP_DOWN_LIST_ACTION_PARAMS_FIELD_SELECTED_INDEX] = *((int *)param);
propagateValue(flowState, componentOutput->componentIndex, componentOutput->componentOutputIndex, params);
} else {
auto params = Value::makeArrayRef(defs_v3::SYSTEM_STRUCTURE_ACTION_PARAMS_NUM_FIELDS, defs_v3::SYSTEM_STRUCTURE_ACTION_PARAMS, 0x285940bb);
// index
((ArrayValueRef *)params.refValue)->arrayValue.values[defs_v3::SYSTEM_STRUCTURE_ACTION_PARAMS_FIELD_INDEX] = widgetCursor.iterators[0];
// indexes
auto indexes = Value::makeArrayRef(MAX_ITERATORS, defs_v3::ARRAY_TYPE_INTEGER, 0xb1f68ef8);
for (size_t i = 0; i < MAX_ITERATORS; i++) {
((ArrayValueRef *)indexes.refValue)->arrayValue.values[i] = (int)widgetCursor.iterators[i];
}
((ArrayValueRef *)params.refValue)->arrayValue.values[defs_v3::SYSTEM_STRUCTURE_ACTION_PARAMS_FIELD_INDEXES] = indexes;
propagateValue(flowState, componentOutput->componentIndex, componentOutput->componentOutputIndex, params);
}
}
}
for (int i = 0; i < 3; i++) {
tick();
}
}
void dataOperation(int16_t dataId, DataOperationEnum operation, const gui::WidgetCursor &widgetCursor, Value &value) {
if (!isFlowRunningHook()) {
return;
}
auto flowState = widgetCursor.flowState;
auto flowDataId = -dataId - 1;
auto flow = flowState->flow;
if (flowDataId >= 0 && flowDataId < (int16_t)flow->widgetDataItems.count) {
WidgetDataItem *widgetDataItem = flow->widgetDataItems[flowDataId];
auto component = flow->components[widgetDataItem->componentIndex];
if (operation == DATA_OPERATION_GET) {
getValue(flowDataId, operation, widgetCursor, value);
if (component->type == WIDGET_TYPE_INPUT && dataId == widgetCursor.widget->data) {
value = getInputWidgetData(widgetCursor, value);
}
} else if (operation == DATA_OPERATION_COUNT) {
Value arrayValue;
getValue(flowDataId, operation, widgetCursor, arrayValue);
if (arrayValue.getType() == VALUE_TYPE_ARRAY || arrayValue.getType() == VALUE_TYPE_ARRAY_REF) {
value = arrayValue.getArray()->arraySize;
} else {
value = 0;
}
}
#if OPTION_KEYPAD
else if (operation == DATA_OPERATION_GET_TEXT_CURSOR_POSITION) {
Keypad *keypad = getActiveKeypad();
if (keypad) {
value = keypad->getCursorPosition();
}
}
#endif
else if (operation == DATA_OPERATION_GET_MIN) {
if (component->type == WIDGET_TYPE_INPUT) {
value = getInputWidgetMin(widgetCursor);
}
} else if (operation == DATA_OPERATION_GET_MAX) {
if (component->type == WIDGET_TYPE_INPUT) {
value = getInputWidgetMax(widgetCursor);
}
} else if (operation == DATA_OPERATION_GET_PRECISION) {
if (component->type == WIDGET_TYPE_INPUT) {
value = getInputWidgetPrecision(widgetCursor);
}
} else if (operation == DATA_OPERATION_GET_UNIT) {
if (component->type == WIDGET_TYPE_INPUT) {
value = getBaseUnit(getInputWidgetUnit(widgetCursor));
}
} else if (operation == DATA_OPERATION_SET) {
if (component->type == WIDGET_TYPE_INPUT) {
auto inputWidget = (InputWidget *)widgetCursor.widget;
if (inputWidget->flags & INPUT_WIDGET_TYPE_NUMBER) {
if (value.isInt32()) {
setValue(flowDataId, widgetCursor, value);
} else {
Value precisionValue = getInputWidgetPrecision(widgetCursor);
float precision = precisionValue.toFloat();
float valueFloat = value.toFloat();
Unit unit = getInputWidgetUnit(widgetCursor);
setValue(flowDataId, widgetCursor, Value(roundPrec(valueFloat, precision) / getUnitFactor(unit), VALUE_TYPE_FLOAT));
}
} else {
setValue(flowDataId, widgetCursor, value);
}
executeFlowAction(widgetCursor, inputWidget->action, nullptr);
} else {
setValue(flowDataId, widgetCursor, value);
}
}
} else {
// TODO this shouldn't happen
value = Value();
}
}
} // namespace flow
} // namespace eez