streamer/halstreamer doesn't work?!? BUG!

More
15 Nov 2014 02:27 - 15 Nov 2014 16:39 #53093 by eslavko
Hello I have trouble setting up streamer component.
In hal I added these lines:
loadrt streamer depth=10000 cfg=b
setp streamer.0.enable 0
addf streamer.0 base-thread

and started halmeter to monitor streamer.0.curr-depth
at start I suspects to be 0 and it is. but then I open console and just invoke halstreamer.
Now if I enter number 0 or 1 (and press enter) I expect that cur-depth should increase by 1 as streamer is disabled by setp. If I enter "wrong" character 5 as example in halstreamer it report that line is skipped. So where data goes?
Last edit: 15 Nov 2014 16:39 by eslavko. Reason: Changed subject

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

More
15 Nov 2014 16:50 #53109 by eslavko
Hello...
After more digging in streamer I can declared that it's have BUG.
I do the check the source streamer.c (with my limited C knowledge).

if streamer.x.enable = false then none of other parameters are updated as update routine exit immediatly.
I think that
    /* are we enabled? */
    if ( ! *(str->enable) ) {
	/* no, done */
	return;
    }

should be repositioned at least after line
*(str->curr_depth) = tmpin - tmpout;

And making another pin named clock/clocktype should be nice to. I think something like:
streamer.x.clocktype 0(default) no clock - stream by call rate. 1=falling edge, 2=rising edge, 3=both edges
streamer.c.clock

I can probably do that but code will be messy as I'm not C programmer.

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

More
15 Nov 2014 18:02 #53111 by eslavko
Hello...
Can someone help me to proffread the code needed for adding clock.
I know that I need to add new parameters and new pins.
Actualy I need to add storage for old clock value, new clock pin and parameter clock type.
and working code something like this
	/* Clock option added by eSlavko */
	/* clocktype 0 means no clocking - step every call */
	/* clocktype 1 means faling edge */
	/* clocktype 2 means rising edge */
	/* clocktype 3 means both edges */
	/* make update old clock value (just swap old and new clock) */
	n=(str->oldclock);
	(str->oldclock)=(str->clock);
	(str->clock)=n;
    /* are we enabled? */
    if ( ! *(str->enable) ) {
	/* no, done */
	return;
    }
	switch (str->clocktype ) {
	case 0:
	    break;
	case 1:
		if (str->clock & !str->oldclock){
			return;	//invalid edge - finish
		}
	    break;

	case 2:
		if (!str->clock & str->oldclock){
			return;	//invalid edge - finish
		}
	    break;

	case 3:
		if (str->clock ^ str->oldclock){
			return;	//none edge - finish
		}
	    break;
	}
	/* end of clock option*/
I don't know how to proper manage pointers. checking the code sometime is (*something) other time is *(something) and I don't know the difference.

Thanks in advice.
Slavko.

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

More
15 Nov 2014 19:51 #53115 by ArcEye
Hi

I have never used streamer, but would have thought that the enable pin preventing updates if 0 was deliberate, so that it could be switched on and off whilst still loaded.

Regards pointers in C

streamer_t *str; allocates a pointer to a memory location at which a value of type streamer_t can be found

*str references the value pointed to by the pointer rather than the address it is at.

So for example

// allocate a variable of type double and give it value 200
double d = 200;
// create a pointer to a double value
double *double_ptr;
// pass the address of d to the pointer
double_ptr = &d;
// pointer now points to d and value can be referenced by *(ptr) dereferencing
*(double_ptr) is now 200

hope that assists

regards

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

More
15 Nov 2014 20:32 #53119 by eslavko

Hi

I have never used streamer, but would have thought that the enable pin preventing updates if 0 was deliberate, so that it could be switched on and off whilst still loaded.
regards


I think that enable should just enable IO operation, but status should be updated regardles of enable.
So if FIFO is empty and then enable is set to false we should read empty bit as true. But when new data arrive the fifo is not empty and bit empty should toogle to false. I hav managed to correct that. Now I just stumbling to add clock pin.

I think this update is essential if streamer is used for raster imaging. As FIFO fill is userspace component in this way we can monitor from gcode if fifo is ready for next line

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

More
15 Nov 2014 22:35 #53124 by eslavko
Hello....
Finally I got fix.
But realy don't know how to update it. I think that streamer has now much better capability as before.
Changes are:
The status (curr-depth, empty, underruns) are reported correctly even if enable is false.
And addition for clock (by default param is same as without change).
There are streamer.N.clock pin for data clocking
and streamer.N.clock-mode.
Mode 0 (default) just ingore clock pin and do as before.
Mode 1 means falling edge of clock
Mode 2 means rising edge of clock
Mode 3 means both edges of clock

With this we can now connect step pulse to clock and get rastering with sinc of stepper motion.

Can someone update streamer.c file? I was trying with git but I do some mess... so please.
in the begining of file is comment to put in manual too.
/********************************************************************
* Description:  streamer.c
*               A HAL component that can be used to stream data
*               from a file onto HAL pins at a specific realtime
*               sample rate.
*
* Author: John Kasunich <jmkasunich at sourceforge dot net>
* License: GPL Version 2
*    
* Copyright (c) 2006 All rights reserved.
*
********************************************************************/
/** This file, 'streamer.c', is the realtime part of a HAL component
    that allows numbers stored in a file to be "streamed" onto HAL
    pins at a uniform realtime sample rate.  When the realtime module
    is loaded, it creates a fifo in shared memory.  Then, the user
    space program 'hal_streamer' is invoked.  'hal_streamer' takes 
    input from stdin and writes it to the fifo, and this component 
    transfers the data from the fifo to HAL pins.

    Loading:

    loadrt streamer depth=100 cfg=uffb


*/

/** Copyright (C) 2006 John Kasunich
 */

/** This program is free software; you can redistribute it and/or
    modify it under the terms of version 2 of the GNU General
    Public License as published by the Free Software Foundation.
    This library 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 this library; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111 USA

    THE AUTHORS OF THIS LIBRARY ACCEPT ABSOLUTELY NO LIABILITY FOR
    ANY HARM OR LOSS RESULTING FROM ITS USE.  IT IS _EXTREMELY_ UNWISE
    TO RELY ON SOFTWARE ALONE FOR SAFETY.  Any machinery capable of
    harming persons must have provisions for completely removing power
    from all motors, etc, before persons enter any danger area.  All
    machinery must be designed to comply with local and national safety
    codes, and the authors of this software can not, and do not, take
    any responsibility for such compliance.

    This code was written as part of the EMC HAL project.  For more
    information, go to www.linuxcnc.org.
*/


/*	15. November 2014 addition by eslavko
 * streamer.N.cur-depth, streamer.N.empty and streamer.N.underruns are
 * now (correctly) updated even if streamer.N.enabled is set to false.
 * in addition there are two new pins to enable clocking streamer.
 * by default values the streamer behaviour is as before update.
 * the new pin is
 * streamer.N.clock bit input
 * it's clock input with actions defined by clock_mode pin.
 * streamer.N.clock-mode s32 input
 * define behaviour of clock pin. 
 * value 0 (default) freerun at every loop
 * value 1 means clock by falling edge
 * value 2 means clock by rising edge
 * value 3 meand clock by any edge
 */
 
#include "rtapi.h"              /* RTAPI realtime OS API */
#include "rtapi_app.h"          /* RTAPI realtime module decls */
#include "hal.h"                /* HAL public API decls */
#include "streamer.h"		/* decls and such for fifos */
#include "rtapi_errno.h"
#include "rtapi_string.h"

/* module information */
MODULE_AUTHOR("John Kasunich");
MODULE_DESCRIPTION("Realtime File Streamer HAL");
MODULE_LICENSE("GPL");
static char *cfg[MAX_STREAMERS];	/* config string, no default */
RTAPI_MP_ARRAY_STRING(cfg,MAX_STREAMERS,"config string");
static int depth[MAX_STREAMERS];	/* depth of fifo, default 0 */
RTAPI_MP_ARRAY_INT(depth,MAX_STREAMERS,"fifo depth");

/***********************************************************************
*                STRUCTURES AND GLOBAL VARIABLES                       *
************************************************************************/

/* this structure contains the HAL shared memory data for one streamer */

typedef struct {
    fifo_t *fifo;		/* pointer to user/RT fifo */
    hal_s32_t *curr_depth;	/* pin: current fifo depth */
    hal_bit_t *empty;		/* pin: underrun flag */
    hal_bit_t *enable;		/* pin: enable streaming */
    hal_s32_t *underruns;	/* pin: number of underruns */
    hal_bit_t *clock;		/* pin: clock input */
    hal_s32_t *clock_mode;	/* pin: clock mode */
  
} streamer_t;

/* other globals */
static int comp_id;		/* component ID */
static int myclockedge;		/* clock edge detector */
static int shmem_id[MAX_STREAMERS];

/***********************************************************************
*                  LOCAL FUNCTION DECLARATIONS                         *
************************************************************************/

static int parse_types(fifo_t *f, char *cfg);
static int init_streamer(int num, fifo_t *tmp_fifo);
static void update(void *arg, long period);

/***********************************************************************
*                       INIT AND EXIT CODE                             *
************************************************************************/

int rtapi_app_main(void)
{
    int n, numchan, max_depth, retval;
    fifo_t tmp_fifo[MAX_STREAMERS];

    /* validate config info */
    for ( n = 0 ; n < MAX_STREAMERS ; n++ ) {
	if (( cfg[n] == NULL ) || ( *cfg == '\0' ) || ( depth[n] <= 0 )) {
	    break;
	}
	tmp_fifo[n].num_pins = parse_types(&(tmp_fifo[n]), cfg[n]);
	if ( tmp_fifo[n].num_pins == 0 ) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"STREAMER: ERROR: bad config string '%s'\n", cfg[n]);
	    return -EINVAL;
	}
	max_depth = MAX_SHMEM / (sizeof(shmem_data_t) * tmp_fifo[n].num_pins);
	if ( depth[n] > max_depth ) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"STREAMER: ERROR: depth too large, max is %d\n", max_depth);
	    return -ENOMEM;
	}
	tmp_fifo[n].depth = depth[n];
    }
    if ( n == 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: no channels specified\n");
	return -EINVAL;
    }
    numchan = n;
    /* clear shmem IDs */
    for ( n = 0 ; n < MAX_STREAMERS ; n++ ) {
	shmem_id[n] = -1;
    }

    /* have good config info, connect to the HAL */
    comp_id = hal_init("streamer");
    if (comp_id < 0) {
	rtapi_print_msg(RTAPI_MSG_ERR, "STREAMER: ERROR: hal_init() failed\n");
	return -EINVAL;
    }

    /* create the streamers - allocate memory, export pins, etc. */
    for (n = 0; n < numchan; n++) {
	retval = init_streamer(n, &(tmp_fifo[n]));
	if (retval != 0) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"STREAMER: ERROR: streamer %d init failed\n", n);
	    hal_exit(comp_id);
	    return retval;
	}
    }
    rtapi_print_msg(RTAPI_MSG_INFO,
	"STREAMER: installed %d data streamers\n", numchan);
    hal_ready(comp_id);
    return 0;
}

void rtapi_app_exit(void)
{
    int n;

    /* free any shmem blocks */
    for ( n = 0 ; n < MAX_STREAMERS ; n++ ) {
	if ( shmem_id[n] > 0 ) {
	    rtapi_shmem_delete(shmem_id[n], comp_id);
	}
    }
    hal_exit(comp_id);
}

/***********************************************************************
*            REALTIME COUNTER COUNTING AND UPDATE FUNCTIONS            *
************************************************************************/

static void update(void *arg, long period)
{
    streamer_t *str;
    fifo_t *fifo;
    pin_data_t *pptr;
    shmem_data_t *dptr;
    int tmpin, tmpout, n, doclk;
    /* point at streamer struct in HAL shmem */
    str = arg;
	/* keep last two clock states to get all possible clock edges */
	myclockedge=((myclockedge<<1) | (*(str->clock) & 1)) & 3; 
    /* are we enabled? - generate doclock if enabled and right mode  */
	doclk=0;
    if ( *(str->enable) ) {
		doclk=1;
		switch (*str->clock_mode) {
		/* clock-mode 0 means do clock if enabled */
		case 0: 
			break;
		/* clock-mode 1 means enabled & faling edge */
		case 1: 
		    if ( myclockedge!=2) {
				doclk=0;
			}
			break;
		/* clock-mode 2 means enabled & rising edge */
		case 2: 
		    if ( myclockedge!=1) {
				doclk=0;
			}
			break;
		/* clock-mode 3 means enabled & both edges */
		case 3: 
		    if ((myclockedge==0) | ( myclockedge==3)) {
				doclk=0;
			}
			break;
		default:
		    break;
		}
	}
    /* HAL pins are right after the streamer_t struct in HAL shmem */
    pptr = (pin_data_t *)(str+1);
    /* point at user/RT fifo in other shmem */
    fifo = str->fifo;
    /* fifo data area is right after the fifo_t struct in shmem */
    dptr = (shmem_data_t *)(fifo+1);
    /* find the next block of data in the fifo */
    tmpin = fifo->in;
    tmpout = fifo->out;
    if  (tmpout == tmpin) {
		/* fifo empty - log it */
		*(str->empty) = 1;
		*(str->curr_depth) = 0;
		/* increase underrun only for valid clock*/
		if (doclk==1){
			(*str->underruns)++;
		}
		/* done - output pins retain current values */
		return;
    }
    /* clear the "empty" pin */
    *(str->empty) = 0;
    /* calculate current depth */
    if ( tmpin < tmpout ) {
		tmpin += fifo->depth;
    }
    *(str->curr_depth) = tmpin - tmpout;
	/* don't preceed if there are no valid clock*/
	if (doclk==0){
		return;
	}
    /* make ptr to first data item of the current fifo record */
    dptr += tmpout * fifo->num_pins;
    /* copy data from fifo to HAL pins */
    for ( n = 0 ; n < fifo->num_pins ; n++ ) {
	switch ( fifo->type[n] ) {
	case HAL_FLOAT:
	    *(pptr->hfloat) = dptr->f;
	    break;
	case HAL_BIT:
	    if ( dptr->b ) {
		*(pptr->hbit) = 1;
	    } else {
		*(pptr->hbit) = 0;
	    }
	    break;
	case HAL_U32:
	    *(pptr->hu32) = dptr->u;
	    break;
	case HAL_S32:
	    *(pptr->hs32) = dptr->s;
	    break;
	default:
	    break;
	}
	dptr++;
	pptr++;
    }
    tmpout++;
    if ( tmpout >= fifo->depth ) {
        tmpout = 0;
    }
    /* store new value of out */
    fifo->out = tmpout;
}

/***********************************************************************
*                   LOCAL FUNCTION DEFINITIONS                         *
************************************************************************/

static int parse_types(fifo_t *f, char *cfg)
{
    char *c;
    int n;

    c = cfg;
    n = 0;
    while (( n < MAX_PINS ) && ( *c != '\0' )) {
	switch (*c) {
	case 'f':
	case 'F':
	    f->type[n++] = HAL_FLOAT;
	    c++;
	    break;
	case 'b':
	case 'B':
	    f->type[n++] = HAL_BIT;
	    c++;
	    break;
	case 'u':
	case 'U':
	    f->type[n++] = HAL_U32;
	    c++;
	    break;
	case 's':
	case 'S':
	    f->type[n++] = HAL_S32;
	    c++;
	    break;
	default:
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"STREAMER: ERROR: unknown type '%c', must be F, B, U, or S\n", *c);
	    return 0;
	}
    }
    if ( *c != '\0' ) {
	/* didn't reach end of cfg string */
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: more than %d items\n", MAX_PINS);
	return 0;
    }
    return n;
}

static int init_streamer(int num, fifo_t *tmp_fifo)
{
    int size, retval, n, usefp;
    void *shmem_ptr;
    streamer_t *str;
    pin_data_t *pptr;
    fifo_t *fifo;
    char buf[HAL_NAME_LEN + 1];

    /* alloc shmem for base streamer data and user specified pins */
    size = sizeof(streamer_t) + tmp_fifo->num_pins*sizeof(pin_data_t);
    str = hal_malloc(size);

    if (str == 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: couldn't allocate HAL shared memory\n");
	return -ENOMEM;
    }
    /* export "standard" pins and params */
    retval = hal_pin_bit_newf(HAL_OUT, &(str->empty), comp_id,
	"streamer.%d.empty", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'empty' pin export failed\n");
	return -EIO;
    }
    retval = hal_pin_bit_newf(HAL_IN, &(str->enable), comp_id,
	"streamer.%d.enable", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'enable' pin export failed\n");
	return -EIO;
    }
    retval = hal_pin_s32_newf(HAL_OUT, &(str->curr_depth), comp_id,
	"streamer.%d.curr-depth", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'curr_depth' pin export failed\n");
	return -EIO;
    }

    retval = hal_pin_s32_newf(HAL_IO, &(str->underruns), comp_id,
	"streamer.%d.underruns", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'underruns' pin export failed\n");
	return -EIO;
    }
    
    retval = hal_pin_bit_newf(HAL_IN, &(str->clock), comp_id,
	"streamer.%d.clock", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'clock' pin export failed\n");
	return -EIO;
    }
    
    retval = hal_pin_s32_newf(HAL_IN, &(str->clock_mode), comp_id,
	"streamer.%d.clock-mode", num);
    if (retval != 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: 'clock_mode' pin export failed\n");
	return -EIO;
    }
    
    
    /* init the standard pins and params */
    *(str->empty) = 1;
    *(str->enable) = 1;
    *(str->curr_depth) = 0;
    *(str->underruns) = 0;
    *(str->clock_mode) = 0;
    
    /* HAL pins are right after the streamer_t struct in HAL shmem */
    pptr = (pin_data_t *)(str+1);
    usefp = 0;
    /* export user specified pins (the ones that stream data) */
    for ( n = 0 ; n < tmp_fifo->num_pins ; n++ ) {
	rtapi_snprintf(buf, sizeof(buf), "streamer.%d.pin.%d", num, n);
	retval = hal_pin_new(buf, tmp_fifo->type[n], HAL_OUT, (void **)pptr, comp_id );
	if (retval != 0 ) {
	    rtapi_print_msg(RTAPI_MSG_ERR,
		"STREAMER: ERROR: pin '%s' export failed\n", buf);
	    return -EIO;
	}
	/* init the pin value */
	switch ( tmp_fifo->type[n] ) {
	case HAL_FLOAT:
	    *(pptr->hfloat) = 0.0;
	    usefp = 1;
	    break;
	case HAL_BIT:
	    *(pptr->hbit) = 0;
	    break;
	case HAL_U32:
	    *(pptr->hu32) = 0;
	    break;
	case HAL_S32:
	    *(pptr->hs32) = 0;
	    break;
	default:
	    break;
	}
	pptr++;
    }
    /* export update function */
    rtapi_snprintf(buf, sizeof(buf), "streamer.%d", num);
    retval = hal_export_funct(buf, update, str, usefp, 0, comp_id);
    if (retval != 0) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: function export failed\n");
	return retval;
    }

    /* alloc shmem for user/RT comms (fifo) */
    size = sizeof(fifo_t) + tmp_fifo->num_pins * tmp_fifo->depth * sizeof(shmem_data_t);
    shmem_id[num] = rtapi_shmem_new(STREAMER_SHMEM_KEY+num, comp_id, size);
    if ( shmem_id[num] < 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: couldn't allocate user/RT shared memory\n");
	return -ENOMEM;
    }
    retval = rtapi_shmem_getptr(shmem_id[num], &shmem_ptr);
    if ( retval < 0 ) {
	rtapi_print_msg(RTAPI_MSG_ERR,
	    "STREAMER: ERROR: couldn't map user/RT shared memory\n");
	return -ENOMEM;
    }
    fifo = shmem_ptr;
    str->fifo = fifo;
    /* copy data from temp_fifo */
    *fifo = *tmp_fifo;
    /* init fields */
    fifo->in = 0;
    fifo->out = 0;
    fifo->last_sample = 0;

    /* mark it inited for user program */
    fifo->magic = FIFO_MAGIC_NUM;
    return 0;
}
The following user(s) said Thank You: cncbasher

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

More
15 Nov 2014 22:52 #53125 by ArcEye
If you want to have the file changed to the way you did it, you need to convince the developers it was broken and that this fixes it.

details of submission process here
www.linuxcnc.org/docs/html/code/Contributing-to-LinuxCNC.html

regards

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

More
15 Nov 2014 23:43 #53126 by eslavko
Hello...
I was thinking that developers check that forum too.
I was send mail to emc-developers now.

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

More
20 Nov 2014 01:01 #53216 by andypugh

streamer_t *str; allocates a pointer to a memory location at which a value of type streamer_t can be found


I find this aspect of C syntax to be less clear than it could be.

For a variable X:
X returns the value of X.
*X returns the value stored at memory address X
&X returns the memory address where the value of X is stored.

Where the confusion arises is when you declare the variable.

int *P

creates a variable that is a pointer to an integer. The value of P is a memory location, and *P will return the value that you store there.
However, the syntax doesn't look like that, really. I tend to read it to myself as int* P, ie P is of type pointer-to-integer.

There are a lot of pointers in LinuxCNC. In fact all a HAL pin is is a pointer to shared memory. When you "net" a HAL pin all that really happens is that all the pins in the net get their pointer address set to the same bit of memory, so all the internal variables in the code read the same value out of the memory.

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

More
20 Nov 2014 02:43 #53218 by eslavko
Andy the pointer stuf is solved. Actualy the whole thing is solved at least for me. The streamer.c works with new pins. I just share that as I think someone other may find it usable.
..and when version is updated I don't need to make change again...

Slavko.


p.s.
in developer mailing list I post same code but nobody "care" as no answers/comment yet.

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

Time to create page: 0.142 seconds
Powered by Kunena Forum