EPICS Controls Argonne National Laboratory

Experimental Physics and
Industrial Control System

2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024  Index 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
<== Date ==> <== Thread ==>

Subject: Re: IOC Redundancy in R3.14.10
From: Ben Franksen <[email protected]>
To: [email protected]
Date: Wed, 9 Jul 2008 02:54:29 +0200
On Dienstag, 8. Juli 2008, Andrew Johnson wrote:
> On Tuesday 08 July 2008 02:16:12 Bernd Schoeneburg wrote:
> > The additional field attribute (copy to partner ioc: yes/no) is not
> > implemented in this way. I have no good idea how to decide this without
> > base modification.
>
> The last time we talked about that you hadn't actually set that attribute
> on any of the base record types, so I wasn't sure whether you still need
> it or not.  I don't think it would be too hard to provide the attribute
> information to the CCE outside of the original DBD file, although
> admittedly it might complicate the code a bit.

The problem might be solved by suitable generalization. I am thinking about 
extensible syntax with pluggable handlers, an idea I've been pondering for 
a long time. This would have a wide range of possible applications, 
including pluggable field properties like 'copy_to_partner' as proposed by 
Bernd, while retaining complete backward compatibility. Let me explain what 
I mean.

Currently all syntax for db and dbd files is hard-coded into the parser. 
That, however, need not be the case. The grammar itself is very simple and 
regular: there is exactly one aggregate syntactical structure which I will 
call <definition>. A definition consists of a <head> of the form:

	<keyword> (<argument>,...)

optionally followed by a <body>:

	{ <definition> ... }

where an <argument> is either a number, or a quoted string (unrestricted 
charset), or a bareword (restricted charset). The top-level is simply a 
sequence of definitions (like the inside of a body).

Let me coin the term 'construct' for the several (semantically different) 
kinds of definitions: some are top-level, such as menu definitions, 
recordtype definitions, record instance definitions, etc., some are nested 
inside other definitions, such as a field description inside a recordtype, 
which is different from a field definition inside a record instance. Each 
construct has a unique keyword, but is in turn uniqely determined only by 
its keyword /and/ the sequence of constructs it appears nested in. By 
induction, the sequence of keywords (of constructs) leading to a definition 
(including its own one) determines the construct. For instance, the 
top-level construct "menu definition" can be referenced by the 
(one-element) keyword sequence [menu], while the construct "menu property 
of a field definition" is referenced by the keyword sequence 
[recordtype,field,menu].

Now the basic idea is that we could allow /syntax-plugins/ to define new 
constructs and also register interest in existing ones. Each plugin gets 
called by the parser exactly once for each definition the parser encounters 
that matches a declared interest, passing the values of the parsed 
arguments. There is no need (and thus no way) for the plugin to do any 
parsing itself (apart from interpreting the argument strings): descending 
deeper into a possible body containing nested definitions is done by the 
parser. If a new construct shall contain sub-constructs, these must be 
registered separately.

A minimal API could be:

--8<-------------------------------------------------------------
typedef enum { syntaxPlugOk, syntaxPlugError } syntaxPlugResult;

typedef syntaxPlugResult syntaxPlugOnPush(int argc, char **argv);
typedef syntaxPlugResult syntaxPlugOnPop(void);

syntaxPlugResult syntaxPlugRegister(int keyc, char **keyv,
    syntaxPlugOnPush *onPush, syntaxPlugOnPop *onPop);
--8<-------------------------------------------------------------

The onPush handler gets called when the parser encounters a definition head 
matching the registered interest (keyword sequence); the onPop handler gets 
called after the body has been parsed as well (or immediately afterwards if 
there is no body). The onPop handler enables the plugin to do proper 
book-keeping about how a sub-definition is nested inside another 
definition. It may be set to NULL.

For instance, the redundancy plugin would register interest for these 
constructs:

- [recordtype]
- [recordtype, field]
- [recordtype, field, copy_to_partner]

It should be obvious how the plugin implements the onPush and onPop handlers 
for these constructs (the onPop handler for the third construct can be 
NULL, of course as it has no body).

One side note is in order here: underlying this extremely simple API is the 
(as yet unspoken) assumption that we can simply and safely /ignore/ any 
definitions -- including nested ones -- for which no syntax plugin has been 
registered. The parser itself accepts any input that consists of 
syntactically valid definitions as defined above. This is important for 
compatibility between plugins. For instance, a dbd file for one IOC will be 
a (syntactically) valid dbd file for any other IOC, regardless of what 
plugins (if any) are installed on both IOCs. It is perfectly ok for a 
plugin to 'eaves-drop' on some other plugin's (or on built-in) constructs. 
Ownership of keywords is best managed by a community process, such as a 
listing plugins and their (new) keywords on the EPICS wiki. This model has 
the very nice feature of always retaining not only backwards, but also 
forward compatibility, i.e. new files, supporting new extensions can still 
be used unchanged on IOCs w/o the new plugin.

Alternatively, a stricter checking policy can be implemented, too, by 
extending the API with a construct creation call, and demanding that a new 
construct must always be created before handlers for it can be registered. 
This, however, makes it harder for plugins to engage in mutual co-operation 
(first both must create their constructs, then both register interest in 
the other's constructs).

As to implementation, the parser itself becomes rather simpler. It must 
merely keep a tree-like data structure representing the registered (and the 
built-in) constructs, and to each construct a list of onPush handlers and 
onPop handlers. It must also maintain a stack of constructs (represented as 
pointers into the construct tree) to decide which handlers to call at each 
step.

I am almost of a mind to start hacking on a prototype implementation...

Cheers
Ben

Replies:
Re: IOC Redundancy in R3.14.10 Andrew Johnson
References:
IOC Redundancy in R3.14.10 Schoeneburg, Bernd
Re: IOC Redundancy in R3.14.10 Bernd Schoeneburg
Re: IOC Redundancy in R3.14.10 Andrew Johnson

Navigate by Date:
Prev: Re: IOC Redundancy in R3.14.10 Andrew Johnson
Next: Re: IOC Redundancy in R3.14.10 Schoeneburg, Bernd
Index: 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
Navigate by Thread:
Prev: Re: IOC Redundancy in R3.14.10 Andrew Johnson
Next: Re: IOC Redundancy in R3.14.10 Andrew Johnson
Index: 2002  2003  2004  2005  2006  2007  <20082009  2010  2011  2012  2013  2014  2015  2016  2017  2018  2019  2020  2021  2022  2023  2024 
ANJ, 02 Feb 2012 Valid HTML 4.01! · Home · News · About · Base · Modules · Extensions · Distributions · Download ·
· Search · EPICS V4 · IRMIS · Talk · Bugs · Documents · Links · Licensing ·