Open dong-king opened 2 months ago
I don't think this is memory size issue. If you can post the eez-project file with which I can reproduce this problem it would be of great help.
Sometimes I want to execute a slightly complex js code directly in the action, so I customize an action based on your EvalJSExprActionComponent, as follows:
export class JsActionComponent extends ActionComponent {
static classInfo = makeDerivedClassInfo(ActionComponent.classInfo, {
componentPaletteGroupName: "Dashboard Specific",
label: () => "JS",
componentPaletteLabel: "JS",
properties: [
{
name: "expression",
type: PropertyType.MultilineText,
propertyGridGroup: specificGroup,
monospaceFont: true,
disableSpellcheck: true,
flowProperty: "template-literal"
},
makeExpressionProperty(
{
// The escape character corresponding to '{'
name: "leftEscape",
type: PropertyType.MultilineText,
propertyGridGroup: specificGroup
},
"string"
),
makeExpressionProperty(
{
// // The escape character corresponding to '}'
name: "rightEscape",
type: PropertyType.MultilineText,
propertyGridGroup: specificGroup
},
"string"
)
],
beforeLoadHook: (
component: JsActionComponent,
jsComponent: Partial<JsActionComponent>
) => {
if (
!jsComponent.customOutputs ||
jsComponent.customOutputs.length == 0
) {
jsComponent.customOutputs = [
{
name: "result",
type: "any"
}
] as any;
}
},
check: (component: EvalJSExprActionComponent, messages: IMessage[]) => {
const { valueExpressions } = component.expandExpressionForBuild();
valueExpressions.forEach(valueExpression => {
try {
checkExpression(component, valueExpression);
} catch (err) {
messages.push(
new Message(
MessageType.ERROR,
`Invalid expression "${valueExpression}": ${err}`,
getChildOfObject(component, "expression")
)
);
}
});
},
icon: (
<svg viewBox="0 0 1664 1792">
<path d="M384 1536q0-53-37.5-90.5T256 1408t-90.5 37.5T128 1536t37.5 90.5T256 1664t90.5-37.5T384 1536zm384 0q0-53-37.5-90.5T640 1408t-90.5 37.5T512 1536t37.5 90.5T640 1664t90.5-37.5T768 1536zm-384-384q0-53-37.5-90.5T256 1024t-90.5 37.5T128 1152t37.5 90.5T256 1280t90.5-37.5T384 1152zm768 384q0-53-37.5-90.5T1024 1408t-90.5 37.5T896 1536t37.5 90.5 90.5 37.5 90.5-37.5 37.5-90.5zm-384-384q0-53-37.5-90.5T640 1024t-90.5 37.5T512 1152t37.5 90.5T640 1280t90.5-37.5T768 1152zM384 768q0-53-37.5-90.5T256 640t-90.5 37.5T128 768t37.5 90.5T256 896t90.5-37.5T384 768zm768 384q0-53-37.5-90.5T1024 1024t-90.5 37.5T896 1152t37.5 90.5 90.5 37.5 90.5-37.5 37.5-90.5zM768 768q0-53-37.5-90.5T640 640t-90.5 37.5T512 768t37.5 90.5T640 896t90.5-37.5T768 768zm768 768v-384q0-52-38-90t-90-38-90 38-38 90v384q0 52 38 90t90 38 90-38 38-90zm-384-768q0-53-37.5-90.5T1024 640t-90.5 37.5T896 768t37.5 90.5T1024 896t90.5-37.5T1152 768zm384-320V192q0-26-19-45t-45-19H192q-26 0-45 19t-19 45v256q0 26 19 45t45 19h1280q26 0 45-19t19-45zm0 320q0-53-37.5-90.5T1408 640t-90.5 37.5T1280 768t37.5 90.5T1408 896t90.5-37.5T1536 768zm128-640v1536q0 52-38 90t-90 38H128q-52 0-90-38t-38-90V128q0-52 38-90t90-38h1408q52 0 90 38t38 90z" />
</svg>
),
componentHeaderColor: "#A6BBCF",
defaultValue: {
leftEscape: `"#l#"`,
rightEscape: `"#r#"`,
customOutputs: [
{
name: "result",
type: "any"
}
]
},
execute: async (context: IDashboardComponentContext) => {
// const expression = context.evalProperty<string>("expression");
// console.log(expression);
let leftEscape = context.evalProperty("leftEscape");
if (
leftEscape == undefined ||
typeof leftEscape != "string" ||
leftEscape.trim() == ""
) {
context.throwError(`Invalid leftEscape property`);
return;
}
let rightEscape = context.evalProperty("rightEscape");
if (
rightEscape == undefined ||
typeof rightEscape != "string" ||
rightEscape.trim() == ""
) {
context.throwError(`Invalid rightEscape property`);
return;
}
let expression = context.getStringParam(0);
const expressionValues = context.getExpressionListParam(4);
const values: any = {};
for (let i = 0; i < expressionValues.length; i++) {
const name = `_val${i}`;
values[name] = expressionValues[i];
}
let regex = new RegExp(`${leftEscape}`, "g");
expression = expression.replace(regex, `{`);
regex = new RegExp(`${rightEscape}`, "g");
expression = expression.replace(regex, `}`);
context = context.startAsyncExecution();
let param = {
context,
values
};
(async function (
code: string,
globalModules: { [moduleName: string]: any}) {
try {
const moduleNames = Object.keys(globalModules);
const args = moduleNames.join(", ");
const factoryFnCode = `return async (${args}) => {
${code}
}`;
const factoryFn = new Function(factoryFnCode);
console.log(factoryFnCode);
const fn = factoryFn();
await fn(
...moduleNames.map(moduleName => globalModules[moduleName])
);
} catch (err) {
context.throwError(err.toString());
} finally {
context.endAsyncExecution();
}
})(expression || "", param);
}
});
expression: string;
constructor() {
super();
makeObservable(this, {
expression: observable
});
}
static parse(expression: string) {
const inputs = new Set<string>();
if (expression) {
EvalJSExprActionComponent.PARAMS_REGEXP.lastIndex = 0;
let str = expression;
while (true) {
let matches = str.match(
EvalJSExprActionComponent.PARAMS_REGEXP
);
if (!matches) {
break;
}
const input = matches[1].trim();
inputs.add(input);
str = str.substring(matches.index! + matches[1].length);
}
}
return Array.from(inputs.keys());
}
getInputs() {
return [
{
name: "@seqin",
type: "any" as ValueType,
isSequenceInput: true,
isOptionalInput: true
},
...super.getInputs()
];
}
getOutputs(): ComponentOutput[] {
return [
{
name: "@seqout",
type: "null",
isSequenceOutput: true,
isOptionalOutput: true
},
...super.getOutputs()
];
}
getBody(flowContext: IFlowContext): React.ReactNode {
return (
<div className="body">
<pre>{this.expression}</pre>
</div>
);
}
expandExpressionForBuild() {
let expression = this.expression;
let valueExpressions: any[] = [];
JsActionComponent.parse(expression).forEach(
(valueExpression: any, i: number) => {
const name = `_val${i}`;
valueExpressions.push(valueExpression);
let regex = new RegExp(`{${valueExpression}}`, "g");
expression = expression.replace(regex, `values.${name}`);
}
);
return { expression, valueExpressions };
}
buildFlowComponentSpecific(assets: Assets, dataBuffer: DataBuffer) {
const { expression, valueExpressions } =
this.expandExpressionForBuild();
dataBuffer.writeObjectOffset(() => dataBuffer.writeString(expression));
dataBuffer.writeArray(valueExpressions, valueExpression => {
try {
// as property
buildExpression(assets, dataBuffer, this, valueExpression);
} catch (err) {
assets.projectStore.outputSectionsStore.write(
Section.OUTPUT,
MessageType.ERROR,
err,
getChildOfObject(this, "expression")
);
dataBuffer.writeUint16NonAligned(makeEndInstruction());
}
});
}
}
registerClass("JsActionComponent", JsActionComponent);
Because {
and }
cannot be used, I provide leftEscape
(default is #l#
) and rightEscape
(default is #r#
) to escape {
and }
.
The following screenshot is a simple usage:
This custom action works fine in most cases. But sometimes it prompts: Uncaught RuntimeError: memory access out of bounds and cannot read properties of null (reading 'context').
I cant find the reason.
BTW If you think this action is useful, you can merge it into eez-studio.
BTW If you think this action is useful, you can merge it into eez-studio.
Yes, this is useful. I was aware of this issue with {
and }
, but I wasn't sure which way is the best to resolve it. I think maybe better option is to set left escape and right escape for the EEZ Flow expressions not for the JavaScript's {
and }
.
BTW There is JSON.parse
action, so you don't need to use EvalJS for that.
I tried your Action with the same JavaScript code and I didn't see the problem so far. Can you tell me here:
What type you selected for the result
output and in
input?
This particular error I had before:
and it is fixed now in the latest version of Studio. You are probably on some older version.
What type you selected for the
result
output andin
input?
result
and in
correspond to my custom structure.
The previous screenshot is just to illustrate how to use this action, not the action that will cause an error.
In fact, an action that cause errors will not fail every time. I have a dashboard project with a lot of business logic. When I put the actions that cause errors in this project into a new dashboard project, they will not cause errors.
Maybe there are other reasons in this complex dashboard project that cause errors.
Basically, the errors occur when const factoryFn = new Function(factoryFnCode);
or await fn( ...moduleNames.map(moduleName => globalModules[moduleName]) );
is executed.
Can you log the value of inputSelectInfoStr
, I want to know the value when error occurs. Also, how structure WAVE_TABLESOURCE... is defined?
This particular error I had before:
and it is fixed now in the latest version of Studio. You are probably on some older version. My colleague synchronized to version 0.13.0.
Never mind, this error in lz4.js isn't important.
Describe the bug When I tested a certain action separately, it was normal. But when I put it into the project, it sometimes exposed a memory error:memory access out of bounds. I don't know if it is because my dashboard project has many business functions, which caused the memory to exceed the bounds.
Is the memory size defined in the memory.h file of eez-framework:
Is there any way to dynamically adjust the memory size?
Screenshots![image](https://github.com/eez-open/studio/assets/58795990/d38970bc-fd58-4d40-9017-fe5c9151f7d1)