mirror of
https://github.com/grblHAL/core.git
synced 2026-02-05 08:34:01 +08:00
$700 controls whether they are scanned for internally in the main program (1) or always located externally (0). If scanned for internally the program is run twice, initially in check mode to locate the subroutines before it is rewound and run in normal mode. If stored externally the file P<macro number>.macro is run, <macro number> is the M98 P value.
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;
|
|
|
|
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
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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
|
|
|
|
void ngc_flowctrl_unwind_stack (vfs_file_t *file)
|
|
{
|
|
clear_subs(file);
|
|
while(stack_idx >= 0 && stack[stack_idx].file == file)
|
|
stack_pull();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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.
|
|
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;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
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
|