/* * 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 . */ #include #include #include #include #include #include #include #include #include #include using namespace eez::gui; namespace eez { namespace flow { bool isComponentReadyToRun(FlowState *flowState, unsigned componentIndex) { auto component = flowState->flow->components[componentIndex]; if (component->type == defs_v3::COMPONENT_TYPE_CATCH_ERROR_ACTION) { return false; } if (component->type == defs_v3::COMPONENT_TYPE_ON_EVENT_ACTION) { return false; } if (component->type < defs_v3::COMPONENT_TYPE_START_ACTION) { // always execute widget return true; } if (component->type == defs_v3::COMPONENT_TYPE_START_ACTION) { auto parentComponent = flowState->parentComponent; if (parentComponent) { auto flowInputIndex = parentComponent->inputs[0]; auto value = flowState->parentFlowState->values[flowInputIndex]; return value.getType() != VALUE_TYPE_UNDEFINED; } else { return true; } } // check if required inputs are defined: // - at least 1 seq input must be defined // - all non optional data inputs must be defined int numSeqInputs = 0; int numDefinedSeqInputs = 0; for (unsigned inputIndex = 0; inputIndex < component->inputs.count; inputIndex++) { auto inputValueIndex = component->inputs[inputIndex]; auto input = flowState->flow->componentInputs[inputValueIndex]; if (input & COMPONENT_INPUT_FLAG_IS_SEQ_INPUT) { numSeqInputs++; auto &value = flowState->values[inputValueIndex]; if (value.type != VALUE_TYPE_UNDEFINED) { numDefinedSeqInputs++; } } else { if (!(input & COMPONENT_INPUT_FLAG_IS_OPTIONAL)) { auto &value = flowState->values[inputValueIndex]; if (value.type == VALUE_TYPE_UNDEFINED) { // non optional data input is undefined return false; } } } } if (numSeqInputs && !numDefinedSeqInputs) { // no seq input is defined return false; } return true; } static bool pingComponent(FlowState *flowState, unsigned componentIndex, int sourceComponentIndex = -1, int sourceOutputIndex = -1, int targetInputIndex = -1) { if (isComponentReadyToRun(flowState, componentIndex)) { if (!addToQueue(flowState, componentIndex, sourceComponentIndex, sourceOutputIndex, targetInputIndex)) { throwError(flowState, componentIndex, "Execution queue is full\n"); return false; } return true; } return false; } static FlowState *initFlowState(Assets *assets, int flowIndex, FlowState *parentFlowState, int parentComponentIndex) { auto flowDefinition = static_cast(assets->flowDefinition); auto flow = flowDefinition->flows[flowIndex]; auto nValues = flow->componentInputs.count + flow->localVariables.count; FlowState *flowState = (FlowState *)alloc( sizeof(FlowState) + nValues * sizeof(Value) + flow->components.count * sizeof(ComponenentExecutionState *) + flow->components.count * sizeof(bool), 0x4c3b6ef5 ); flowState->flowStateIndex = (int)((uint8_t *)flowState - ALLOC_BUFFER); flowState->assets = assets; flowState->flowDefinition = static_cast(assets->flowDefinition); flowState->flow = flowDefinition->flows[flowIndex]; flowState->flowIndex = flowIndex; flowState->error = false; flowState->numAsyncComponents = 0; flowState->parentFlowState = parentFlowState; flowState->timelinePosition = 0; if (parentFlowState) { if (parentFlowState->lastChild) { parentFlowState->lastChild->nextSibling = flowState; flowState->previousSibling = parentFlowState->lastChild; parentFlowState->lastChild = flowState; } else { flowState->previousSibling = nullptr; parentFlowState->firstChild = flowState; parentFlowState->lastChild = flowState; } flowState->parentComponentIndex = parentComponentIndex; flowState->parentComponent = parentFlowState->flow->components[parentComponentIndex]; } else { if (g_lastFlowState) { g_lastFlowState->nextSibling = flowState; flowState->previousSibling = g_lastFlowState; g_lastFlowState = flowState; } else { flowState->previousSibling = nullptr; g_firstFlowState = flowState; g_lastFlowState = flowState; } flowState->parentComponentIndex = -1; flowState->parentComponent = nullptr; } flowState->firstChild = nullptr; flowState->lastChild = nullptr; flowState->nextSibling = nullptr; flowState->values = (Value *)(flowState + 1); flowState->componenentExecutionStates = (ComponenentExecutionState **)(flowState->values + nValues); flowState->componenentAsyncStates = (bool *)(flowState->componenentExecutionStates + flow->components.count); for (unsigned i = 0; i < nValues; i++) { new (flowState->values + i) Value(); } auto &undefinedValue = *flowDefinition->constants[UNDEFINED_VALUE_INDEX]; for (unsigned i = 0; i < flow->componentInputs.count; i++) { flowState->values[i] = undefinedValue; } for (unsigned i = 0; i < flow->localVariables.count; i++) { auto value = flow->localVariables[i]; flowState->values[flow->componentInputs.count + i] = *value; } for (unsigned i = 0; i < flow->components.count; i++) { flowState->componenentExecutionStates[i] = nullptr; flowState->componenentAsyncStates[i] = false; } onFlowStateCreated(flowState); for (unsigned componentIndex = 0; componentIndex < flow->components.count; componentIndex++) { pingComponent(flowState, componentIndex); } return flowState; } FlowState *initActionFlowState(int flowIndex, FlowState *parentFlowState, int parentComponentIndex) { auto flowState = initFlowState(parentFlowState->assets, flowIndex, parentFlowState, parentComponentIndex); if (flowState) { flowState->isAction = true; } return flowState; } FlowState *initPageFlowState(Assets *assets, int flowIndex, FlowState *parentFlowState, int parentComponentIndex) { auto flowState = initFlowState(assets, flowIndex, parentFlowState, parentComponentIndex); if (flowState) { flowState->isAction = false; } return flowState; } bool canFreeFlowState(FlowState *flowState, bool includingWatchVariable) { if (!flowState->isAction) { return false; } if (flowState->numAsyncComponents > 0) { return false; } if (isThereAnyTaskInQueueForFlowState(flowState, includingWatchVariable)) { return false; } for (uint32_t componentIndex = 0; componentIndex < flowState->flow->components.count; componentIndex++) { auto component = flowState->flow->components[componentIndex]; if ( component->type != defs_v3::COMPONENT_TYPE_INPUT_ACTION && component->type != defs_v3::COMPONENT_TYPE_LOOP_ACTION && component->type != defs_v3::COMPONENT_TYPE_COUNTER_ACTION && (includingWatchVariable || component->type != defs_v3::COMPONENT_TYPE_WATCH_VARIABLE_ACTION) && flowState->componenentExecutionStates[componentIndex] ) { return false; } } return true; } void freeFlowState(FlowState *flowState) { auto parentFlowState = flowState->parentFlowState; if (flowState->parentFlowState) { auto componentExecutionState = flowState->parentFlowState->componenentExecutionStates[flowState->parentComponentIndex]; if (componentExecutionState) { deallocateComponentExecutionState(flowState->parentFlowState, flowState->parentComponentIndex); return; } if (parentFlowState->firstChild == flowState) { parentFlowState->firstChild = flowState->nextSibling; } if (parentFlowState->lastChild == flowState) { parentFlowState->lastChild = flowState->previousSibling; } } else { if (g_firstFlowState == flowState) { g_firstFlowState = flowState->nextSibling; } if (g_lastFlowState == flowState) { g_lastFlowState = flowState->previousSibling; } } if (flowState->previousSibling) { flowState->previousSibling->nextSibling = flowState->nextSibling; } if (flowState->nextSibling) { flowState->nextSibling->previousSibling = flowState->previousSibling; } auto flow = flowState->flow; auto valuesCount = flow->componentInputs.count + flow->localVariables.count; for (unsigned int i = 0; i < valuesCount; i++) { (flowState->values + i)->~Value(); } for (unsigned i = 0; i < flow->components.count; i++) { deallocateComponentExecutionState(flowState, i); } onFlowStateDestroyed(flowState); free(flowState); if (parentFlowState) { if (canFreeFlowState(parentFlowState)) { freeFlowState(parentFlowState); } } } void deallocateComponentExecutionState(FlowState *flowState, unsigned componentIndex) { auto executionState = flowState->componenentExecutionStates[componentIndex]; if (executionState) { flowState->componenentExecutionStates[componentIndex] = nullptr; onComponentExecutionStateChanged(flowState, componentIndex); ObjectAllocator::deallocate(executionState); } } void propagateValue(FlowState *flowState, unsigned componentIndex, unsigned outputIndex, const gui::Value &value) { auto component = flowState->flow->components[componentIndex]; auto componentOutput = component->outputs[outputIndex]; auto value2 = value.getValue(); for (unsigned connectionIndex = 0; connectionIndex < componentOutput->connections.count; connectionIndex++) { auto connection = componentOutput->connections[connectionIndex]; auto pValue = &flowState->values[connection->targetInputIndex]; if (*pValue != value2) { *pValue = value2; //if (!(flowState->flow->componentInputs[connection->targetInputIndex] & COMPONENT_INPUT_FLAG_IS_SEQ_INPUT)) { onValueChanged(pValue); //} } pingComponent(flowState, connection->targetComponentIndex, componentIndex, outputIndex, connection->targetInputIndex); } } void propagateValue(FlowState *flowState, unsigned componentIndex, unsigned outputIndex) { auto &nullValue = *flowState->flowDefinition->constants[NULL_VALUE_INDEX]; propagateValue(flowState, componentIndex, outputIndex, nullValue); } void propagateValueThroughSeqout(FlowState *flowState, unsigned componentIndex) { // find @seqout output // TODO optimization hint: always place @seqout at 0-th index auto component = flowState->flow->components[componentIndex]; for (uint32_t i = 0; i < component->outputs.count; i++) { if (component->outputs[i]->isSeqOut) { propagateValue(flowState, componentIndex, i); return; } } } //////////////////////////////////////////////////////////////////////////////// void getValue(uint16_t dataId, DataOperationEnum operation, const WidgetCursor &widgetCursor, Value &value) { if (isFlowRunningHook()) { FlowState *flowState = widgetCursor.flowState; auto flow = flowState->flow; WidgetDataItem *widgetDataItem = flow->widgetDataItems[dataId]; if (widgetDataItem && widgetDataItem->componentIndex != -1 && widgetDataItem->propertyValueIndex != -1) { evalProperty(flowState, widgetDataItem->componentIndex, widgetDataItem->propertyValueIndex, value, "doGetFlowValue failed", nullptr, widgetCursor.iterators, operation); } } } void setValue(uint16_t dataId, const WidgetCursor &widgetCursor, const Value& value) { if (isFlowRunningHook()) { FlowState *flowState = widgetCursor.flowState; auto flow = flowState->flow; WidgetDataItem *widgetDataItem = flow->widgetDataItems[dataId]; if (widgetDataItem && widgetDataItem->componentIndex != -1 && widgetDataItem->propertyValueIndex != -1) { auto component = flow->components[widgetDataItem->componentIndex]; auto property = component->properties[widgetDataItem->propertyValueIndex]; Value dstValue; if (evalAssignableExpression(flowState, widgetDataItem->componentIndex, property->evalInstructions, dstValue, "doSetFlowValue failed", nullptr, widgetCursor.iterators)) { assignValue(flowState, widgetDataItem->componentIndex, dstValue, value); } } } } //////////////////////////////////////////////////////////////////////////////// void assignValue(FlowState *flowState, int componentIndex, Value &dstValue, const Value &srcValue) { if (dstValue.getType() == VALUE_TYPE_FLOW_OUTPUT) { propagateValue(flowState, componentIndex, dstValue.getUInt16(), srcValue); } else if (dstValue.getType() == VALUE_TYPE_NATIVE_VARIABLE) { set(g_widgetCursor, dstValue.getInt(), srcValue); } else { Value *pDstValue; if (dstValue.getType() == VALUE_TYPE_ARRAY_ELEMENT_VALUE) { auto arrayElementValue = (ArrayElementValue *)dstValue.refValue; auto array = arrayElementValue->arrayValue.getArray(); pDstValue = &array->values[arrayElementValue->elementIndex]; } else { pDstValue = dstValue.pValueValue; } if (assignValue(*pDstValue, srcValue)) { onValueChanged(pDstValue); } else { char errorMessage[100]; snprintf(errorMessage, sizeof(errorMessage), "Can not assign %s to %s\n", g_valueTypeNames[pDstValue->type](srcValue), g_valueTypeNames[srcValue.type](*pDstValue) ); throwError(flowState, componentIndex, errorMessage); } } } //////////////////////////////////////////////////////////////////////////////// void startAsyncExecution(FlowState *flowState, int componentIndex) { if (!flowState->componenentAsyncStates[componentIndex]) { flowState->componenentAsyncStates[componentIndex] = true; onComponentAsyncStateChanged(flowState, componentIndex); flowState->numAsyncComponents++; } } void endAsyncExecution(FlowState *flowState, int componentIndex) { if (!g_firstFlowState) { return; } if (flowState->componenentAsyncStates[componentIndex]) { flowState->componenentAsyncStates[componentIndex] = false; onComponentAsyncStateChanged(flowState, componentIndex); flowState->numAsyncComponents--; if (canFreeFlowState(flowState)) { freeFlowState(flowState); } } } void onEvent(FlowState *flowState, FlowEvent flowEvent) { for (unsigned componentIndex = 0; componentIndex < flowState->flow->components.count; componentIndex++) { auto component = flowState->flow->components[componentIndex]; if (component->type == defs_v3::COMPONENT_TYPE_ON_EVENT_ACTION) { struct OnEventComponent : public Component { uint8_t event; }; auto onEventComponent = (OnEventComponent *)component; if (onEventComponent->event == flowEvent) { if (!addToQueue(flowState, componentIndex, -1, -1, -1, false)) { throwError(flowState, componentIndex, "Execution queue is full\n"); return; } } } } } //////////////////////////////////////////////////////////////////////////////// bool findCatchErrorComponent(FlowState *flowState, FlowState *&catchErrorFlowState, int &catchErrorComponentIndex) { if (!flowState) { return false; } for (unsigned componentIndex = 0; componentIndex < flowState->flow->components.count; componentIndex++) { auto component = flowState->flow->components[componentIndex]; if (component->type == defs_v3::COMPONENT_TYPE_CATCH_ERROR_ACTION) { catchErrorFlowState = flowState; catchErrorComponentIndex = componentIndex; return true; } } return findCatchErrorComponent(flowState->parentFlowState, catchErrorFlowState, catchErrorComponentIndex); } void throwError(FlowState *flowState, int componentIndex, const char *errorMessage) { auto component = flowState->flow->components[componentIndex]; #if defined(__EMSCRIPTEN__) printf("throwError: %s\n", errorMessage); #endif if (component->errorCatchOutput != -1) { propagateValue( flowState, componentIndex, component->errorCatchOutput, Value::makeStringRef(errorMessage, strlen(errorMessage), 0xef6f8414) ); } else { FlowState *catchErrorFlowState; int catchErrorComponentIndex; if ( findCatchErrorComponent( component->type == defs_v3::COMPONENT_TYPE_ERROR_ACTION ? flowState->parentFlowState : flowState, catchErrorFlowState, catchErrorComponentIndex ) ) { auto catchErrorComponentExecutionState = allocateComponentExecutionState(catchErrorFlowState, catchErrorComponentIndex); catchErrorComponentExecutionState->message = Value::makeStringRef(errorMessage, strlen(errorMessage), 0x9473eef2); if (!addToQueue(catchErrorFlowState, catchErrorComponentIndex, -1, -1, -1)) { catchErrorFlowState->error = true; onFlowError(catchErrorFlowState, catchErrorComponentIndex, "Execution queue is full\n"); stopScriptHook(); } } else { flowState->error = true; onFlowError(flowState, componentIndex, errorMessage); stopScriptHook(); } } } void throwError(FlowState *flowState, int componentIndex, const char *errorMessage, const char *errorMessageDescription) { if (errorMessage) { char throwErrorMessage[512]; snprintf(throwErrorMessage, sizeof(throwErrorMessage), "%s: %s", errorMessage, errorMessageDescription); throwError(flowState, componentIndex, throwErrorMessage); } else { throwError(flowState, componentIndex, errorMessageDescription); } } } // namespace flow } // namespace eez