mirror of
https://github.com/grblHAL/core.git
synced 2026-03-24 05:12:31 +08:00
Improved alarm handling, if a critical alarm is active when a non-critical alarm is raised the non-critical alarm will be delayed until after reset for the critical alarm. Improved Modbus exception handling and added high level API call for creating and sending Modbus messages. Added G65P7 inbuilt macro for interacting with Modbus devices from gcode. See the Wiki for details.
780 lines
27 KiB
C
780 lines
27 KiB
C
/*
|
|
ngc_flowctrl.c - An embedded CNC Controller with rs274/ngc (g-code) support
|
|
|
|
Program flow control, for filesystem macros
|
|
|
|
Part of grblHAL
|
|
|
|
Copyright (c) 2023-2024 Terje Io
|
|
|
|
grblHAL 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.
|
|
|
|
grblHAL 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 grblHAL. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include "hal.h"
|
|
|
|
#if NGC_EXPRESSIONS_ENABLE
|
|
|
|
#include <math.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "errors.h"
|
|
#include "ngc_expr.h"
|
|
#include "ngc_params.h"
|
|
#include "stream_file.h"
|
|
//#include "string_registers.h"
|
|
|
|
#ifndef NGC_STACK_DEPTH
|
|
#define NGC_STACK_DEPTH 20
|
|
#endif
|
|
|
|
typedef enum {
|
|
NGCFlowCtrl_NoOp = 0,
|
|
NGCFlowCtrl_If,
|
|
NGCFlowCtrl_ElseIf,
|
|
NGCFlowCtrl_Else,
|
|
NGCFlowCtrl_EndIf,
|
|
NGCFlowCtrl_Do,
|
|
NGCFlowCtrl_Continue,
|
|
NGCFlowCtrl_Break,
|
|
NGCFlowCtrl_While,
|
|
NGCFlowCtrl_EndWhile,
|
|
NGCFlowCtrl_Repeat,
|
|
NGCFlowCtrl_EndRepeat,
|
|
NGCFlowCtrl_Sub,
|
|
NGCFlowCtrl_EndSub,
|
|
NGCFlowCtrl_Call,
|
|
NGCFlowCtrl_Return,
|
|
NGCFlowCtrl_RaiseAlarm,
|
|
NGCFlowCtrl_RaiseError
|
|
} ngc_cmd_t;
|
|
|
|
typedef struct ngc_sub {
|
|
uint32_t o_label;
|
|
vfs_file_t *file;
|
|
size_t file_pos;
|
|
struct ngc_sub *next;
|
|
} ngc_sub_t;
|
|
|
|
typedef struct {
|
|
uint32_t o_label;
|
|
ngc_cmd_t operation;
|
|
ngc_sub_t *sub;
|
|
vfs_file_t *file;
|
|
size_t file_pos;
|
|
char *expr;
|
|
uint32_t repeats;
|
|
bool skip;
|
|
bool handled;
|
|
bool brk;
|
|
} ngc_stack_entry_t;
|
|
|
|
static volatile int_fast8_t stack_idx = -1;
|
|
static bool skip_sub = false;
|
|
static ngc_sub_t *subs = NULL, *exec_sub = NULL;
|
|
static ngc_stack_entry_t stack[NGC_STACK_DEPTH] = {0};
|
|
static on_gcode_message_ptr on_gcode_comment;
|
|
|
|
FLASHMEM static status_code_t read_command (char *line, uint_fast8_t *pos, ngc_cmd_t *operation)
|
|
{
|
|
char c = line[*pos];
|
|
status_code_t status = Status_OK;
|
|
|
|
(*pos)++;
|
|
|
|
switch(c) {
|
|
|
|
case 'A':
|
|
if (!strncmp(line + *pos, "LARM", 4)) {
|
|
*operation = NGCFlowCtrl_RaiseAlarm;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with A
|
|
break;
|
|
|
|
case 'B':
|
|
if (!strncmp(line + *pos, "REAK", 4)) {
|
|
*operation = NGCFlowCtrl_Break;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with B
|
|
break;
|
|
|
|
case 'C':
|
|
if (!strncmp(line + *pos, "ONTINUE", 7)) {
|
|
*operation = NGCFlowCtrl_Continue;
|
|
*pos += 7;
|
|
} else if (!strncmp(line + *pos, "ALL", 3)) {
|
|
*operation = NGCFlowCtrl_Call;
|
|
*pos += 3;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with C
|
|
break;
|
|
|
|
case 'D':
|
|
if (line[*pos] == 'O') {
|
|
*operation = NGCFlowCtrl_Do;
|
|
(*pos)++;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with D
|
|
break;
|
|
|
|
case 'E':
|
|
if (!strncmp(line + *pos, "LSEIF", 5)) {
|
|
*operation = NGCFlowCtrl_ElseIf;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "LSE", 3)) {
|
|
*operation = NGCFlowCtrl_Else;
|
|
*pos += 3;
|
|
} else if (!strncmp(line + *pos, "NDIF", 4)) {
|
|
*operation = NGCFlowCtrl_EndIf;
|
|
*pos += 4;
|
|
} else if (!strncmp(line + *pos, "NDWHILE", 7)) {
|
|
*operation = NGCFlowCtrl_EndWhile;
|
|
*pos += 7;
|
|
} else if (!strncmp(line + *pos, "NDREPEAT", 8)) {
|
|
*operation = NGCFlowCtrl_EndRepeat;
|
|
*pos += 8;
|
|
} else if (!strncmp(line + *pos, "NDSUB", 5)) {
|
|
*operation = NGCFlowCtrl_EndSub;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "RROR", 4)) {
|
|
*operation = NGCFlowCtrl_RaiseError;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with E
|
|
break;
|
|
|
|
case 'I':
|
|
if(line[*pos] == 'F') {
|
|
*operation = NGCFlowCtrl_If;
|
|
(*pos)++;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with F
|
|
break;
|
|
|
|
case 'R':
|
|
if (!strncmp(line + *pos, "EPEAT", 5)) {
|
|
*operation = NGCFlowCtrl_Repeat;
|
|
*pos += 5;
|
|
} else if (!strncmp(line + *pos, "ETURN", 5)) {
|
|
*operation = NGCFlowCtrl_Return;
|
|
*pos += 5;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with R
|
|
break;
|
|
|
|
case 'S':
|
|
if (!strncmp(line + *pos, "UB", 2)) {
|
|
*operation = NGCFlowCtrl_Sub;
|
|
*pos += 2;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with S
|
|
break;
|
|
|
|
case 'W':
|
|
if (!strncmp(line + *pos, "HILE", 4)) {
|
|
*operation = NGCFlowCtrl_While;
|
|
*pos += 4;
|
|
} else
|
|
status = Status_FlowControlSyntaxError; // Unknown statement name starting with W
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError; // Unknown statement
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
// Returns the last called named sub with reference count
|
|
FLASHMEM static ngc_sub_t *get_refcount (uint32_t *refcount)
|
|
{
|
|
ngc_sub_t *sub, *last = NULL;
|
|
uint32_t o_label = 0;
|
|
|
|
if((sub = subs)) do {
|
|
if(sub->o_label > NGC_MAX_PARAM_ID)
|
|
o_label = sub->o_label;
|
|
} while((sub = sub->next));
|
|
|
|
if((sub = subs)) do {
|
|
if(sub->o_label == o_label) {
|
|
last = sub;
|
|
(*refcount)++;
|
|
}
|
|
} while((sub = sub->next));
|
|
|
|
return last;
|
|
}
|
|
|
|
FLASHMEM static ngc_sub_t *add_sub (uint32_t o_label, vfs_file_t *file)
|
|
{
|
|
ngc_sub_t *sub;
|
|
|
|
if((sub = malloc(sizeof(ngc_sub_t))) != NULL) {
|
|
sub->o_label = o_label;
|
|
sub->file = file;
|
|
sub->file_pos = vfs_tell(file);
|
|
sub->next = NULL;
|
|
if(subs == NULL)
|
|
subs = sub;
|
|
else {
|
|
ngc_sub_t *last = subs;
|
|
while(last->next)
|
|
last = last->next;
|
|
last->next = sub;
|
|
}
|
|
}
|
|
|
|
return sub;
|
|
}
|
|
|
|
FLASHMEM static void clear_subs (vfs_file_t *file)
|
|
{
|
|
ngc_sub_t *current = subs, *prev = NULL, *next;
|
|
|
|
subs = NULL;
|
|
|
|
while(current) {
|
|
next = current->next;
|
|
if(file == NULL || file == current->file) {
|
|
free(current);
|
|
if(prev)
|
|
prev->next = next;
|
|
} else {
|
|
if(subs == NULL)
|
|
subs = current;
|
|
prev = current;
|
|
}
|
|
current = next;
|
|
}
|
|
}
|
|
|
|
FLASHMEM static status_code_t stack_push (uint32_t o_label, ngc_cmd_t operation)
|
|
{
|
|
if(stack_idx < (NGC_STACK_DEPTH - 1) && (operation != NGCFlowCtrl_Call || ngc_call_push(&stack[stack_idx + 1]))) {
|
|
stack[++stack_idx].o_label = o_label;
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].operation = operation;
|
|
stack[stack_idx].sub = exec_sub;
|
|
return Status_OK;
|
|
}
|
|
|
|
return Status_FlowControlStackOverflow;
|
|
}
|
|
|
|
FLASHMEM static bool stack_pull (void)
|
|
{
|
|
bool ok;
|
|
|
|
if((ok = stack_idx >= 0)) {
|
|
if(stack[stack_idx].expr)
|
|
free(stack[stack_idx].expr);
|
|
if(stack[stack_idx].operation == NGCFlowCtrl_Call)
|
|
ngc_call_pop();
|
|
memset(&stack[stack_idx], 0, sizeof(ngc_stack_entry_t));
|
|
stack_idx--;
|
|
}
|
|
|
|
return ok;
|
|
}
|
|
|
|
FLASHMEM static void stack_unwind_sub (uint32_t o_label)
|
|
{
|
|
while(stack_idx >= 0 && stack[stack_idx].o_label != o_label)
|
|
stack_pull();
|
|
|
|
if(stack_idx >= 0) {
|
|
if(o_label > NGC_MAX_PARAM_ID) {
|
|
uint32_t count = 0;
|
|
if(stack[stack_idx].sub == get_refcount(&count) && count == 1)
|
|
ngc_string_param_delete((ngc_string_id_t)o_label);
|
|
clear_subs(stack[stack_idx].file);
|
|
stream_redirect_close(stack[stack_idx].file);
|
|
} else
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
|
|
stack_pull();
|
|
}
|
|
|
|
exec_sub = stack_idx >= 0 ? stack[stack_idx].sub : NULL;
|
|
}
|
|
|
|
// Public functions
|
|
|
|
FLASHMEM void ngc_flowctrl_unwind_stack (vfs_file_t *file)
|
|
{
|
|
clear_subs(file);
|
|
while(stack_idx >= 0 && stack[stack_idx].file == file)
|
|
stack_pull();
|
|
}
|
|
|
|
FLASHMEM static status_code_t onGcodeComment (char *comment)
|
|
{
|
|
uint_fast8_t pos = 6;
|
|
status_code_t status = Status_OK;
|
|
|
|
if(!strncasecmp(comment, "ABORT,", 6)) {
|
|
char *msg = NULL;
|
|
*comment = '!';
|
|
msg = grbl.on_process_gcode_comment(comment);
|
|
if(msg == NULL)
|
|
msg = ngc_substitute_parameters(comment + pos);
|
|
if(msg) {
|
|
report_message(msg, Message_Error);
|
|
free(msg);
|
|
}
|
|
status = Status_UserException;
|
|
} else if(on_gcode_comment)
|
|
status = on_gcode_comment(comment);
|
|
|
|
return status;
|
|
}
|
|
|
|
FLASHMEM void ngc_flowctrl_init (void)
|
|
{
|
|
static bool init_ok = false;
|
|
|
|
if(!init_ok) {
|
|
init_ok = true;
|
|
on_gcode_comment = grbl.on_gcode_comment;
|
|
grbl.on_gcode_comment = onGcodeComment;
|
|
}
|
|
|
|
clear_subs(NULL);
|
|
while(stack_idx >= 0)
|
|
stack_pull();
|
|
}
|
|
|
|
// NOTE: onNamedSubError will be called recursively for each
|
|
// redirected file by the grbl.report.status_message() call.
|
|
FLASHMEM static status_code_t onNamedSubError (status_code_t status)
|
|
{
|
|
static bool closing = false;
|
|
|
|
if(stack_idx >= 0) {
|
|
|
|
ngc_sub_t *sub;
|
|
uint32_t o_label = 0;
|
|
|
|
if((sub = subs)) do {
|
|
if(sub->file == hal.stream.file && sub->o_label > NGC_MAX_PARAM_ID)
|
|
o_label = sub->o_label;
|
|
} while(o_label == 0 && (sub = sub->next));
|
|
|
|
if(sub) {
|
|
|
|
if(!closing) {
|
|
char *name, msg[100];
|
|
closing = true;
|
|
if((name = ngc_string_param_get((ngc_string_id_t)o_label))) {
|
|
sprintf(msg, "error %d in named sub %s.macro", (uint8_t)status, name);
|
|
report_message(msg, Message_Warning);
|
|
}
|
|
}
|
|
|
|
stack_unwind_sub(o_label);
|
|
status = grbl.report.status_message(status);
|
|
}
|
|
|
|
closing = false;
|
|
ngc_flowctrl_init();
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
FLASHMEM static status_code_t onNamedSubEOF (vfs_file_t *file, status_code_t status)
|
|
{
|
|
if(stack_idx >= 0 && stack[stack_idx].file == file) {
|
|
stream_redirect_close(stack[stack_idx].file);
|
|
ngc_flowctrl_unwind_stack(stack[stack_idx].file);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
FLASHMEM status_code_t ngc_flowctrl (uint32_t o_label, char *line, uint_fast8_t *pos, bool *skip)
|
|
{
|
|
float value;
|
|
bool skipping;
|
|
ngc_cmd_t operation, last_op;
|
|
|
|
status_code_t status;
|
|
|
|
if((status = read_command(line, pos, &operation)) != Status_OK)
|
|
return status;
|
|
|
|
skipping = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
|
|
last_op = stack_idx >= 0 ? stack[stack_idx].operation : (skip_sub ? NGCFlowCtrl_Sub : NGCFlowCtrl_NoOp);
|
|
|
|
switch(operation) {
|
|
|
|
case NGCFlowCtrl_If:
|
|
if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
stack[stack_idx].skip = value == 0.0f;
|
|
stack[stack_idx].handled = !stack[stack_idx].skip;
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NGCFlowCtrl_ElseIf:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf) {
|
|
if(o_label == stack[stack_idx].o_label &&
|
|
!(stack[stack_idx].skip = stack[stack_idx].handled) && !stack[stack_idx].handled &&
|
|
(status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f)) {
|
|
stack[stack_idx].operation = operation;
|
|
stack[stack_idx].handled = true;
|
|
}
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Else:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(!(stack[stack_idx].skip = stack[stack_idx].handled))
|
|
stack[stack_idx].operation = operation;
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndIf:
|
|
if(last_op == NGCFlowCtrl_If || last_op == NGCFlowCtrl_ElseIf || last_op == NGCFlowCtrl_Else) {
|
|
if(o_label == stack[stack_idx].o_label)
|
|
stack_pull();
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Do:
|
|
if(hal.stream.file) {
|
|
if(!skipping && (status = stack_push(o_label, operation)) == Status_OK) {
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].skip = false;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_While:
|
|
if(hal.stream.file) {
|
|
char *expr = line + *pos;
|
|
if(stack_idx >= 0 && stack[stack_idx].brk) {
|
|
if(last_op == NGCFlowCtrl_Do && o_label == stack[stack_idx].o_label)
|
|
stack_pull();
|
|
} else if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if(last_op == NGCFlowCtrl_Do && o_label == stack[stack_idx].o_label) {
|
|
if(value != 0.0f)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack_pull();
|
|
} else if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f)) {
|
|
if((stack[stack_idx].expr = malloc(strlen(expr) + 1))) {
|
|
strcpy(stack[stack_idx].expr, expr);
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
} else
|
|
status = Status_FlowControlOutOfMemory;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndWhile:
|
|
if(hal.stream.file) {
|
|
if(last_op == NGCFlowCtrl_While) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(!skipping) {
|
|
uint_fast8_t pos = 0;
|
|
if(!stack[stack_idx].skip && (status = ngc_eval_expression(stack[stack_idx].expr, &pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0.0f))
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
}
|
|
}
|
|
if(stack[stack_idx].skip)
|
|
stack_pull();
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Repeat:
|
|
if(hal.stream.file) {
|
|
if(!skipping && (status = ngc_eval_expression(line, pos, &value)) == Status_OK) {
|
|
if((status = stack_push(o_label, operation)) == Status_OK) {
|
|
value = nearbyintf(value);
|
|
if(!(stack[stack_idx].skip = value <= 0.0f)) {
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].repeats = (uint32_t)value;
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndRepeat:
|
|
if(hal.stream.file) {
|
|
if(last_op == NGCFlowCtrl_Repeat) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
if(!skipping && stack[stack_idx].repeats && --stack[stack_idx].repeats)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack_pull();
|
|
}
|
|
} else if(!skipping)
|
|
status = Status_FlowControlSyntaxError;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Break:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
while(o_label != stack[stack_idx].o_label && stack_pull());
|
|
last_op = stack_idx >= 0 ? stack[stack_idx].operation : NGCFlowCtrl_NoOp;
|
|
if(last_op == NGCFlowCtrl_Do || last_op == NGCFlowCtrl_While || last_op == NGCFlowCtrl_Repeat) {
|
|
if(o_label == stack[stack_idx].o_label) {
|
|
stack[stack_idx].repeats = 0;
|
|
stack[stack_idx].brk = stack[stack_idx].skip = stack[stack_idx].handled = true;
|
|
}
|
|
} else
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Continue:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
while(o_label != stack[stack_idx].o_label && stack_pull());
|
|
if(stack_idx >= 0 && o_label == stack[stack_idx].o_label) switch(stack[stack_idx].operation) {
|
|
|
|
case NGCFlowCtrl_Repeat:
|
|
if(stack[stack_idx].repeats && --stack[stack_idx].repeats)
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
else
|
|
stack[stack_idx].skip = true;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Do:
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
break;
|
|
|
|
case NGCFlowCtrl_While:
|
|
{
|
|
uint_fast8_t pos = 0;
|
|
if(!stack[stack_idx].skip && (status = ngc_eval_expression(stack[stack_idx].expr, &pos, &value)) == Status_OK) {
|
|
if(!(stack[stack_idx].skip = value == 0))
|
|
vfs_seek(stack[stack_idx].file, stack[stack_idx].file_pos);
|
|
}
|
|
if(stack[stack_idx].skip) {
|
|
if(stack[stack_idx].expr) {
|
|
free(stack[stack_idx].expr);
|
|
stack[stack_idx].expr = NULL;
|
|
}
|
|
stack_pull();
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError;
|
|
break;
|
|
} else
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_RaiseAlarm:
|
|
if(!skipping && ngc_eval_expression(line, pos, &value) == Status_OK)
|
|
system_raise_alarm((alarm_code_t)value);
|
|
break;
|
|
|
|
case NGCFlowCtrl_RaiseError:
|
|
if(!skipping && ngc_eval_expression(line, pos, &value) == Status_OK)
|
|
status = (status_code_t)value;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Sub:
|
|
if(hal.stream.file) {
|
|
ngc_sub_t *sub;
|
|
if(o_label > NGC_MAX_PARAM_ID) {
|
|
|
|
if((sub = subs)) do {
|
|
if(sub->o_label == o_label && sub->file == hal.stream.file)
|
|
break;
|
|
} while(sub->next && (sub = sub->next));
|
|
|
|
if(sub == NULL || sub->o_label != o_label)
|
|
status = Status_FlowControlSyntaxError;
|
|
} else if(!(skip_sub = (sub = add_sub(o_label, hal.stream.file)) != NULL))
|
|
status = Status_FlowControlOutOfMemory;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_EndSub:
|
|
if(hal.stream.file) {
|
|
if(!skip_sub) {
|
|
stack_unwind_sub(o_label);
|
|
if(ngc_eval_expression(line, pos, &value) == Status_OK) {
|
|
ngc_named_param_set("_value", value);
|
|
ngc_named_param_set("_value_returned", 1.0f);
|
|
} else
|
|
ngc_named_param_set("_value_returned", 0.0f);
|
|
}
|
|
skip_sub = false;
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Call:
|
|
|
|
if(hal.stream.file || o_label > NGC_MAX_PARAM_ID) {
|
|
|
|
if(!skipping) {
|
|
|
|
ngc_sub_t *sub = NULL;
|
|
|
|
if(o_label > NGC_MAX_PARAM_ID) {
|
|
|
|
char *subname;
|
|
if((subname = ngc_string_param_get((ngc_string_id_t)o_label))) {
|
|
char filename[60];
|
|
vfs_file_t *file;
|
|
#if LITTLEFS_ENABLE == 1
|
|
sprintf(filename, "/littlefs/%s.macro", subname);
|
|
|
|
if((file = stream_redirect_read(filename, onNamedSubError, onNamedSubEOF)) == NULL) {
|
|
sprintf(filename, "/%s.macro", subname);
|
|
file = stream_redirect_read(filename, onNamedSubError, onNamedSubEOF);
|
|
}
|
|
#else
|
|
sprintf(filename, "/%s.macro", subname);
|
|
file = stream_redirect_read(filename, onNamedSubError, onNamedSubEOF);
|
|
#endif
|
|
if(file) {
|
|
if((sub = add_sub(o_label, file)) == NULL)
|
|
status = Status_FlowControlOutOfMemory;
|
|
} else
|
|
status = Status_FileOpenFailed;
|
|
}
|
|
} else if((sub = subs)) do {
|
|
if(sub->o_label == o_label && sub->file == hal.stream.file)
|
|
break;
|
|
} while((sub = sub->next));
|
|
|
|
if(sub == NULL)
|
|
status = Status_FlowControlSyntaxError;
|
|
else {
|
|
|
|
float params[30];
|
|
ngc_param_id_t param_id = 1;
|
|
|
|
while(line[*pos] && status == Status_OK && param_id <= 30) {
|
|
status = ngc_eval_expression(line, pos, ¶ms[param_id - 1]);
|
|
param_id++;
|
|
}
|
|
|
|
if(status == Status_OK && param_id < 30) do {
|
|
ngc_param_get(param_id, ¶ms[param_id - 1]);
|
|
} while(++param_id <= 30);
|
|
|
|
if(status == Status_OK && (status = stack_push(o_label, operation)) == Status_OK) {
|
|
|
|
stack[stack_idx].sub = exec_sub = sub;
|
|
stack[stack_idx].file = hal.stream.file;
|
|
stack[stack_idx].file_pos = vfs_tell(hal.stream.file);
|
|
stack[stack_idx].repeats = 1;
|
|
|
|
for(param_id = 1; param_id <= 30; param_id++) {
|
|
if(params[param_id - 1] != 0.0f) {
|
|
if(!ngc_param_set(param_id, params[param_id - 1]))
|
|
status = Status_FlowControlOutOfMemory;
|
|
}
|
|
}
|
|
|
|
if(status == Status_OK) {
|
|
ngc_named_param_set("_value", 0.0f);
|
|
ngc_named_param_set("_value_returned", 0.0f);
|
|
vfs_seek(sub->file, sub->file_pos);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
case NGCFlowCtrl_Return:
|
|
if(hal.stream.file) {
|
|
if(!skipping) {
|
|
|
|
bool g65_return = false;
|
|
|
|
if(exec_sub)
|
|
stack_unwind_sub(o_label);
|
|
else if((g65_return = !!grbl.on_macro_return))
|
|
ngc_flowctrl_unwind_stack(stack[stack_idx].file);
|
|
|
|
if(ngc_eval_expression(line, pos, &value) == Status_OK) {
|
|
ngc_named_param_set("_value", value);
|
|
ngc_named_param_set("_value_returned", 1.0f);
|
|
} else
|
|
ngc_named_param_set("_value_returned", 0.0f);
|
|
|
|
if(g65_return)
|
|
grbl.on_macro_return();
|
|
}
|
|
} else
|
|
status = Status_FlowControlNotExecutingMacro;
|
|
break;
|
|
|
|
default:
|
|
status = Status_FlowControlSyntaxError;
|
|
}
|
|
|
|
if(status != Status_OK) {
|
|
ngc_flowctrl_init();
|
|
*skip = false;
|
|
if(settings.flags.ngc_debug_out)
|
|
report_message(line, Message_Plain);
|
|
} else {
|
|
*skip = skip_sub || (stack_idx >= 0 && stack[stack_idx].skip);
|
|
// char buf[200];
|
|
// sprintf(buf, "%d %d %d %s", *skip, stack_idx, stack[stack_idx].operation, line);
|
|
// report_message(buf, Message_Plain);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
#endif // NGC_EXPRESSIONS_ENABLE
|