EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Mass PV assign and event flag sync [was: Creating INLINK/OUTLINK in record support]
From: Benjamin Franksen <[email protected]>
To: <[email protected]>
Date: Thu, 30 Aug 2012 01:46:32 +0200
Hi Tim

many thanks for your feedback.

Am Montag, 27. August 2012, 20:35:29 schrieben Sie:
> As to (a) (c string handling, macro substitution, etc.):
>
> Personally, I'm ok with building PV names by hand, calling macValueGet() as
> needed.

I guess what irks me most is the need to conjure up some fixed buffer size.
Though using a C99 compatible snprintf that always returns the number of
characters it /would/ have written, had it enough space, one could easily
avoid that. Looking at the epicsStdio.h implementation for WIN32 I see that
the real problem here is MINGW which seems to offer no way to find out how large
the buffer /would/ have to be (whereas Windows has _vscprintf which does
exactly that, and VxWorks has fioFormatV). GNU even provides an asprintf which
sounds like it does what I want; maybe EPICS base should provide

        char *epicsAsprintf(const char *format, ...);
        char *epicsVasprintf(const char *format, va_list ap);

where I took the liberty to improve the API a bit (instead of returning int
and taking an extra char **, just return NULL to indicate an error).

Or maybe I should write that, and bundle it with the sequencer?

> Yes it would be nice if pvAssign automatically ran its PV-name argument
> through macLib, but the "stream" possibility seems less attractive to me,
> because I anticipate getting into a varargs-declaration fight with at
> least one compiler among the architectures we support.

I wouldn't worry too much about that, even EPICS base uses varargs for IOC
code, see e.g. the errlog facility. The stdargs.h interface has been in the
standard for a long time, i.e. since C89/90.

Anyway, the net win in the example I gave isn't huge, so maybe it's just not
worth to bother.

What do you think about the idea of adding some limited macro capability to
SNL, so that one could extend the built-in functions with self-written ones?
Something like (quoting myself):

        macro pvAssignV(v, ...) = seq_pvAssignV(ssId, pvIndex(v), ...);

Note, I am not talking about arbitrary string replacements here, like with SNL
program arguments and PV names. Rather, I mean "macro" in the sense of what
CPP does for C programs, only built-in to the language, instead of relying on
a separate preprocessor. There are lots of advantages over using CPP, mostly
due to better integration with SNL:

* variable number of macro arguments, as well as macro-local variables
* checking for syntax errors /before/ macro expansion, as well as afterwards
* no name-capture (i.e. /hygienic/ macros)

The more I think about the idea the more I like it. It would be a very
powerful tool, while being far less dangerous and awkward to use than CPP.

> As to (b) (how to know dynamically assigned PVs are ready for use):
> I like the pvConnected() idea. I think "+c" would be less useful.

Well, pvConnected() covers uses of pvAssign right in the middle of a running
program, not only at startup. So it /is/ more powerful. I am just not so sure
why one would want to re-assign to another PV in the midst of a running
program. I have never much used pvAssign in practice, so my experience is
limited here. Mayhap you have a good example?

> The problem I don't know how to handle with arrays of PVs is to have
> corresponding arrays of event flags.  I know how to get an event flag for
> the array as a whole, but how to find out which element changed or
> completed processing?

Well, for asynchronous pvPut, some of my predecessors invented the extended
form of pvPutComplete with two additional arguments: a number of elements, and
a pointer to an array of booleans (ints) into which the answers get written
(completed or not). Something similar could be done for async pvGet, and
perhaps for arrays synced to a single event flag, too.

However, allowing arrays of event flags as you proposed strikes me as the much
more elegant solution:

double x[20];
evflag e[20];

sync x to e;

or something similar, with the idea that the 'sync x to e' is a shorthand for

sync x[0] to e[0];
...
sync x[19] to e[19];

This has the advantage of being less resource intensive, since each event flag
is represented as a single bit. Note, efTestAndClear still operates on a
single event flag, so you'd have to call efTestAndClear(x[i]) for each index i
separately. But that was the idea, anyway, right? Or could you post a short
example to illustrate how you'd like to use event flag arrays, assuming what I
sketched above were valid and working SNL?
--
Ben Franksen
()  ascii ribbon campaign - against html e-mail
/\  www.asciiribbon.org   - against proprietary attachments

> ----- Original Message -----
> From: "Benjamin Franksen" <[email protected]>
> To: [email protected]
> Sent: Friday, August 24, 2012 6:32:28 PM
> Subject: Re: Creating INLINK/OUTLINK in record support
>
> tl;dr: I propose some new features for SNL to improve mass pv assign,
> feedback appreciated
>
> Am Freitag, 24. August 2012, 00:47:22 schrieb Zhang, Dehong:
> > I am trying to build an IOC/record to watch and protect a large system,
> > where I need to access 100+ PVs.  Since those PVs have very similiar
> > names, I was hoping to hard-code the common part of the names, then use
> > something like the dbNameToAddr function to create an array of links to
> > use in for loops.
>
> Others suggested you use SNL, which prompts me to ask what features you or
> others could imagine that would make this kind of "mass PV assign"
> applications easier to code.
>
> The state of the art in SNL is this: you do it either statically, e.g.
>
> double x[100];
>
> assign x to {
>         "PV0",
>         "PV1",
>         "PV2",
>         ...etc etc...
> };
>
> or dynamically, at runtime, e.g.
>
> double x[100];
>
> assign x to {};
>
> entry {
>         int i;
>         for (i=0; i < 100; i++) {
>                 char name[50];
>                 snprintf(name, 50, "PV%d", i);
>                 pvAssign(x[i], name);
>         }
> }
>
> The first solution involves lots of repetition and I do not recommend it
> for more than a hand full of PVs. The second solution suffers mainly from
> (a) the poor string handling capabilities of C and (b) a less than optimal
> integration of pvAssign with SNL.
>
> This is the point where I hijack the thread to discuss possible
> improvements.
>
> As to (a):
>
> In the above example I would say the (syntactic) overhead of sprintf-ing
> into a buffer is acceptable, but if the correspondence between name and
> array position is more complex then it gets a bit heavy. It gets still
> heavier if you want to use program arguments, since the automatic macro
> (i.e. program argument) replacement works only for static assign clauses.
>
> I have been wondering how to improve on this. One idea is to automatically
> resolve macros for the PV name argument of pvAssign, just like with the
> static assign clause. Another idea is to improve pvAssign so that it
> treats the resulting PV name as a stream. In other words, we allow
> printf-like variable number of arguments to pvAssign. In the above code
> snippet we could say
>
>         for (i=0; i < 100; i++) {
>                 pvAssign(x[i], "PV%d", i);
>         }
>
> Combined with the macro replacement, you could even say
>
>         for (i=0; i < 100; i++) {
>                 pvAssign(x[i], "{DEVICE}:channel%d", i);
>         }
>
> Alternatively, it might be better to let the user specify the buffer size:
>
> (1)             pvAssign(x[i], 50, "PV%d", i);
>
> Automatically expanding macros would not be necessary as you could write
>
>                 pvAssign(x[i], "%s:channel%d", macValueGet("DEVICE"), i);
>
> One might ask oneself why such a function cannot be written by the user.
> Well, it can:
>
> %{
> pvStat seq_pvAssignV(SS_ID ssId, CH_ID chId, unsigned size, const char
> *fmt, ...)
> {
>         char buf[size];
>         va_list args;
>         va_start(args, fmt);
>         vsnprintf(buf, size, fmt, args);
>         va_end(args);
>         return seq_pvAssign(ssId, chId, buf);
> }
> }%
>
> (This assumes your C compiler supports C99, for non-C99 compilers you have
> to use a non-portable call to alloca() or use a malloc()/free() pair.)
>
> Unfortunately a look at the call-site of our self-written pvAssignV reveals
> that it is not really a built-in function:
>
> entry {
>         int i;
>         for (i = 0; i < 100; i++) {
>                 seq_pvAssignV(ssId, pvIndex(a[i]), 10, "PV%d", i);
>         }
> }
>
> This makes me think that it would be nice if the user could extend SNL with
> other pv functions, so we could use the simpler notation (1) even for our
> self-written pvAssignV. This would be possible with no changes to the SNL
> compiler simply by using the macro preprocessor, i.e.
>
>         #define pvAssignV(v,sz,fmt,...) seq_pvAssignV(ssId, pvIndex(v), sz,
> __VA_ARGS__)
>
> except that, again, the "..." and __VA_ARGS__ have been standardised only
> in C99. Ask me what I think about Microsoft and their deliberate refusal
> to support C99.
>
> Actually, if only we could assume a C99 capable compiler, there would be no
> need to treat built-in functions in any special way by the SNL compiler.
>
> SInce we cannot, we need to tell the compiler that certain functions are to
> be treated specially, so the compiler can insert the appropriate ssId and
> pvIndex calls. The obvious place is inside the program, and a simple
> solution might be to just declare a (escaped C) function in SNL as a
> special pvFunction:
>
>         pvFunction pvAssignV;
>
> where it is understood that the first two arguments must be of type SS_ID
> and CH_ID. A more flexible, but also more complex and a bit more verbose,
> way to do this would require to specify which arguments should be handled
> specially; specifically, which is the hidden ssId argument, and at which
> positions does the function expect a channel rather than a value. We
> could, for instance, mirror the (thanks to M$ unavailable) varargs macro
> and say
>
>         macro pvAssignV(v, ...) = seq_pvAssignV(ssId, pvIndex(v), ...);
>
> As to (b):
>
> A problem with dynamic PV assignment is that the option -c (which is turned
> on by default) does not take calls to pvAssign into account. This option
> takes care that all (statically) assigned channels have connected (and for
> those that are monitored, an initial monitor event has been received)
> before state sets are started. You can fake this, at least for the connect
> test, by doing
>
> ss test {
>         state init {
>                 when (pvConnectCount() == pvAssignCount()) {
>                 } state ...
>         }
>         ...
> }
>
> but this has a number of disadvantages:
>
> * There is no check for initial monitor events, so you cannot know whether
> teh values of monitored variables are actually valid. You can use event
> flags and sync them to the variables in question, but this further
> complicates your program.
> * In case some of the underlying PVs cannot be connected (maybe you
> misspelled the names, IOCs are down, network hangs, whatever) there is no
> feedback: your program just hangs and nothing happens. Again, you can code
> around this:
>
> ss test {
>         state init {
>                 when (pvConnectCount() == pvAssignCount()) {
>                 } state ...
>                 when(delay(2.0)) {
>                         printf("still waiting for some PVs to
> connect...\n"); } state init
>         }
>         ...
> }
>
> but again here, too, this further complicates your program which hasn't
> done anything useful yet. It gets worse with more than one state set per
> program: either you repeat this stuff in each state set, or you create yet
> another state set only for checking/reporting connection status and use an
> event flag to communicate readiness to the other state sets.
>
> Instead I am thinking about adding yet another built-in function
>
>         boolean pvConnected(); /* no arguments */
>
> with the idea that you call this inside a when clause. It would return true
> only if all PVs visible at the point of call (according to the static
> scoping rules) have connected and received an initial monitor event (if
> they are monitored), just like with the +c option, but taking preceeding
> calls to pvAssign into account. It would also regularly print the usual
> message reporting how many channels have connected etc.
>
> Alternatively, and even easier to use, I could add a _state set_ option +c,
> that does the same. For instance
>
> entry {
>         for (...)
>                 pvAssign(...)
> }
>
> ss test {
>         option +c;
>         state init {
>                 /* can assume here that above pvAssign have connected */
>                 ...
>         }
> }
>
> This is a bit less flexible, in that this works for pvAssign calls inside
> the global entry{} block, but not for pvAssign calls inside a state set.
>
> Ok, enough for one message. Thanks to anyone who has actually read until
> this point. I would very much appreciate any kind of feedback.
> --
> Ben Franksen
> ()  ascii ribbon campaign - against html e-mail
> /\  www.asciiribbon.org   - against proprietary attachments
>
> ________________________________
>
> Helmholtz-Zentrum Berlin für Materialien und Energie GmbH
>
> Mitglied der Hermann von Helmholtz-Gemeinschaft Deutscher Forschungszentren
> e.V.
>
> Aufsichtsrat: Vorsitzender Prof. Dr. Dr. h.c. mult. Joachim Treusch, stv.
> Vorsitzende Dr. Beatrix Vierkorn-Rudolph Geschäftsführung: Prof. Dr. Anke
> Rita Kaysser-Pyzalla, Thomas Frederking
>
> Sitz Berlin, AG Charlottenburg, 89 HRB 5583
>
> Postadresse:
> Hahn-Meitner-Platz 1
> D-14109 Berlin
>
> http://www.helmholtz-berlin.de

________________________________

Helmholtz-Zentrum Berlin für Materialien und Energie GmbH

Mitglied der Hermann von Helmholtz-Gemeinschaft Deutscher Forschungszentren e.V.

Aufsichtsrat: Vorsitzender Prof. Dr. Dr. h.c. mult. Joachim Treusch, stv. Vorsitzende Dr. Beatrix Vierkorn-Rudolph
Geschäftsführung: Prof. Dr. Anke Rita Kaysser-Pyzalla, Thomas Frederking

Sitz Berlin, AG Charlottenburg, 89 HRB 5583

Postadresse:
Hahn-Meitner-Platz 1
D-14109 Berlin

http://www.helmholtz-berlin.de


Replies:
Re: Mass PV assign and event flag sync [was: Creating INLINK/OUTLINK in record support] Andrew Johnson
References:
Re: Creating INLINK/OUTLINK in record support Tim Mooney

Navigate by Date:
Prev: Re: aiRecord conversion Benjamin Franksen
Next: Location of caTools sources Shankar, Murali
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: Creating INLINK/OUTLINK in record support Tim Mooney
Next: Re: Mass PV assign and event flag sync [was: Creating INLINK/OUTLINK in record support] Andrew Johnson
Index: 1994  1995  1996  1997  1998  1999  2000  2001  2002  2003  2004  2005  2006  2007  2008  2009  2010  2011  <20122013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 18 Nov 2013 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·