#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#define BOOST_PYTHON_MAX_ARITY 4
#include "python_plugin.hh"
#include "interp_python.hh"
#include <boost/python/list.hpp>
namespace bp = boost::python;
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "rs274ngc.hh"
#include "rs274ngc_return.hh"
#include "rs274ngc_interp.hh"
#include "interp_internal.hh"
bool Interp::has_user_mcode(setup_pointer settings,block_pointer block)
{
unsigned i;
for(i = 0; i < sizeof(block->m_modes)/sizeof(int); i++) {
if (block->m_modes[i] == -1)
continue;
if (M_REMAPPABLE(block->m_modes[i]) &&
settings->m_remapped[block->m_modes[i]])
return true;
}
return false;
}
bool Interp::remap_in_progress(const char *code)
{
remap_pointer rp = remapping(code);
if (rp == NULL)
return false;
for (int i = _setup.remap_level; i > 0; i--) {
if (_setup.blocks[i].executing_remap == rp) {
return true;
}
}
return false;
}
int Interp::convert_remapped_code(block_pointer block,
setup_pointer settings,
int phase,
char letter,
int number)
{
remap_pointer remap;
char key[2];
int status;
block_pointer cblock;
bp::list plist;
char cmd[LINELEN];
if (number == -1)
logRemap("convert_remapped_code '%c'", letter);
else
logRemap("convert_remapped_code '%c%d'", letter, number);
switch (toupper(letter)) {
case 'M':
remap = settings->m_remapped[number];
break;
case 'G':
remap = settings->g_remapped[number];
break;
default:
key[0] = letter;
key[1] = '\0';
remap = remapping((const char *)key);
}
CHKS((remap == NULL), "BUG: convert_remapped_code: no remapping");
snprintf(cmd, sizeof(cmd),"O <%s> call ", REMAP_FUNC(remap));
cblock = &CONTROLLING_BLOCK(*settings);
cblock->executing_remap = remap; cblock->param_cnt = 0;
if (remap->argspec && (strchr(remap->argspec, '@') != NULL)) {
CHP(add_parameters(settings, cblock, &cmd[strlen(cmd)]));
}
if ((_setup.debugmask & EMC_DEBUG_REMAP) &&
(_setup.loggingLevel > 2)) {
logRemap("convert_remapped_code(%s)", cmd);
}
status = read(cmd);
block_pointer eblock = &EXECUTING_BLOCK(*settings);
eblock->call_type = CT_REMAP;
CHKS(status != INTERP_OK,
"convert_remapped_code: initial read returned %s",
interp_status(status));
return(- phase);
}
int Interp::add_parameters(setup_pointer settings,
block_pointer cblock,
char *posarglist)
{
const char *s,*argspec, *code;
block_pointer block;
char missing[30],optional[30],required[30];
char *m = missing;
char *o = optional;
char *r = required;
char msg[LINELEN], tail[LINELEN];
bool errored = false;
remap_pointer rptr = cblock->executing_remap;
context_pointer active_frame = &settings->sub_context[settings->call_level];
if (!rptr) {
ERS("BUG: add_parameters: remap_frame: executing_remap == NULL ");
}
code = rptr->name;
bool pydict = rptr->remap_py || rptr->prolog_func || rptr->epilog_func;
std::fill(missing, std::end(missing), 0);
std::fill(optional, std::end(optional), 0);
std::fill(required, std::end(required), 0);
std::fill(msg, std::end(msg), 0);
std::fill(tail, std::end(tail), 0);
s = argspec = rptr->argspec;
CHKS((argspec == NULL),"BUG: add_parameters: argspec = NULL");
while (*s) {
if (isupper(*s) && !strchr(required,*s)) *r++ = tolower(*s);
if (islower(*s) && !strchr(optional,*s)) *o++ = *s;
if (strchr(">^Nn",*s) && !strchr(required,*s)) *r++ = *s;
s++;
}
block = &CONTROLLING_BLOCK((*settings));
logNP("add_parameters code=%s argspec=%s call_level=%d r=%s o=%s pydict=%d\n",
code,argspec,settings->call_level,required,optional,pydict);
#define STORE(name,value) \
if (pydict) { \
try { \
active_frame->pystuff.impl->kwargs[name] = value; \
} \
catch (bp::error_already_set) { \
PyErr_Print(); \
PyErr_Clear(); \
ERS("add_parameters: cant add '%s' to args",name); \
} \
} \
if (posarglist) { \
char actual[LINELEN]; \
snprintf(actual, sizeof(actual),"[%.4lf]", value); \
strcat(posarglist, actual); \
cblock->param_cnt++; \
} else { \
add_named_param(name,0); \
store_named_param(settings,name,value,0); \
}
#define PARAM(spec,name,flag,value) \
if ((flag)) { \
\
if (strchr(required,spec) || strchr(optional,spec)) { \
STORE(name,value); \
} \
} else { \
if (strchr(required,spec)) { \
*m++ = spec; \
errored = true; \
} \
}
s = rptr->argspec;
while (*s) {
switch (tolower(*s)) {
case 'a' : PARAM('a',"a",block->a_flag,block->a_number); break;
case 'b' : PARAM('b',"b",block->b_flag,block->b_number); break;
case 'c' : PARAM('c',"c",block->c_flag,block->c_number); break;
case 'd' : PARAM('d',"d",block->d_flag,block->d_number_float); break;
case 'e' : PARAM('e',"e",block->e_flag,block->e_number); break;
case 'f' : PARAM('f',"f",block->f_flag,block->f_number); break;
case 'h' : PARAM('h',"h",block->h_flag,(double) block->h_number); break;
case 'i' : PARAM('i',"i",block->i_flag,block->i_number); break;
case 'j' : PARAM('j',"j",block->j_flag,block->j_number); break;
case 'k' : PARAM('k',"k",block->k_flag,block->k_number); break;
case 'l' : PARAM('l',"l",block->l_flag,(double) block->l_number); break;
case 'p' : PARAM('p',"p",block->p_flag,block->p_number); break;
case 'q' : PARAM('q',"q",block->q_flag,block->q_number); break;
case 'r' : PARAM('r',"r",block->r_flag,block->r_number); break;
case 's' : PARAM('s',"s",block->s_flag,block->s_number); break;
case 't' : PARAM('t',"t",block->t_flag, (double) block->t_number); break;
case 'u' : PARAM('u',"u",block->u_flag,block->u_number); break;
case 'v' : PARAM('v',"v",block->v_flag,block->v_number); break;
case 'w' : PARAM('w',"w",block->w_flag,block->w_number); break;
case 'x' : PARAM('x',"x",block->x_flag,block->x_number); break;
case 'y' : PARAM('y',"y",block->y_flag,block->y_number); break;
case 'z' : PARAM('z',"z",block->z_flag,block->z_number); break;
case '-' : break; default: ;
}
s++;
}
s = missing;
if (*s) {
strcat(tail," missing: ");
}
while (*s) {
errored = true;
char c = toupper(*s);
strncat(tail,&c,1);
if (*(s+1)) strcat(tail,",");
s++;
}
if (strchr(required,'n') || strchr(required,'N')) {
STORE("n",(double) cblock->saved_line_number);
}
if (strchr(required,'>')) {
if (settings->feed_rate > 0.0) {
STORE("f",settings->feed_rate);
} else {
strcat(tail,"F>0,");
errored = true;
}
}
if (strchr(required,'^')) {
if (settings->speed[0] > 0.0) {
STORE("s",settings->speed[0]);
} else {
strcat(tail,"S>0,");
errored = true;
}
}
if (errored) {
ERS("user-defined %s:%s",
code, tail);
}
return INTERP_OK;
}
remap_pointer Interp::remapping(const char *code)
{
remap_iterator n = _setup.remaps.find(code);
if (n != _setup.remaps.end())
return &n->second;
else
return NULL;
}
int Interp::parse_remap(const char *inistring, int lineno)
{
char iniline[LINELEN];
char *argv[MAX_REMAPOPTS];
int argc = 0;
const char *code;
remap r;
bool errored = false;
int g1 = 0, g2 = 0;
int mcode = -1;
int gcode = -1;
char *s;
memset((void *)&r, 0, sizeof(remap));
r.modal_group = -1; r.motion_code = INT_MIN;
strcpy(iniline, inistring);
if ((s = strchr(iniline, '#')) != NULL) {
*s = '\0';
}
s = strtok((char *) iniline, " \t");
while( s != NULL && argc < MAX_REMAPOPTS - 1) {
argv[argc++] = s;
s = strtok( NULL, " \t" );
}
if (argc == MAX_REMAPOPTS) {
Error("parse_remap: too many arguments (max %d)", MAX_REMAPOPTS);
goto fail;
}
argv[argc] = NULL;
code = strstore(argv[0]);
r.name = code;
for (int i = 1; i < argc; i++) {
int kwlen = 0;
char *kw = argv[i];
char *arg = strchr(argv[i],'=');
if (arg != NULL) {
kwlen = arg - argv[i];
arg++;
if (!strlen(arg)) { Error("option '%s' - zero length value: %d:REMAP = %s",
kw,lineno,inistring);
errored = true;
continue;
}
} else { Error("option '%s' - missing '=<value>: %d:REMAP = %s",
kw,lineno,inistring);
errored = true;
continue;;
}
if (!strncasecmp(kw,"modalgroup",kwlen)) {
r.modal_group = atoi(arg);
continue;
}
if (!strncasecmp(kw,"argspec",kwlen)) {
size_t pos = strspn (arg,
"ABCDEFGHIJKLMNPQRSTUVWXYZabcdefghijklmnpqrstuvwxyz>^@");
if (pos != strlen(arg)) {
Error("argspec: illegal word '%c' - %d:REMAP = %s",
arg[pos],lineno,inistring);
errored = true;
continue;
}
r.argspec = strstore(arg);
continue;
}
if (!strncasecmp(kw,"prolog",kwlen)) {
if (PYUSABLE) {
r.prolog_func = strstore(arg);
} else {
Error("Python plugin required for prolog=, but not available: %d:REMAP = %s",
lineno,inistring);
errored = true;
continue;
}
continue;
}
if (!strncasecmp(kw,"epilog",kwlen)) {
if (PYUSABLE) {
r.epilog_func = strstore(arg);
} else {
Error("Python plugin required for epilog=, but not available: %d:REMAP = %s",
lineno,inistring);
errored = true;
continue;
}
continue;
}
if (!strncasecmp(kw,"ngc",kwlen)) {
if (r.remap_py) {
Error("cant remap to an ngc file and a Python function: - %d:REMAP = %s",
lineno,inistring);
errored = true;
continue;
}
FILE *fp = find_ngc_file(&_setup,arg);
if (fp) {
r.remap_ngc = strstore(arg);
fclose(fp);
} else {
Error("NGC file not found: ngc=%s - %d:REMAP = %s",
arg, lineno,inistring);
errored = true;
}
continue;
}
if (!strncasecmp(kw,"python",kwlen)) {
if (r.remap_ngc ) {
Error("cant remap to an ngc file and a Python function: - %d:REMAP = %s",
lineno,inistring);
errored = true;
continue;
}
if (!PYUSABLE) {
Error("Python plugin required for python=, but not available: %d:REMAP = %s",
lineno,inistring);
errored = true;
continue;
}
if (!is_pycallable(&_setup, REMAP_MODULE, arg)) {
Error("'%s' is not a Python callable function - %d:REMAP = %s",
arg,lineno,inistring);
errored = true;
continue;
}
r.remap_py = strstore(arg);
continue;
}
Error("unrecognized option '%*s' in %d:REMAP = %s",
kwlen,kw,lineno,inistring);
}
if (errored) {
goto fail;
}
if (remapping(code)) {
Error("code '%s' already remapped : %d:REMAP = %s",
code,lineno,inistring);
goto fail;
}
if ((r.remap_ngc == NULL) && (r.remap_py == NULL)) {
Error("code '%s' - no remap function given, use either 'python=<function>' or 'ngc=<basename>' : %d:REMAP = %s",
code,lineno,inistring);
goto fail;
}
#define CHECK(bad, fmt, ...) \
do { \
if (bad) { \
Log(fmt, ## __VA_ARGS__); \
goto fail; \
} \
} while(0)
switch (towlower(*code)) {
case 't':
case 's':
case 'f':
CHECK((strlen(code) > 1),"%d: %c remap - only single letter code allowed", lineno, *code);
CHECK((r.modal_group != -1), "%d: %c remap - modal group setting ignored - fixed sequencing", lineno, *code);
_setup.remaps[code] = r;
break;
case 'm':
if (sscanf(code + 1, "%d", &mcode) != 1) {
Error("parsing M-code: expecting integer like 'M420', got '%s' : %d:REMAP = %s",
code,lineno,inistring);
goto fail;
}
if (r.modal_group == -1) {
Error("warning: code '%s' : no modalgroup=<int> given, using default group %d : %d:REMAP = %s",
code, MCODE_DEFAULT_MODAL_GROUP,lineno,inistring);
r.modal_group = MCODE_DEFAULT_MODAL_GROUP;
}
if (!M_MODE_OK(r.modal_group)) {
Error("error: code '%s' : invalid modalgroup=<int> given (currently valid: 4..10) : %d:REMAP = %s",
code,lineno,inistring);
goto fail;
}
_setup.remaps[code] = r;
_setup.m_remapped[mcode] = &_setup.remaps[code];
break;
case 'g':
if (sscanf(code + 1, "%d.%d", &g1, &g2) == 2) {
gcode = g1 * 10 + g2;
}
if ( gcode == -1) {
if (sscanf(code + 1, "%d", &gcode) != 1) {
Error("code '%s' : cant parse G-code : %d:REMAP = %s",
code, lineno, inistring);
goto fail;
}
gcode *= 10;
}
r.motion_code = gcode;
if (r.modal_group == -1) {
Error("warning: code '%s' : no modalgroup=<int> given, using default group %d : %d:REMAP = %s",
code, GCODE_DEFAULT_MODAL_GROUP, lineno, inistring);
r.modal_group = GCODE_DEFAULT_MODAL_GROUP;
}
if (!G_MODE_OK(r.modal_group)) {
Error("error: code '%s' : %s modalgroup=<int> given : %d:REMAP = %s",
argv[0],
r.modal_group == -1 ? "no" : "invalid",
lineno,
inistring);
goto fail;
}
_setup.remaps[code] = r;
_setup.g_remapped[gcode] = &_setup.remaps[code];
break;
default:
if ((r.prolog_func || r.remap_py || r.epilog_func) &&
(!PYUSABLE)) {
fprintf(stderr, "fatal: REMAP requires the Python plugin, which did not initialize\n");
break;
}
Log("REMAP BUG=%s %d:REMAP = %s",
code,lineno,inistring);
}
return INTERP_OK;
fail:
return INTERP_ERROR;
}