C/C++ user space HAL component compilation

More
26 May 2014 17:54 - 26 May 2014 17:57 #47337 by eneuro
Hello,
I've created my own RT kins component for my CNC machine www.linuxcnc.org/index.php/english/forum...ine-coordinate#47061
using linuxcnc comp utility and successfuly changed linuxcnc axis configuration to activate those kins www.linuxcnc.org/index.php/english/forum...inate?start=10#47258 .
Now I'd like to create user space C/C++ machine visualisation OpenGL appliaction calling HAL to read pins provided in my kins RT component and I'm confused if I could use this comp utility to compile it while as I read in linuxdev-cnc/src/hal/hal.h it looks like I need
#define ULAPI
in this case.

HAL uses the RTAPI/ULAPI interface. If RTAPI is #defined, (done
by the makefile) hal_lib.c would generate a kernel module hal_lib.o
that is insmoded and provides the functions for all kernel module
based components. The same source file compiled with the ULAPI
#define
would make a user space hal_lib.o that is staticlly linked
to user space code to make user space executables.


How to tell comp utility I'm going to make user space HAL component-not RT component?
Maybe I could create my own application makefile and include headers for HAL library passing
-DULAPI
to gcc/g++ compilation?
It looks like linuxcnc-dev/bin/comp creates its own Makefile this way:
makefile = os.path.join(tempdir, "Makefile")
    f = open(makefile, "w")
    print >>f, "%s: %s" % (binname, filename)
    print >>f, "\t$(CC) $(EXTRA_CFLAGS) -URTAPI -U__MODULE__ -DULAPI -Os %s -o $@ $< -Wl,-rpath,$(LIBDIR) -L$(LIBDIR) -llinuxcnchal %s" % (
        options.get("extra_compile_args", ""),
        options.get("extra_link_args", ""))
    print >>f, "include %s" % find_modinc()
    f.close()
Why -DULAPI is passed there while this utility created and installed RT HAL component eneurokins: ?
File Attachment:

I looks like either RTAPI or ULAPI should be defined:
./src/rtapi/rtapi.h:#if ( !defined RTAPI ) && ( !defined ULAPI )
./src/rtapi/rtapi.h:#error "Please define either RTAPI or ULAPI!"
./src/rtapi/rtapi.h:#if ( defined RTAPI ) && ( defined ULAPI )
./src/rtapi/rtapi.h:#error "Can't define both RTAPI and ULAPI!"
Last edit: 26 May 2014 17:57 by eneuro.

Please Log in or Create an account to join the conversation.

More
26 May 2014 19:00 #47342 by ArcEye
Hi

You have crossed the threshold of what can usefully be done with comp.

Once you want to start using C++ libraries, you really need to write the whole component from scratch.
Running comp without any arguements on a simple component will get you the generated C file which will give you a basis to work from
and capturing the output gcc string from a build will give you the basis of the build syntax

I have written a lot of Qt based C++ userspace components, for which I now have seperate libraries and Qt makespecs.

When I was writing in C and needed to add extra libraries I used the below from a script, which shows most of the defines and switches required
which may be of some use to you
g++ -Os -g -I. -I/usr/src/build/include -I/usr/realtime-2.6.32-122-rtai/include -I. -I/usr/realtime-2.6.32-122-rtai/include -D_FORTIFY_SOURCE=0 \
	    -mhard-float -fno-fast-math -mieee-fp -fno-unsafe-math-optimizations -D_GNU_SOURCE \
	    -I/usr/include/emc2 -Wframe-larger-than=2560 -DULAPI -Os \
	     -o $2 $2.cpp  -Wl,-rpath,/lib -L/lib -llinuxcncini -llinuxcnchal -lnml -llinuxcnc -lposemath -$3

regards

Please Log in or Create an account to join the conversation.

More
26 May 2014 21:03 - 26 May 2014 21:15 #47348 by eneuro
Thank you for these hints.
Finally I was able to compile my simple user space HAL enabled application which creates a few HAL signals needed and I was able to run it as standalone from command line

$ ./proparabolic

or using HAL utilities

$ halcmd loadusr proparabolic


I've used this simple command while catched first what comp writes to its temporary Makefile during RT HAL component compilation, but it looks like that user space HAL library is this linuxcnchal.so

$ make pro
g++ -I/usr/include/linuxcnc -I. -DULAPI -O4 proparabolic.cpp -o proparabolic -L/usr/lib -llinuxcnchal

In this application simply added this to ensure only ULAPI is defined whatever is specified in compilator options:
// For user space HAL RTAPI
#ifdef RTAPI
#undef RTAPI
#endif

#ifndef ULAPI
#define ULAPI
#endif

#include "hal.h"

Now, I can monitor those HAL signals created in this application: eneurokinsml eneurokinsmr eneurokinsmz as expected when issue MDI G-codes with my eneurokins loaded in linuxcnc axis configuration:
File Attachment:


However, I have no idea... how to read created HAL signal using this HAL interface in my C++ application :)
// signals
        hal_signal_delete(eneurokins_ml_sig);
        sigml=  hal_signal_new(eneurokins_ml_sig, HAL_FLOAT );
        hal_link(eneurokins_ml, eneurokins_ml_sig );

        hal_signal_delete(eneurokins_mr_sig);
        sigmr=  hal_signal_new(eneurokins_mr_sig, HAL_FLOAT );
        hal_link(eneurokins_mr, eneurokins_mr_sig );

        hal_signal_delete(eneurokins_mz_sig);
        sigmz=  hal_signal_new(eneurokins_mz_sig, HAL_FLOAT );
        hal_link(eneurokins_mz, eneurokins_mz_sig );

Functions: hal_signal_new and hal_link return int numbers.
Maybe missed something, while reading this hal.h , but ... I have no idea how to read those created float signals and store in C++ variable :S
I hope

linuxcnc-dev$ grep -ir hal_signal .

will help find how to convert those signals to real C++ float/double numbers ?:pinch:
Last edit: 26 May 2014 21:15 by eneuro.

Please Log in or Create an account to join the conversation.

More
26 May 2014 21:19 #47350 by ArcEye
Hi

This extract from my library will give you an idea about writing to and reading pins
int set_common(hal_type_t type, void *d_ptr, QString& value) 
{
// This function assumes that the mutex is held
int retval = 0;
double fval = 0;
long lval = 0;
unsigned long ulval = 0;

    switch (type) 
        {
        case HAL_BIT:
                    if( (value.contains("TRUE") || (value.contains("1"))))
	                    *(hal_bit_t *) (d_ptr) = 1;
	                else if( (value.contains("FALSE") || (value.contains("0"))))
	                    *(hal_bit_t *) (d_ptr) = 0;
                    else
                	    retval = -EINVAL;
	            	break;
        case HAL_FLOAT:
	                fval = value.toFloat();
	               	    *((hal_float_t *) (d_ptr)) = fval;
	            	break;
        case HAL_S32:
	                lval = value.toLong();
	                    *((hal_s32_t *) (d_ptr)) = lval;
	            	break;
        case HAL_U32:
	                ulval = value.toULong();
	                    *((hal_u32_t *) (d_ptr)) = ulval;
	            	break;
	    default:
	                /* Shouldn't get here, but just in case... */
	                retval = -EINVAL;
        }
    return retval;
}


int get_common(hal_type_t type, void *d_ptr, QString& value) 
{
// This function assumes that the mutex is held
int retval = 0;
int bitval = 0;
double fval = 0;
long lval = 0;
unsigned long ulval = 0;
QString str;

    switch (type) 
        {
        case HAL_BIT:
                    bitval = *(hal_bit_t *) (d_ptr);
                    str.sprintf("%d", bitval);
	            	break;
        case HAL_FLOAT:
                    fval = *((hal_float_t *) (d_ptr));
	                str.sprintf("%f", fval);
	            	break;
        case HAL_S32:
	                lval =  *((hal_s32_t *) (d_ptr));
                    str.sprintf("%ld", lval);
	            	break;
        case HAL_U32:
	                ulval = *((hal_u32_t *) (d_ptr));
	                str.sprintf("%lu", ulval);
	            	break;
	    default:
	                /* Shouldn't get here, but just in case... */
	                retval = -EINVAL;
        }
    
        //qDebug() <<  "str string = " << str;
    value = str;
        //qDebug() <<  "value string = " << value;   
    return 0;
}


The mutex code is something like
// get mutex before accessing shared data 
    rtapi_mutex_get(&(hal_data->mutex));
...
           get or set the value
...
    rtapi_mutex_give(&(hal_data->mutex));

regards

Please Log in or Create an account to join the conversation.

More
26 May 2014 22:27 - 26 May 2014 22:28 #47358 by eneuro

...
int get_common(hal_type_t type, void *d_ptr, QString& value)
{
...
int bitval = 0;
...
bitval = *(hal_bit_t *) (d_ptr);
...

Very nice, but I have NO pointers so far, only int numbers when signals are created using this hal_signal_new functions in user space ;)

HAL pins are created in kins RT kernel component:
struct haldata {
    hal_float_t *md;  // Machine diameter       
    hal_float_t *cx, *cy, *cz; // G-code cartesian coords
    hal_float_t *ml, *mr, *mz; // Machine coords
} *haldata;
...
if((result = hal_pin_float_new("eneurokins.ml", HAL_IO, &(haldata->ml), comp_id)) < 0) goto error;
if((result = hal_pin_float_new("eneurokins.mr", HAL_IO, &(haldata->mr), comp_id)) < 0) goto error;
if((result = hal_pin_float_new("eneurokins.mz", HAL_IO, &(haldata->mz), comp_id)) < 0) goto error;

How to find those pointers needed for signal/pin get/set in C++? :whistle:
Last edit: 26 May 2014 22:28 by eneuro.

Please Log in or Create an account to join the conversation.

More
26 May 2014 22:50 #47360 by ArcEye
Hi

When you create any pin you have to malloc the memory required to hold the data it can carry, that is the pointer.

As I said my libraries were for interfacing with Qt, I created various typedefs
// These are used as the basic data units for any pins created
// via the functions in this class
typedef struct {
  hal_bit_t *pin;
} bit_data_t;

typedef struct {
  hal_s32_t *pin;
} s32_data_t;

typedef struct {
  hal_float_t *pin;
} float_data_t;

and held the pins created in a local arrays for ease of access
// Access arrays    
    bit_data_t* bit_data[ARRAY_SIZE];
    s32_data_t* s32_data[ARRAY_SIZE];
    float_data_t* float_data[ARRAY_SIZE];
    
    hal_bit_t* bit_param_data[ARRAY_SIZE];
    hal_s32_t* s32_param_data[ARRAY_SIZE];
    hal_float_t* float_param_data[ARRAY_SIZE];
    
    //     if(strcmp((char*)float_name[y], name) == 0) finds name
    
    char bit_name [ARRAY_SIZE][NAME_LEN];
    char s32_name [ARRAY_SIZE][NAME_LEN];
    char float_name [ARRAY_SIZE][NAME_LEN];

    char bit_param_name [ARRAY_SIZE][NAME_LEN];
    char s32_param_name [ARRAY_SIZE][NAME_LEN];    
    char float_param_name [ARRAY_SIZE][NAME_LEN];
    
    int bit_index;      // index to the relevant array, left at last entry so size is known
    int s32_index;
    int float_index;  

    int bit_param_index;
    int s32_param_index;
    int float_param_index;

and this is routine to create a float pin and eneter it into the relevant array
int HAL_Access::createFloatPin(QString& name, bool in)
{
int retval;

    QStrncpy(lname, name, name.length());

    if(float_index >= ARRAY_SIZE)   
        {
        rtapi_print_msg(RTAPI_MSG_ERR,"ERROR: pin %s creation not possible, local array full\n", lname);
        return -1;
        }
    else
        {
        float_data_t *data = (float_data_t*)hal_malloc(sizeof(float_data_t));

        if (data == 0) 
            {
        	rtapi_print_msg(RTAPI_MSG_ERR, "ERROR: hal_malloc() for pin %s creation failed\n", lname);
        	cleanUp();
        	}
        else
            {
            float_data[float_index] = data;
            strcpy((char*)float_name[float_index++], lname);
            }
        	
        retval = hal_pin_float_newf(in ? HAL_IN : HAL_OUT, &(data->pin), comp_id, "%s.%s", prefix,  lname);
        
        if (retval < 0) 
            {
        	rtapi_print_msg(RTAPI_MSG_ERR,"ERROR: pin %s export failed with err=%i\n", lname, retval);
    	    cleanUp();
    	    return -1;
            }
        return 0;
        }
}


If you write a simple comp file that creates a do nothing component with a couple of pins, run comp filename to generate a .c file
you can then see the pure C method of creating a pin.
struct __comp_state {
    struct __comp_state *_next;
    hal_float_t *out;
    hal_float_t *in0;
    hal_float_t *in1;
};
struct __comp_state *__comp_inst=0;
struct __comp_state *__comp_first_inst=0, *__comp_last_inst=0;

static void _(struct __comp_state *__comp_inst, long period);
static int __comp_get_data_size(void);
#undef TRUE
#define TRUE (1)
#undef FALSE
#define FALSE (0)
#undef true
#define true (1)
#undef false
#define false (0)

static int export(char *prefix, long extra_arg) {
    char buf[HAL_NAME_LEN + 1];
    int r = 0;
    int sz = sizeof(struct __comp_state) + __comp_get_data_size();
    struct __comp_state *inst = hal_malloc(sz);
    memset(inst, 0, sz);
    r = hal_pin_float_newf(HAL_OUT, &(inst->out), comp_id,
        "%s.out", prefix);
    if(r != 0) return r;
    r = hal_pin_float_newf(HAL_IN, &(inst->in0), comp_id,
        "%s.in0", prefix);
    if(r != 0) return r;
    r = hal_pin_float_newf(HAL_IN, &(inst->in1), comp_id,
        "%s.in1", prefix);
    if(r != 0) return r;
    rtapi_snprintf(buf, sizeof(buf), "%s", prefix);
    r = hal_export_funct(buf, (void(*)(void *inst, long))_, inst, 1, 0, comp_id);
    if(r != 0) return r;
    if(__comp_last_inst) __comp_last_inst->_next = inst;
    __comp_last_inst = inst;
    if(!__comp_first_inst) __comp_first_inst = inst;
    return 0;
}

This in fact mallocs memory for the whole struct in one go, but the priciple is the same

regards

Please Log in or Create an account to join the conversation.

More
26 May 2014 22:58 #47361 by ArcEye
If you are dealing with pins created by another process, you can find them by name and get their values
int HAL_Access::getPValue(QString& name, QString& value)
{
int retval;
hal_param_t *param;
hal_pin_t *pin;
hal_type_t type;
void *d_ptr;

    QStrncpy(lname, name, name.length());
    
    // get mutex before accessing shared data 
    rtapi_mutex_get(&(hal_data->mutex));
    // search param list for name 
    param = halpr_find_param_by_name(lname);
    if (param == 0) 
        {
        pin = halpr_find_pin_by_name(lname);
        if(pin == 0) 
            {
            rtapi_mutex_give(&(hal_data->mutex));
            qDebug() << "pin " << lname << " not found";
            return -EINVAL;
            } 
        else // pin
            {
            type = pin->type;
             // d_ptr = (void*)SHMPTR(pin->dummysig);
            d_ptr = (void*)&pin->dummysig;
            }
        } 
    else  // param
        {
        type = param->type;
        d_ptr = SHMPTR(param->data_ptr);
        }

    retval = get_common(type, d_ptr, value);

    rtapi_mutex_give(&(hal_data->mutex));
    if (retval != 0) 
       qDebug() << "getPValue() " << lname << " failed";
    return retval;
}

Please Log in or Create an account to join the conversation.

More
26 May 2014 23:08 - 26 May 2014 23:09 #47363 by eneuro

When you create any pin you have to malloc the memory required to hold the data it can carry, that is the pointer.

OK, I think this is what I missed during my signal creation-looking at this figure below from linuxcnc.org/docs/html/hal/basic_hal.html :

my signal sources are those I/O pins created in RT kins module eneurokins.ml and I should create in my user space app input pin and connect to the same signal.
This way, I will have pointer to input pin needed to read this signal in my application while
linuxcnc.org/docs/html/man/man3/intro.3hal.html says:

In the HAL, a signal contains the actual data value that passes from one pin to another. When a signal is created, space is allocated for the data value. A pin on the other hand, is a pointer, not a data value. When a pin is connected to a signal, the pin’s pointer is set to point at the signal’s data value. This allows the component to access the signal with very little run-time overhead. (If a pin is not linked to any signal, the pointer points to a dummy location, so the realtime code doesn’t have to deal with null pointers or treat unlinked variables as a special case in any way.)


Thx for help B)
Last edit: 26 May 2014 23:09 by eneuro.

Please Log in or Create an account to join the conversation.

More
26 May 2014 23:23 #47364 by ArcEye
You're welcome.

A good place to start looking to see how hal pins etc can be created, found, read etc is the source for halcmd

Good Luck

Please Log in or Create an account to join the conversation.

More
27 May 2014 02:56 - 27 May 2014 03:05 #47367 by eneuro
Added those input pins to signal I/O pins from RT kins in this user space app as described above:

// Pins
typedef struct thaldata {
hal_float_t *md; // Machine diameter
hal_float_t *cx, *cy, *cz; // G-code cartesian coords
hal_float_t *ml, *mr, *mz; // Machine coords
} thaldata;
thaldata *haldata;
...
#define ML (*(haldata->ml))
...
double ml;
...
// input pins
if((ret = hal_pin_float_new("eneurokinsd.ml", HAL_IN, &(haldata->ml), comp_id)) < 0) goto error;
....
// signals
hal_signal_delete(eneurokins_ml_sig);
sigml= hal_signal_new(eneurokins_ml_sig, HAL_FLOAT );
hal_link(eneurokins_ml, eneurokins_ml_sig );
hal_link("eneurokinsd.ml", eneurokins_ml_sig );
...
// read
ml= ML;
// delay
// goto read

It looks like those pointers are set to signal value, while it now reads HAL pins set in RT eneurokins component updated by linuxcnc axis in MDI mode for test G-code entered B)

$ halcmd loadusr proparabolic
proparabolic: HAL component: proparabolic init: comp id: 18

EneuroKins HAL signals: eneurokinsml: 0.000 eneurokinsmr: 0.000 eneurokinsmz: 0.000
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 0.678 eneurokinsmz: 0.139
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 0.883 eneurokinsmz: 0.182
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 1.092 eneurokinsmz: 0.225
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 1.297 eneurokinsmz: 0.267
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 1.507 eneurokinsmz: 0.310
EneuroKins HAL signals: eneurokinsml: 46.365 eneurokinsmr: 1.710 eneurokinsmz: 0.352
...


Maybe I should add code for its mutexes, but in this machine visualisation software, where I do not expect high frame refresh rates while this milling process is not too fast and for the moment I need only read a few signals with tool position set by forward/inverse kinetics in this RT eneurokins component, there is no need to play with mutexes I guess in read code which is something like this when definitions are preprocessed in C++:
double mx= (*(haldata->mx));
This what I expected to get in my C++ visualisation software with added HAL interface and it looks like it works :woohoo:
Last edit: 27 May 2014 03:05 by eneuro.

Please Log in or Create an account to join the conversation.

Time to create page: 0.088 seconds
Powered by Kunena Forum