LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org


The "motion id zero" anomaly is found and fixed: http://git.linuxcnc.org/gitweb?p=linuxcnc.git;a=commit;h=cfd174ad104f85ca82f8f5bebde4ecdf83575a92 and here: http://git.linuxcnc.org/gitweb?p=linuxcnc.git;a=commit;h=18d437822cf1b9a4694dacb0564201410e456a6b . This means that any motion id (which is also used as a line number feedback for the GUIs) can be used except the special value(s) described in motion.h .

It turns out that the collective subconscious must have known about this for a long time, and worked around it several times. Unfortunately these workarounds had several cascading side effects. Now that the real cause is found and fixed, the path is open to several repairs and imrpovements, which are:

 command.c:240:                reportError(_("%s move in MDI would exceed joint %d's positive limit"),
 command.c:250:                reportError(_("%s move in MDI would exceed joint %d's negative limit"),
 control.c:404:            reportError(_("Probe tripped during non-probe MDI command."));

While technically these will now report the proper linenumber, the test for id >= 0 and the corresponding MDI error message can now be removed to report the actual line number like in auto mode.

Line Number handling in Linuxcnc

One linuxcnc defect which I think is due for repair is the current state of affairs with respect to "line numbers".

The issue is quite complex, touches many corners of linuxcnc and has substantial breakage potential. Repair will not be achieved by a casual evening fix - some planning and design is required.

I'm therefore laying out the issue, and a sketch for a solution, and encourage discussion and rough consensus before actually doing anything about it.

The issue

Line numbers are used to correlate canon/motion commands with a program line number, and features like run-from-line. They are also key in control strcuture handling in the interpreter. Since the current abstraction is a single integer , it cannot cope with multiple files, or file types, which explains some of the issues with RFL. One of the consequences is limitations on tracing the current line in UI's, like the current line in an oword sub in Axis. Or generating a callstack for UI purposes.

There was a need for a different type of line number back in the history of Linuxcnc, for instance for indicating 'MDI mode' (which is basically a limited 'execute a string, not a file' operation). However, a shortcut was taken - instead of creating a abstraction for source location, like a tuple (type, source, offsets), the choice was to indicate 'MDI mode' as negative line numbers.

Another defect of line numbers in 'MDI mode' is that they are not idempotent, that is, reexecution of a statement does *not* yield identical line numbers for the same source location, which make comparison of 'line numbers' in MDI mode a bit meaningless. This for instance broke my attempt at better diagnostics of botched oword label handling, and I had to revert http://git.linuxcnc.org/gitweb?p=emc2.git;a=commit;h=73dcf03ed13064416219f2eff7150bb683e53530 .

This method just didnt break run-from-line in MDI mode because nobody ever tried to implement run-from-line in MDI mode, which is a perfectly reasonable feature wrt oword procedures btw. Do a 'grep pseudoMdiLineNumber? src/emc/task/*cc' to see why non-idempotency is the way it is. The only reason I can think of why this decision was made was to distinguish the motions queued by several MDI commands in turn, but I dont see where this is used in the code.

Remapping added another type of source location - Python scripts.

Revisiting the original idea of an integer for source location, it becomes pretty clear: correlation of source location based on ints as in current linuxcnc needs work.

Task intervening in line number calculation

One would think the interpreter knows best which line it is currently on, and would pass that information downstream on the interplist/node line_number attribute.

This is not the case for MDI handling: task explicitly uses pseudoMdiLineNumber? to override line number information generated by the interpreter after each interp.read()/interp.execute() cycle. This is obviously done to distinguish modes (negative line numbers indicate MDI). This use rests on the assumption that task always knows the right line number (because there arent any procedures or remappings, just a sequence of statements).

To generate accurate source coordinates, this has to go. TBD: are there any assumptions / use of the SIGN of a linenumber to determine mode? Experimentally setting the pseudoMdiLineNumber? to MAX_INT/2 so they are all positive seems to have no negative impact.

Reparing 'line numbers' : introducing 'source location'

To make a terminologic cut, I will name the replacement for line numbers 'source location' (sloc). This will be a new datatype which will encompass the current line numbers, and able to uniquely denote a 'source context'. A 'source context' will likely be a range of characters of some form (a range of bytes in an MDI string, a line or a word in a NGC file, a line in a Python file). Note that a 'line' may contain one or several source contexts.

Preliminary requirements

sloc's shall have the following features:

Note that support for source location and source context are independent of the language being used, be it rs274ngc, canterp, python, any new pluggable interpreter. A good implementation should be able to handle any current or future language.

Please add/change as you see fit.

line numbers and motion id's

Line numbers are used to tag motion commands processed in motion and the trajectory planner. They are treated as signed int in tp and motion.

Motion id's are used to distinguish separate motions when stepping. It is decided here (line 1955): http://git.linuxcnc.org/gitweb?p=emc2.git;a=blob;f=src/emc/motion/control.c;h=d3590a26e969e11777d487d46d3e8711e86b9ac6;hb=2cea8d434c775a3abcc28ac0f61930135b4e86c8#l1955

Through experimentation I determined: valid motion id's better not be zero (this seems to mean: "the motion queue is empty"), and for stepping between motions to work, they need to be different (they need not be in ascending order).

Jeff has discovered this fact independently: http://git.mah.priv.at/gitweb/emc2-dev.git/commit/314f3aa2d90e5ec1f7840166f3e0ad11a9fbc0a6

Bottom line: motion id's have to be different for stepping, and shall not be zero.

'source location' versus 'source context'

I am distinguishing 'source location' and 'source context' here. Source locations are an execution semantics concept. Source contexts are a UI thing.

I think any canon+motion command should carry an sloc, but it need not necessarily carry everthing that goes with it, like a filename, or a string. Separating the two concepts implies there needs to be a global lookup mechanism to retrieve a source context given a source location. UI's will definitely derive source contexts from sloc's, RT modules might or might not.

Note that the granularity of source contexts impacts the storage and retrieval mechanism and memory requirements. Storing filenames and MDI strings has limited memory requirements; tracing back to words, or other intra-line objects will require more memory. Shared memory is not necessarily a good place to store source contexts, because this will not work nicely if UI and task are on different machines.

This might or might not be an issue or make sense, I'm posing this to discussion. Maybe we have enough memory today to repeat the filename or underlying string in every canon command.

execution serials

Currently, separate interpreter invocations cannot be distinguished at the motion level. Since there is currently also no way, for instance, to break out of auto mode into mdi and execute some other code, there is no use case for that concept. However, as soon that becomes possible, it would be desirable to distinguish them. For instance, it would permit

Implementation options

I see the following options:

this would leave us with 32-3-8-3-8 = 10 bits of line number, or 1024. Not good enough. Now we could chop of some bits from source context (fewer files supported) or watchpoints but still less than ideal. Maybe not using the MSB to avoid sign extension is a good idea too.

Changing all int references where line numbers are stored to an sloc_t looks relatively harmless. It also has minimal impact on nml. For the watchpoints field, see: http://wiki.linuxcnc.org/cgi-bin/wiki.pl?WatchPoints

Processing: either define a few macros to extract/set subfields, or make it a struct with bitfields.

A tentative implementation of option 3 with bitfields could look like so:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

typedef struct sloc {
    union upper {
	unsigned long upr;
	struct {
	    unsigned watchpoints:8;
	    unsigned execserial:8;
	    unsigned un_used:16;
	} udetail;
    } upper;
    union lower {
	unsigned long lwr;
	struct {
	    unsigned lineno:20;  // > 1mio lines of NGC..ok.
	    unsigned type:3;
	    unsigned context:8;
	    unsigned unused_signbit:1; // dont tickle sign extension
	} ldetail;
    } lower;
} sloc_t;

#define exec_serial(x)  (x).upper.udetail.execserial
#define watch_points(x) (x).upper.udetail.watchpoints
#define sloc_type(x)    (x).lower.ldetail.type
#define sloc_context(x) (x).lower.ldetail.context
#define sloc_lineno(x)  (x).lower.ldetail.lineno
#define unused(x)       (x).upper.udetail.un_used

#define upper32(x)  (x).upper.upr
#define lower32(x)  (x).lower.lwr

void foo(sloc_t s)
    printf("exec_serial=%d\n", exec_serial(s));
    printf("watch_points=%d\n", watch_points(s));
    printf("sloc_type=%d\n", sloc_type(s));
    printf("sloc_context=%d\n", sloc_context(s));
    printf("sloc_lineno=%d\n", sloc_lineno(s));

int main(int argc, char **argv)
    sloc_t s;
    printf("sizeof sloc_t=%d\n",sizeof(sloc_t));
    exec_serial(s) = 1;
    watch_points(s) = 2;
    sloc_type(s) = 3;
    sloc_context(s) = 4;
    sloc_lineno(s)   = 0xbeef;
    unused(s)   = 0;



Open questions:

Related bugs + missing features:



Note there are several closed bugs with RFL, but some have less-than-optimal solutions.


LinuxCNCKnowledgeBase | RecentChanges | PageIndex | Preferences | LinuxCNC.org
This page is read-only. Follow the BasicSteps to edit pages. | View other revisions
Last edited April 3, 2012 12:30 am by MichaelHaberler (diff)
Published under a Creative Commons License