Merge lp:~epics-core/epics-base/link-support-2 into lp:~epics-core/epics-base/3.16

Proposed by Andrew Johnson on 2016-08-11
Status: Rejected
Rejected by: Andrew Johnson on 2017-04-26
Proposed branch: lp:~epics-core/epics-base/link-support-2
Merge into: lp:~epics-core/epics-base/3.16
Diff against target: 9005 lines (+4850/-1173) (has conflicts)
130 files modified
documentation/RELEASE_NOTES.html (+146/-0)
src/ioc/db/Makefile (+9/-1)
src/ioc/db/dbAccess.c (+8/-19)
src/ioc/db/dbAccessDefs.h (+1/-0)
src/ioc/db/dbCa.c (+21/-11)
src/ioc/db/dbCa.h (+1/-2)
src/ioc/db/dbChannel.c (+1/-1)
src/ioc/db/dbConstLink.c (+140/-0)
src/ioc/db/dbConstLink.h (+34/-0)
src/ioc/db/dbConvertJSON.c (+172/-0)
src/ioc/db/dbConvertJSON.h (+27/-0)
src/ioc/db/dbDbLink.c (+353/-0)
src/ioc/db/dbDbLink.h (+35/-0)
src/ioc/db/dbIocRegister.c (+12/-0)
src/ioc/db/dbJLink.c (+542/-0)
src/ioc/db/dbJLink.h (+132/-0)
src/ioc/db/dbLink.c (+129/-68)
src/ioc/db/dbLink.h (+41/-4)
src/ioc/db/dbLock.c (+2/-0)
src/ioc/db/dbUnitTest.c (+10/-12)
src/ioc/db/recGbl.c (+23/-3)
src/ioc/db/recGbl.h (+2/-0)
src/ioc/db/test/Makefile (+6/-3)
src/ioc/db/test/dbLinkdset.c (+1/-0)
src/ioc/db/test/dbLinkdset.dbd (+1/-0)
src/ioc/db/test/dbPutLinkTest.c (+185/-87)
src/ioc/db/test/dbPutLinkTest.db (+4/-0)
src/ioc/db/test/dbPutLinkTestJ.db (+12/-0)
src/ioc/db/test/devx.c (+2/-6)
src/ioc/db/test/jlinkz.c (+250/-0)
src/ioc/db/test/jlinkz.dbd (+1/-0)
src/ioc/db/test/jlinkz.h (+27/-0)
src/ioc/db/test/xLink.c (+88/-0)
src/ioc/db/test/xLink.dbd (+1/-0)
src/ioc/dbStatic/dbBase.h (+8/-0)
src/ioc/dbStatic/dbLex.l (+47/-8)
src/ioc/dbStatic/dbLexRoutines.c (+33/-1)
src/ioc/dbStatic/dbStaticIocRegister.c (+9/-0)
src/ioc/dbStatic/dbStaticLib.c (+123/-95)
src/ioc/dbStatic/dbStaticLib.h (+5/-2)
src/ioc/dbStatic/dbStaticPvt.h (+10/-2)
src/ioc/dbStatic/dbYacc.y (+89/-11)
src/ioc/dbStatic/link.h (+9/-2)
src/ioc/dbtemplate/dbLoadTemplate_lex.l (+2/-2)
src/ioc/misc/iocInit.c (+5/-8)
src/ioc/registry/Makefile (+2/-0)
src/ioc/registry/registryCommon.c (+13/-0)
src/ioc/registry/registryCommon.h (+3/-0)
src/ioc/registry/registryJLinks.c (+23/-0)
src/ioc/registry/registryJLinks.h (+28/-0)
src/libCom/dbmf/dbmf.c (+38/-19)
src/libCom/dbmf/dbmf.h (+9/-6)
src/libCom/error/errMdef.h (+1/-0)
src/libCom/error/errSymLib.c (+26/-12)
src/libCom/error/makeStatTbl.pl (+1/-1)
src/libCom/misc/dbDefs.h (+4/-0)
src/libCom/misc/epicsString.c (+9/-0)
src/libCom/misc/epicsString.h (+1/-0)
src/libCom/yacc/antelope.c (+5/-1)
src/libCom/yacc/error.c (+31/-31)
src/libCom/yajl/Makefile (+1/-0)
src/libCom/yajl/yajl_alloc.h (+1/-1)
src/std/Makefile (+1/-0)
src/std/dev/devAaiSoft.c (+26/-20)
src/std/dev/devAaoSoft.c (+1/-12)
src/std/dev/devAiSoft.c (+5/-17)
src/std/dev/devAiSoftRaw.c (+3/-15)
src/std/dev/devAoSoftCallback.c (+11/-13)
src/std/dev/devBiDbState.c (+1/-1)
src/std/dev/devBiSoft.c (+4/-18)
src/std/dev/devBiSoftRaw.c (+2/-15)
src/std/dev/devBoSoftCallback.c (+13/-16)
src/std/dev/devCalcoutSoftCallback.c (+9/-11)
src/std/dev/devEventSoft.c (+4/-17)
src/std/dev/devHistogramSoft.c (+3/-15)
src/std/dev/devLiSoft.c (+4/-16)
src/std/dev/devLoSoftCallback.c (+14/-16)
src/std/dev/devLsiSoft.c (+1/-1)
src/std/dev/devLsoSoftCallback.c (+8/-12)
src/std/dev/devMbbiDirectSoft.c (+5/-18)
src/std/dev/devMbbiDirectSoftRaw.c (+7/-17)
src/std/dev/devMbbiSoft.c (+6/-18)
src/std/dev/devMbbiSoftRaw.c (+7/-17)
src/std/dev/devMbboDirectSoftCallback.c (+6/-13)
src/std/dev/devMbboSoftCallback.c (+14/-16)
src/std/dev/devPrintfSoftCallback.c (+7/-12)
src/std/dev/devSASoft.c (+11/-16)
src/std/dev/devSiSoft.c (+4/-17)
src/std/dev/devSoSoftCallback.c (+17/-18)
src/std/dev/devWfSoft.c (+15/-18)
src/std/link/Makefile (+18/-0)
src/std/link/links.dbd.pod (+118/-0)
src/std/link/lnkCalc.c (+625/-0)
src/std/link/lnkConst.c (+566/-0)
src/std/rec/aSubRecord.c (+8/-56)
src/std/rec/aaiRecord.c (+1/-4)
src/std/rec/aaoRecord.c (+1/-4)
src/std/rec/aiRecord.c (+2/-9)
src/std/rec/aoRecord.c (+5/-10)
src/std/rec/boRecord.c (+28/-31)
src/std/rec/calcRecord.c (+1/-3)
src/std/rec/calcoutRecord.c (+65/-39)
src/std/rec/dfanoutRecord.c (+14/-11)
src/std/rec/eventRecord.c (+2/-7)
src/std/rec/fanoutRecord.c (+2/-3)
src/std/rec/histogramRecord.c (+2/-7)
src/std/rec/longinRecord.c (+26/-27)
src/std/rec/longoutRecord.c (+32/-28)
src/std/rec/mbbiDirectRecord.c (+2/-5)
src/std/rec/mbbiRecord.c (+2/-5)
src/std/rec/mbboDirectRecord.c (+4/-7)
src/std/rec/mbboRecord.c (+4/-7)
src/std/rec/printfRecord.c (+5/-5)
src/std/rec/selRecord.c (+7/-10)
src/std/rec/seqRecord.c (+5/-7)
src/std/rec/stringinRecord.c (+26/-27)
src/std/rec/stringoutRecord.c (+35/-32)
src/std/rec/subRecord.c (+1/-3)
src/std/rec/test/arrayOpTest.c (+6/-5)
src/std/rec/test/arrayOpTest.db (+1/-0)
src/std/rec/waveformRecord.c (+4/-7)
src/std/softIoc/RULES (+2/-0)
src/std/softIoc/base.dbd (+3/-0)
src/tools/DBD.pm (+6/-0)
src/tools/DBD/Device.pm (+1/-0)
src/tools/DBD/Link.pm (+22/-0)
src/tools/DBD/Output.pm (+9/-0)
src/tools/DBD/Parser.pm (+6/-0)
src/tools/Makefile (+1/-0)
src/tools/registerRecordDeviceDriver.pl (+18/-0)
Text conflict in src/ioc/db/dbLink.c
Text conflict in src/std/dev/devSoSoftCallback.c
To merge this branch: bzr merge lp:~epics-core/epics-base/link-support-2
Reviewer Review Type Date Requested Status
Andrew Johnson Needs Resubmitting on 2017-04-26
mdavidsaver 2016-08-11 Needs Fixing on 2016-08-13
Review via email: mp+302733@code.launchpad.net

Description of the change

Progress on JSON Link types, including constant array value initialization.

This branch passes the existing test programs, but I haven't added new test programs or documentation yet.

The functionality is present for device support to register itself as taking a JSON_LINK address type, and for such addresses to be checked for valid JSON syntax when the INP/OUT field gets set. The device must do its own parsing of the stored string, probably from the call to its dsxt->addRecord() routine. DB files may contain unquoted JSON in link field values and in info tags. Constant links containing JSON arrays are supported by the devAaiSoft and devWfSoft device support and the aSub record type; code changes are needed for adding constant array value support.

Still in progress: A DBD file may declare a link(name, lset) type, but nothing uses the registered data yet, and the lset needs extending to handle extensible link types anyway.

To post a comment you must log in.
mdavidsaver (mdavidsaver) wrote :

What I see looks like JSON_LINK as another hardware link type, which as you say device support would parse/consume the json string. How would this relate to my use case of a CA_LINK-like arrangement, where device support should leave the string alone? Is this part of the remaining work? Some slight of hand changing the link type code to CA_LINK?

review: Needs Information
mdavidsaver (mdavidsaver) wrote :

Did some playing around with this. Looks extra trailing brackets and commas don't errors as I would expect. I find some unexpected parser successes.

> field(INP, "{x: 4 }, {{}}}}")
> field(INP, "{y:3} }}}")

mdavidsaver (mdavidsaver) wrote :

Oh, I see. I don't want the quotes.

12759. By mdavidsaver on 2016-08-12

iocInit: close CA_LINKs through lset

mdavidsaver (mdavidsaver) wrote :

From what I see it looks like the remaining work is:

* Add the registerLinks infrastructure and hooks in registerRecordDeviceDriver.pl
* Hooks in iocInit.c to populate linkSup::lset
* Hooks in dbInitLink() and dbAddLink() to select lset

FYI, before merging this branch I'm going to want to port my PVA link code to work with it. I don't think this will be a big task.

https://github.com/mdavidsaver/epics-base/tree/calinkplug

From my branch, have you seen the report lset function?

https://github.com/mdavidsaver/epics-base/commit/905c5e33362d940f92c12de5f528fe9ab00a0c20

review: Needs Fixing
12760. By Andrew Johnson on 2016-08-13

A cleaner way to close CA & DB links

12761. By Andrew Johnson on 2016-08-13

Fix issues related to const array initialization

12762. By Andrew Johnson on 2016-08-13

Undo a small & unnecessary behaviour change

Andrew Johnson (anj) wrote :

Yes, the JSON_LINK type is intended for something like asynDriver that wants flexibility. The IOC may not know until iocInit or even runtime how to parse the complete hardware address, but the device layer can work it out.

I had forgotten exactly what you were working on, so your responses are helpful. I don't really like your lset hijacking approach, I'm trying to see if I can provide an alternative that will put both pva and ca link types at the same level, but I'm not there yet.

I'm currently adding an LSET routine for putCallback as required by the dev*oSoftCallback device supports — actually it implements put-wait-reprocess since the only thing the callbacks are ever used for is to reprocess the record containing the link after the put has completed. Currently these device supports only work over CA, but I see no real reason they can't be extended to support dbNotify (i.e. local DB) links as well, that's JMOP.

I also want to add a dbLinkIsRemote() routine which is really what many of the remaining tests for (plink->type == CA_LINK) are really asking about; that should allow all the CA-specific calls in the calcout record type to use the generic API as well. A remote link type is one that can connect or disconnect asynchronously, so both ca and pva are remote; I want to remove as much code that looks at the plink->type value as possible.

For fully generic link types we will need one or more routines that get integrated into the JSON parser used for link-resolution and either accept or reject the link address. I'm not sure yet if these should be included in the LSET or in some other table (the one that the DBD link entry actually refers to); I'm leaning towards the latter for simplicity's sake, but haven't implemented anything yet.

mdavidsaver (mdavidsaver) wrote :

> Yes, the JSON_LINK type is intended for something like asynDriver that wants flexibility.

Well, this would be convenience, though for me the critical need for 3.16.1 is an API for adding code on par with CA_LINK.

> I had forgotten exactly what you were working on, so your responses are helpful. I don't really like your lset hijacking approach, I'm trying to see if I can provide an alternative that will put both pva and ca link types at the same level, but I'm not there yet.

My API is a placeholder, you aren't meant to like it (I don't). I wanted to test the principle. If you can come up with something better before 3.16.1 then great. If would take me ~1 week to re-cast my API into a more maintainable form.

12763. By Andrew Johnson on 2016-08-14

Add lset::dbPutLinkAsync and S_db_noLSET

12764. By Andrew Johnson on 2016-08-14

Use dbPutLinkAsync() in all output SoftCallback dev's

12765. By Andrew Johnson on 2016-08-16

Remove 2 obsolete dbStatic routines (forms)

12766. By Andrew Johnson on 2016-08-18

Missed a spot handling JSON_LINK field values

Andrew Johnson (anj) wrote :

One thing I realized earlier that I want to be possible at some point: The JSON link parsing code and operations must be re-entrant and designed so a link can embed other links if it wants to. Here's one possibility that should allow:

record(ai, "current") {
  field(DTYP, "some-psu")
  field(INP, "#C0 S0 @whatever")
  field(SIML, "simulate")
  field(SIOL, {calc:{
    expr:"(val+a/b)/2",
    in:[{ca:"voltage CP"}, 10e3]
  }})
}

I'm not saying the implementation will support this to start with, but I want to bear the use-case in mind so as not to preclude its addition in the future. This comment is here to remind me...

12767. By Andrew Johnson on 2016-08-27

Add isConstant and isVolatile to LSET

12768. By Andrew Johnson on 2016-08-27

Use new dbLink APIs instead of checking link.type

12769. By Andrew Johnson on 2016-08-27

Start documenting changes

Andrew Johnson (anj) wrote :

Hi Michael,

I just committed changes so LSETs indicate if they are constants (i.e. they implement LoadScalar and/or LoadArray and don't set any data in Get), or volatile (links can disconnect like CA). I have changed most places where the record and device types look at plink->type to use these flags instead, so they will still work with JSON-links as well. Not done the SoftCallback devices though, the LSET doesn't have a processNotify API yet anyway.

Needless to say I haven't tested most of the changes to the std/dev or std/rec sources. Many of them should be simple enough to accept by inspection, but the calcout changes should probably be checked carefully (now uses the volatile property and the dbLink APIs instead of dbCa; I forgot to remove that header though).

I also started to document what I've been doing in the RELEASE_NOTES, probably several bits missing there still though.

Lots more code on the way, I have implemented the "remaining work" you itemized and created a const JSON link type to help prove my new dbJLink.c code. I don't have the ability to embed links inside other links yet though, I think that will need an API change. This stuff isn't quite ready to commit yet, maybe a day or two more.

I will be asking you to think about locking and if/how it will be possible to implement DB links type through a JSON link. That will probably require adding one or more routines to the LSET or even to the JLink interface for querying lock-sets, but I'll get back to you on that topic after I've committed the JLinks.

Sorry I haven't been too communicative recently, I've been on occasion working until 1:30/2am on this stuff since I don't have time at work.

- Andrew

12770. By Andrew Johnson on 2016-08-29

Undo buggy change

12771. By Andrew Johnson on 2016-08-29

JSON Links implementation

The lnkConst.c implementation is not yet complete, no support for arrays of
strings (JMOP).

Link error messages should display their record & field name, which is not yet
possible.

The ability to embed links as parameters to other link types is not complete
yet; this will be required for the calc link type.

This code currently passes all existing tests, but additional tests are needed
for the new functionality.

12772. By Andrew Johnson on 2016-08-29

Fix build warning from clang

mdavidsaver (mdavidsaver) wrote :

> I will be asking you to think about locking and if/how it will be possible to implement DB links type through a JSON link.

Not for 3.16.1. I'm going to draw a line at making further changes to lockset code for this release.

From your prospective, a hard requirement will be that all involved records be known when dbParseLink() returns. I see no way to relax this without introducing tremendous complexity (eg. higher level fail and re-try). So the fact that there are no link support callbacks before dbSetLink() seems like a show stopper for changing lock sets.

In general I'd like to see more validation in dbParseLink() as this is the point where such errors can be failed cleanly. Forgetting the '@' w/ INST_IO is easy. I see typos in json strings being even more common. I'm sure you won't like the idea of parsing twice, but I see this as worthwhile trade off.

> I have implemented the "remaining work" you itemized

Ok, I'll give it a shot.

12773. By Andrew Johnson on 2016-08-30

Pass link dbfType to jlif allocator; needed!

mdavidsaver (mdavidsaver) wrote :

> status = dbPutLinkAsync(plink, DBR_DOUBLE, &prec->oval, 1);
> if (!status)
> prec->pact = TRUE;
> else if (status == S_db_noLSET)
> status = dbPutLink(plink, DBR_DOUBLE, &prec->oval, 1);

Can you describe the difference between dbPutLink() and dbPutLinkAsync()? Naively I would expect that dbPutLinkAsync() would include a completion callback, and possibly some means of cancellation.

Perhaps what you are after isn't so much a decision, but an effect. Maybe it would be better to either require the caller to check dbLinkIsVolatile() to find out whether puts complete immediately, or have a special status code to indicate that the request was successfully sent?

Andrew Johnson (anj) wrote :

We will have to mark the dbLink.h and possibly dbJLink.h APIs as incomplete for the 3.16.1 release then, because they will have to change to add the locking support. I would also expect that change to move the code for handling DB_LINKs from dbLock.c into dbDbLink.c.

> all involved records be known when dbParseLink() returns.
...
> I'd like to see more validation in dbParseLink()

There is syntax checking of the JSON that happens at dbLoadRecords() time, but no semantic checks of the content then. Currently dbInitLink() and hence dbJInitLink() are called from doResolveLinks() in iocInit.c which is part of initDatabase(), whereas dbParseLink() is run from prepareLinks() which happens earlier, before any record initialization has happened.

I don't see any problems with doing the JSON parsing earlier though, so I will work to split dbJLinkInit() into two parts: One called from dbParseLink() for parsing the JSON into a jlink pointer stored in the dbLinkInfo object, and a second one called from dbInitLink() that assigns the jlink object to its specific link field and sets plink->lset.

> Can you describe the difference between dbPutLink() and dbPutLinkAsync()?

dbPutLinkAsync() is used by all of the dev*oSoftCallback.c device support, i.e. Async Soft Channel support for output record types. This isn't new functionality, it's why CA has ca_put_complete(), to wait until all record processing at the far end has finished. If you come up with a better name for this routine let me know.

On completion the link's implementation must call the routine dbLinkAsyncComplete(plink) which locks the record and calls the record's process() routine. Previously for CA links the device supports all called
    dbCaPutLinkCallback(plink, DBR_xx, &prec->oval, 1, dbCaCallbackProcess, plink)
but since they all specified the same callback routine and context parameter I thought we might as well take those out of the argument list.

I was wondering if we could make the completion more useful by adding another RSET routine which (if set) would be called instead of process(), passing it a pointer to the link that just completed; the idea being to simplify record types like motor and table that remain active for long periods and want completion notifications. However I need to consider how the callback would work for embedded links, so I might need to reinstate those callback parameters).

12774. By Andrew Johnson on 2016-08-31

Add code for arrays of strings

12775. By Andrew Johnson on 2016-08-31

Added loadLS routine to lset

The long string in/out records use a different initializer
for constant links. The new loadLS method allows a constant
link type to initialize such a long string field.

NB: This routine was added in the middle of the lset table.
Any external link support implementations must be adjusted.

12776. By Andrew Johnson on 2016-09-01

Split dbJLinkInit, JLinks are now parsed at load-time

Andrew Johnson (anj) wrote :

I have split up the dbJLink initialization, but I also had to change the link type registration code for it to work. For the other entry table pointers (drvet, dset etc) the "vtable" pointer gets set during iocInit, but in this case it must be set earlier in the ioc_registerRecordDeviceDriver() routine so the dbLoadRecords() calls to dbParseLink() can call the link-type's parsing routines.

This reorganization cleaned up the JLink code slightly and the result works again, but now that it's returning errors properly from the JSON parser the dbPutLinkTest.c program is failing. I know what at least some of the issues are in that, but I haven't tried to fix them for this commit. I'll probably have to write a test link type for that code anyway.

mdavidsaver (mdavidsaver) wrote :

I haven't check your latest changes, so this may be fixed. I find that the jlif free callback isn't being called if parsing is stopped. Thus a memory leak.

I would definitely say a unit test is in order w/ ref. counting to detect situations like this. Something with lnkConst.c which does link modification and checks a ref. count on shutdown to ensure that cleanup is done.

mdavidsaver (mdavidsaver) wrote :

> jlink* (*alloc_jlink)(short dbfType);

I see that alloc_link no longer gets a DBLINK* argument. I had been using this to find the associated record name for use in error messages. Can this be re-added?

Andrew Johnson (anj) wrote :

> the jlif free callback isn't being called if parsing is stopped

I partially fixed that in last night's commit, but there are more places in dbJLink.c that need fixes. I'll work on that tonight.

> I see that alloc_link no longer gets a DBLINK* argument.

Your dbParseLink() routine doesn't get the link pointer, so I couldn't pass it on (I was using it for that too). Obviously I could add it, but actually I think the inability to access the link at parse-time is probably good protection from someone writing a link type that changes the fields in the link while parsing the string instead of storing the parse results in their jlink. I believe the record and link field name will get printed anyway as long as the parse is aborted (that's probably new with last night's commit though). dbParseLink() doesn't print any error messages at the moment either.

This latest version is almost sufficient for child links (just one more jlif routine to add) and with that there's the question of how to identify a link embedded inside another link inside a record instance. Children get a link to their parent so can print the names of their antecedents, but they don't currently know how to self-identify among siblings.

mdavidsaver (mdavidsaver) wrote :

> Your dbParseLink() routine doesn't get the link pointer,

Good point. Maybe add a char* with the record name to dbLinkInfo?

> I believe the record and link field name will get printed anyway

I'll check this out.

mdavidsaver (mdavidsaver) wrote :

I'd like to use ->get_lset() as the place to open PVA (or CA) channels. It's in the right place, and has DBLINK* which I need for CP/CPP decisions, but the 'const jlink*' is complicating this. Can the 'const' go away?

12777. By Andrew Johnson on 2016-09-02

Various improvements

* Added new lset::openLink() method, called on JSON_LINKs only
* Cleanup in dbJLink.c to prevent memory leaks.
* Removed jlif::start_parse() method.
* Renamed jlif::end_parse() to end_child, which will be called on
the parent link when a child link has succesfully finished parsing.

Andrew Johnson (anj) wrote :

The const was to try and keep all the run-time stuff happening through the lset routines, so it sounds like it has been doing its job! I would like to keep get_lset as a single-purpose routine so it can be called from elsewhere without having any side-effects. The link pointer was only provided to it because at one point I thought the routine might set the plink->lset field directly, but that isn't going to happen now so I just removed it.

I do understand the need for a method to start off the connection process, but that should be an lset method, not a jlink one. I've added the following optional routine at the top of the lset table that I now call from dbJLinkInit():

    /* Activation */
    void (*openLink)(struct link *plink);

You should also look at using the lset's loadScalar(), loadLS() and loadArray() methods for configuring monitors for input links, they are given the data type that the record is going to be asking for when it later calls getValue(). I have been removing the conditionals from the calls to recGblConstInit() so these routines can be used for exactly this purpose, although your code must still work if they don't get called before getValue() does. They don't have to set a value, they can just return a non-zero value to indicate that they didn't.

BTW some people have written subroutine record code that abuses the INPA-L input links and uses them for outputs. That works OK with the existing link-types, so your code should expect it and either handle it or reject it.

I think I fixed the memory leaks too, but it's hard to be sure that I got all of them.

mdavidsaver (mdavidsaver) wrote :

> they are given the data type that the record is going to be asking for when it later calls getValue

Given that this isn't certain, I don't think I'll try to use it. A caller could first use dbGetLinkDBFtype() in an effort to get the "native" type. The cost of looking up a conversion function each time has never seemed like much.

> BTW some people have written subroutine record code that abuses the INPA-L input links and uses them for outputs

I've done this myself. As far as I'm concerned, "input" vs. "output" is a label/convention. There won't be a functional difference. In fact, with PVA, forward links also won't be different either.

> I think I fixed the memory leaks too, but it's hard to be sure that I got all of them.

A unit test would help...

Andrew Johnson (anj) wrote :

> Given that this isn't certain

It is certain for the input links of all the built-in record types. I think you would also be justified in printing a warning message if a subsequent getValue() disagrees with the data type or requests a larger size than the initial load*() request.

> A caller could first use dbGetLinkDBFtype()

They could, but the existing code doesn't, it relies on dbCa to handle all the data type stuff. That routine is also going to return -1 until the link gets connected, whereas if you use the hints the link would be able to subscribe sooner and so start delivering real data sooner.

> The cost of looking up a conversion function each time has never seemed like much.

It's not the cost of looking up the conversion function, but the data type and size you subscribe for that I was thinking about. If the link is an ai record asking for the first element from a waveform record and you always subscribe for the target's array size…

> A unit test would help...

Yeah, luckily there's a long wekend coming up and my wife will be away for most of it. I do have a hard deadline on some other EPICS stuff coming up, but I'll work on adding that.

12778. By Andrew Johnson on 2016-09-03

Added epicsStrnDup() and dbmfStrndup() routines

The JSON parser passes string arguments with a length
instead or nil-terminating them. These routines make it
simple to copy such strings into either permanent or
temporary storage.

12779. By Andrew Johnson on 2016-09-03

Use new epicsStrnDup() API

12780. By Andrew Johnson on 2016-09-03

Fixes to dbJLink, added dbJLinkFree()

Moved the clearing of key_is_link to the right place,
embedded links now parse correctly.

mdavidsaver (mdavidsaver) wrote :

I noticed epicsStrnDup(), which calls cantProceed() on allocation failure. While this is fine in startup-only code like dbmf.c, I don't like to see it used in lnkConst.c where it is run after startup.

From our previous discussions (some time ago) the idea that I have is that after IOC startup, cantProceed() is only appropriate for unrecoverable errors (eg. linked list corruption or pthread mutex errors).

mdavidsaver (mdavidsaver) wrote :

Any reason that doCloseLinks() in iocInit() doesn't close JSON_LINK?

mdavidsaver (mdavidsaver) wrote :

I think I have the basics of pvalink.cpp w/ input links working again with json links, except for the lack of cleanup on shutdown. Still need to test output link.

https://github.com/mdavidsaver/v4workspace.git

Previously I had hooked into dbcar() for reporting. Do you intend some general reporting function for json links, or do I need to add a PVA link specific reporting function?

12781. By Andrew Johnson on 2016-09-04

Moved PV_LINK-specific code out of dbLink.c into link types

This required a change to the lset::getValue arguments, removing
the pstat and psevr pointers. Links can still return a non-zero
value from getValue and trigger a LINK:INVALID alarm, but for any
other alarm settings they must manipulate the record themselves.

12782. By Andrew Johnson on 2016-09-04

Added calc link-type

Andrew Johnson (anj) wrote :

Just committed a couple more changes, one of which will require you to modify your getValue() routine slightly (the pstat & psevr args have gone, look at the commit for why).

You should take a look at the new calc link type that I also added (some docs generated in links.html).

I agree about the cantProceed stuff, for now I was keeping the routines the same but we need to fix them properly including their call-sites. The new dbmfStrndup() will segfault like dbmfStrdup() if dbmfMalloc() ever returns NULL.

doCloseLinks() should close JSON_LINKs, I'll fix that later.

I'm hitting string length limits, dbpr can't display long JSON_LINK strings, and I think there's also a length limit to the long-string one can caput to a link field which will need to be increased somewhat.

I need to stop for supper now, later!

12783. By Andrew Johnson on 2016-09-04

Minor cleanups

12784. By Andrew Johnson on 2016-09-04

Clean up JSON_LINKs in doCloseLinks

12785. By Andrew Johnson on 2016-09-04

Minor fixes to the calc link-type

Andrew Johnson (anj) wrote :

Fixed doCloseLinks(), which now cleans up JSON links (and ensures that the lockset is locked before cleaning up DBlinks on a buildIsolated IOC.

There is still a bug in the embedded JLink handling, multiple levels of embeds don't work properly. I know roughly what's going on, I just have to work out how to fix it.

12786. By Andrew Johnson on 2016-09-04

Fix jlink memory leak in dbStaticLib

Added dbFreeLinkInfo(), use everywhere to release dbLinkInfo resources.

Renamed link_type => expected_type in db[Can]SetLink(),
my brain understands this name faster.

12787. By Andrew Johnson on 2016-09-04

Add lnkConst_remove, fix debug messages

12788. By Andrew Johnson on 2016-09-04

JLink: Convert parser->linkDepth into jlink->parseDepth

This counter is indicates when we've finished parsing a link,
and needs to be stored with link rather than the parser so it
keeps its value while parsing embedded links. This fixes the
embedded links bug.

Also removed the limit on a link name's length.

12789. By Andrew Johnson on 2016-09-04

Make the long-string buffer for link fields bigger

When representing a link field as a long string (.INP$)
we have to pick some size limit for the buffer.
Previously this was the max length of a PV name + 12 chars,
but with JSON links that's not big enough.

This commit sets it to 1KB and defines a macro so it will be
easier to change in the future if necessary.

12790. By Andrew Johnson on 2016-09-05

Added JLink reporting infrastructure

Command 'dbjlr <record|*> <level>' calls the report method for
all JSON links in all records, or in one named record.
Added level and indent arguments to the jlif::report() method.

Added jlif::map_children() method for recursing through all
JSON links, plus dbJLinkMapChildren() and dbJLinkMapAll() APIs.

Implemented the report and map_children methods in the const
and calc link types.

Andrew Johnson (anj) wrote :

Added the 'dbjlr <record> <level>' command that calls jlif::report() for all JSON_LINKs:
    void (*report)(const jlink *, int level, int indent);
        /* Optional, print status information about this link instance, then
         * if (level > 0) print a link identifier (at indent+2) and call
         * dbJLinkReport(child, level-1, indent+4)
         * for each child.
         */

The report method is intended for individual status reports and not collecting summary statistics, but I've also added another optional jlif routine for recursing through all child links:
    typedef long (*jlink_map_fn)(jlink *, void *ctx);

    long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
        /* Optional, call dbJLinkMapChildren() on all embedded links.
         * Stop immediately and return status if non-zero.
         */
There is also a top-level API for running that on all records:
    long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)

Question about alarms and embedded links: Currently a link that wants to raise an alarm does so by calling recGblSetSevr(plink->record, stat, sevr), so for embedded links the parent link has no idea that the child raised an alarm or what alarm state it's in. Should I add an lset routine and a dbLink wrapper for handling alarms from child links, which for a top-level link (or when this is unimplemented in the parent) would call recGblSetSevr()? This way the parent could see and filter the child's alarms if it wanted to.

I think I'm done implementing for the weekend now. I'll check back for any comments later, but I have to produce a 45-minute talk to give at LBNL in 7 days time, and 4.6.0 is also due out this week too.

What if anything do you think is still missing or wrong in this code? Ahh, except for still having a broken dbPutLinkTest...

Thanks!

Andrew Johnson (anj) wrote :

... and 'dbpr' dying when it tries to print a JSON link that is way too big for its buffer.

Did we fix that problem already on another branch?

mdavidsaver (mdavidsaver) wrote :

> Did we fix that problem already on another branch?

Supposedly I fixed this for 3.16 as per lp:1462214.

mdavidsaver (mdavidsaver) wrote :

> Should I add an lset routine and a dbLink wrapper for handling alarms from child links, which for a top-level link (or when this is unimplemented in the parent) would call recGblSetSevr()? This way the parent could see and filter the child's alarms if it wanted to.

Do you have a use case in mind? I can't think of anything other than maximize severity.

12791. By Andrew Johnson on 2016-09-05

Some documentation updates

Andrew Johnson (anj) wrote :

> Supposedly I fixed this for 3.16 as per lp:1462214.

I thought we did, thanks.

> Do you have a use case in mind?

I think the NMS/MS/MSI/MSS flag belongs in the child link, not in the parent.

I was thinking the user may want to find out which link a link alarm is coming from, but I guess that information should appear in the report from the child link itself (fix to lnkCalc needed). Ok, no API changes needed there, thanks!

12792. By Andrew Johnson on 2016-09-05

Minor updates

12793. By Andrew Johnson on 2016-09-06

Fix warnings from clang

mdavidsaver (mdavidsaver) wrote :

I see that doCloseLinks() now calls lset::removeLink, but not jlif::free_jlink. Is this intended? I think it's consistent that I should free() in both place, I just want to clarify the lifecycle to avoid leaks.

12794. By Andrew Johnson on 2016-09-07

Fixed a small memory leak in lnkCalc

Andrew Johnson (anj) wrote :

Yes, it feels a bit strange but both routines are needed and do almost the same thing, but with different arguments. jlif:free_jlink() is called when a jlink doesn't parse correctly; it doesn't have a plink or an lset pointer yet, so we couldn't call the lset::removeLink() method at this point anyway. The lset::removeLink() routine is called after a link has been opened when the link field's value gets overwritten, and is also needed for the CA_LINK and DB_LINK types which don't have a jlif.

A link that has embedded child links must call all their free_jlink() routines from its free_jlink(), and all their removeLink() routines from its removeLink() routine.

Unfortunately it is fairly easy for the two routines to get out of step during development, as evidenced by my lnkCalc where I forgot to update removeLink() after adding some fields for the report displays; another fix committed!

I started working on the test code again and found a major flaw: When I moved the JSON parsing to before record initialization I forgot that while parsing a JSON link for an OUT/INP field we need to be able to tell whether DTYP is set to a device support like "Soft Channel" (when link name lookup must occur while parsing) or to "My JSON-addressed device support" (when it shouldn't). I'm going to need to sleep on this to work out what to do, but for now we can't handle JSON_LINK addresses for device support routines.

12795. By Andrew Johnson on 2016-09-07

Clean up memory leaks

12796. By Andrew Johnson on 2016-09-07

Added test link type, fix dbPutLinkTest for JSON_LINKs

Andrew Johnson (anj) wrote :

dbPutLinkTest now works and doesn't leak memory when checking JSON_LINK values.

The Release Notes entries are still incomplete, and there's that device(JSON_LINK) issue...

mdavidsaver (mdavidsaver) wrote :

valgrind tells me that jlink::parent and jlink::parseDepth are not being initialized, at least for a top level jlink.

12797. By Andrew Johnson on 2016-09-08

Initialize all fields of jlink, link types may not use calloc

Andrew Johnson (anj) wrote :

Your alloc_jlink() method is probably using malloc() rather than calloc(), which is why valgrind doesn't flag my link types with that problem. Ok, I added code to dbJLink.c to explicitly initialize those fields.

Andrew Johnson (anj) wrote :

So instead of the DTYP controlling what address types are accepted in the INP/OUT link (as with the original hardware addresses), how about making it so that putting a JSON address into the INP/OUT link would also set the DTYP field to match the device type named in the address? The user won't have to set DTYP at all when they're using a JSON device type. The interface between record support and device support doesn't change at all, but there will be a new table for the JSON parsing routines.

There will need to be a new DBD keyword, probably
    devlink(<rectype>, <linktype>, <devlinkIf>, "DTYP Name")
The <linktype> name will be matched against the keys in the JSON INP/OUT address, so I have to pass them from the record type into the dbParseLink() routine. I haven't worked out yet where the DTYP field would actually get set, I guess that could be just another value stored in the link_info object, so it should probably be in dbSetLinkJSON() except that the plink->precord links haven't been set up yet so it can't find the prec->dtyp field.

Haven't implemented anything yet, but I think this idea has a good chance of working, do you see any obvious flaws?

12798. By Andrew Johnson on 2016-09-09

Fixes for Windows builds

12799. By Andrew Johnson on 2016-12-12

Update comments in calcoutRecord

12800. By Andrew Johnson on 2017-02-17

Remove constant link checks from test device

12801. By Andrew Johnson on 2017-02-17

Fix HTML entities

12802. By mdavidsaver on 2017-03-01

add testing lset

12803. By mdavidsaver on 2017-03-01

libCom: add errSymMsg() error message lookup

Like errSymLookup() but always returns a static string.

12804. By mdavidsaver on 2017-03-01

dbUnitTest: more informative dbPutField*()

12805. By Andrew Johnson on 2017-03-01

dbUnitTest: Improve output slightly

12806. By mdavidsaver on 2017-03-01

db/test: dbPutLinkTest include json links and more

12807. By mdavidsaver on 2017-03-01

dbLock: add assert in dbScanLock

catch locking attempts before iocInit()

Andrew Johnson (anj) wrote :

Cherry-picked the additional tests from your integration branch (different order though, and I merged 2 of your commits into one to keep the tests passing).

Need to write some more text for the Release Notes and the std/link/links.dbd.pod file.

12808. By Andrew Johnson on 2017-03-03

db/test: Fix warning from clang

Andrew Johnson (anj) wrote :

F2F 3/15: OK for merging with the API being PROVISIONAL with doLocked method. The link API might still see drastic changes in the future. At least one more link type implementation desired to prove API validity and completeness. AJ to merge doLocked code.

mdavidsaver (mdavidsaver) wrote :

As an exercise in exercising the new API I started on a second implementation of CA links. Only input links work w/o CP. In the process I came across two points.

* What to do about pvlOptTSELisTime? How does TSEL work wrt. link support. It seems quite redundant to copy+paste INP/OUT into TSEL every time.

* I realized that the meta-data functions in struct lset taken together are equivalent to struct dbr_ctrl_double (aka always double). So the typeiness of dbGet() with *poptions isn't needed. Why not have a single lset call which is given a struct to fill in. Perhaps with a bit-mask to reduce work (although I wonder if the locking overhead would be greater).

> long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
> long *pnRequest, dbr_ctrl_double *meta);

(I'm aware that dbr_ctrl_double is in db_access.h, and can't appear in dbLink.h, this is an example)

https://github.com/mdavidsaver/epics-base/blob/integration/src/std/link/lnkCa.c

mdavidsaver (mdavidsaver) wrote :

> How does TSEL work wrt. link support.

So far my thinking is to add another special case (in addition to .TIME). That linking TSEL to INP/OUT of the same record will cause dbGetTimeStamp to be called with the INP/OUT link. This would look like:

> record(ai, "xxxx") {
> field(INP, {....})
> field(TSEL, "xxxx.INP")
> }

mdavidsaver (mdavidsaver) wrote :

> That linking TSEL to INP/OUT of the same record

or maybe any DBF_*LINK field of the same record.

mdavidsaver (mdavidsaver) wrote :

wrt. lset locking and consistency between value/alarm/time. I still don't like the proposed doLocked() method. It seems cumbersome to use, and requires re-fetching the (possibly different) value. For example, this isn't compatible with the present handling of TSEL.

My thinking so far is to latch value+meta data at some point, so that subsequent calls to eg. dbGetAlarm() return the cached value. I can think of two ways this could work (there are certainly more).

1. A call to dbGetLink() latches alarm/time (and GR/CTRL meta). This requires that dbGetLink() is called before dbGetAlarm()/dbGetTimeStamp() or values would be returned. This wouldn't break any code I've written or seen. Such errors could be detected (and a warning printed?)

2. The first call to a dbGet*() function after a record starts dbProcess() could cause a latch.

Both 2. and the warning in 1. could be implemented with a per-record sequence counter.

mdavidsaver (mdavidsaver) wrote :

More ca link work adding CP and output link (put). Now testable.

https://github.com/mdavidsaver/epics-base/blob/integration/src/std/link/lnkCa.c

Andrew Johnson (anj) wrote :

doLocked() only requires the value re-fetching when you're writing aSub code, where the problem is that you don't (currently) have a way to prevent the record type from doing the initial fetch of all the INP* links. When writing a record type or device support which does the get itself that wouldn't be a problem. We could add a way to allow an aSub Init routine to tell the record type not to get the values of the record's INP* links but leave them to the subroutine itself.

I don't see any other way to avoid doing wasted work — either the link will be copying metadata which will never be used (this is likely to be the vast majority of cases, so arguably your suggestion would waste more effort here) or (absent the above aSub changes) you have to do the get/convert thing twice in that particular case. All link types that provide their own locks (i.e. except for DB-links) should be using an epicsMutex, so I don't see the recursive locking as being a big problem.

Your discussion about TSEL is for the case where you want to copy the timestamp from the same place as the INP link, without having to set up a second link — is this correct? If you're using the standard record types with DTYP=Soft Channel you can set TSEL=-2 (epicsTimeEventDeviceTime) and we already do almost the right thing. The code that handles TSEL==epicsTimeEventDeviceTime in the existing soft device support code can be made atomic using doLocked(), e.g. for devAiSoft.c (untested):

static long readLocked(struct link *pinp, void *dummy)
{
    double val;
    aiRecord *prec = pinp->precord;
    long status = dbGetLink(pinp, DBR_DOUBLE, &val, 0, 0);

    if (status) return status;

    /* Apply smoothing algorithm */
    if (prec->smoo != 0.0 && prec->dpvt && finite(prec->val))
        prec->val = val * (1.00 - prec->smoo) + (prec->val * prec->smoo);
    else
        prec->val = val;

    prec->udf = FALSE;
    prec->dpvt = &devAiSoft; /* Any non-zero value */

    if (dbLinkIsConstant(&prec->tsel) &&
        prec->tse == epicsTimeEventDeviceTime)
        dbGetTimeStamp(pinp, &prec->time);

    return 0;
}

static long read_ai(aiRecord *prec)
{
    if (dbLinkIsConstant(&prec->inp))
        return 2;

    if (dbLinkDoLocked(&prec->inp, readLocked, NULL)) {
        prec->dpvt = NULL;
    }
    return 2;
}

mdavidsaver (mdavidsaver) wrote :

> ... link will be copying metadata which will never be used ...

Oh, Andrew I thought you knew me better than this :) There is no reason to extra copying. Double buffering and/or a bit mask (like dbGet *options) can avoid this. I don't see that any significant overhead need be added.

To modify my previous:

> long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
> long *pnRequest, const dbr_ctrl_double **meta);

where:

> const dbr_ctrl_double *pmeta = NULL;
> ...->getValue(... , &pmeta);

The pointer stored in 'pmeta' would remain stable until the subsequent call to getValue.

Do you have any thoughts about imposing an ordering requirement? (dbGetLink first then metadata calls)

> ... When writing a record type or device support which does the get itself
> ... you can set TSEL=-2 ...

> Your discussion about TSEL is for the case where you want to copy the timestamp from the same place as the INP link, without having to set up a second link — is this correct?

Yes. Although I'm focused on CA-like links, not the HW links that I think you're considering. The TSE=-2 hack is sufficient for HW links, but isn't relevant otherwise. It might be better if you think about this in the context of calcRecord, which doesn't (and shouldn't) know about the difference between DB/CA/... links.

To rephrase my previous example:

> record(calc, "xxxx") {
> field(INPA, {ca:{pv:"src:pv",proc:"CP"})
> ...
> field(TSEL, "xxxx.INPA")
> }

My requirement is to ensure that the result will have the same timestamp as the input update it derives from.

Andrew Johnson (anj) wrote :

What changes will you have to make to the record types to actually make use of your solution? You forgot to pass in the bitmask BTW, or is that going to be stored inside the data structure? The fact that you have to consider the ordering is a code smell, and one of the reasons I don't like your approach. It seems to me that implementing your getValue() method would involve adding quite a bit of code to each link type.

> My requirement is to ensure that the result will have the same timestamp as the input update it derives from.

I'm not discussing hardware links at all, I'm pretty sure that I'm talking about the same thing you are. TSEL support for epicsTimeEventDeviceTime has been in place for a very long time; it tells recGblGetTimeStamp() that device support has already set the TIME field, and my modified devAiSoft.c shows how Soft Channel input device support can get the timestamp and the data atomically from any link type that supports dbLinkDoLocked().

The devAiSoft.c implementation I posted does currently assume that all link types provide the doLocked() method, so I do need to modify the read_ai routine slightly to cover the case where a link type doesn't (in which case we can't provide atomicity with that link type), but that's a simple change. I could modify dbLinkDoLocked() to call the subroutine directly in that case, but I think it's better to let the caller do that so it can warn the user about non-atomic operations if it wants to.

What is your actual requirement for timestamps with a calc record? It may be implementable with my solution by interposing (with a DB link) an ai record with TSEL=-2 in front of the INPx link which provides the timestamp, and pointing the calc's TSEL to that ai.TIME field using another DB link. The lockset should ensure atomicity of the calc's fetch of both value and timestamp from that ai record.

It may be though that your requirements can't be solved using a generic record type, or that the aSub needs to add more fields to hold the timestamps associated with the data from each of its INP* links, but you haven't really said what those additional requirements might be.

Andrew Johnson (anj) wrote :

Ok, you did give the calc example, I described how to solve it with an extra ai record. How much would you have to change the calc record to implement your solution? Note that most record types don't have to handle the TSEL field at the moment, it's all automatic, but I don't think that could be the case with your getValue() approach (or significantly complicating recGblGetTimeStamp()).

Andrew Johnson (anj) wrote :

Another approach which would work for your calc.INPx links directly would be to add a flag to the link type telling it to set the TIME field as well as fetch the data — similar to the way implementing MS/MSI/MSS flags requires the link's getValue() method to call recGblInheritSevr() to flag link alarms. Note this still require TSEL=-2. Thusly:

> record(calc, "xxxx") {
> field(INPA, {ca:{pv:"src:pv",proc:"CP",ts:true})
> ...
> field(TSEL, "-2")
> }

If the user doesn't set TSEL=-2 the timestamp will be overwritten when process() calls recGblGetTimeStamp().

mdavidsaver (mdavidsaver) wrote :

From 4 April meeting: waiting for ANJ to post code adding dbLinkDoLocked()

Andrew Johnson (anj) wrote :

dbLinkDoLocked() changes pushed (to the git repo at https://git.launchpad.net/~epics-core/epics-base/+git/link-support-2 ) including a test of the new API for dbCa links and changes to synchronize the soft device support supplied time-stamps.

Still some documentation missing (FIXME and ... in the Release Notes).

mdavidsaver (mdavidsaver) wrote :

wrt. FIXMEs

Doesn't look like any code calling dbmfMalloc() directly or indirectly (via dbmfStrdup) every checks for allocation failure. So imo. this should be cantProceed().

"lnkConst: Mixed data types in array". imo error is the best choice atm.

"FIXME: const strings are now supported". What is to be fixed here?

"FIXME This handles the common case, but not the general one...". also doesn't test for len>=4. The string handling in dbConstLoadLS() looks too similar to what caused problems of lp:1678494. After strlen() is called and compared with size, better to use memcpy() and no reason for second strlen().

arrayOpTest should have tests of array initialization failure. eg. missing ']', number parse error, and mixed data type.

mdavidsaver (mdavidsaver) wrote :

Also seeing the device support changes related to dbLinkDoLocked() does somewhat confirm my fear that too much work will be done in this callback. All work from the read functions is moved into the locked callback. And as we know, these soft channel dsets are frequently taken as best practice example by users.

Andrew Johnson (anj) wrote :

>> Doesn't look like any code calling dbmfMalloc() directly or indirectly
>> (via dbmfStrdup) every checks for allocation failure. So imo. this
>> should be cantProceed().

Right. This isn't a new issue though, I added those FIXMEs to libCom/dbmf/dbmf.c when I noticed the problem in the current implementation, while adding dbmfStrndup().

>> "lnkConst: Mixed data types in array". imo error is the best choice atm.

Agreed, they do currently print an error and fall through to return jlif_stop, so maybe we just need to remove those comments.

>> "FIXME: const strings are now supported". What is to be fixed here?

Don't see anything obvious, maybe I just forgot to remove the comment.

>> "FIXME This handles the common case, but not the general one...".
>> also doesn't test for len>=4. The string handling in dbConstLoadLS()
>> looks too similar to what caused problems of lp:1678494. After strlen()
>> is called and compared with size, better to use memcpy() and no reason
>> for second strlen().

The if condition will always be false when len < 4 because one of those character comparisons won't match — it tests them in order and uses && so it's never in danger of dereferencing an element after the first 0 byte in the string.

I think my point about the general case may have been multiple string items, but I don't have time right now to investigate.

>> arrayOpTest should have tests of array initialization failure.
>> eg. missing ']', number parse error, and mixed data type.

(out of time to comment, tomorrow...)

I am looking at fixing the device support. For some such as devBiSoft I see no point changing the code, but I agree that devAiSoft needs work.

mdavidsaver (mdavidsaver) wrote :

Should these three be equivalent initializations of INP w/ a long string? Hint, longstr2 seems to truncate.

record(lsi, "longstr1") {
  field(SIZV, "100")
  field(INP, ["!----------------------------------------------!"])
}

record(lsi, "longstr2") {
  field(SIZV, "100")
  field(INP, {const: ["!----------------------------------------------!"]})
}

record(lsi, "longstr3") {
  field(SIZV, "100")
  field(INP, {const: "!----------------------------------------------!"})
}

mdavidsaver (mdavidsaver) wrote :

Also, I want to use testdbGetArrFieldEqual() with these tests, so I will either rebase this branch onto the latest 3.16, or merge the latest 3.16 into this branch (I haven't decided which).

Andrew Johnson (anj) wrote :

>> Should these three be equivalent initializations of INP w/ a long string?
>> longstr2 seems to truncate.

If you create a const link with an array of strings the elements do currently get truncated, the assumption being that you're initializing a waveform with an array of DBF_STRINGs, which are stored as a series of 40 character buffers for something like this:

record(waveform, "longstr4") {
  field(FTVL, "STRING")
  field(NELM, 100)
  field(INP, ["!----------------------------------------------!", "2..."])
}
record(waveform, "longstr5") {
  field(FTVL, "STRING")
  field(NELM, 100)
  field(INP, {const: ["!----------------------------------------------!", "2..."]})
}

Looking at lnkConst_loadArray() though I guess there's no reason to truncate the individual elements when storing them in lnkConst_string(), the truncation will happen when they get copied into the waveform anyway, and doing this lets your 3 initializations be equivalent. Commit that fix?

Andrew Johnson (anj) wrote :

Fix Committed.

Interestingly mixed data types are legal (they work fine) in an array when using dbConstLink, but aren't with the lnkConst type. That's because in dbConstLink the JSON parsing gets deferred until you know what data type you're initializing.

epics> dbpr longstr4 1
APST: Always ASG: BKPT: 00 BUSY: 0
DESC: DISA: 0 DISP: 0 DISS: NO_ALARM
DISV: 1 DTYP: Soft Channel EGU: EVNT:
FLNK:CONSTANT 0 FTVL: STRING HOPR: 0
INP:CONSTANT ["!- One -!",2,3.0] LOPR: 0 MPST: Always
NAME: longstr4 NELM: 100 NORD: 3 PACT: 0
PHAS: 0 PINI: NO PREC: 0 PRIO: LOW
PUTF: 0 RARM: 0 RPRO: 0 SCAN: Passive
SDIS:CONSTANT SEVR: INVALID SIML:CONSTANT SIMM: NO
SIOL:CONSTANT STAT: UDF TPRO: 0 TSE: 0
TSEL:CONSTANT UDF: 0 UDFS: INVALID VAL: (nil)

tux% caget -c longstr4
longstr4 3 !- One -! 2 3.000000

mdavidsaver (mdavidsaver) wrote :

found and fixed mem leak in dbConstLink. found buffer overrun in linkCalc, fixed by anj. Also added unittest of linksupport initialization, merged in latest 3.16. Testing continues.

Andrew Johnson (anj) wrote :

Pushed a clean-up and slight simplification of lnkConst.c

Andrew Johnson (anj) wrote :

Have been working on Release Notes and other documentation, not complete yet.

Andrew Johnson (anj) wrote :

Pushed a fix to the printfRecord which resolves the FIXME (the comment was right), and adds tests.

Andrew Johnson (anj) wrote :

Resolved the FIXME in dbConstLink.c by parsing the JSON properly — added a new parser to dbConvertJSON.c. Additional test in linkInitTest which would have failed before.

Andrew Johnson (anj) wrote :

Committed updates to the Release Notes and links.dbd.pod, this documentation is probably done for now.

I also committed some changes to make the dbLinkIsConstant() and dbLinkIsVolatile() routines return only TRUE (1) or FALSE (0) — they used to return -1 when plset was NULL, but now that condition gives a constant, non-volatile result.

Still working on fixing the readLocked() methods of the soft device layers and adding a number of tests, but I won't have as much free time after committing those changes (probably tomorrow).

Andrew Johnson (anj) wrote :

Done what I think are my final commits, moving code out of readLocked(), fixing issues and adding tests.

I also added a feature to the subArray record, which can now be used as a lookup table, and fixed a bug in the event record.

I think I'm going to close this merge request and open up a new one for the git repo, to make it easier to examine the diffs against the current Base-3.16.

review: Needs Resubmitting

Unmerged revisions

12808. By Andrew Johnson on 2017-03-03

db/test: Fix warning from clang

12807. By mdavidsaver on 2017-03-01

dbLock: add assert in dbScanLock

catch locking attempts before iocInit()

12806. By mdavidsaver on 2017-03-01

db/test: dbPutLinkTest include json links and more

12805. By Andrew Johnson on 2017-03-01

dbUnitTest: Improve output slightly

12804. By mdavidsaver on 2017-03-01

dbUnitTest: more informative dbPutField*()

12803. By mdavidsaver on 2017-03-01

libCom: add errSymMsg() error message lookup

Like errSymLookup() but always returns a static string.

12802. By mdavidsaver on 2017-03-01

add testing lset

12801. By Andrew Johnson on 2017-02-17

Fix HTML entities

12800. By Andrew Johnson on 2017-02-17

Remove constant link checks from test device

12799. By Andrew Johnson on 2016-12-12

Update comments in calcoutRecord

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'documentation/RELEASE_NOTES.html'
2--- documentation/RELEASE_NOTES.html 2017-02-01 17:57:04 +0000
3+++ documentation/RELEASE_NOTES.html 2017-03-03 18:23:23 +0000
4@@ -21,6 +21,152 @@
5
6 -->
7
8+<h3>Device Support Address Type <tt>JSON_LINK</tt></h3>
9+
10+<p>Device support may be written to expect hardware addresses in the new
11+<tt>JSON_LINK</tt> address type. Addresses loaded from a database file will be
12+checked against the JSON rules, and a strict JSON representation of the result
13+is provided as a C string pointed to by <tt>link.value.json.string</tt>.</p>
14+
15+<p>Currently the device support is responsible for parsing the JSON text itself.
16+An event-driven JSON parser library has been included in libCom since Base-3.15,
17+<a href="https://lloyd.github.io/yajl/">YAJL (Yet Another JSON Library)</a>
18+Version 1.012, <a href="https://lloyd.github.io/yajl/yajl-1.0.12/">documented
19+here</a>.</p>
20+
21+
22+<h3>JSON Link Addressing</h3>
23+
24+<blockquote>
25+
26+<p><b style="color:red">FIXME</b> text missing here...</p>
27+
28+<h4>Support routine changes</h4>
29+
30+<p>For link fields in external record types and soft device support to be able
31+to use the new JSON link types properly, the following changes are
32+necessary:</p>
33+
34+<ul>
35+
36+<li>Make all calls to <tt>recGblInitConstantLink()</tt> unconditional on the
37+link type, i.e. change this code:
38+
39+<pre>
40+ if (prec->siml.type == CONSTANT) {
41+ recGblInitConstantLink(&amp;prec->siml, DBF_USHORT, &amp;prec->simm);
42+ }
43+</pre>
44+
45+into this:
46+
47+<pre>
48+ recGblInitConstantLink(&amp;prec->siml, DBF_USHORT, &amp;prec->simm);
49+</pre>
50+
51+Note that <tt>recGblInitConstantLink()</tt> still returns true if the field was
52+successfully initialized from the link.</li>
53+
54+<li>Code like this:
55+
56+<pre>
57+ if ((prec->dol.type != CONSTANT) &amp;&amp;
58+</pre>
59+
60+should usually become:
61+
62+<pre>
63+ if (!dbLinkIsConstant(&amp;prec->dol) &amp;&amp;
64+</pre>
65+</li>
66+
67+<li>Other code that compares a link type with CONSTANT should be modified to
68+use the new routine <tt>dbLinkIsConstant(plink)</tt> instead.</li>
69+
70+<li>Any code that calls dbCa routines directly or that explicitly checks if a
71+link has been resolved as a CA link using code such as
72+
73+<pre>
74+ if (plink->type == CA_LINK)
75+</pre>
76+
77+should be modified to use the new generic routines defined in dbLink.h. As an
78+example, the calcout record has been modified to use the new dbLink API.</li>
79+
80+<li>...</li>
81+
82+</ul>
83+
84+<p><b style="color:red">FIXME</b> text missing here...</p>
85+
86+</blockquote>
87+
88+
89+<h3>Constant Link Values</h3>
90+
91+<p>Previously a constant link (i.e. a link that does not point to another PV,
92+either local or over Channel Access) has only been able to provide a numeric
93+value; any string found in a link field that was not recognized as a number was
94+treated as a PV name. In this release, constant links may contain string values,
95+arrays, or even arrays of strings. These are indicated by ...</p>
96+
97+<p><b style="color:red">FIXME</b> text missing here...</p>
98+
99+
100+<h3>Database Parsing of "Relaxed JSON" Values</h3>
101+
102+<p>A database file can now provide a "relaxed JSON" value for a database field
103+value or an info tag. Only a few field types can currently accept such values,
104+but the capability is now available for use in other places in the future. If a
105+JSON-capable field is written to at run-time though only strictly compliant JSON
106+may be used (the dbStaticLib parser rewrites relaxed JSON values into strict
107+JSON before passing them to the datase for interpretation, where the strict
108+rules must be followed).</p>
109+
110+<p>"Relaxed JSON" was developed to maximize compatibility with the previous
111+database parser rules and reduce the number of double-quotes that would be
112+needed using strict JSON syntax. The parser will also accept strict JSON, which
113+should be used when machine-generating database files. The differences are:</p>
114+
115+<ul>
116+
117+<li>Strings containing only the characters <tt><b>a-z A-Z 0-9 _ - + .</b></tt>
118+do not have to be enclosed in double-quote characters.</li>
119+
120+<li>The above rule applies to map keys as well as to regular string values.</li>
121+
122+<li>The JSON keywords <tt>null</tt>, <tt>true</tt> and <tt>false</tt> (all
123+lower-case) will be recognized as keywords, so must be quoted to use any of
124+these single words as a string.</li>
125+
126+<li>Comments may be used, introduced as usual by the <tt><b>#</b></tt>
127+character and extending to the end of the line.</li>
128+
129+</ul>
130+
131+<p>A JSON field or info value is only enclosed in quotes when the value being
132+provided is a single string, and even here the quotes can be omitted in some
133+cases as described above. The following shows both correct and incorrect
134+excerpts from a database file:</p>
135+
136+<pre>
137+ record(ai, math:pi) {
138+ field(INP, {const: 3.14159265358979}) # Correct
139+ field(SIOL, "{const: 3.142857}") # Wrong
140+
141+ info(autosave, { # White-space and comments are allowed
142+ fields:[DESC, SIMM],
143+ pass0:[VAL]
144+ }) # Correct
145+ }
146+</pre>
147+
148+<p>Note that the record, field and info-tag names do <em>not</em> accept JSON
149+values, so they follows the older bareword rules for quoting where the colon
150+<tt><b>:</b></tt> and several additional characters are legal in a bareword
151+string.</p>
152+
153+
154 <h3>Echoless comments in iocsh</h3>
155
156 <p>The way comments are parsed by the iocsh interpreter has changed. The
157
158=== modified file 'src/ioc/db/Makefile'
159--- src/ioc/db/Makefile 2016-05-03 00:51:11 +0000
160+++ src/ioc/db/Makefile 2017-03-03 18:23:23 +0000
161@@ -14,14 +14,18 @@
162 INC += callback.h
163 INC += dbAccess.h
164 INC += dbAccessDefs.h
165-INC += dbCa.h
166 INC += dbAddr.h
167 INC += dbBkpt.h
168+INC += dbCa.h
169 INC += dbChannel.h
170+INC += dbConstLink.h
171 INC += dbConvert.h
172 INC += dbConvertFast.h
173+INC += dbConvertJSON.h
174+INC += dbDbLink.h
175 INC += dbExtractArray.h
176 INC += dbEvent.h
177+INC += dbJLink.h
178 INC += dbLink.h
179 INC += dbLock.h
180 INC += dbNotify.h
181@@ -64,9 +68,13 @@
182 dbCore_SRCS += dbAccess.c
183 dbCore_SRCS += dbBkpt.c
184 dbCore_SRCS += dbChannel.c
185+dbCore_SRCS += dbConstLink.c
186 dbCore_SRCS += dbConvert.c
187+dbCore_SRCS += dbConvertJSON.c
188+dbCore_SRCS += dbDbLink.c
189 dbCore_SRCS += dbFastLinkConv.c
190 dbCore_SRCS += dbExtractArray.c
191+dbCore_SRCS += dbJLink.c
192 dbCore_SRCS += dbLink.c
193 dbCore_SRCS += dbNotify.c
194 dbCore_SRCS += dbScan.c
195
196=== modified file 'src/ioc/db/dbAccess.c'
197--- src/ioc/db/dbAccess.c 2017-02-01 17:57:04 +0000
198+++ src/ioc/db/dbAccess.c 2017-03-03 18:23:23 +0000
199@@ -41,7 +41,6 @@
200 #include "dbAddr.h"
201 #include "dbBase.h"
202 #include "dbBkpt.h"
203-#include "dbCa.h"
204 #include "dbCommon.h"
205 #include "dbConvertFast.h"
206 #include "dbConvert.h"
207@@ -668,7 +667,7 @@
208 paddr->dbr_field_type = DBR_CHAR;
209 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
210 /* Clients see a char array, but keep original dbfType */
211- paddr->no_elements = PVNAME_STRINGSZ + 12;
212+ paddr->no_elements = PVLINK_STRINGSZ;
213 paddr->field_size = 1;
214 paddr->dbr_field_type = DBR_CHAR;
215 } else {
216@@ -1059,23 +1058,12 @@
217 }
218 }
219
220- switch (plink->type) { /* Old link type */
221- case DB_LINK:
222- case CA_LINK:
223- case CONSTANT:
224- dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */
225- break;
226-
227- case PV_LINK:
228- case MACRO_LINK:
229- break; /* should never get here */
230-
231- default: /* Hardware address */
232- if (!isDevLink) {
233- status = S_db_badHWaddr;
234- goto restoreScan;
235- }
236- break;
237+ if (dbLinkIsDefined(plink)) {
238+ dbRemoveLink(&locker, plink); /* Clear out old link */
239+ }
240+ else if (!isDevLink) {
241+ status = S_db_badHWaddr;
242+ goto restoreScan;
243 }
244
245 if (special) status = dbPutSpecial(paddr, 0);
246@@ -1108,6 +1096,7 @@
247 switch (plink->type) { /* New link type */
248 case PV_LINK:
249 case CONSTANT:
250+ case JSON_LINK:
251 dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
252 break;
253
254
255=== modified file 'src/ioc/db/dbAccessDefs.h'
256--- src/ioc/db/dbAccessDefs.h 2016-05-22 12:38:18 +0000
257+++ src/ioc/db/dbAccessDefs.h 2017-03-03 18:23:23 +0000
258@@ -181,6 +181,7 @@
259 #define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/
260 #define S_db_badField (M_dbAccess|15) /*Illegal field value*/
261 #define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/
262+#define S_db_noLSET (M_dbAccess|21) /*No link support table or entry*/
263 #define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/
264 #define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/
265 #define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/
266
267=== modified file 'src/ioc/db/dbCa.c'
268--- src/ioc/db/dbCa.c 2017-02-01 17:57:04 +0000
269+++ src/ioc/db/dbCa.c 2017-03-03 18:23:23 +0000
270@@ -49,6 +49,7 @@
271 #include "dbLock.h"
272 #include "dbScan.h"
273 #include "link.h"
274+#include "recGbl.h"
275 #include "recSup.h"
276
277 /* defined in dbContext.cpp
278@@ -231,11 +232,8 @@
279 void dbCaCallbackProcess(void *userPvt)
280 {
281 struct link *plink = (struct link *)userPvt;
282- dbCommon *pdbCommon = plink->precord;
283
284- dbScanLock(pdbCommon);
285- pdbCommon->rset->process(pdbCommon);
286- dbScanUnlock(pdbCommon);
287+ dbLinkAsyncComplete(plink);
288 }
289
290 void dbCaShutdown(void)
291@@ -339,8 +337,8 @@
292 addAction(pca, CA_CLEAR_CHANNEL);
293 }
294
295-long dbCaGetLink(struct link *plink,short dbrType, void *pdest,
296- epicsEnum16 *pstat, epicsEnum16 *psevr, long *nelements)
297+long dbCaGetLink(struct link *plink, short dbrType, void *pdest,
298+ long *nelements)
299 {
300 caLink *pca = (caLink *)plink->value.pv_link.pvt;
301 long status = 0;
302@@ -412,13 +410,23 @@
303 aConvert(&dbAddr, pdest, ntoget, ntoget, 0);
304 }
305 done:
306- if (pstat) *pstat = pca->stat;
307- if (psevr) *psevr = pca->sevr;
308- if (link_action) addAction(pca, link_action);
309+ if (link_action)
310+ addAction(pca, link_action);
311+ if (!status)
312+ recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
313+ plink->precord, pca->stat, pca->sevr);
314 epicsMutexUnlock(pca->lock);
315+
316 return status;
317 }
318
319
320+static long dbCaPutAsync(struct link *plink,short dbrType,
321+ const void *pbuffer,long nRequest)
322+{
323+ return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest,
324+ dbCaCallbackProcess, plink);
325+}
326+
327 long dbCaPutLinkCallback(struct link *plink,short dbrType,
328 const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt)
329 {
330@@ -708,14 +716,16 @@
331 }
332
333 static lset dbCa_lset = {
334- dbCaRemoveLink,
335+ 0, 1, /* not Constant, Volatile */
336+ NULL, dbCaRemoveLink,
337+ NULL, NULL, NULL,
338 isConnected,
339 getDBFtype, getElements,
340 dbCaGetLink,
341 getControlLimits, getGraphicLimits, getAlarmLimits,
342 getPrecision, getUnits,
343 getAlarm, getTimeStamp,
344- dbCaPutLink,
345+ dbCaPutLink, dbCaPutAsync,
346 scanForward
347 };
348
349
350=== modified file 'src/ioc/db/dbCa.h'
351--- src/ioc/db/dbCa.h 2017-02-01 17:57:04 +0000
352+++ src/ioc/db/dbCa.h 2017-03-03 18:23:23 +0000
353@@ -33,8 +33,7 @@
354 epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
355
356 epicsShareFunc long dbCaGetLink(struct link *plink,
357- short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,
358- long *nRequest);
359+ short dbrType, void *pbuffer, long *nRequest);
360
361 epicsShareFunc long dbCaGetAttributes(const struct link *plink,
362 dbCaCallback callback, void *userPvt);
363
364=== modified file 'src/ioc/db/dbChannel.c'
365--- src/ioc/db/dbChannel.c 2017-02-01 17:57:04 +0000
366+++ src/ioc/db/dbChannel.c 2017-03-03 18:23:23 +0000
367@@ -531,7 +531,7 @@
368 paddr->dbr_field_type = DBR_CHAR;
369 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
370 /* Clients see a char array, but keep original dbfType */
371- paddr->no_elements = PVNAME_STRINGSZ + 12;
372+ paddr->no_elements = PVLINK_STRINGSZ;
373 paddr->field_size = 1;
374 paddr->dbr_field_type = DBR_CHAR;
375 } else {
376
377=== added file 'src/ioc/db/dbConstLink.c'
378--- src/ioc/db/dbConstLink.c 1970-01-01 00:00:00 +0000
379+++ src/ioc/db/dbConstLink.c 2017-03-03 18:23:23 +0000
380@@ -0,0 +1,140 @@
381+/*************************************************************************\
382+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
383+* National Laboratory.
384+* Copyright (c) 2002 The Regents of the University of California, as
385+* Operator of Los Alamos National Laboratory.
386+* EPICS BASE is distributed subject to a Software License Agreement found
387+* in file LICENSE that is included with this distribution.
388+\*************************************************************************/
389+/* dbConstLink.c
390+ *
391+ * Original Authors: Bob Dalesio, Marty Kraimer
392+ * Current Author: Andrew Johnson
393+ */
394+
395+#include <stdio.h>
396+#include <string.h>
397+
398+#include "dbDefs.h"
399+
400+#define epicsExportSharedSymbols
401+#include "dbAccessDefs.h"
402+#include "dbAddr.h"
403+#include "dbCommon.h"
404+#include "dbConstLink.h"
405+#include "dbConvertFast.h"
406+#include "dbConvertJSON.h"
407+#include "dbFldTypes.h"
408+#include "dbLink.h"
409+#include "link.h"
410+
411+/***************************** Constant Links *****************************/
412+
413+/* Forward definition */
414+static lset dbConst_lset;
415+
416+void dbConstInitLink(struct link *plink)
417+{
418+ plink->lset = &dbConst_lset;
419+}
420+
421+void dbConstAddLink(struct link *plink)
422+{
423+ plink->lset = &dbConst_lset;
424+}
425+
426+/**************************** Member functions ****************************/
427+
428+static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
429+{
430+ const char *pstr = plink->value.constantStr;
431+ size_t len;
432+
433+ if (!pstr)
434+ return S_db_badField;
435+ len = strlen(pstr);
436+
437+ /* Choice values must be numeric */
438+ if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
439+ dbrType = DBF_USHORT;
440+
441+ if (*pstr == '[' && pstr[len-1] == ']') {
442+ /* Convert from JSON array */
443+ long nReq = 1;
444+
445+ return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq);
446+ }
447+
448+ return dbFastPutConvertRoutine[DBR_STRING][dbrType]
449+ (pstr, pbuffer, NULL);
450+}
451+
452+static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
453+ epicsUInt32 *plen)
454+{
455+ const char *pstr = plink->value.constantStr;
456+ size_t len;
457+
458+ if (!pstr)
459+ return S_db_badField;
460+ len = strlen(pstr);
461+
462+ /* FIXME This handles the common case, but not the general one... */
463+ if (pstr[0] == '[' && pstr[1] == '"' &&
464+ pstr[len-2] == '"' && pstr[len-1] == ']') {
465+ pstr += 2;
466+ len -= 4;
467+ }
468+ if (--size > len) size = len;
469+
470+ strncpy(pbuffer, pstr, size);
471+ pbuffer[size] = 0;
472+ *plen = (epicsUInt32) strlen(pbuffer) + 1;
473+ return 0;
474+}
475+
476+static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
477+ long *pnReq)
478+{
479+ const char *pstr = plink->value.constantStr;
480+
481+ if (!pstr)
482+ return S_db_badField;
483+
484+ /* Choice values must be numeric */
485+ if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
486+ dbrType = DBF_USHORT;
487+
488+ return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
489+}
490+
491+static long dbConstGetNelements(const struct link *plink, long *nelements)
492+{
493+ *nelements = 0;
494+ return 0;
495+}
496+
497+static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
498+ long *pnRequest)
499+{
500+ if (pnRequest)
501+ *pnRequest = 0;
502+ return 0;
503+}
504+
505+static lset dbConst_lset = {
506+ 1, 0, /* Constant, not Volatile */
507+ NULL, NULL,
508+ dbConstLoadScalar,
509+ dbConstLoadLS,
510+ dbConstLoadArray,
511+ NULL,
512+ NULL, dbConstGetNelements,
513+ dbConstGetValue,
514+ NULL, NULL, NULL,
515+ NULL, NULL,
516+ NULL, NULL,
517+ NULL, NULL,
518+ NULL
519+};
520+
521
522=== added file 'src/ioc/db/dbConstLink.h'
523--- src/ioc/db/dbConstLink.h 1970-01-01 00:00:00 +0000
524+++ src/ioc/db/dbConstLink.h 2017-03-03 18:23:23 +0000
525@@ -0,0 +1,34 @@
526+/*************************************************************************\
527+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
528+* National Laboratory.
529+* Copyright (c) 2002 The Regents of the University of California, as
530+* Operator of Los Alamos National Laboratory.
531+* EPICS BASE is distributed subject to a Software License Agreement found
532+* in file LICENSE that is included with this distribution.
533+\*************************************************************************/
534+/* dbConstLink.h
535+ *
536+ * Created on: April 3rd, 2016
537+ * Author: Andrew Johnson
538+ */
539+
540+#ifndef INC_dbConstLink_H
541+#define INC_dbConstLink_H
542+
543+#include "shareLib.h"
544+
545+#ifdef __cplusplus
546+extern "C" {
547+#endif
548+
549+struct link;
550+
551+epicsShareFunc void dbConstInitLink(struct link *plink);
552+epicsShareFunc void dbConstAddLink(struct link *plink);
553+
554+#ifdef __cplusplus
555+}
556+#endif
557+
558+#endif /* INC_dbConstLink_H */
559+
560
561=== added file 'src/ioc/db/dbConvertJSON.c'
562--- src/ioc/db/dbConvertJSON.c 1970-01-01 00:00:00 +0000
563+++ src/ioc/db/dbConvertJSON.c 2017-03-03 18:23:23 +0000
564@@ -0,0 +1,172 @@
565+/*************************************************************************\
566+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
567+* National Laboratory.
568+* EPICS BASE is distributed subject to a Software License Agreement found
569+* in file LICENSE that is included with this distribution.
570+\*************************************************************************/
571+/* dbConvertJSON.c */
572+
573+#include <string.h>
574+#include <stdio.h>
575+
576+#include "dbDefs.h"
577+#include "errlog.h"
578+#include "yajl_alloc.h"
579+#include "yajl_parse.h"
580+
581+#define epicsExportSharedSymbols
582+#include "dbAccessDefs.h"
583+#include "dbConvertFast.h"
584+
585+typedef long (*FASTCONVERT)();
586+
587+typedef struct parseContext {
588+ int depth;
589+ short dbrType;
590+ short dbrSize;
591+ char *pdest;
592+ int elems;
593+} parseContext;
594+
595+static int dbcj_null(void *ctx) {
596+ return 0; /* Illegal */
597+}
598+
599+static int dbcj_boolean(void *ctx, int val) {
600+ return 0; /* Illegal */
601+}
602+
603+static int dbcj_integer(void *ctx, long num) {
604+ parseContext *parser = (parseContext *) ctx;
605+ epicsInt32 val32 = num;
606+ FASTCONVERT conv = dbFastPutConvertRoutine[DBF_LONG][parser->dbrType];
607+
608+ if (parser->elems > 0) {
609+ conv(&val32, parser->pdest, NULL);
610+ parser->pdest += parser->dbrSize;
611+ parser->elems--;
612+ }
613+ return 1;
614+}
615+
616+static int dbcj_double(void *ctx, double num) {
617+ parseContext *parser = (parseContext *) ctx;
618+ FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType];
619+
620+ if (parser->elems > 0) {
621+ conv(&num, parser->pdest, NULL);
622+ parser->pdest += parser->dbrSize;
623+ parser->elems--;
624+ }
625+ return 1;
626+}
627+
628+static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) {
629+ parseContext *parser = (parseContext *) ctx;
630+ char *pdest = parser->pdest;
631+
632+ /* Not attempting to handle char-array fields here, they need more
633+ * metadata about the field than we have available at the moment.
634+ */
635+ if (parser->dbrType != DBF_STRING) {
636+ errlogPrintf("dbPutConvertJSON: String provided, numeric value(s) expected\n");
637+ return 0; /* Illegal */
638+ }
639+
640+ if (parser->elems > 0) {
641+ if (len > parser->dbrSize - 1)
642+ len = parser->dbrSize - 1;
643+ strncpy(pdest, (const char *) val, len);
644+ pdest[len] = 0;
645+ parser->pdest += parser->dbrSize;
646+ parser->elems--;
647+ }
648+ return 1;
649+}
650+
651+static int dbcj_start_map(void *ctx) {
652+ errlogPrintf("dbPutConvertJSON: Map type not supported\n");
653+ return 0; /* Illegal */
654+}
655+
656+static int dbcj_map_key(void *ctx, const unsigned char *key, unsigned int len) {
657+ return 0; /* Illegal */
658+}
659+
660+static int dbcj_end_map(void *ctx) {
661+ return 0; /* Illegal */
662+}
663+
664+static int dbcj_start_array(void *ctx) {
665+ parseContext *parser = (parseContext *) ctx;
666+
667+ if (++parser->depth > 1)
668+ errlogPrintf("dbPutConvertJSON: Embedded arrays not supported\n");
669+
670+ return (parser->depth == 1);
671+}
672+
673+static int dbcj_end_array(void *ctx) {
674+ parseContext *parser = (parseContext *) ctx;
675+
676+ parser->depth--;
677+ return (parser->depth == 0);
678+}
679+
680+
681+static yajl_callbacks dbcj_callbacks = {
682+ dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string,
683+ dbcj_start_map, dbcj_map_key, dbcj_end_map,
684+ dbcj_start_array, dbcj_end_array
685+};
686+
687+static const yajl_parser_config dbcj_config =
688+ { 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
689+
690+long dbPutConvertJSON(const char *json, short dbrType,
691+ void *pdest, long *pnRequest)
692+{
693+ parseContext context, *parser = &context;
694+ yajl_alloc_funcs dbcj_alloc;
695+ yajl_handle yh;
696+ yajl_status ys;
697+ size_t jlen = strlen(json);
698+ long status;
699+
700+ parser->depth = 0;
701+ parser->dbrType = dbrType;
702+ parser->dbrSize = dbValueSize(dbrType);
703+ parser->pdest = pdest;
704+ parser->elems = *pnRequest;
705+
706+ yajl_set_default_alloc_funcs(&dbcj_alloc);
707+ yh = yajl_alloc(&dbcj_callbacks, &dbcj_config, &dbcj_alloc, parser);
708+ if (!yh)
709+ return S_db_noMemory;
710+
711+ ys = yajl_parse(yh, (const unsigned char *) json, (unsigned int) jlen);
712+ if (ys == yajl_status_insufficient_data)
713+ ys = yajl_parse_complete(yh);
714+
715+ switch (ys) {
716+ case yajl_status_ok:
717+ *pnRequest -= parser->elems;
718+ status = 0;
719+ break;
720+
721+ case yajl_status_error: {
722+ unsigned char *err = yajl_get_error(yh, 1,
723+ (const unsigned char *) json, (unsigned int) jlen);
724+ fprintf(stderr, "dbPutConvertJSON: %s\n", err);
725+ yajl_free_error(yh, err);
726+ }
727+ /* fall through */
728+ default:
729+ status = S_db_badField;
730+ }
731+
732+ yajl_free(yh);
733+ return status;
734+}
735+
736+
737
738=== added file 'src/ioc/db/dbConvertJSON.h'
739--- src/ioc/db/dbConvertJSON.h 1970-01-01 00:00:00 +0000
740+++ src/ioc/db/dbConvertJSON.h 2017-03-03 18:23:23 +0000
741@@ -0,0 +1,27 @@
742+/*************************************************************************\
743+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
744+* National Laboratory.
745+* EPICS BASE is distributed subject to a Software License Agreement found
746+* in file LICENSE that is included with this distribution.
747+\*************************************************************************/
748+/* dbConvertJSON.h */
749+
750+#ifndef INC_dbConvertJSON_H
751+#define INC_dbConvertJSON_H
752+
753+#include <shareLib.h>
754+
755+#ifdef __cplusplus
756+extern "C" {
757+#endif
758+
759+/* This name should probably be changed to inclue "array" */
760+epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType,
761+ void *pdest, long *psize);
762+
763+#ifdef __cplusplus
764+}
765+#endif
766+
767+#endif /* INC_dbConvertJSON_H */
768+
769
770=== added file 'src/ioc/db/dbDbLink.c'
771--- src/ioc/db/dbDbLink.c 1970-01-01 00:00:00 +0000
772+++ src/ioc/db/dbDbLink.c 2017-03-03 18:23:23 +0000
773@@ -0,0 +1,353 @@
774+/*************************************************************************\
775+* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
776+* National Laboratory.
777+* Copyright (c) 2002 The Regents of the University of California, as
778+* Operator of Los Alamos National Laboratory.
779+* EPICS BASE is distributed subject to a Software License Agreement found
780+* in file LICENSE that is included with this distribution.
781+\*************************************************************************/
782+/* dbDbLink.c
783+ *
784+ * Original Authors: Bob Dalesio, Marty Kraimer
785+ * Current Author: Andrew Johnson
786+ */
787+
788+#include <stddef.h>
789+#include <stdlib.h>
790+#include <stdarg.h>
791+#include <stdio.h>
792+#include <string.h>
793+
794+#include "alarm.h"
795+#include "cantProceed.h"
796+#include "cvtFast.h"
797+#include "dbDefs.h"
798+#include "ellLib.h"
799+#include "epicsTime.h"
800+#include "errlog.h"
801+
802+#include "caeventmask.h"
803+
804+#define epicsExportSharedSymbols
805+#include "dbAccessDefs.h"
806+#include "dbAddr.h"
807+#include "dbBase.h"
808+#include "dbBkpt.h"
809+#include "dbCommon.h"
810+#include "dbConvertFast.h"
811+#include "dbConvert.h"
812+#include "db_field_log.h"
813+#include "dbFldTypes.h"
814+#include "dbLink.h"
815+#include "dbLockPvt.h"
816+#include "dbNotify.h"
817+#include "dbScan.h"
818+#include "dbStaticLib.h"
819+#include "devSup.h"
820+#include "link.h"
821+#include "recGbl.h"
822+#include "recSup.h"
823+#include "special.h"
824+
825+/***************************** Database Links *****************************/
826+
827+/* Forward definition */
828+static lset dbDb_lset;
829+
830+long dbDbInitLink(struct link *plink, short dbfType)
831+{
832+ DBADDR dbaddr;
833+ long status;
834+ DBADDR *pdbAddr;
835+
836+ status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
837+ if (status)
838+ return status;
839+
840+ plink->lset = &dbDb_lset;
841+ plink->type = DB_LINK;
842+ pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
843+ *pdbAddr = dbaddr; /* structure copy */
844+ plink->value.pv_link.pvt = pdbAddr;
845+ ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
846+ /* merging into the same lockset is deferred to the caller.
847+ * cf. initPVLinks()
848+ */
849+ dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
850+ assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
851+ return 0;
852+}
853+
854+void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
855+ DBADDR *ptarget)
856+{
857+ plink->lset = &dbDb_lset;
858+ plink->type = DB_LINK;
859+ plink->value.pv_link.pvt = ptarget;
860+ ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
861+
862+ /* target record is already locked in dbPutFieldLink() */
863+ dbLockSetMerge(locker, plink->precord, ptarget->precord);
864+}
865+
866+static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
867+{
868+ DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
869+
870+ plink->type = PV_LINK;
871+
872+ /* locker is NULL when an isolated IOC is closing its links */
873+ if (locker) {
874+ plink->value.pv_link.pvt = 0;
875+ plink->value.pv_link.getCvt = 0;
876+ plink->value.pv_link.pvlMask = 0;
877+ plink->value.pv_link.lastGetdbrType = 0;
878+ ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
879+ dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
880+ }
881+ free(pdbAddr);
882+}
883+
884+static int dbDbIsConnected(const struct link *plink)
885+{
886+ return TRUE;
887+}
888+
889+static int dbDbGetDBFtype(const struct link *plink)
890+{
891+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
892+
893+ return paddr->field_type;
894+}
895+
896+static long dbDbGetElements(const struct link *plink, long *nelements)
897+{
898+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
899+
900+ *nelements = paddr->no_elements;
901+ return 0;
902+}
903+
904+static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
905+ long *pnRequest)
906+{
907+ struct pv_link *ppv_link = &plink->value.pv_link;
908+ DBADDR *paddr = ppv_link->pvt;
909+ dbCommon *precord = plink->precord;
910+ long status;
911+
912+ /* scan passive records if link is process passive */
913+ if (ppv_link->pvlMask & pvlOptPP) {
914+ unsigned char pact = precord->pact;
915+
916+ precord->pact = TRUE;
917+ status = dbScanPassive(precord, paddr->precord);
918+ precord->pact = pact;
919+ if (status)
920+ return status;
921+ }
922+
923+ if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
924+ status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
925+ } else {
926+ unsigned short dbfType = paddr->field_type;
927+
928+ if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
929+ return S_db_badDbrtype;
930+
931+ if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
932+ && paddr->special != SPC_ATTRIBUTE) {
933+ ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
934+ status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
935+ } else {
936+ ppv_link->getCvt = NULL;
937+ status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
938+ }
939+ ppv_link->lastGetdbrType = dbrType;
940+ }
941+
942+ if (!status)
943+ recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
944+ plink->precord, paddr->precord->stat, paddr->precord->sevr);
945+ return status;
946+}
947+
948+static long dbDbGetControlLimits(const struct link *plink, double *low,
949+ double *high)
950+{
951+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
952+ struct buffer {
953+ DBRctrlDouble
954+ double value;
955+ } buffer;
956+ long options = DBR_CTRL_DOUBLE;
957+ long number_elements = 0;
958+ long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
959+ NULL);
960+
961+ if (status)
962+ return status;
963+
964+ *low = buffer.lower_ctrl_limit;
965+ *high = buffer.upper_ctrl_limit;
966+ return 0;
967+}
968+
969+static long dbDbGetGraphicLimits(const struct link *plink, double *low,
970+ double *high)
971+{
972+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
973+ struct buffer {
974+ DBRgrDouble
975+ double value;
976+ } buffer;
977+ long options = DBR_GR_DOUBLE;
978+ long number_elements = 0;
979+ long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
980+ NULL);
981+
982+ if (status)
983+ return status;
984+
985+ *low = buffer.lower_disp_limit;
986+ *high = buffer.upper_disp_limit;
987+ return 0;
988+}
989+
990+static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
991+ double *low, double *high, double *hihi)
992+{
993+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
994+ struct buffer {
995+ DBRalDouble
996+ double value;
997+ } buffer;
998+ long options = DBR_AL_DOUBLE;
999+ long number_elements = 0;
1000+ long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
1001+ 0);
1002+
1003+ if (status)
1004+ return status;
1005+
1006+ *lolo = buffer.lower_alarm_limit;
1007+ *low = buffer.lower_warning_limit;
1008+ *high = buffer.upper_warning_limit;
1009+ *hihi = buffer.upper_alarm_limit;
1010+ return 0;
1011+}
1012+
1013+static long dbDbGetPrecision(const struct link *plink, short *precision)
1014+{
1015+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1016+ struct buffer {
1017+ DBRprecision
1018+ double value;
1019+ } buffer;
1020+ long options = DBR_PRECISION;
1021+ long number_elements = 0;
1022+ long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
1023+ 0);
1024+
1025+ if (status)
1026+ return status;
1027+
1028+ *precision = (short) buffer.precision.dp;
1029+ return 0;
1030+}
1031+
1032+static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
1033+{
1034+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1035+ struct buffer {
1036+ DBRunits
1037+ double value;
1038+ } buffer;
1039+ long options = DBR_UNITS;
1040+ long number_elements = 0;
1041+ long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
1042+ 0);
1043+
1044+ if (status)
1045+ return status;
1046+
1047+ strncpy(units, buffer.units, unitsSize);
1048+ return 0;
1049+}
1050+
1051+static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
1052+ epicsEnum16 *severity)
1053+{
1054+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1055+
1056+ if (status)
1057+ *status = paddr->precord->stat;
1058+ if (severity)
1059+ *severity = paddr->precord->sevr;
1060+ return 0;
1061+}
1062+
1063+static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
1064+{
1065+ DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
1066+
1067+ *pstamp = paddr->precord->time;
1068+ return 0;
1069+}
1070+
1071+static long dbDbPutValue(struct link *plink, short dbrType,
1072+ const void *pbuffer, long nRequest)
1073+{
1074+ struct pv_link *ppv_link = &plink->value.pv_link;
1075+ struct dbCommon *psrce = plink->precord;
1076+ DBADDR *paddr = (DBADDR *) ppv_link->pvt;
1077+ dbCommon *pdest = paddr->precord;
1078+ long status = dbPut(paddr, dbrType, pbuffer, nRequest);
1079+
1080+ recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
1081+ psrce->nsev);
1082+ if (status)
1083+ return status;
1084+
1085+ if (paddr->pfield == (void *) &pdest->proc ||
1086+ (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
1087+ /* if dbPutField caused asyn record to process */
1088+ /* ask for reprocessing*/
1089+ if (pdest->putf) {
1090+ pdest->rpro = TRUE;
1091+ } else { /* process dest record with source's PACT true */
1092+ unsigned char pact;
1093+
1094+ if (psrce && psrce->ppn)
1095+ dbNotifyAdd(psrce, pdest);
1096+ pact = psrce->pact;
1097+ psrce->pact = TRUE;
1098+ status = dbProcess(pdest);
1099+ psrce->pact = pact;
1100+ }
1101+ }
1102+ return status;
1103+}
1104+
1105+static void dbDbScanFwdLink(struct link *plink)
1106+{
1107+ dbCommon *precord = plink->precord;
1108+ dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
1109+
1110+ dbScanPassive(precord, paddr->precord);
1111+}
1112+
1113+static lset dbDb_lset = {
1114+ 0, 0, /* not Constant, not Volatile */
1115+ NULL, dbDbRemoveLink,
1116+ NULL, NULL, NULL,
1117+ dbDbIsConnected,
1118+ dbDbGetDBFtype, dbDbGetElements,
1119+ dbDbGetValue,
1120+ dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
1121+ dbDbGetPrecision, dbDbGetUnits,
1122+ dbDbGetAlarm, dbDbGetTimeStamp,
1123+ dbDbPutValue, NULL,
1124+ dbDbScanFwdLink
1125+};
1126+
1127
1128=== added file 'src/ioc/db/dbDbLink.h'
1129--- src/ioc/db/dbDbLink.h 1970-01-01 00:00:00 +0000
1130+++ src/ioc/db/dbDbLink.h 2017-03-03 18:23:23 +0000
1131@@ -0,0 +1,35 @@
1132+/*************************************************************************\
1133+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
1134+* National Laboratory.
1135+* Copyright (c) 2002 The Regents of the University of California, as
1136+* Operator of Los Alamos National Laboratory.
1137+* EPICS BASE is distributed subject to a Software License Agreement found
1138+* in file LICENSE that is included with this distribution.
1139+\*************************************************************************/
1140+/* dbDbLink.h
1141+ *
1142+ * Created on: April 3rd, 2016
1143+ * Author: Andrew Johnson
1144+ */
1145+
1146+#ifndef INC_dbDbLink_H
1147+#define INC_dbDbLink_H
1148+
1149+#include "shareLib.h"
1150+
1151+#ifdef __cplusplus
1152+extern "C" {
1153+#endif
1154+
1155+struct link;
1156+struct dbLocker;
1157+
1158+epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
1159+epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
1160+ short dbfType, DBADDR *ptarget);
1161+
1162+#ifdef __cplusplus
1163+}
1164+#endif
1165+
1166+#endif /* INC_dbDbLink_H */
1167
1168=== modified file 'src/ioc/db/dbIocRegister.c'
1169--- src/ioc/db/dbIocRegister.c 2015-07-13 18:05:33 +0000
1170+++ src/ioc/db/dbIocRegister.c 2017-03-03 18:23:23 +0000
1171@@ -16,6 +16,7 @@
1172 #include "dbCaTest.h"
1173 #include "dbEvent.h"
1174 #include "dbIocRegister.h"
1175+#include "dbJLink.h"
1176 #include "dbLock.h"
1177 #include "dbNotify.h"
1178 #include "dbScan.h"
1179@@ -109,6 +110,16 @@
1180 dbcar(args[0].sval,args[1].ival);
1181 }
1182
1183+/* dbjlr */
1184+static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
1185+static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
1186+static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
1187+static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs};
1188+static void dbjlrCallFunc(const iocshArgBuf *args)
1189+{
1190+ dbjlr(args[0].sval,args[1].ival);
1191+}
1192+
1193 /* dbel */
1194 static const iocshArg dbelArg0 = { "record name",iocshArgString};
1195 static const iocshArg dbelArg1 = { "level",iocshArgInt};
1196@@ -395,6 +406,7 @@
1197 iocshRegister(&dbsrFuncDef,dbsrCallFunc);
1198 iocshRegister(&dbcarFuncDef,dbcarCallFunc);
1199 iocshRegister(&dbelFuncDef,dbelCallFunc);
1200+ iocshRegister(&dbjlrFuncDef,dbjlrCallFunc);
1201
1202 iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);
1203 iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);
1204
1205=== added file 'src/ioc/db/dbJLink.c'
1206--- src/ioc/db/dbJLink.c 1970-01-01 00:00:00 +0000
1207+++ src/ioc/db/dbJLink.c 2017-03-03 18:23:23 +0000
1208@@ -0,0 +1,542 @@
1209+/*************************************************************************\
1210+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
1211+* National Laboratory.
1212+* EPICS BASE is distributed subject to a Software License Agreement found
1213+* in file LICENSE that is included with this distribution.
1214+\*************************************************************************/
1215+/* dbJLink.c */
1216+
1217+#include <stdio.h>
1218+#include <string.h>
1219+
1220+#include "epicsAssert.h"
1221+#include "dbmf.h"
1222+#include "errlog.h"
1223+#include "yajl_alloc.h"
1224+#include "yajl_parse.h"
1225+
1226+#define epicsExportSharedSymbols
1227+#include "dbAccessDefs.h"
1228+#include "dbCommon.h"
1229+#include "dbLink.h"
1230+#include "dbJLink.h"
1231+#include "dbLock.h"
1232+#include "dbStaticLib.h"
1233+#include "link.h"
1234+
1235+/* Change 'undef' to 'define' to turn on debug statements: */
1236+#undef DEBUG_JLINK
1237+
1238+#ifdef DEBUG_JLINK
1239+ int jlinkDebug = 10;
1240+# define IFDEBUG(n) \
1241+ if (jlinkDebug >= n) /* block or statement */
1242+#else
1243+# define IFDEBUG(n) \
1244+ if(0) /* Compiler will elide the block or statement */
1245+#endif
1246+
1247+
1248+typedef struct parseContext {
1249+ jlink *pjlink;
1250+ jlink *product;
1251+ short dbfType;
1252+ short jsonDepth;
1253+ unsigned key_is_link:1;
1254+} parseContext;
1255+
1256+#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
1257+
1258+static int dbjl_return(parseContext *parser, jlif_result result) {
1259+ jlink *pjlink = parser->pjlink;
1260+
1261+ IFDEBUG(10) {
1262+ printf("dbjl_return(%s@%p, %d)\t", pjlink->pif->name, pjlink, result);
1263+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1264+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1265+ }
1266+
1267+ if (result == jlif_stop && pjlink) {
1268+ jlink *parent;
1269+
1270+ while ((parent = pjlink->parent)) {
1271+ pjlink->pif->free_jlink(pjlink);
1272+ pjlink = parent;
1273+ }
1274+ pjlink->pif->free_jlink(pjlink);
1275+ }
1276+
1277+ return result;
1278+}
1279+
1280+static int dbjl_value(parseContext *parser, jlif_result result) {
1281+ jlink *pjlink = parser->pjlink;
1282+ jlink *parent;
1283+
1284+ IFDEBUG(10) {
1285+ printf("dbjl_value(%s@%p, %d)\t", pjlink->pif->name, pjlink, result);
1286+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1287+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1288+ }
1289+
1290+ if (result == jlif_stop || pjlink->parseDepth > 0)
1291+ return dbjl_return(parser, result);
1292+
1293+ parent = pjlink->parent;
1294+ if (!parent)
1295+ parser->product = pjlink;
1296+ else if (parent->pif->end_child)
1297+ parent->pif->end_child(parent, pjlink);
1298+
1299+ parser->pjlink = parent;
1300+
1301+ IFDEBUG(8)
1302+ printf("dbjl_value: product = %p\n", pjlink);
1303+
1304+ return jlif_continue;
1305+}
1306+
1307+static int dbjl_null(void *ctx) {
1308+ parseContext *parser = (parseContext *) ctx;
1309+ jlink *pjlink = parser->pjlink;
1310+
1311+ IFDEBUG(10)
1312+ printf("dbjl_null(%s@%p)\n", pjlink->pif->name, pjlink);
1313+
1314+ assert(pjlink);
1315+ return dbjl_value(parser,
1316+ CALL_OR_STOP(pjlink->pif->parse_null)(pjlink));
1317+}
1318+
1319+static int dbjl_boolean(void *ctx, int val) {
1320+ parseContext *parser = (parseContext *) ctx;
1321+ jlink *pjlink = parser->pjlink;
1322+
1323+ assert(pjlink);
1324+ return dbjl_value(parser,
1325+ CALL_OR_STOP(pjlink->pif->parse_boolean)(pjlink, val));
1326+}
1327+
1328+static int dbjl_integer(void *ctx, long num) {
1329+ parseContext *parser = (parseContext *) ctx;
1330+ jlink *pjlink = parser->pjlink;
1331+
1332+ IFDEBUG(10)
1333+ printf("dbjl_integer(%s@%p, %ld)\n",
1334+ pjlink->pif->name, pjlink, num);
1335+
1336+ assert(pjlink);
1337+ return dbjl_value(parser,
1338+ CALL_OR_STOP(pjlink->pif->parse_integer)(pjlink, num));
1339+}
1340+
1341+static int dbjl_double(void *ctx, double num) {
1342+ parseContext *parser = (parseContext *) ctx;
1343+ jlink *pjlink = parser->pjlink;
1344+
1345+ IFDEBUG(10)
1346+ printf("dbjl_double(%s@%p, %g)\n",
1347+ pjlink->pif->name, pjlink, num);
1348+
1349+ assert(pjlink);
1350+ return dbjl_value(parser,
1351+ CALL_OR_STOP(pjlink->pif->parse_double)(pjlink, num));
1352+}
1353+
1354+static int dbjl_string(void *ctx, const unsigned char *val, unsigned len) {
1355+ parseContext *parser = (parseContext *) ctx;
1356+ jlink *pjlink = parser->pjlink;
1357+
1358+ IFDEBUG(10)
1359+ printf("dbjl_string(%s@%p, \"%.*s\")\n",
1360+ pjlink->pif->name, pjlink, len, val);
1361+
1362+ assert(pjlink);
1363+ return dbjl_value(parser,
1364+ CALL_OR_STOP(pjlink->pif->parse_string)(pjlink, (const char *) val, len));
1365+}
1366+
1367+static int dbjl_start_map(void *ctx) {
1368+ parseContext *parser = (parseContext *) ctx;
1369+ jlink *pjlink = parser->pjlink;
1370+ int result;
1371+
1372+ if (!pjlink) {
1373+ IFDEBUG(10) {
1374+ printf("dbjl_start_map(NULL)\t");
1375+ printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
1376+ parser->jsonDepth, parser->key_is_link);
1377+ }
1378+
1379+ assert(parser->jsonDepth == 0);
1380+ parser->jsonDepth++;
1381+ parser->key_is_link = 1;
1382+ return jlif_continue; /* Opening '{' */
1383+ }
1384+
1385+ IFDEBUG(10) {
1386+ printf("dbjl_start_map(%s@%p)\t", pjlink->pif->name, pjlink);
1387+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1388+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1389+ }
1390+
1391+ pjlink->parseDepth++;
1392+ parser->jsonDepth++;
1393+
1394+ result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
1395+ if (result == jlif_key_child_link) {
1396+ parser->key_is_link = 1;
1397+ result = jlif_continue;
1398+ }
1399+
1400+ IFDEBUG(10)
1401+ printf("dbjl_start_map -> %d\n", result);
1402+
1403+ return dbjl_return(parser, result);
1404+}
1405+
1406+static int dbjl_map_key(void *ctx, const unsigned char *key, unsigned len) {
1407+ parseContext *parser = (parseContext *) ctx;
1408+ jlink *pjlink = parser->pjlink;
1409+ char *link_name;
1410+ linkSup *linkSup;
1411+ jlif *pjlif;
1412+
1413+ if (!parser->key_is_link) {
1414+ if (!pjlink) {
1415+ errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
1416+ len, key);
1417+ return dbjl_return(parser, jlif_stop);
1418+ }
1419+
1420+ IFDEBUG(10) {
1421+ printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
1422+ pjlink->pif->name, pjlink, len, key);
1423+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1424+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1425+ }
1426+
1427+ assert(pjlink->parseDepth > 0);
1428+ return dbjl_return(parser,
1429+ CALL_OR_STOP(pjlink->pif->parse_map_key)(pjlink,
1430+ (const char *) key, len));
1431+ }
1432+
1433+ IFDEBUG(10) {
1434+ printf("dbjl_map_key(NULL, \"%.*s\")\t", len, key);
1435+ printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
1436+ parser->jsonDepth, parser->key_is_link);
1437+ }
1438+
1439+ link_name = dbmfStrndup((const char *) key, len);
1440+
1441+ linkSup = dbFindLinkSup(pdbbase, link_name);
1442+ if (!linkSup) {
1443+ errlogPrintf("dbJLinkInit: Link type '%s' not found\n",
1444+ link_name);
1445+ dbmfFree(link_name);
1446+ return dbjl_return(parser, jlif_stop);
1447+ }
1448+
1449+ pjlif = linkSup->pjlif;
1450+ if (!pjlif) {
1451+ errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n",
1452+ link_name);
1453+ dbmfFree(link_name);
1454+ return dbjl_return(parser, jlif_stop);
1455+ }
1456+
1457+ dbmfFree(link_name);
1458+
1459+ pjlink = pjlif->alloc_jlink(parser->dbfType);
1460+ if (!pjlink) {
1461+ errlogPrintf("dbJLinkInit: Out of memory\n");
1462+ return dbjl_return(parser, jlif_stop);
1463+ }
1464+ pjlink->pif = pjlif;
1465+ pjlink->parent = NULL;
1466+ pjlink->parseDepth = 0;
1467+
1468+ if (parser->pjlink) {
1469+ /* We're starting a child link, save its parent */
1470+ pjlink->parent = parser->pjlink;
1471+ }
1472+ parser->pjlink = pjlink;
1473+ parser->key_is_link = 0;
1474+
1475+ IFDEBUG(8)
1476+ printf("dbjl_map_key: New %s@%p\n", pjlink->pif->name, pjlink);
1477+
1478+ return jlif_continue;
1479+}
1480+
1481+static int dbjl_end_map(void *ctx) {
1482+ parseContext *parser = (parseContext *) ctx;
1483+ jlink *pjlink = parser->pjlink;
1484+ jlif_result result;
1485+
1486+ IFDEBUG(10) {
1487+ printf("dbjl_end_map(%s@%p)\t",
1488+ pjlink ? pjlink->pif->name : "NULL", pjlink);
1489+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1490+ parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
1491+ parser->key_is_link);
1492+ }
1493+
1494+ parser->jsonDepth--;
1495+ if (pjlink) {
1496+ pjlink->parseDepth--;
1497+
1498+ result = dbjl_value(parser,
1499+ CALL_OR_STOP(pjlink->pif->parse_end_map)(pjlink));
1500+ }
1501+ else {
1502+ result = jlif_continue;
1503+ }
1504+ return result;
1505+}
1506+
1507+static int dbjl_start_array(void *ctx) {
1508+ parseContext *parser = (parseContext *) ctx;
1509+ jlink *pjlink = parser->pjlink;
1510+
1511+ IFDEBUG(10) {
1512+ printf("dbjl_start_array(%s@%p)\t", pjlink->pif->name, pjlink);
1513+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1514+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1515+ }
1516+
1517+ assert(pjlink);
1518+ pjlink->parseDepth++;
1519+ parser->jsonDepth++;
1520+
1521+ return dbjl_return(parser,
1522+ CALL_OR_STOP(pjlink->pif->parse_start_array)(pjlink));
1523+}
1524+
1525+static int dbjl_end_array(void *ctx) {
1526+ parseContext *parser = (parseContext *) ctx;
1527+ jlink *pjlink = parser->pjlink;
1528+
1529+ IFDEBUG(10) {
1530+ printf("dbjl_end_array(%s@%p)\t", pjlink->pif->name, pjlink);
1531+ printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
1532+ parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
1533+ }
1534+
1535+ assert(pjlink);
1536+ pjlink->parseDepth--;
1537+ parser->jsonDepth--;
1538+
1539+ return dbjl_value(parser,
1540+ CALL_OR_STOP(pjlink->pif->parse_end_array)(pjlink));
1541+}
1542+
1543+
1544+static yajl_callbacks dbjl_callbacks = {
1545+ dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string,
1546+ dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array
1547+};
1548+
1549+static const yajl_parser_config dbjl_config =
1550+ { 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
1551+
1552+long dbJLinkParse(const char *json, size_t jlen, short dbfType,
1553+ jlink **ppjlink)
1554+{
1555+ parseContext context, *parser = &context;
1556+ yajl_alloc_funcs dbjl_allocs;
1557+ yajl_handle yh;
1558+ yajl_status ys;
1559+ long status;
1560+
1561+ IFDEBUG(10)
1562+ printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
1563+ (int) jlen, json, dbfType, ppjlink);
1564+
1565+ parser->pjlink = NULL;
1566+ parser->product = NULL;
1567+ parser->dbfType = dbfType;
1568+ parser->jsonDepth = 0;
1569+ parser->key_is_link = 0;
1570+
1571+ IFDEBUG(10)
1572+ printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
1573+ parser->jsonDepth, parser->key_is_link);
1574+
1575+ yajl_set_default_alloc_funcs(&dbjl_allocs);
1576+ yh = yajl_alloc(&dbjl_callbacks, &dbjl_config, &dbjl_allocs, parser);
1577+ if (!yh)
1578+ return S_db_noMemory;
1579+
1580+ ys = yajl_parse(yh, (const unsigned char *) json, (unsigned) jlen);
1581+ if (ys == yajl_status_insufficient_data)
1582+ ys = yajl_parse_complete(yh);
1583+
1584+ switch (ys) {
1585+ unsigned char *err;
1586+
1587+ case yajl_status_ok:
1588+ assert(parser->jsonDepth == 0);
1589+ *ppjlink = parser->product;
1590+ status = 0;
1591+ break;
1592+
1593+ case yajl_status_error:
1594+ err = yajl_get_error(yh, 1, (const unsigned char *) json, (unsigned) jlen);
1595+ errlogPrintf("dbJLinkInit: %s\n", err);
1596+ yajl_free_error(yh, err);
1597+ dbJLinkFree(parser->pjlink);
1598+ /* fall through */
1599+ default:
1600+ status = S_db_badField;
1601+ }
1602+
1603+ yajl_free(yh);
1604+ return status;
1605+}
1606+
1607+long dbJLinkInit(struct link *plink)
1608+{
1609+ jlink *pjlink;
1610+
1611+ assert(plink);
1612+ pjlink = plink->value.json.jlink;
1613+
1614+ if (pjlink)
1615+ plink->lset = pjlink->pif->get_lset(pjlink);
1616+
1617+ dbLinkOpen(plink);
1618+ return 0;
1619+}
1620+
1621+void dbJLinkFree(jlink *pjlink)
1622+{
1623+ if (pjlink)
1624+ pjlink->pif->free_jlink(pjlink);
1625+}
1626+
1627+void dbJLinkReport(jlink *pjlink, int level, int indent) {
1628+ if (pjlink && pjlink->pif->report)
1629+ pjlink->pif->report(pjlink, level, indent);
1630+}
1631+
1632+long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx)
1633+{
1634+ jlink *pjlink;
1635+ long status;
1636+
1637+ if (!plink || plink->type != JSON_LINK)
1638+ return 0;
1639+
1640+ pjlink = plink->value.json.jlink;
1641+ if (!pjlink)
1642+ return 0;
1643+
1644+ status = rtn(pjlink, ctx);
1645+ if (!status && pjlink->pif->map_children)
1646+ status = pjlink->pif->map_children(pjlink, rtn, ctx);
1647+
1648+ return status;
1649+}
1650+
1651+long dbjlr(const char *recname, int level)
1652+{
1653+ DBENTRY dbentry;
1654+ DBENTRY * const pdbentry = &dbentry;
1655+ long status;
1656+
1657+ if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) {
1658+ recname = NULL;
1659+ printf("JSON links in all records\n\n");
1660+ }
1661+ else
1662+ printf("JSON links in record '%s'\n\n", recname);
1663+
1664+ dbInitEntry(pdbbase, pdbentry);
1665+ for (status = dbFirstRecordType(pdbentry);
1666+ status == 0;
1667+ status = dbNextRecordType(pdbentry)) {
1668+ for (status = dbFirstRecord(pdbentry);
1669+ status == 0;
1670+ status = dbNextRecord(pdbentry)) {
1671+ dbRecordType *pdbRecordType = pdbentry->precordType;
1672+ dbCommon *precord = pdbentry->precnode->precord;
1673+ char *prec = (char *) precord;
1674+ int i;
1675+
1676+ if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
1677+ continue;
1678+ if (dbIsAlias(pdbentry))
1679+ continue;
1680+
1681+ printf(" %s record '%s':\n", pdbRecordType->name, precord->name);
1682+
1683+ dbScanLock(precord);
1684+ for (i = 0; i < pdbRecordType->no_links; i++) {
1685+ int idx = pdbRecordType->link_ind[i];
1686+ dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
1687+ DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
1688+
1689+ if (plink->type != JSON_LINK)
1690+ continue;
1691+ if (!dbLinkIsDefined(plink))
1692+ continue;
1693+
1694+ printf(" Link field '%s':\n", pdbFldDes->name);
1695+ dbJLinkReport(plink->value.json.jlink, level, 6);
1696+ }
1697+ dbScanUnlock(precord);
1698+ if (recname)
1699+ goto done;
1700+ }
1701+ }
1702+done:
1703+ return 0;
1704+}
1705+
1706+long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)
1707+{
1708+ DBENTRY dbentry;
1709+ DBENTRY * const pdbentry = &dbentry;
1710+ long status;
1711+
1712+ if (recname && (recname[0] = '\0' || !strcmp(recname, "*")))
1713+ recname = NULL;
1714+
1715+ dbInitEntry(pdbbase, pdbentry);
1716+ for (status = dbFirstRecordType(pdbentry);
1717+ status == 0;
1718+ status = dbNextRecordType(pdbentry)) {
1719+ for (status = dbFirstRecord(pdbentry);
1720+ status == 0;
1721+ status = dbNextRecord(pdbentry)) {
1722+ dbRecordType *pdbRecordType = pdbentry->precordType;
1723+ dbCommon *precord = pdbentry->precnode->precord;
1724+ char *prec = (char *) precord;
1725+ int i;
1726+
1727+ if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
1728+ continue;
1729+ if (dbIsAlias(pdbentry))
1730+ continue;
1731+
1732+ dbScanLock(precord);
1733+ for (i = 0; i < pdbRecordType->no_links; i++) {
1734+ int idx = pdbRecordType->link_ind[i];
1735+ dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
1736+ DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
1737+
1738+ status = dbJLinkMapChildren(plink, rtn, ctx);
1739+ if (status)
1740+ goto unlock;
1741+ }
1742+unlock:
1743+ dbScanUnlock(precord);
1744+ if (status || recname)
1745+ goto done;
1746+ }
1747+ }
1748+done:
1749+ return status;
1750+}
1751
1752=== added file 'src/ioc/db/dbJLink.h'
1753--- src/ioc/db/dbJLink.h 1970-01-01 00:00:00 +0000
1754+++ src/ioc/db/dbJLink.h 2017-03-03 18:23:23 +0000
1755@@ -0,0 +1,132 @@
1756+/*************************************************************************\
1757+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
1758+* National Laboratory.
1759+* EPICS BASE is distributed subject to a Software License Agreement found
1760+* in file LICENSE that is included with this distribution.
1761+\*************************************************************************/
1762+/* dbJLink.h */
1763+
1764+#ifndef INC_dbJLink_H
1765+#define INC_dbJLink_H
1766+
1767+#include <stdlib.h>
1768+#include <shareLib.h>
1769+
1770+#ifdef __cplusplus
1771+extern "C" {
1772+#endif
1773+
1774+typedef enum {
1775+ jlif_stop = 0,
1776+ jlif_continue = 1
1777+} jlif_result;
1778+
1779+typedef enum {
1780+ jlif_key_stop = jlif_stop,
1781+ jlif_key_continue = jlif_continue,
1782+ jlif_key_child_link
1783+} jlif_key_result;
1784+
1785+struct link;
1786+struct lset;
1787+struct jlif;
1788+
1789+typedef struct jlink {
1790+ struct jlif *pif; /* Link methods */
1791+ struct jlink *parent; /* NULL for top-level links */
1792+ int parseDepth; /* Used by parser, unused afterwards */
1793+ /* Link types extend or embed this structure for private storage */
1794+} jlink;
1795+
1796+typedef long (*jlink_map_fn)(jlink *, void *ctx);
1797+
1798+typedef struct jlif {
1799+ /* Optional parser methods below given as NULL are equivalent to
1800+ * providing a routine that always returns jlif_stop, meaning that
1801+ * this JSON construct is not allowed at this point in the parse.
1802+ */
1803+
1804+ const char *name;
1805+ /* Name for the link type, used in link value */
1806+
1807+ jlink* (*alloc_jlink)(short dbfType);
1808+ /* Required, allocate new link structure */
1809+
1810+ void (*free_jlink)(jlink *);
1811+ /* Required, release all resources allocated for link */
1812+
1813+ jlif_result (*parse_null)(jlink *);
1814+ /* Optional, parser saw a null value */
1815+
1816+ jlif_result (*parse_boolean)(jlink *, int val);
1817+ /* Optional, parser saw a boolean value */
1818+
1819+ jlif_result (*parse_integer)(jlink *, long num);
1820+ /* Optional, parser saw an integer value */
1821+
1822+ jlif_result (*parse_double)(jlink *, double num);
1823+ /* Optional, parser saw a double value */
1824+
1825+ jlif_result (*parse_string)(jlink *, const char *val, size_t len);
1826+ /* Optional, parser saw a string value */
1827+
1828+ jlif_key_result (*parse_start_map)(jlink *);
1829+ /* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
1830+ * to expect a child link next (extra key/value pairs may follow).
1831+ */
1832+
1833+ jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
1834+ /* Optional, parser saw a map key */
1835+
1836+ jlif_result (*parse_end_map)(jlink *);
1837+ /* Optional, parser saw a close-brace '}' */
1838+
1839+ jlif_result (*parse_start_array)(jlink *);
1840+ /* Optional, parser saw an open-bracket */
1841+
1842+ jlif_result (*parse_end_array)(jlink *);
1843+ /* Optional, parser saw a close-bracket */
1844+
1845+ void (*end_child)(jlink *parent, jlink *child);
1846+ /* Optional, called with pointer to the new child link after
1847+ * parse_start_map() returned jlif_key_child_link */
1848+
1849+ struct lset* (*get_lset)(const jlink *);
1850+ /* Required, return lset for this link instance */
1851+
1852+ void (*report)(const jlink *, int level, int indent);
1853+ /* Optional, print status information about this link instance, then
1854+ * if (level > 0) print a link identifier (at indent+2) and call
1855+ * dbJLinkReport(child, level-1, indent+4)
1856+ * for each child.
1857+ */
1858+
1859+ long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
1860+ /* Optional, call dbJLinkMapChildren() on all embedded links.
1861+ * Stop immediately and return status if non-zero.
1862+ */
1863+
1864+ /* Link types must NOT extend this table with their own routines,
1865+ * this space is reserved for extensions to the jlink interface.
1866+ */
1867+} jlif;
1868+
1869+epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
1870+ jlink **ppjlink);
1871+epicsShareFunc long dbJLinkInit(struct link *plink);
1872+
1873+epicsShareFunc void dbJLinkFree(jlink *);
1874+epicsShareFunc void dbJLinkReport(jlink *, int level, int indent);
1875+
1876+epicsShareFunc long dbJLinkMapChildren(struct link *,
1877+ jlink_map_fn rtn, void *ctx);
1878+
1879+epicsShareFunc long dbjlr(const char *recname, int level);
1880+epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
1881+
1882+#ifdef __cplusplus
1883+}
1884+#endif
1885+
1886+#endif /* INC_dbJLink_H */
1887+
1888
1889=== modified file 'src/ioc/db/dbLink.c'
1890--- src/ioc/db/dbLink.c 2017-02-01 17:57:04 +0000
1891+++ src/ioc/db/dbLink.c 2017-03-03 18:23:23 +0000
1892@@ -19,62 +19,35 @@
1893 #include <string.h>
1894
1895 #include "alarm.h"
1896-#include "cantProceed.h"
1897 #include "cvtFast.h"
1898 #include "dbDefs.h"
1899 #include "ellLib.h"
1900-#include "epicsThread.h"
1901 #include "epicsTime.h"
1902 #include "errlog.h"
1903
1904 #include "caeventmask.h"
1905
1906 #define epicsExportSharedSymbols
1907-#include "callback.h"
1908 #include "dbAccessDefs.h"
1909 #include "dbAddr.h"
1910 #include "dbBase.h"
1911-#include "dbBkpt.h"
1912 #include "dbCa.h"
1913 #include "dbCommon.h"
1914-#include "dbConvertFast.h"
1915-#include "dbConvert.h"
1916-#include "dbEvent.h"
1917+#include "dbConstLink.h"
1918+#include "dbDbLink.h"
1919 #include "db_field_log.h"
1920 #include "dbFldTypes.h"
1921-#include "dbFldTypes.h"
1922+#include "dbJLink.h"
1923 #include "dbLink.h"
1924-#include "dbLockPvt.h"
1925-#include "dbNotify.h"
1926+#include "dbLock.h"
1927 #include "dbScan.h"
1928 #include "dbStaticLib.h"
1929 #include "devSup.h"
1930-#include "epicsEvent.h"
1931-#include "errMdef.h"
1932 #include "link.h"
1933 #include "recGbl.h"
1934 #include "recSup.h"
1935 #include "special.h"
1936
1937-static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
1938- epicsEnum16 stat, epicsEnum16 sevr)
1939-{
1940- switch (ppv_link->pvlMask & pvlOptMsMode) {
1941- case pvlOptNMS:
1942- break;
1943- case pvlOptMSI:
1944- if (sevr < INVALID_ALARM)
1945- break;
1946- /* Fall through */
1947- case pvlOptMS:
1948- recGblSetSevr(pdest, LINK_ALARM, sevr);
1949- break;
1950- case pvlOptMSS:
1951- recGblSetSevr(pdest, stat, sevr);
1952- break;
1953- }
1954-}
1955-
1956 /* How to identify links in error messages */
1957 static const char * link_field_name(const struct link *plink)
1958 {
1959@@ -94,6 +67,7 @@
1960 }
1961
1962
1963+<<<<<<< TREE
1964 /***************************** Constant Links *****************************/
1965
1966 /* Forward definition */
1967@@ -443,6 +417,8 @@
1968 dbDbScanFwdLink
1969 };
1970
1971+=======
1972+>>>>>>> MERGE-SOURCE
1973 /***************************** Generic Link API *****************************/
1974
1975 void dbInitLink(struct link *plink, short dbfType)
1976@@ -454,6 +430,11 @@
1977 return;
1978 }
1979
1980+ if (plink->type == JSON_LINK) {
1981+ dbJLinkInit(plink);
1982+ return;
1983+ }
1984+
1985 if (plink->type != PV_LINK)
1986 return;
1987
1988@@ -487,7 +468,8 @@
1989 }
1990 }
1991
1992-void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)
1993+void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
1994+ DBADDR *ptarget)
1995 {
1996 struct dbCommon *precord = plink->precord;
1997
1998@@ -496,6 +478,18 @@
1999 return;
2000 }
2001
2002+ if (plink->type == JSON_LINK) {
2003+ /*
2004+ * FIXME: Can't create DB links as dbJLink types yet,
2005+ * dbLock.c doesn't have any way to find/track them.
2006+ */
2007+ dbJLinkInit(plink);
2008+ return;
2009+ }
2010+
2011+ if (plink->type != PV_LINK)
2012+ return;
2013+
2014 if (plink == &precord->tsel)
2015 recGblTSELwasModified(plink);
2016
2017@@ -518,16 +512,15 @@
2018 }
2019 }
2020
2021-long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
2022+void dbLinkOpen(struct link *plink)
2023 {
2024- if (plink->type == CONSTANT)
2025- return dbConstLoadLink(plink, dbrType, pbuffer);
2026+ lset *plset = plink->lset;
2027
2028- /* Could pass a type hint to the other link types here */
2029- return S_db_notFound;
2030+ if (plset && plset->openLink)
2031+ plset->openLink(plink);
2032 }
2033
2034-void dbRemoveLink(dbLocker *locker, struct link *plink)
2035+void dbRemoveLink(struct dbLocker *locker, struct link *plink)
2036 {
2037 lset *plset = plink->lset;
2038
2039@@ -536,6 +529,65 @@
2040 plset->removeLink(locker, plink);
2041 plink->lset = NULL;
2042 }
2043+ if (plink->type == JSON_LINK)
2044+ plink->value.json.jlink = NULL;
2045+}
2046+
2047+int dbLinkIsDefined(const struct link *plink)
2048+{
2049+ return (plink->lset != 0);
2050+}
2051+
2052+int dbLinkIsConstant(const struct link *plink)
2053+{
2054+ lset *plset = plink->lset;
2055+
2056+ if (plset)
2057+ return plset->isConstant;
2058+
2059+ return -1;
2060+}
2061+
2062+int dbLinkIsVolatile(const struct link *plink)
2063+{
2064+ lset *plset = plink->lset;
2065+
2066+ if (plset)
2067+ return plset->isVolatile;
2068+
2069+ return -1;
2070+}
2071+
2072+long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
2073+{
2074+ lset *plset = plink->lset;
2075+
2076+ if (plset && plset->loadScalar)
2077+ return plset->loadScalar(plink, dbrType, pbuffer);
2078+
2079+ return S_db_noLSET;
2080+}
2081+
2082+long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
2083+ epicsUInt32 *plen)
2084+{
2085+ lset *plset = plink->lset;
2086+
2087+ if (plset && plset->loadLS)
2088+ return plset->loadLS(plink, pbuffer, size, plen);
2089+
2090+ return S_db_noLSET;
2091+}
2092+
2093+long dbLoadLinkArray(struct link *plink, short dbrType, void *pbuffer,
2094+ long *pnRequest)
2095+{
2096+ lset *plset = plink->lset;
2097+
2098+ if (plset && plset->loadArray)
2099+ return plset->loadArray(plink, dbrType, pbuffer, pnRequest);
2100+
2101+ return S_db_noLSET;
2102 }
2103
2104 int dbIsLinkConnected(const struct link *plink)
2105@@ -563,7 +615,7 @@
2106 lset *plset = plink->lset;
2107
2108 if (!plset || !plset->getElements)
2109- return S_db_badField;
2110+ return S_db_noLSET;
2111
2112 return plset->getElements(plink, nelements);
2113 }
2114@@ -572,24 +624,20 @@
2115 long *poptions, long *pnRequest)
2116 {
2117 struct dbCommon *precord = plink->precord;
2118- epicsEnum16 sevr = 0, stat = 0;
2119 lset *plset = plink->lset;
2120 long status;
2121
2122 if (poptions && *poptions) {
2123- printf("dbGetLinkValue: Use of poptions no longer supported\n");
2124+ printf("dbGetLink: Use of poptions no longer supported\n");
2125 *poptions = 0;
2126 }
2127
2128 if (!plset || !plset->getValue)
2129 return -1;
2130
2131- status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);
2132- if (status) {
2133+ status = plset->getValue(plink, dbrType, pbuffer, pnRequest);
2134+ if (status)
2135 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
2136- } else {
2137- inherit_severity(&plink->value.pv_link, precord, stat, sevr);
2138- }
2139 return status;
2140 }
2141
2142@@ -598,7 +646,7 @@
2143 lset *plset = plink->lset;
2144
2145 if (!plset || !plset->getControlLimits)
2146- return S_db_notFound;
2147+ return S_db_noLSET;
2148
2149 return plset->getControlLimits(plink, low, high);
2150 }
2151@@ -608,7 +656,7 @@
2152 lset *plset = plink->lset;
2153
2154 if (!plset || !plset->getGraphicLimits)
2155- return S_db_notFound;
2156+ return S_db_noLSET;
2157
2158 return plset->getGraphicLimits(plink, low, high);
2159 }
2160@@ -619,7 +667,7 @@
2161 lset *plset = plink->lset;
2162
2163 if (!plset || !plset->getAlarmLimits)
2164- return S_db_notFound;
2165+ return S_db_noLSET;
2166
2167 return plset->getAlarmLimits(plink, lolo, low, high, hihi);
2168 }
2169@@ -629,7 +677,7 @@
2170 lset *plset = plink->lset;
2171
2172 if (!plset || !plset->getPrecision)
2173- return S_db_notFound;
2174+ return S_db_noLSET;
2175
2176 return plset->getPrecision(plink, precision);
2177 }
2178@@ -639,7 +687,7 @@
2179 lset *plset = plink->lset;
2180
2181 if (!plset || !plset->getUnits)
2182- return S_db_notFound;
2183+ return S_db_noLSET;
2184
2185 return plset->getUnits(plink, units, unitsSize);
2186 }
2187@@ -650,7 +698,7 @@
2188 lset *plset = plink->lset;
2189
2190 if (!plset || !plset->getAlarm)
2191- return S_db_notFound;
2192+ return S_db_noLSET;
2193
2194 return plset->getAlarm(plink, status, severity);
2195 }
2196@@ -660,7 +708,7 @@
2197 lset *plset = plink->lset;
2198
2199 if (!plset || !plset->getTimeStamp)
2200- return S_db_notFound;
2201+ return S_db_noLSET;
2202
2203 return plset->getTimeStamp(plink, pstamp);
2204 }
2205@@ -672,7 +720,7 @@
2206 long status;
2207
2208 if (!plset || !plset->putValue)
2209- return S_db_notFound;
2210+ return S_db_noLSET;
2211
2212 status = plset->putValue(plink, dbrType, pbuffer, nRequest);
2213 if (status) {
2214@@ -683,6 +731,33 @@
2215 return status;
2216 }
2217
2218+void dbLinkAsyncComplete(struct link *plink)
2219+{
2220+ dbCommon *pdbCommon = plink->precord;
2221+
2222+ dbScanLock(pdbCommon);
2223+ pdbCommon->rset->process(pdbCommon);
2224+ dbScanUnlock(pdbCommon);
2225+}
2226+
2227+long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer,
2228+ long nRequest)
2229+{
2230+ lset *plset = plink->lset;
2231+ long status;
2232+
2233+ if (!plset || !plset->putAsync)
2234+ return S_db_noLSET;
2235+
2236+ status = plset->putAsync(plink, dbrType, pbuffer, nRequest);
2237+ if (status) {
2238+ struct dbCommon *precord = plink->precord;
2239+
2240+ recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
2241+ }
2242+ return status;
2243+}
2244+
2245 void dbScanFwdLink(struct link *plink)
2246 {
2247 lset *plset = plink->lset;
2248@@ -693,20 +768,6 @@
2249
2250 /* Helper functions for long string support */
2251
2252-long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
2253- epicsUInt32 *plen)
2254-{
2255- if (plink->type == CONSTANT &&
2256- plink->value.constantStr) {
2257- strncpy(pbuffer, plink->value.constantStr, --size);
2258- pbuffer[size] = 0;
2259- *plen = (epicsUInt32) strlen(pbuffer) + 1;
2260- return 0;
2261- }
2262-
2263- return S_db_notFound;
2264-}
2265-
2266 long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
2267 epicsUInt32 *plen)
2268 {
2269
2270=== modified file 'src/ioc/db/dbLink.h'
2271--- src/ioc/db/dbLink.h 2017-02-01 17:57:04 +0000
2272+++ src/ioc/db/dbLink.h 2017-03-03 18:23:23 +0000
2273@@ -28,12 +28,31 @@
2274 struct dbLocker;
2275
2276 typedef struct lset {
2277+ /* Characteristics of the link type */
2278+ const unsigned isConstant:1;
2279+ const unsigned isVolatile:1;
2280+
2281+ /* Activation */
2282+ void (*openLink)(struct link *plink);
2283+
2284+ /* Destructor */
2285 void (*removeLink)(struct dbLocker *locker, struct link *plink);
2286+
2287+ /* Const init, data type hinting */
2288+ long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
2289+ long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
2290+ epicsUInt32 *plen);
2291+ long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
2292+ long *pnRequest);
2293+
2294+ /* Metadata */
2295 int (*isConnected)(const struct link *plink);
2296 int (*getDBFtype)(const struct link *plink);
2297 long (*getElements)(const struct link *plink, long *nelements);
2298+
2299+ /* Get data */
2300 long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
2301- epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);
2302+ long *pnRequest);
2303 long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
2304 long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
2305 long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
2306@@ -43,8 +62,14 @@
2307 long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
2308 epicsEnum16 *severity);
2309 long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
2310+
2311+ /* Put data */
2312 long (*putValue)(struct link *plink, short dbrType,
2313 const void *pbuffer, long nRequest);
2314+ long (*putAsync)(struct link *plink, short dbrType,
2315+ const void *pbuffer, long nRequest);
2316+
2317+ /* Process */
2318 void (*scanForward)(struct link *plink);
2319 } lset;
2320
2321@@ -52,11 +77,20 @@
2322 dbGetAlarm(link, NULL, sevr)
2323
2324 epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
2325-epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
2326- DBADDR *ptarget);
2327+epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
2328+ short dbfType, DBADDR *ptarget);
2329+
2330+epicsShareFunc void dbLinkOpen(struct link *plink);
2331+epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
2332+
2333+epicsShareFunc int dbLinkIsDefined(const struct link *plink); /* 0 or 1 */
2334+epicsShareFunc int dbLinkIsConstant(const struct link *plink); /* -1, 0 or 1 */
2335+epicsShareFunc int dbLinkIsVolatile(const struct link *plink); /* -1, 0 or 1 */
2336+
2337 epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
2338 void *pbuffer);
2339-epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
2340+epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
2341+ long *pnRequest);
2342
2343 epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
2344 epicsShareFunc int dbIsLinkConnected(const struct link *plink);
2345@@ -78,6 +112,9 @@
2346 epicsTimeStamp *pstamp);
2347 epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
2348 const void *pbuffer, long nRequest);
2349+epicsShareFunc void dbLinkAsyncComplete(struct link *plink);
2350+epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType,
2351+ const void *pbuffer, long nRequest);
2352 epicsShareFunc void dbScanFwdLink(struct link *plink);
2353
2354 epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer,
2355
2356=== modified file 'src/ioc/db/dbLock.c'
2357--- src/ioc/db/dbLock.c 2017-02-01 17:57:04 +0000
2358+++ src/ioc/db/dbLock.c 2017-03-03 18:23:23 +0000
2359@@ -190,6 +190,8 @@
2360 lockRecord * const lr = precord->lset;
2361 lockSet *ls;
2362
2363+ assert(lr);
2364+
2365 ls = dbLockGetRef(lr);
2366 assert(epicsAtomicGetIntT(&ls->refcount)>0);
2367
2368
2369=== modified file 'src/ioc/db/dbTest.c'
2370=== modified file 'src/ioc/db/dbUnitTest.c'
2371--- src/ioc/db/dbUnitTest.c 2017-02-01 17:57:04 +0000
2372+++ src/ioc/db/dbUnitTest.c 2017-03-03 18:23:23 +0000
2373@@ -106,8 +106,8 @@
2374 DBADDR addr;
2375 union anybuf pod;
2376
2377- if(dbNameToAddr(pv, &addr)) {
2378- testFail("Missing PV %s", pv);
2379+ if (dbNameToAddr(pv, &addr)) {
2380+ testFail("Missing PV \"%s\"", pv);
2381 return S_dbLib_recNotFound;
2382 }
2383
2384@@ -152,7 +152,7 @@
2385 ret = testdbVPutField(pv, dbrType, ap);
2386 va_end(ap);
2387
2388- testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret);
2389+ testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret));
2390 }
2391
2392 void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
2393@@ -164,10 +164,8 @@
2394 ret = testdbVPutField(pv, dbrType, ap);
2395 va_end(ap);
2396
2397- if(ret==status)
2398- testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status);
2399- else
2400- testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret);
2401+ testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)",
2402+ pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret));
2403 }
2404
2405 void testdbGetFieldEqual(const char* pv, short dbrType, ...)
2406@@ -187,13 +185,13 @@
2407 long status;
2408
2409 if(dbNameToAddr(pv, &addr)) {
2410- testFail("Missing PV %s", pv);
2411+ testFail("Missing PV \"%s\"", pv);
2412 return;
2413 }
2414
2415 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
2416- if(status) {
2417- testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status);
2418+ if (status) {
2419+ testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
2420 return;
2421 }
2422
2423@@ -226,8 +224,8 @@
2424 {
2425 DBADDR addr;
2426
2427- if(dbNameToAddr(pv, &addr))
2428- testAbort("Missing record %s", pv);
2429+ if (dbNameToAddr(pv, &addr))
2430+ testAbort("Missing record \"%s\"", pv);
2431
2432 return addr.precord;
2433 }
2434
2435=== modified file 'src/ioc/db/recGbl.c'
2436--- src/ioc/db/recGbl.c 2017-02-01 17:57:04 +0000
2437+++ src/ioc/db/recGbl.c 2017-03-03 18:23:23 +0000
2438@@ -17,6 +17,7 @@
2439 #include <string.h>
2440 #include <limits.h>
2441
2442+#include "alarm.h"
2443 #include "dbDefs.h"
2444 #include "epicsMath.h"
2445 #include "epicsPrint.h"
2446@@ -30,7 +31,6 @@
2447 #include "dbAccessDefs.h"
2448 #include "dbAddr.h"
2449 #include "dbBase.h"
2450-#include "dbCa.h"
2451 #include "dbCommon.h"
2452 #include "dbEvent.h"
2453 #include "db_field_log.h"
2454@@ -214,7 +214,27 @@
2455 }
2456 return FALSE;
2457 }
2458-
2459
2460+
2461+void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
2462+ epicsEnum16 sevr)
2463+{
2464+ switch (msMode) {
2465+ case pvlOptNMS:
2466+ break;
2467+ case pvlOptMSI:
2468+ if (sevr < INVALID_ALARM)
2469+ break;
2470+ /* Fall through */
2471+ case pvlOptMS:
2472+ recGblSetSevr(precord, LINK_ALARM, sevr);
2473+ break;
2474+ case pvlOptMSS:
2475+ recGblSetSevr(precord, stat, sevr);
2476+ break;
2477+ }
2478+}
2479+
2480+
2481 void recGblFwdLink(void *precord)
2482 {
2483 dbCommon *pdbc = precord;
2484@@ -236,7 +256,7 @@
2485 dbCommon* prec = (dbCommon*)pvoid;
2486 struct link *plink = &prec->tsel;
2487
2488- if (plink->type != CONSTANT) {
2489+ if (!dbLinkIsConstant(plink)) {
2490 struct pv_link *ppv_link = &plink->value.pv_link;
2491
2492 if (ppv_link->pvlMask & pvlOptTSELisTime) {
2493
2494=== modified file 'src/ioc/db/recGbl.h'
2495--- src/ioc/db/recGbl.h 2014-10-31 21:18:25 +0000
2496+++ src/ioc/db/recGbl.h 2017-03-03 18:23:23 +0000
2497@@ -59,6 +59,8 @@
2498 epicsShareFunc unsigned short recGblResetAlarms(void *precord);
2499 epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat,
2500 epicsEnum16 new_sevr);
2501+epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
2502+ epicsEnum16 sevr);
2503 epicsShareFunc void recGblFwdLink(void *precord);
2504 epicsShareFunc void recGblGetTimeStamp(void *precord);
2505 epicsShareFunc void recGblTSELwasModified(struct link *plink);
2506
2507=== modified file 'src/ioc/db/test/Makefile'
2508--- src/ioc/db/test/Makefile 2017-02-01 17:57:04 +0000
2509+++ src/ioc/db/test/Makefile 2017-03-03 18:23:23 +0000
2510@@ -18,7 +18,9 @@
2511 dbTestIoc_SRCS += arrRecord.c
2512 dbTestIoc_SRCS += xRecord.c
2513 dbTestIoc_SRCS += dbLinkdset.c
2514+dbTestIoc_SRCS += xLink.c
2515 dbTestIoc_SRCS += devx.c
2516+dbTestIoc_SRCS += jlinkz.c
2517 dbTestIoc_LIBS = dbCore ca Com
2518
2519 TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
2520@@ -26,10 +28,11 @@
2521 dbTestIoc_DBD += menuGlobal.dbd
2522 dbTestIoc_DBD += menuConvert.dbd
2523 dbTestIoc_DBD += menuScan.dbd
2524-#dbTestIoc_DBD += arrRecord.dbd
2525 dbTestIoc_DBD += xRecord.dbd
2526 dbTestIoc_DBD += arrRecord.dbd
2527+dbTestIoc_DBD += xLink.dbd
2528 dbTestIoc_DBD += devx.dbd
2529+dbTestIoc_DBD += jlinkz.dbd
2530 dbTestIoc_DBD += dbLinkdset.dbd
2531 TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
2532
2533@@ -54,7 +57,7 @@
2534 dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
2535 testHarness_SRCS += dbPutLinkTest.c
2536 TESTS += dbPutLinkTest
2537-TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db
2538+TESTFILES += ../dbPutLinkTest.db ../dbPutLinkTestJ.db ../dbBadLink.db
2539
2540 TESTPROD_HOST += dbLockTest
2541 dbLockTest_SRCS += dbLockTest.c
2542@@ -152,7 +155,7 @@
2543 testHarness_SRCS += recGblCheckDeadbandTest.c
2544 TESTS += recGblCheckDeadbandTest
2545
2546-# The testHarness runs all the test programs in a known working order.
2547+# This runs all the test programs in a known working order:
2548 testHarness_SRCS += epicsRunDbTests.c
2549
2550 dbTestHarness_SRCS += $(testHarness_SRCS)
2551
2552=== modified file 'src/ioc/db/test/dbLinkdset.c'
2553--- src/ioc/db/test/dbLinkdset.c 2017-02-01 17:57:04 +0000
2554+++ src/ioc/db/test/dbLinkdset.c 2017-03-03 18:23:23 +0000
2555@@ -29,6 +29,7 @@
2556 static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
2557 epicsExportAddress(dset, devxLTest ## LTYPE);
2558
2559+DEFDSET(JSON_LINK)
2560 DEFDSET(VME_IO)
2561 DEFDSET(CAMAC_IO)
2562 DEFDSET(AB_IO)
2563
2564=== modified file 'src/ioc/db/test/dbLinkdset.dbd'
2565--- src/ioc/db/test/dbLinkdset.dbd 2017-02-01 17:57:04 +0000
2566+++ src/ioc/db/test/dbLinkdset.dbd 2017-03-03 18:23:23 +0000
2567@@ -1,3 +1,4 @@
2568+device(x, JSON_LINK, devxLTestJSON_LINK, "Unit Test JSON_LINK")
2569 device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
2570 device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
2571 device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
2572
2573=== modified file 'src/ioc/db/test/dbPutLinkTest.c'
2574--- src/ioc/db/test/dbPutLinkTest.c 2017-02-01 17:57:04 +0000
2575+++ src/ioc/db/test/dbPutLinkTest.c 2017-03-03 18:23:23 +0000
2576@@ -24,8 +24,10 @@
2577 #include "osiFileName.h"
2578 #include "dbmf.h"
2579 #include "errlog.h"
2580+#include <epicsAtomic.h>
2581
2582 #include "xRecord.h"
2583+#include "jlinkz.h"
2584
2585 #include "testMain.h"
2586
2587@@ -60,6 +62,7 @@
2588 {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
2589 {" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
2590 {" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
2591+ {" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}},
2592 {NULL}
2593 };
2594
2595@@ -67,7 +70,7 @@
2596 {
2597 const struct testParseDataT *td = testParseData;
2598 dbLinkInfo info;
2599- testDiag("link parsing");
2600+ testDiag("\n# Checking link parsing\n#");
2601 testdbPrepare();
2602
2603 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2604@@ -80,27 +83,32 @@
2605 testIocInitOk();
2606 eltc(1);
2607
2608- for(;td->str; td++) {
2609+ for (;td->str; td++) {
2610 int i, N;
2611- testDiag("Parse \"%s\"", td->str);
2612- testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);
2613- testOk1(info.ltype==td->info.ltype);
2614- if(td->info.target)
2615+ testDiag("Parsing \"%s\"", td->str);
2616+ testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK");
2617+ if (!testOk(info.ltype == td->info.ltype, "Link type value"))
2618+ testDiag("Expected %d, got %d", td->info.ltype, info.ltype);
2619+ if (td->info.target)
2620 testStrcmp(0, info.target, td->info.target);
2621- if(info.ltype==td->info.ltype) {
2622- switch(info.ltype) {
2623+ if (info.ltype == td->info.ltype) {
2624+ switch (info.ltype) {
2625 case PV_LINK:
2626- testOk1(info.modifiers==td->info.modifiers);
2627+ if (!testOk(info.modifiers == td->info.modifiers,
2628+ "PV Link modifier flags"))
2629+ testDiag("Expected %d, got %d", td->info.modifiers,
2630+ info.modifiers);
2631 break;
2632 case VME_IO:
2633+ case CAMAC_IO:
2634 testStrcmp(0, info.hwid, td->info.hwid);
2635 N = strlen(td->info.hwid);
2636- for(i=0; i<N; i++)
2637+ for (i=0; i<N; i++)
2638 testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
2639 info.hwnums[i], td->info.hwnums[i]);
2640 }
2641 }
2642- free(info.target);
2643+ dbFreeLinkInfo(&info);
2644 }
2645
2646 testIocShutdownOk();
2647@@ -124,7 +132,7 @@
2648 {
2649 const char * const *td = testParseFailData;
2650 dbLinkInfo info;
2651- testDiag("link parsing of invalid input");
2652+ testDiag("\n# Check parsing of invalid inputs\n#");
2653 testdbPrepare();
2654
2655 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2656@@ -138,8 +146,8 @@
2657 eltc(1);
2658
2659 for(;*td; td++) {
2660- testDiag("Expect failure \"%s\"", *td);
2661- testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);
2662+ testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField,
2663+ "dbParseLink correctly rejected \"%s\"", *td);
2664 }
2665
2666 testIocShutdownOk();
2667@@ -179,7 +187,7 @@
2668 const struct testDataT *td = testSetData;
2669 xRecord *prec;
2670 DBLINK *plink;
2671- testDiag("DB/CA link retargeting");
2672+ testDiag("\n# Checking DB/CA link retargeting\n#");
2673 testdbPrepare();
2674
2675 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2676@@ -196,21 +204,22 @@
2677 plink = &prec->lnk;
2678
2679 for (;td->linkstring;td++) {
2680- testDiag("x1.LNK <- \"%s\"", td->linkstring);
2681+ testDiag("Trying field value \"%s\"", td->linkstring);
2682
2683 testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
2684 if (td->linkback)
2685 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
2686 else
2687 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
2688- testOk1(plink->type==td->linkType);
2689+ if (!testOk(plink->type == td->linkType, "Link type"))
2690+ testDiag("Expected %d, got %d", td->linkType, plink->type);
2691
2692- if (plink->type==td->linkType) {
2693- switch(td->linkType) {
2694+ if (plink->type == td->linkType) {
2695+ switch (td->linkType) {
2696 case CONSTANT:
2697- if(plink->value.constantStr)
2698- testOk1(strcmp(plink->value.constantStr,td->linkstring)==0);
2699- else if(td->linkstring[0]=='\0')
2700+ if (plink->value.constantStr)
2701+ testOk1(strcmp(plink->value.constantStr, td->linkstring) == 0);
2702+ else if (td->linkstring[0]=='\0')
2703 testPass("Empty String");
2704 else
2705 testFail("oops");
2706@@ -218,7 +227,7 @@
2707
2708 case DB_LINK:
2709 case CA_LINK:
2710- testOk(plink->value.pv_link.pvlMask==td->pvlMask,
2711+ testOk(plink->value.pv_link.pvlMask == td->pvlMask,
2712 "pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask);
2713 break;
2714 }
2715@@ -239,6 +248,7 @@
2716 } testHWDataT;
2717
2718 static const testHWDataT testHWData[] = {
2719+ {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
2720 {"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"},
2721 {"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"},
2722 {"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"},
2723@@ -255,63 +265,66 @@
2724 static void testLink(DBLINK *plink, const testHWDataT *td)
2725 {
2726 switch(td->ltype) {
2727+ case JSON_LINK:
2728+ testOk1(strcmp(plink->value.json.string, td->parm) == 0);
2729+ break;
2730 case VME_IO:
2731- testOk1(plink->value.vmeio.card==td->vals[0]);
2732- testOk1(plink->value.vmeio.signal==td->vals[1]);
2733- testOk1(strcmp(plink->value.vmeio.parm, td->parm)==0);
2734+ testOk1(plink->value.vmeio.card == td->vals[0]);
2735+ testOk1(plink->value.vmeio.signal == td->vals[1]);
2736+ testOk1(strcmp(plink->value.vmeio.parm, td->parm) == 0);
2737 break;
2738 case CAMAC_IO:
2739- testOk1(plink->value.camacio.b==td->vals[0]);
2740- testOk1(plink->value.camacio.c==td->vals[1]);
2741- testOk1(plink->value.camacio.n==td->vals[2]);
2742- testOk1(plink->value.camacio.a==td->vals[3]);
2743- testOk1(plink->value.camacio.f==td->vals[4]);
2744- testOk1(strcmp(plink->value.camacio.parm, td->parm)==0);
2745+ testOk1(plink->value.camacio.b == td->vals[0]);
2746+ testOk1(plink->value.camacio.c == td->vals[1]);
2747+ testOk1(plink->value.camacio.n == td->vals[2]);
2748+ testOk1(plink->value.camacio.a == td->vals[3]);
2749+ testOk1(plink->value.camacio.f == td->vals[4]);
2750+ testOk1(strcmp(plink->value.camacio.parm, td->parm) == 0);
2751 break;
2752 case AB_IO:
2753- testOk1(plink->value.abio.link==td->vals[0]);
2754- testOk1(plink->value.abio.adapter==td->vals[1]);
2755- testOk1(plink->value.abio.card==td->vals[2]);
2756- testOk1(plink->value.abio.signal==td->vals[3]);
2757- testOk1(strcmp(plink->value.abio.parm, td->parm)==0);
2758+ testOk1(plink->value.abio.link == td->vals[0]);
2759+ testOk1(plink->value.abio.adapter == td->vals[1]);
2760+ testOk1(plink->value.abio.card == td->vals[2]);
2761+ testOk1(plink->value.abio.signal == td->vals[3]);
2762+ testOk1(strcmp(plink->value.abio.parm, td->parm) == 0);
2763 break;
2764 case GPIB_IO:
2765- testOk1(plink->value.gpibio.link==td->vals[0]);
2766- testOk1(plink->value.gpibio.addr==td->vals[1]);
2767- testOk1(strcmp(plink->value.gpibio.parm, td->parm)==0);
2768+ testOk1(plink->value.gpibio.link == td->vals[0]);
2769+ testOk1(plink->value.gpibio.addr == td->vals[1]);
2770+ testOk1(strcmp(plink->value.gpibio.parm, td->parm) == 0);
2771 break;
2772 case BITBUS_IO:
2773- testOk1(plink->value.bitbusio.link==td->vals[0]);
2774- testOk1(plink->value.bitbusio.node==td->vals[1]);
2775- testOk1(plink->value.bitbusio.port==td->vals[2]);
2776- testOk1(plink->value.bitbusio.signal==td->vals[3]);
2777- testOk1(strcmp(plink->value.bitbusio.parm, td->parm)==0);
2778+ testOk1(plink->value.bitbusio.link == td->vals[0]);
2779+ testOk1(plink->value.bitbusio.node == td->vals[1]);
2780+ testOk1(plink->value.bitbusio.port == td->vals[2]);
2781+ testOk1(plink->value.bitbusio.signal == td->vals[3]);
2782+ testOk1(strcmp(plink->value.bitbusio.parm, td->parm) == 0);
2783 break;
2784 case INST_IO:
2785- testOk1(strcmp(plink->value.instio.string, td->parm)==0);
2786+ testOk1(strcmp(plink->value.instio.string, td->parm) == 0);
2787 break;
2788 case BBGPIB_IO:
2789- testOk1(plink->value.bbgpibio.link==td->vals[0]);
2790- testOk1(plink->value.bbgpibio.bbaddr==td->vals[1]);
2791- testOk1(plink->value.bbgpibio.gpibaddr==td->vals[2]);
2792- testOk1(strcmp(plink->value.bbgpibio.parm, td->parm)==0);
2793+ testOk1(plink->value.bbgpibio.link == td->vals[0]);
2794+ testOk1(plink->value.bbgpibio.bbaddr == td->vals[1]);
2795+ testOk1(plink->value.bbgpibio.gpibaddr == td->vals[2]);
2796+ testOk1(strcmp(plink->value.bbgpibio.parm, td->parm) == 0);
2797 break;
2798 case RF_IO:
2799- testOk1(plink->value.rfio.cryo==td->vals[0]);
2800- testOk1(plink->value.rfio.micro==td->vals[1]);
2801- testOk1(plink->value.rfio.dataset==td->vals[2]);
2802- testOk1(plink->value.rfio.element==td->vals[3]);
2803+ testOk1(plink->value.rfio.cryo == td->vals[0]);
2804+ testOk1(plink->value.rfio.micro == td->vals[1]);
2805+ testOk1(plink->value.rfio.dataset == td->vals[2]);
2806+ testOk1(plink->value.rfio.element == td->vals[3]);
2807 break;
2808 case VXI_IO:
2809- if(plink->value.vxiio.flag==VXIDYNAMIC) {
2810- testOk1(plink->value.vxiio.frame==td->vals[0]);
2811- testOk1(plink->value.vxiio.slot==td->vals[1]);
2812- testOk1(plink->value.vxiio.signal==td->vals[2]);
2813+ if(plink->value.vxiio.flag == VXIDYNAMIC) {
2814+ testOk1(plink->value.vxiio.frame == td->vals[0]);
2815+ testOk1(plink->value.vxiio.slot == td->vals[1]);
2816+ testOk1(plink->value.vxiio.signal == td->vals[2]);
2817 } else {
2818- testOk1(plink->value.vxiio.la==td->vals[0]);
2819- testOk1(plink->value.vxiio.signal==td->vals[1]);
2820+ testOk1(plink->value.vxiio.la == td->vals[0]);
2821+ testOk1(plink->value.vxiio.signal == td->vals[1]);
2822 }
2823- testOk1(strcmp(plink->value.vxiio.parm, td->parm)==0);
2824+ testOk1(strcmp(plink->value.vxiio.parm, td->parm) == 0);
2825 break;
2826 }
2827 }
2828@@ -319,7 +332,7 @@
2829 static void testHWInitSet(void)
2830 {
2831 const testHWDataT *td = testHWData;
2832- testDiag("HW link parsing during initialization");
2833+ testDiag("\n# Checking HW link parsing during initialization\n#");
2834 testdbPrepare();
2835
2836 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2837@@ -332,13 +345,14 @@
2838 testIocInitOk();
2839 eltc(1);
2840
2841- for(;td->recname;td++) {
2842+ for (;td->recname; td++) {
2843 char buf[MAX_STRING_SIZE];
2844 xRecord *prec;
2845 DBLINK *plink;
2846+
2847 testDiag("%s == \"%s\"", td->recname, td->wval);
2848
2849- prec = (xRecord*)testdbRecordPtr(td->recname);
2850+ prec = (xRecord *) testdbRecordPtr(td->recname);
2851 plink = &prec->inp;
2852
2853 strcpy(buf, td->recname);
2854@@ -346,9 +360,11 @@
2855
2856 testdbGetFieldEqual(buf, DBR_STRING, td->wval);
2857
2858- testOk(plink->type==td->ltype, "link type %d == %d",
2859- plink->type, td->ltype);
2860- if(plink->type==td->ltype) {
2861+ if (!testOk(plink->type == td->ltype, "Link type")) {
2862+ testDiag("Expected %d, got %d",
2863+ td->ltype, plink->type);
2864+ }
2865+ else {
2866 testLink(plink, td);
2867 }
2868
2869@@ -360,6 +376,7 @@
2870 }
2871
2872 static const testHWDataT testHWData2[] = {
2873+ {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
2874 {"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"},
2875 {"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"},
2876 {"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"},
2877@@ -376,7 +393,8 @@
2878 static void testHWMod(void)
2879 {
2880 const testHWDataT *td = testHWData2;
2881- testDiag("HW link parsing during retarget");
2882+
2883+ testDiag("\n# Checking HW link parsing during retarget\n#");
2884 testdbPrepare();
2885
2886 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2887@@ -405,9 +423,11 @@
2888
2889 testdbGetFieldEqual(buf, DBR_STRING, td->wval);
2890
2891- testOk(plink->type==td->ltype, "link type %d == %d",
2892- plink->type, td->ltype);
2893- if(plink->type==td->ltype) {
2894+ if (!testOk(plink->type == td->ltype, "Link type")) {
2895+ testDiag("Expected %d, got %d",
2896+ td->ltype, plink->type);
2897+ }
2898+ else {
2899 testLink(plink, td);
2900 }
2901
2902@@ -422,42 +442,42 @@
2903 {
2904 xRecord *prec;
2905 DBLINK *plink;
2906- testDiag("Link parsing failures during initialization");
2907+ testDiag("\n# Checking link parse failures at iocInit\n#");
2908 testdbPrepare();
2909
2910 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2911
2912 dbTestIoc_registerRecordDeviceDriver(pdbbase);
2913
2914-
2915 /* this load will fail */
2916 eltc(0);
2917- testOk1(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR
2918- "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)!=0);
2919+ testOk(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR
2920+ ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR
2921+ "O.Common", NULL) != 0, "dbReadDatabase returned error (expected)");
2922
2923 testIocInitOk();
2924 eltc(1);
2925
2926 testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @");
2927
2928- prec = (xRecord*)testdbRecordPtr("eVME_IO1");
2929+ prec = (xRecord *) testdbRecordPtr("eVME_IO1");
2930 plink = &prec->inp;
2931- testOk1(plink->type==VME_IO);
2932- testOk1(plink->value.vmeio.parm!=NULL);
2933+ testOk1(plink->type == VME_IO);
2934+ testOk1(plink->value.vmeio.parm != NULL);
2935
2936 testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
2937
2938- prec = (xRecord*)testdbRecordPtr("eVME_IO2");
2939+ prec = (xRecord *) testdbRecordPtr("eVME_IO2");
2940 plink = &prec->inp;
2941- testOk1(plink->type==VME_IO);
2942- testOk1(plink->value.vmeio.parm!=NULL);
2943+ testOk1(plink->type == VME_IO);
2944+ testOk1(plink->value.vmeio.parm != NULL);
2945
2946 testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@");
2947
2948- prec = (xRecord*)testdbRecordPtr("eINST_IO");
2949+ prec = (xRecord *) testdbRecordPtr("eINST_IO");
2950 plink = &prec->inp;
2951- testOk1(plink->type==INST_IO);
2952- testOk1(plink->value.instio.string!=NULL);
2953+ testOk1(plink->type == INST_IO);
2954+ testOk1(plink->value.instio.string != NULL);
2955
2956 testIocShutdownOk();
2957
2958@@ -466,7 +486,7 @@
2959
2960 static void testLinkFail(void)
2961 {
2962- testDiag("Link parsing failures");
2963+ testDiag("\n# Checking runtime link parse failures\n#");
2964 testdbPrepare();
2965
2966 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
2967@@ -482,9 +502,22 @@
2968 /* INST_IO doesn't accept empty string */
2969 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
2970
2971- /* INST_IO doesn't accept empty string */
2972+ /* INST_IO doesn't accept string without @ */
2973 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
2974
2975+ /* JSON_LINK dies when expected */
2976+ testdbPutFieldOk("rJSON_LINK.INP", DBR_STRING, "{\"x\":true}");
2977+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":false}");
2978+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":null}");
2979+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1}");
2980+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1.1}");
2981+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":\"x\"}");
2982+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":[]}");
2983+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":{}}");
2984+
2985+ /* JSON_LINK syntax errors */
2986+ testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":bbbb}");
2987+
2988 /* syntax errors */
2989 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
2990 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201");
2991@@ -502,9 +535,73 @@
2992 testdbCleanup();
2993 }
2994
2995+static
2996+void testNumZ(int expect)
2997+{
2998+ int numz = epicsAtomicGetIntT(&numzalloc);
2999+ testOk(numz==expect, "numzalloc==%d (%d)", expect, numz);
3000+}
3001+
3002+static
3003+void testJLink(void)
3004+{
3005+ testDiag("Test json link setup/retarget");
3006+
3007+ testNumZ(0);
3008+
3009+ testDiag("Link parsing failures");
3010+ testdbPrepare();
3011+
3012+ testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
3013+
3014+ dbTestIoc_registerRecordDeviceDriver(pdbbase);
3015+
3016+ testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
3017+ testdbReadDatabase("dbPutLinkTestJ.db", NULL, NULL);
3018+
3019+ testNumZ(0);
3020+
3021+ eltc(0);
3022+ testIocInitOk();
3023+ eltc(1);
3024+
3025+ testNumZ(3);
3026+
3027+ testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
3028+ testdbPutFieldOk("j2.PROC", DBF_LONG, 1);
3029+ testdbPutFieldOk("j3.PROC", DBF_LONG, 1);
3030+
3031+ testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}");
3032+ testdbGetFieldEqual("j1.VAL", DBF_LONG, 1);
3033+ testdbGetFieldEqual("j2.VAL", DBF_LONG, 2);
3034+ testdbGetFieldEqual("j3.VAL", DBF_LONG, 3);
3035+
3036+ testNumZ(3);
3037+
3038+ testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
3039+ testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
3040+ testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
3041+
3042+ testNumZ(3);
3043+
3044+ testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"fail\":5}}");
3045+ testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
3046+ testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
3047+ /* put failure in parsing stage doesn't modify link */
3048+ testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
3049+
3050+ testNumZ(3);
3051+
3052+ testIocShutdownOk();
3053+
3054+ testNumZ(0);
3055+
3056+ testdbCleanup();
3057+}
3058+
3059 MAIN(dbPutLinkTest)
3060 {
3061- testPlan(251);
3062+ testPlan(301);
3063 testLinkParse();
3064 testLinkFailParse();
3065 testCADBSet();
3066@@ -512,5 +609,6 @@
3067 testHWMod();
3068 testLinkInitFail();
3069 testLinkFail();
3070+ testJLink();
3071 return testDone();
3072 }
3073
3074=== modified file 'src/ioc/db/test/dbPutLinkTest.db'
3075--- src/ioc/db/test/dbPutLinkTest.db 2014-07-31 20:22:02 +0000
3076+++ src/ioc/db/test/dbPutLinkTest.db 2017-03-03 18:23:23 +0000
3077@@ -3,6 +3,10 @@
3078 record(x, "x3") {}
3079 record(x, "x4") {}
3080
3081+record(x, "rJSON_LINK") {
3082+ field(DTYP, "Unit Test JSON_LINK")
3083+ field(INP, {x:true})
3084+}
3085 record(x, "rVME_IO") {
3086 field(DTYP, "Unit Test VME_IO")
3087 field(INP, "#C100 S101 @parm VME_IO")
3088
3089=== added file 'src/ioc/db/test/dbPutLinkTestJ.db'
3090--- src/ioc/db/test/dbPutLinkTestJ.db 1970-01-01 00:00:00 +0000
3091+++ src/ioc/db/test/dbPutLinkTestJ.db 2017-03-03 18:23:23 +0000
3092@@ -0,0 +1,12 @@
3093+
3094+record(x, "j1") {
3095+ field(INP, {z:{good:1}})
3096+}
3097+
3098+record(x, "j2") {
3099+ field(INP, {z:{good:2}})
3100+}
3101+
3102+record(x, "j3") {
3103+ field(INP, {z:{good:3}})
3104+}
3105
3106=== modified file 'src/ioc/db/test/devx.c'
3107--- src/ioc/db/test/devx.c 2015-03-13 19:24:06 +0000
3108+++ src/ioc/db/test/devx.c 2017-03-03 18:23:23 +0000
3109@@ -141,17 +141,13 @@
3110 /* basic DTYP="Soft Channel" */
3111 static long xsoft_init_record(xRecord *prec)
3112 {
3113- if(prec->inp.type==CONSTANT)
3114- recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
3115+ recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
3116 return 0;
3117 }
3118
3119 static long xsoft_read(xRecord *prec)
3120 {
3121- if(prec->inp.type==CONSTANT)
3122- return 0;
3123- dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
3124- return 0;
3125+ return dbGetLink(&prec->inp, DBR_LONG, &prec->val, NULL, NULL);
3126 }
3127
3128 static struct xdset devxSoft = {
3129
3130=== added file 'src/ioc/db/test/jlinkz.c'
3131--- src/ioc/db/test/jlinkz.c 1970-01-01 00:00:00 +0000
3132+++ src/ioc/db/test/jlinkz.c 2017-03-03 18:23:23 +0000
3133@@ -0,0 +1,250 @@
3134+/*************************************************************************\
3135+* Copyright (c) 2016 Michael Davidsaver
3136+* EPICS BASE is distributed subject to a Software License Agreement found
3137+* in file LICENSE that is included with this distribution.
3138+ \*************************************************************************/
3139+
3140+#include <stdlib.h>
3141+#include <string.h>
3142+
3143+#include <epicsExport.h>
3144+#include <dbAccess.h>
3145+#include <link.h>
3146+#include <alarm.h>
3147+#include <dbJLink.h>
3148+#include <dbDefs.h>
3149+#include <dbConvertFast.h>
3150+#include <epicsMutex.h>
3151+#include <epicsAtomic.h>
3152+#include <epicsUnitTest.h>
3153+
3154+#define epicsExportSharedSymbols
3155+
3156+#include "jlinkz.h"
3157+
3158+int numzalloc;
3159+
3160+
3161+static
3162+void z_open(struct link *plink)
3163+{
3164+ zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
3165+
3166+ if(priv->isopen)
3167+ testDiag("lsetZ re-open");
3168+ priv->isopen = 1;
3169+ testDiag("Open jlinkz %p", priv);
3170+}
3171+
3172+static
3173+void z_remove(struct dbLocker *locker, struct link *plink)
3174+{
3175+ zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
3176+
3177+ epicsMutexLock(priv->lock);
3178+
3179+ if(!priv->isopen)
3180+ testDiag("lsetZ remove without open");
3181+
3182+ epicsMutexUnlock(priv->lock);
3183+
3184+ testDiag("Remove/free jlinkz %p", priv);
3185+
3186+ epicsAtomicDecrIntT(&numzalloc);
3187+
3188+ epicsMutexDestroy(priv->lock);
3189+ free(priv);
3190+ plink->value.json.jlink = NULL; /* paranoia */
3191+}
3192+
3193+static
3194+int z_connected(const struct link *plink)
3195+{
3196+ return 1; /* TODO: not provided should be connected */
3197+}
3198+
3199+static
3200+int z_dbftype(const struct link *plink)
3201+{
3202+ return DBF_LONG;
3203+}
3204+
3205+static
3206+long z_elements(const struct link *plink, long *nelements)
3207+{
3208+ *nelements = 1;
3209+ return 0;
3210+}
3211+
3212+static
3213+long z_getval(struct link *plink, short dbrType, void *pbuffer,
3214+ long *pnRequest)
3215+{
3216+ long ret;
3217+ long (*pconv)(const epicsInt32 *, void *, const dbAddr *) = dbFastGetConvertRoutine[DBF_LONG][dbrType];
3218+ zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
3219+
3220+ if(pnRequest && *pnRequest==0) return 0;
3221+
3222+ epicsMutexLock(priv->lock);
3223+ ret = (*pconv)(&priv->value, pbuffer, NULL);
3224+ epicsMutexUnlock(priv->lock);
3225+ if(ret==0 && pnRequest) *pnRequest = 1;
3226+ return ret;
3227+}
3228+
3229+/* TODO: atomicly get value and alarm */
3230+static
3231+long z_getalarm(const struct link *plink, epicsEnum16 *status,
3232+ epicsEnum16 *severity)
3233+{
3234+ zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
3235+ epicsEnum16 sevr, stat;
3236+
3237+ epicsMutexLock(priv->lock);
3238+ sevr = priv->isset ? 0 : INVALID_ALARM;
3239+ stat = priv->isset ? 0 : LINK_ALARM;
3240+ epicsMutexUnlock(priv->lock);
3241+
3242+ if(status) *status = stat;
3243+ if(severity) *severity = sevr;
3244+ return 0;
3245+}
3246+
3247+static
3248+long z_putval(struct link *plink, short dbrType,
3249+ const void *pbuffer, long nRequest)
3250+{
3251+ long ret;
3252+ long (*pconv)(epicsInt32 *, const void *, const dbAddr *) = dbFastPutConvertRoutine[DBF_LONG][dbrType];
3253+ zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
3254+
3255+ if(nRequest==0) return 0;
3256+
3257+ epicsMutexLock(priv->lock);
3258+ ret = (*pconv)(&priv->value, pbuffer, NULL);
3259+ epicsMutexUnlock(priv->lock);
3260+ return ret;
3261+}
3262+
3263+static lset lsetZ = {
3264+ 0, 0, /* non-const, non-volatile */
3265+ &z_open,
3266+ &z_remove,
3267+ NULL, NULL, NULL, /* load */
3268+ &z_connected,
3269+ &z_dbftype,
3270+ &z_elements,
3271+ &z_getval,
3272+ NULL, /* control limits */
3273+ NULL, /* display limits */
3274+ NULL, /* alarm limits */
3275+ NULL, /* prec */
3276+ NULL, /* units */
3277+ &z_getalarm,
3278+ NULL, /* time */
3279+ &z_putval,
3280+ NULL, /* putasync */
3281+ NULL, /* forward */
3282+};
3283+
3284+static
3285+jlink* z_alloc(short dbfType)
3286+{
3287+ zpriv *priv;
3288+ priv = calloc(1, sizeof(*priv));
3289+ if(!priv) goto fail;
3290+
3291+ priv->lock = epicsMutexCreate();
3292+ if(!priv->lock) goto fail;
3293+
3294+ epicsAtomicIncrIntT(&numzalloc);
3295+
3296+ testDiag("Alloc jlinkz %p", priv);
3297+
3298+ return &priv->base;
3299+fail:
3300+ if(priv && priv->lock) epicsMutexDestroy(priv->lock);
3301+ free(priv);
3302+ return NULL;
3303+}
3304+
3305+static
3306+void z_free(jlink *pj)
3307+{
3308+ zpriv *priv = CONTAINER(pj, zpriv, base);
3309+
3310+ if(priv->isopen)
3311+ testDiag("lsetZ jlink free after open()");
3312+
3313+ testDiag("Free jlinkz %p", priv);
3314+
3315+ epicsAtomicDecrIntT(&numzalloc);
3316+
3317+ epicsMutexDestroy(priv->lock);
3318+ free(priv);
3319+}
3320+
3321+static
3322+jlif_result z_int(jlink *pj, long num)
3323+{
3324+ zpriv *priv = CONTAINER(pj, zpriv, base);
3325+
3326+ priv->value = num;
3327+ priv->isset = 1;
3328+
3329+ return jlif_continue;
3330+}
3331+
3332+static
3333+jlif_key_result z_start(jlink *pj)
3334+{
3335+ return jlif_key_continue;
3336+}
3337+
3338+static
3339+jlif_result z_key(jlink *pj, const char *key, size_t len)
3340+{
3341+ zpriv *priv = CONTAINER(pj, zpriv, base);
3342+
3343+ if(len==4 && strncmp(key,"fail", len)==0) {
3344+ testDiag("Found fail key jlinkz %p", priv);
3345+ return jlif_stop;
3346+ } else {
3347+ return jlif_continue;
3348+ }
3349+}
3350+
3351+static
3352+jlif_result z_end(jlink *pj)
3353+{
3354+ return jlif_continue;
3355+}
3356+
3357+static
3358+struct lset* z_lset(const jlink *pj)
3359+{
3360+ return &lsetZ;
3361+}
3362+
3363+static jlif jlifZ = {
3364+ "z",
3365+ &z_alloc,
3366+ &z_free,
3367+ NULL, /* null */
3368+ NULL, /* bool */
3369+ &z_int,
3370+ NULL, /* double */
3371+ NULL, /* string */
3372+ &z_start,
3373+ &z_key,
3374+ &z_end,
3375+ NULL, /* start array */
3376+ NULL, /* end array */
3377+ NULL, /* end child */
3378+ &z_lset,
3379+ NULL, /* report */
3380+ NULL /* map child */
3381+};
3382+
3383+epicsExportAddress(jlif, jlifZ);
3384
3385=== added file 'src/ioc/db/test/jlinkz.dbd'
3386--- src/ioc/db/test/jlinkz.dbd 1970-01-01 00:00:00 +0000
3387+++ src/ioc/db/test/jlinkz.dbd 2017-03-03 18:23:23 +0000
3388@@ -0,0 +1,1 @@
3389+link("z", "jlifZ")
3390
3391=== added file 'src/ioc/db/test/jlinkz.h'
3392--- src/ioc/db/test/jlinkz.h 1970-01-01 00:00:00 +0000
3393+++ src/ioc/db/test/jlinkz.h 2017-03-03 18:23:23 +0000
3394@@ -0,0 +1,27 @@
3395+/*************************************************************************\
3396+* Copyright (c) 2016 Michael Davidsaver
3397+* EPICS BASE is distributed subject to a Software License Agreement found
3398+* in file LICENSE that is included with this distribution.
3399+ \*************************************************************************/
3400+
3401+#ifndef JLINKZ_H
3402+#define JLINKZ_H
3403+
3404+#include <dbJLink.h>
3405+#include <epicsMutex.h>
3406+#include <epicsTypes.h>
3407+
3408+#include <shareLib.h>
3409+
3410+epicsShareExtern
3411+int numzalloc;
3412+
3413+typedef struct {
3414+ jlink base;
3415+ epicsMutexId lock;
3416+ unsigned isset:1;
3417+ unsigned isopen:1;
3418+ epicsInt32 value;
3419+} zpriv;
3420+
3421+#endif /* JLINKZ_H */
3422
3423=== added file 'src/ioc/db/test/xLink.c'
3424--- src/ioc/db/test/xLink.c 1970-01-01 00:00:00 +0000
3425+++ src/ioc/db/test/xLink.c 2017-03-03 18:23:23 +0000
3426@@ -0,0 +1,88 @@
3427+/*************************************************************************\
3428+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3429+* National Laboratory.
3430+* EPICS BASE is distributed subject to a Software License Agreement found
3431+* in file LICENSE that is included with this distribution.
3432+\*************************************************************************/
3433+/* xLink.c */
3434+
3435+#include "dbDefs.h"
3436+#include "dbLink.h"
3437+#include "dbJLink.h"
3438+#include "epicsExport.h"
3439+
3440+
3441+typedef struct xlink {
3442+ jlink jlink; /* embedded object */
3443+ /* ... */
3444+} xlink;
3445+
3446+static lset xlink_lset;
3447+
3448+static jlink* xlink_alloc(short dbfType)
3449+{
3450+ xlink *xlink = calloc(1, sizeof(struct xlink));
3451+
3452+ return &xlink->jlink;
3453+}
3454+
3455+static void xlink_free(jlink *pjlink)
3456+{
3457+ xlink *xlink = CONTAINER(pjlink, struct xlink, jlink);
3458+
3459+ free(xlink);
3460+}
3461+
3462+static jlif_result xlink_boolean(jlink *pjlink, int val)
3463+{
3464+ return val; /* False triggers a parse failure */
3465+}
3466+
3467+static struct lset* xlink_get_lset(const jlink *pjlink)
3468+{
3469+ return &xlink_lset;
3470+}
3471+
3472+
3473+static void xlink_remove(struct dbLocker *locker, struct link *plink)
3474+{
3475+ xlink_free(plink->value.json.jlink);
3476+}
3477+
3478+static long xlink_getNelements(const struct link *plink, long *nelements)
3479+{
3480+ *nelements = 0;
3481+ return 0;
3482+}
3483+
3484+static long xlink_getValue(struct link *plink, short dbrType, void *pbuffer,
3485+ long *pnRequest)
3486+{
3487+ if (pnRequest)
3488+ *pnRequest = 0;
3489+ return 0;
3490+}
3491+
3492+
3493+static lset xlink_lset = {
3494+ 1, 0, /* Constant, not Volatile */
3495+ NULL, xlink_remove,
3496+ NULL, NULL, NULL, NULL,
3497+ NULL, xlink_getNelements, xlink_getValue,
3498+ NULL, NULL, NULL,
3499+ NULL, NULL,
3500+ NULL, NULL,
3501+ NULL, NULL,
3502+ NULL
3503+};
3504+
3505+static jlif xlinkIf = {
3506+ "x", xlink_alloc, xlink_free,
3507+ NULL, xlink_boolean, NULL, NULL, NULL,
3508+ NULL, NULL, NULL,
3509+ NULL, NULL,
3510+ NULL, xlink_get_lset,
3511+ NULL, NULL
3512+};
3513+epicsExportAddress(jlif, xlinkIf);
3514+
3515
3516=== added file 'src/ioc/db/test/xLink.dbd'
3517--- src/ioc/db/test/xLink.dbd 1970-01-01 00:00:00 +0000
3518+++ src/ioc/db/test/xLink.dbd 2017-03-03 18:23:23 +0000
3519@@ -0,0 +1,1 @@
3520+link(x, xlinkIf)
3521
3522=== modified file 'src/ioc/dbStatic/dbBase.h'
3523--- src/ioc/dbStatic/dbBase.h 2017-02-01 17:57:04 +0000
3524+++ src/ioc/dbStatic/dbBase.h 2017-03-03 18:23:23 +0000
3525@@ -43,6 +43,13 @@
3526 struct dsxt *pdsxt; /* Extended device support */
3527 }devSup;
3528
3529+typedef struct linkSup {
3530+ ELLNODE node;
3531+ char *name;
3532+ char *jlif_name;
3533+ struct jlif *pjlif;
3534+} linkSup;
3535+
3536 typedef struct dbDeviceMenu {
3537 int nChoice;
3538 char **papChoice;
3539@@ -161,6 +168,7 @@
3540 ELLLIST menuList;
3541 ELLLIST recordTypeList;
3542 ELLLIST drvList;
3543+ ELLLIST linkList;
3544 ELLLIST registrarList;
3545 ELLLIST functionList;
3546 ELLLIST variableList;
3547
3548=== modified file 'src/ioc/dbStatic/dbLex.l'
3549--- src/ioc/dbStatic/dbLex.l 2015-10-13 17:03:32 +0000
3550+++ src/ioc/dbStatic/dbLex.l 2017-03-03 18:23:23 +0000
3551@@ -1,11 +1,12 @@
3552 /*************************************************************************\
3553-* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne
3554+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3555 * National Laboratory.
3556 * Copyright (c) 2002 The Regents of the University of California, as
3557 * Operator of Los Alamos National Laboratory.
3558 * EPICS BASE is distributed subject to a Software License Agreement found
3559 * in file LICENSE that is included with this distribution.
3560 \*************************************************************************/
3561+
3562 newline "\n"
3563 backslash "\\"
3564 doublequote "\""
3565@@ -15,6 +16,19 @@
3566 stringchar [^"\n\\]
3567 bareword [a-zA-Z0-9_\-+:.\[\]<>;]
3568
3569+punctuation [:,\[\]{}]
3570+normalchar [^"\\\0-\x1f]
3571+barechar [a-zA-Z0-9_\-+.]
3572+escapedchar ({backslash}["\\/bfnrt])
3573+hexdigit [0-9a-fA-F]
3574+unicodechar ({backslash}"u"{hexdigit}{4})
3575+jsonchar ({normalchar}|{escapedchar}|{unicodechar})
3576+jsondqstr ({doublequote}{jsonchar}*{doublequote})
3577+int ("-"?([0-9]|[1-9][0-9]+))
3578+frac ("."[0-9]+)
3579+exp ([eE][+-]?[0-9]+)
3580+number ({int}{frac}?{exp}?)
3581+
3582 %{
3583 #undef YY_INPUT
3584 #define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms))
3585@@ -27,6 +41,8 @@
3586
3587 %}
3588
3589+%x JSON
3590+
3591 %%
3592
3593 "include" return(tokenINCLUDE);
3594@@ -38,6 +54,7 @@
3595 "field" return(tokenFIELD);
3596 "device" return(tokenDEVICE);
3597 "driver" return(tokenDRIVER);
3598+"link" return(tokenLINK);
3599 "breaktable" return(tokenBREAKTABLE);
3600 "record" return(tokenRECORD);
3601 "grecord" return(tokenGRECORD);
3602@@ -48,18 +65,18 @@
3603 "variable" return(tokenVARIABLE);
3604
3605 {bareword}+ { /* unquoted string or number */
3606- yylval.Str = dbmfStrdup(yytext);
3607+ yylval.Str = dbmfStrdup((char *) yytext);
3608 return(tokenSTRING);
3609 }
3610
3611 {doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
3612- yylval.Str = dbmfStrdup(yytext+1);
3613+ yylval.Str = dbmfStrdup((char *) yytext+1);
3614 yylval.Str[strlen(yylval.Str)-1] = '\0';
3615 return(tokenSTRING);
3616 }
3617
3618 %.* { /*C definition in recordtype*/
3619- yylval.Str = dbmfStrdup(yytext+1);
3620+ yylval.Str = dbmfStrdup((char *) yytext+1);
3621 return(tokenCDEFS);
3622 }
3623
3624@@ -69,14 +86,36 @@
3625 ")" return(yytext[0]);
3626 "," return(yytext[0]);
3627
3628-{comment}.* ;
3629-{whitespace} ;
3630-
3631 {doublequote}({stringchar}|{escape})*{newline} { /* bad string */
3632 yyerrorAbort("Newline in string, closing quote missing");
3633 }
3634
3635-. {
3636+<JSON>"null" return jsonNULL;
3637+<JSON>"true" return jsonTRUE;
3638+<JSON>"false" return jsonFALSE;
3639+
3640+<JSON>{punctuation} return yytext[0];
3641+
3642+<JSON>{jsondqstr} {
3643+ yylval.Str = dbmfStrdup((char *) yytext);
3644+ return jsonSTRING;
3645+}
3646+
3647+<JSON>{number} {
3648+ yylval.Str = dbmfStrdup((char *) yytext);
3649+ return jsonNUMBER;
3650+}
3651+
3652+<JSON>{barechar}+ {
3653+ yylval.Str = dbmfStrdup((char *) yytext);
3654+ return jsonBARE;
3655+}
3656+
3657+<INITIAL,JSON>{comment}.* ;
3658+
3659+<INITIAL,JSON>{whitespace} ;
3660+
3661+<INITIAL,JSON>. {
3662 char message[40];
3663 YY_BUFFER_STATE *dummy=0;
3664
3665
3666=== modified file 'src/ioc/dbStatic/dbLexRoutines.c'
3667--- src/ioc/dbStatic/dbLexRoutines.c 2017-02-01 17:57:04 +0000
3668+++ src/ioc/dbStatic/dbLexRoutines.c 2017-03-03 18:23:23 +0000
3669@@ -77,6 +77,7 @@
3670 static void dbDevice(char *recordtype,char *linktype,
3671 char *dsetname,char *choicestring);
3672 static void dbDriver(char *name);
3673+static void dbLinkType(char *name, char *jlif_name);
3674 static void dbRegistrar(char *name);
3675 static void dbFunction(char *name);
3676 static void dbVariable(char *name, char *type);
3677@@ -815,6 +816,26 @@
3678 ellAdd(&pdbbase->drvList,&pdrvSup->node);
3679 }
3680
3681+static void dbLinkType(char *name, char *jlif_name)
3682+{
3683+ linkSup *pLinkSup;
3684+ GPHENTRY *pgphentry;
3685+
3686+ pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList);
3687+ if (pgphentry) {
3688+ return;
3689+ }
3690+ pLinkSup = dbCalloc(1,sizeof(linkSup));
3691+ pLinkSup->name = epicsStrDup(name);
3692+ pLinkSup->jlif_name = epicsStrDup(jlif_name);
3693+ pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
3694+ if (!pgphentry) {
3695+ yyerrorAbort("gphAdd failed");
3696+ }
3697+ pgphentry->userPvt = pLinkSup;
3698+ ellAdd(&pdbbase->linkList, &pLinkSup->node);
3699+}
3700+
3701 static void dbRegistrar(char *name)
3702 {
3703 dbText *ptext;
3704@@ -1057,7 +1078,12 @@
3705 yyerror(NULL);
3706 return;
3707 }
3708- dbTranslateEscape(value, value); /* yuck: in-place, but safe */
3709+ if (*value == '"') {
3710+ /* jsonSTRING values still have their quotes */
3711+ value++;
3712+ value[strlen(value) - 1] = 0;
3713+ }
3714+ dbTranslateEscape(value, value); /* in-place; safe & legal */
3715 status = dbPutString(pdbentry,value);
3716 if(status) {
3717 epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",
3718@@ -1076,6 +1102,12 @@
3719 if(duplicate) return;
3720 ptempListNode = (tempListNode *)ellFirst(&tempList);
3721 pdbentry = ptempListNode->item;
3722+ if (*value == '"') {
3723+ /* jsonSTRING values still have their quotes */
3724+ value++;
3725+ value[strlen(value) - 1] = 0;
3726+ }
3727+ dbTranslateEscape(value, value); /* yuck: in-place, but safe */
3728 status = dbPutInfo(pdbentry,name,value);
3729 if(status) {
3730 epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
3731
3732=== modified file 'src/ioc/dbStatic/dbStaticIocRegister.c'
3733--- src/ioc/dbStatic/dbStaticIocRegister.c 2014-10-06 05:57:02 +0000
3734+++ src/ioc/dbStatic/dbStaticIocRegister.c 2017-03-03 18:23:23 +0000
3735@@ -86,6 +86,14 @@
3736 dbDumpDriver(*iocshPpdbbase);
3737 }
3738
3739+/* dbDumpLink */
3740+static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase};
3741+static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs};
3742+static void dbDumpLinkCallFunc(const iocshArgBuf *args)
3743+{
3744+ dbDumpLink(*iocshPpdbbase);
3745+}
3746+
3747 /* dbDumpRegistrar */
3748 static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
3749 static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs};
3750@@ -160,6 +168,7 @@
3751 iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc);
3752 iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc);
3753 iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc);
3754+ iocshRegister(&dbDumpLinkFuncDef, dbDumpLinkCallFunc);
3755 iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc);
3756 iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc);
3757 iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc);
3758
3759=== modified file 'src/ioc/dbStatic/dbStaticLib.c'
3760--- src/ioc/dbStatic/dbStaticLib.c 2017-02-01 17:57:04 +0000
3761+++ src/ioc/dbStatic/dbStaticLib.c 2017-03-03 18:23:23 +0000
3762@@ -43,6 +43,7 @@
3763 #include "special.h"
3764
3765 #include "dbCommon.h"
3766+#include "dbJLink.h"
3767
3768 int dbStaticDebug = 0;
3769 static char *pNullString = "";
3770@@ -64,6 +65,7 @@
3771 {"GPIB_IO",GPIB_IO},
3772 {"BITBUS_IO",BITBUS_IO},
3773 {"MACRO_LINK",MACRO_LINK},
3774+ {"JSON_LINK",JSON_LINK},
3775 {"PN_LINK",PN_LINK},
3776 {"DB_LINK",DB_LINK},
3777 {"CA_LINK",CA_LINK},
3778@@ -118,6 +120,10 @@
3779 case CONSTANT: free((void *)plink->value.constantStr); break;
3780 case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;
3781 case PV_LINK: free((void *)plink->value.pv_link.pvname); break;
3782+ case JSON_LINK:
3783+ dbJLinkFree(plink->value.json.jlink);
3784+ parm = plink->value.json.string;
3785+ break;
3786 case VME_IO: parm = plink->value.vmeio.parm; break;
3787 case CAMAC_IO: parm = plink->value.camacio.parm; break;
3788 case AB_IO: parm = plink->value.abio.parm; break;
3789@@ -454,6 +460,7 @@
3790 dbVariableDef *pvarNext;
3791 drvSup *pdrvSup;
3792 drvSup *pdrvSupNext;
3793+ linkSup *plinkSup;
3794 brkTable *pbrkTable;
3795 brkTable *pbrkTableNext;
3796 chFilterPlugin *pfilt;
3797@@ -556,6 +563,11 @@
3798 free((void *)pdrvSup);
3799 pdrvSup = pdrvSupNext;
3800 }
3801+ while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) {
3802+ free(plinkSup->jlif_name);
3803+ free(plinkSup->name);
3804+ free(plinkSup);
3805+ }
3806 ptext = (dbText *)ellFirst(&pdbbase->registrarList);
3807 while(ptext) {
3808 ptextNext = (dbText *)ellNext(&ptext->node);
3809@@ -1097,6 +1109,21 @@
3810 return(0);
3811 }
3812
3813+long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp)
3814+{
3815+ linkSup *plinkSup;
3816+
3817+ if (!pdbbase) {
3818+ fprintf(stderr, "pdbbase not specified\n");
3819+ return -1;
3820+ }
3821+ for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList);
3822+ plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) {
3823+ fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name);
3824+ }
3825+ return 0;
3826+}
3827+
3828 long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp)
3829 {
3830 dbText *ptext;
3831@@ -1877,6 +1904,9 @@
3832 dbMsgCpy(pdbentry, "");
3833 }
3834 break;
3835+ case JSON_LINK:
3836+ dbMsgCpy(pdbentry, plink->value.json.string);
3837+ break;
3838 case PN_LINK:
3839 dbMsgPrint(pdbentry, "%s%s",
3840 plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
3841@@ -1972,6 +2002,9 @@
3842 dbMsgCpy(pdbentry, "");
3843 }
3844 break;
3845+ case JSON_LINK:
3846+ dbMsgCpy(pdbentry, plink->value.json.string);
3847+ break;
3848 case PV_LINK:
3849 case CA_LINK:
3850 case DB_LINK: {
3851@@ -2128,6 +2161,7 @@
3852 */
3853 case CONSTANT: plink->value.constantStr = NULL; break;
3854 case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break;
3855+ case JSON_LINK: plink->value.json.string = pNullString; break;
3856 case VME_IO: plink->value.vmeio.parm = pNullString; break;
3857 case CAMAC_IO: plink->value.camacio.parm = pNullString; break;
3858 case AB_IO: plink->value.abio.parm = pNullString; break;
3859@@ -2160,6 +2194,16 @@
3860 return 0;
3861 }
3862
3863+void dbFreeLinkInfo(dbLinkInfo *pinfo)
3864+{
3865+ if (pinfo->ltype == JSON_LINK) {
3866+ dbJLinkFree(pinfo->jlink);
3867+ pinfo->jlink = NULL;
3868+ }
3869+ free(pinfo->target);
3870+ pinfo->target = NULL;
3871+}
3872+
3873 long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
3874 {
3875 char *pstr;
3876@@ -2194,6 +2238,15 @@
3877 memcpy(pstr, str, len);
3878 pstr[len] = '\0';
3879
3880+ /* Check for braces => JSON */
3881+ if (*str == '{' && str[len-1] == '}') {
3882+ if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
3883+ goto fail;
3884+
3885+ pinfo->ltype = JSON_LINK;
3886+ return 0;
3887+ }
3888+
3889 /* Check for other HW link types */
3890 if (*pstr == '#') {
3891 int ret;
3892@@ -2241,12 +2294,9 @@
3893 /* RF_IO, the string isn't needed at all */
3894 free(pinfo->target);
3895 pinfo->target = NULL;
3896- } else {
3897- /* missing parm when required, or found parm when not expected */
3898- free(pinfo->target);
3899- pinfo->target = NULL;
3900- return S_dbLib_badField;
3901 }
3902+ else goto fail;
3903+
3904 return 0;
3905 }
3906
3907@@ -2256,6 +2306,13 @@
3908 return 0;
3909 }
3910
3911+ /* Link may be an array constant */
3912+ if (pstr[0] == '[' && pstr[len-1] == ']' &&
3913+ (strchr(pstr, ',') || strchr(pstr, '"'))) {
3914+ pinfo->ltype = CONSTANT;
3915+ return 0;
3916+ }
3917+
3918 pinfo->ltype = PV_LINK;
3919 pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
3920 if (pstr) {
3921@@ -2288,27 +2345,28 @@
3922
3923 return 0;
3924 fail:
3925- free(pinfo->target);
3926+ dbFreeLinkInfo(pinfo);
3927 return S_dbLib_badField;
3928 }
3929
3930 long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
3931 {
3932- /* consume allocated string pinfo->target on failure */
3933+ /* Release pinfo resources on failure */
3934+ int expected_type = devsup ? devsup->link_type : CONSTANT;
3935
3936- int link_type = CONSTANT;
3937- if(devsup)
3938- link_type = devsup->link_type;
3939- if(link_type==pinfo->ltype)
3940+ if (pinfo->ltype == expected_type)
3941 return 0;
3942- switch(pinfo->ltype) {
3943+
3944+ switch (pinfo->ltype) {
3945 case CONSTANT:
3946+ case JSON_LINK:
3947 case PV_LINK:
3948- if(link_type==CONSTANT || link_type==PV_LINK)
3949+ if (expected_type == CONSTANT ||
3950+ expected_type == JSON_LINK ||
3951+ expected_type == PV_LINK)
3952 return 0;
3953 default:
3954- free(pinfo->target);
3955- pinfo->target = NULL;
3956+ dbFreeLinkInfo(pinfo);
3957 return 1;
3958 }
3959 }
3960@@ -2333,10 +2391,23 @@
3961 }
3962
3963 static
3964+void dbSetLinkJSON(DBLINK *plink, dbLinkInfo *pinfo)
3965+{
3966+ plink->type = JSON_LINK;
3967+ plink->value.json.string = pinfo->target;
3968+ plink->value.json.jlink = pinfo->jlink;
3969+
3970+ pinfo->target = NULL;
3971+ pinfo->jlink = NULL;
3972+}
3973+
3974+static
3975 void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
3976 {
3977-
3978 switch(pinfo->ltype) {
3979+ case JSON_LINK:
3980+ plink->value.json.string = pinfo->target;
3981+ break;
3982 case INST_IO:
3983 plink->value.instio.string = pinfo->target;
3984 break;
3985@@ -2412,36 +2483,39 @@
3986
3987 long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
3988 {
3989- int ret = 0;
3990- int link_type = CONSTANT;
3991-
3992- if(devsup)
3993- link_type = devsup->link_type;
3994-
3995- if(link_type==CONSTANT || link_type==PV_LINK) {
3996- switch(pinfo->ltype) {
3997+ int expected_type = devsup ? devsup->link_type : CONSTANT;
3998+
3999+ if (expected_type == CONSTANT ||
4000+ expected_type == JSON_LINK ||
4001+ expected_type == PV_LINK) {
4002+ switch (pinfo->ltype) {
4003 case CONSTANT:
4004 dbFreeLinkContents(plink);
4005- dbSetLinkConst(plink, pinfo); break;
4006+ dbSetLinkConst(plink, pinfo);
4007+ break;
4008 case PV_LINK:
4009 dbFreeLinkContents(plink);
4010- dbSetLinkPV(plink, pinfo); break;
4011+ dbSetLinkPV(plink, pinfo);
4012+ break;
4013+ case JSON_LINK:
4014+ dbFreeLinkContents(plink);
4015+ dbSetLinkJSON(plink, pinfo);
4016+ break;
4017 default:
4018 errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error");
4019 goto fail; /* can't assign HW link */
4020 }
4021-
4022- } else if(link_type==pinfo->ltype) {
4023+ }
4024+ else if (expected_type == pinfo->ltype) {
4025 dbFreeLinkContents(plink);
4026 dbSetLinkHW(plink, pinfo);
4027-
4028- } else
4029+ }
4030+ else
4031 goto fail;
4032
4033- return ret;
4034+ return 0;
4035 fail:
4036- free(pinfo->target);
4037- pinfo->target = NULL;
4038+ dbFreeLinkInfo(pinfo);
4039 return S_dbLib_badField;
4040 }
4041
4042@@ -2507,7 +2581,7 @@
4043 /* links not yet initialized by dbInitRecordLinks() */
4044 free(plink->text);
4045 plink->text = epicsStrDup(pstring);
4046- free(link_info.target);
4047+ dbFreeLinkInfo(&link_info);
4048 } else {
4049 /* assignment after init (eg. autosave restore) */
4050 struct dbCommon *prec = pdbentry->precnode->precord;
4051@@ -3035,6 +3109,12 @@
4052 return(rtnval);
4053 }
4054
4055+linkSup* dbFindLinkSup(dbBase *pdbbase, const char *name) {
4056+ GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->linkList);
4057+ if (!pgph) return NULL;
4058+ return (linkSup *) pgph->userPvt;
4059+}
4060+
4061 int dbGetNLinks(DBENTRY *pdbentry)
4062 {
4063 dbRecordType *precordType = pdbentry->precordType;
4064@@ -3087,67 +3167,6 @@
4065 return(-1);
4066 }
4067
4068-long dbCvtLinkToConstant(DBENTRY *pdbentry)
4069-{
4070- dbFldDes *pflddes;
4071- DBLINK *plink;
4072-
4073- dbGetFieldAddress(pdbentry);
4074- pflddes = pdbentry->pflddes;
4075- if(!pflddes) return(-1);
4076- plink = (DBLINK *)pdbentry->pfield;
4077- if(!plink) return(-1);
4078- switch (pflddes->field_type) {
4079- case DBF_INLINK:
4080- case DBF_OUTLINK:
4081- case DBF_FWDLINK:
4082- if(plink->type == CONSTANT) return(0);
4083- if(plink->type != PV_LINK) return(S_dbLib_badLink);
4084- free((void *)plink->value.pv_link.pvname);
4085- plink->value.pv_link.pvname = NULL;
4086- plink->type = CONSTANT;
4087- if(pflddes->initial) {
4088- plink->value.constantStr =
4089- dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
4090- strcpy(plink->value.constantStr,pflddes->initial);
4091- } else {
4092- plink->value.constantStr = NULL;
4093- }
4094- return(0);
4095- default:
4096- epicsPrintf("dbCvtLinkToConstant called for non link field\n");
4097- }
4098- return(S_dbLib_badLink);
4099-}
4100-
4101-long dbCvtLinkToPvlink(DBENTRY *pdbentry)
4102-{
4103- dbFldDes *pflddes;
4104- DBLINK *plink;
4105-
4106- dbGetFieldAddress(pdbentry);
4107- pflddes = pdbentry->pflddes;
4108- if(!pflddes) return(-1);
4109- if(!pdbentry->precnode || !pdbentry->precnode->precord) return(-1);
4110- plink = (DBLINK *)pdbentry->pfield;
4111- if(!plink) return(-1);
4112- switch (pflddes->field_type) {
4113- case DBF_INLINK:
4114- case DBF_OUTLINK:
4115- case DBF_FWDLINK:
4116- if(plink->type == PV_LINK) return(0);
4117- if(plink->type != CONSTANT) return(S_dbLib_badLink);
4118- free(plink->value.constantStr);
4119- plink->type = PV_LINK;
4120- plink->value.pv_link.pvlMask = 0;
4121- plink->value.pv_link.pvname = 0;
4122- return(0);
4123- default:
4124- epicsPrintf("dbCvtLinkToPvlink called for non link field\n");
4125- }
4126- return(S_dbLib_badLink);
4127-}
4128-
4129 void dbDumpPath(DBBASE *pdbbase)
4130 {
4131 ELLLIST *ppathList;
4132@@ -3383,6 +3402,15 @@
4133 dbWriteDriverFP(pdbbase,stdout);
4134 }
4135
4136+void dbDumpLink(DBBASE *pdbbase)
4137+{
4138+ if(!pdbbase) {
4139+ fprintf(stderr,"pdbbase not specified\n");
4140+ return;
4141+ }
4142+ dbWriteLinkFP(pdbbase,stdout);
4143+}
4144+
4145 void dbDumpRegistrar(DBBASE *pdbbase)
4146 {
4147 if(!pdbbase) {
4148
4149=== modified file 'src/ioc/dbStatic/dbStaticLib.h'
4150--- src/ioc/dbStatic/dbStaticLib.h 2016-10-06 11:31:40 +0000
4151+++ src/ioc/dbStatic/dbStaticLib.h 2017-03-03 18:23:23 +0000
4152@@ -102,6 +102,7 @@
4153 epicsShareFunc long dbWriteDriver(DBBASE *pdbbase,
4154 const char *filename);
4155 epicsShareFunc long dbWriteDriverFP(DBBASE *pdbbase, FILE *fp);
4156+epicsShareFunc long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp);
4157 epicsShareFunc long dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp);
4158 epicsShareFunc long dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp);
4159 epicsShareFunc long dbWriteVariableFP(DBBASE *pdbbase, FILE *fp);
4160@@ -209,11 +210,12 @@
4161 const char *name);
4162 epicsShareFunc char * dbGetRelatedField(DBENTRY *pdbentry);
4163
4164+epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
4165+ const char *name);
4166+
4167 epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
4168 epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
4169 epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
4170-epicsShareFunc long dbCvtLinkToConstant(DBENTRY *pdbentry);
4171-epicsShareFunc long dbCvtLinkToPvlink(DBENTRY *pdbentry);
4172
4173 /* Dump routines */
4174 epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
4175@@ -228,6 +230,7 @@
4176 epicsShareFunc void dbDumpDevice(DBBASE *pdbbase,
4177 const char *recordTypeName);
4178 epicsShareFunc void dbDumpDriver(DBBASE *pdbbase);
4179+epicsShareFunc void dbDumpLink(DBBASE *pdbbase);
4180 epicsShareFunc void dbDumpRegistrar(DBBASE *pdbbase);
4181 epicsShareFunc void dbDumpFunction(DBBASE *pdbbase);
4182 epicsShareFunc void dbDumpVariable(DBBASE *pdbbase);
4183
4184=== modified file 'src/ioc/dbStatic/dbStaticPvt.h'
4185--- src/ioc/dbStatic/dbStaticPvt.h 2017-02-01 17:57:04 +0000
4186+++ src/ioc/dbStatic/dbStaticPvt.h 2017-03-03 18:23:23 +0000
4187@@ -36,19 +36,25 @@
4188 char *dbGetStringNum(DBENTRY *pdbentry);
4189 long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
4190
4191+struct jlink;
4192+
4193 typedef struct dbLinkInfo {
4194 short ltype;
4195
4196 /* full link string for CONSTANT and PV_LINK,
4197- * parm string for HW links*/
4198+ * parm string for HW links, JSON for JSON_LINK
4199+ */
4200 char *target;
4201
4202 /* for PV_LINK */
4203 short modifiers;
4204
4205- /* HW links */
4206+ /* for HW links */
4207 char hwid[6]; /* one extra element for a nil */
4208 int hwnums[5];
4209+
4210+ /* for JSON_LINK */
4211+ struct jlink *jlink;
4212 } dbLinkInfo;
4213
4214 long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
4215@@ -68,6 +74,8 @@
4216 * Unconditionally takes ownership of pinfo->target
4217 */
4218 long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);
4219+/* Free dbLinkInfo storage */
4220+epicsShareFunc void dbFreeLinkInfo(dbLinkInfo *pinfo);
4221
4222 /* The following is for path */
4223 typedef struct dbPathNode {
4224
4225=== modified file 'src/ioc/dbStatic/dbYacc.y'
4226--- src/ioc/dbStatic/dbYacc.y 2017-02-01 17:57:04 +0000
4227+++ src/ioc/dbStatic/dbYacc.y 2017-03-03 18:23:23 +0000
4228@@ -17,17 +17,22 @@
4229
4230 %start database
4231
4232+%union
4233+{
4234+ char *Str;
4235+}
4236+
4237 %token tokenINCLUDE tokenPATH tokenADDPATH
4238 %token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE
4239 %token tokenFIELD tokenINFO tokenREGISTRAR
4240-%token tokenDEVICE tokenDRIVER tokenBREAKTABLE
4241+%token tokenDEVICE tokenDRIVER tokenLINK tokenBREAKTABLE
4242 %token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION
4243 %token <Str> tokenSTRING tokenCDEFS
4244
4245-%union
4246-{
4247- char *Str;
4248-}
4249+%token jsonNULL jsonTRUE jsonFALSE
4250+%token <Str> jsonNUMBER jsonSTRING jsonBARE
4251+%type <Str> json_value json_object json_array
4252+%type <Str> json_members json_pair json_elements json_string
4253
4254 %%
4255
4256@@ -46,6 +51,7 @@
4257 | tokenRECORDTYPE recordtype_head recordtype_body
4258 | device
4259 | driver
4260+ | link
4261 | registrar
4262 | function
4263 | variable
4264@@ -162,6 +168,13 @@
4265 dbDriver($3); dbmfFree($3);
4266 };
4267
4268+link: tokenLINK '(' tokenSTRING ',' tokenSTRING ')'
4269+{
4270+ if(dbStaticDebug>2) printf("link %s %s\n",$3,$5);
4271+ dbLinkType($3,$5);
4272+ dbmfFree($3); dbmfFree($5);
4273+};
4274+
4275 registrar: tokenREGISTRAR '(' tokenSTRING ')'
4276 {
4277 if(dbStaticDebug>2) printf("registrar %s\n",$3);
4278@@ -239,15 +252,17 @@
4279 record_field_list: record_field_list record_field
4280 | record_field;
4281
4282-record_field: tokenFIELD '(' tokenSTRING ',' tokenSTRING ')'
4283+record_field: tokenFIELD '(' tokenSTRING ','
4284+ { BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
4285 {
4286- if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$5);
4287- dbRecordField($3,$5); dbmfFree($3); dbmfFree($5);
4288+ if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$6);
4289+ dbRecordField($3,$6); dbmfFree($3); dbmfFree($6);
4290 }
4291- | tokenINFO '(' tokenSTRING ',' tokenSTRING ')'
4292+ | tokenINFO '(' tokenSTRING ','
4293+ { BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
4294 {
4295- if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$5);
4296- dbRecordInfo($3,$5); dbmfFree($3); dbmfFree($5);
4297+ if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$6);
4298+ dbRecordInfo($3,$6); dbmfFree($3); dbmfFree($6);
4299 }
4300 | tokenALIAS '(' tokenSTRING ')'
4301 {
4302@@ -262,6 +277,69 @@
4303 dbAlias($3,$5); dbmfFree($3); dbmfFree($5);
4304 };
4305
4306+json_object: '{' '}'
4307+{
4308+ $$ = dbmfStrdup("{}");
4309+ if (dbStaticDebug>2) printf("json %s\n", $$);
4310+}
4311+ | '{' json_members '}'
4312+{
4313+ $$ = dbmfStrcat3("{", $2, "}");
4314+ dbmfFree($2);
4315+ if (dbStaticDebug>2) printf("json %s\n", $$);
4316+};
4317+
4318+json_members: json_pair
4319+ | json_pair ',' json_members
4320+{
4321+ $$ = dbmfStrcat3($1, ",", $3);
4322+ dbmfFree($1); dbmfFree($3);
4323+ if (dbStaticDebug>2) printf("json %s\n", $$);
4324+};
4325+
4326+json_pair: json_string ':' json_value
4327+{
4328+ $$ = dbmfStrcat3($1, ":", $3);
4329+ dbmfFree($1); dbmfFree($3);
4330+ if (dbStaticDebug>2) printf("json %s\n", $$);
4331+};
4332+
4333+json_string: jsonSTRING
4334+ | jsonBARE
4335+{
4336+ $$ = dbmfStrcat3("\"", $1, "\"");
4337+ dbmfFree($1);
4338+ if (dbStaticDebug>2) printf("json %s\n", $$);
4339+};
4340+
4341+json_array: '[' ']'
4342+{
4343+ $$ = dbmfStrdup("[]");
4344+ if (dbStaticDebug>2) printf("json %s\n", $$);
4345+}
4346+ | '[' json_elements ']'
4347+{
4348+ $$ = dbmfStrcat3("[", $2, "]");
4349+ dbmfFree($2);
4350+ if (dbStaticDebug>2) printf("json %s\n", $$);
4351+};
4352+
4353+json_elements: json_value
4354+ | json_value ',' json_elements
4355+{
4356+ $$ = dbmfStrcat3($1, ",", $3);
4357+ dbmfFree($1); dbmfFree($3);
4358+ if (dbStaticDebug>2) printf("json %s\n", $$);
4359+};
4360+
4361+json_value: jsonNULL { $$ = dbmfStrdup("null"); }
4362+ | jsonTRUE { $$ = dbmfStrdup("true"); }
4363+ | jsonFALSE { $$ = dbmfStrdup("false"); }
4364+ | jsonNUMBER
4365+ | json_string
4366+ | json_array
4367+ | json_object ;
4368+
4369
4370 %%
4371
4372
4373
4374=== modified file 'src/ioc/dbStatic/link.h'
4375--- src/ioc/dbStatic/link.h 2017-02-01 17:57:04 +0000
4376+++ src/ioc/dbStatic/link.h 2017-03-03 18:23:23 +0000
4377@@ -32,7 +32,7 @@
4378 #define GPIB_IO 5
4379 #define BITBUS_IO 6
4380 #define MACRO_LINK 7
4381-
4382+#define JSON_LINK 8
4383 #define PN_LINK 9
4384 #define DB_LINK 10
4385 #define CA_LINK 11
4386@@ -40,7 +40,7 @@
4387 #define BBGPIB_IO 13 /* bitbus -> gpib */
4388 #define RF_IO 14
4389 #define VXI_IO 15
4390-#define LINK_NTYPES 15
4391+#define LINK_NTYPES 16
4392 typedef struct maplinkType {
4393 char *strvalue;
4394 int value;
4395@@ -86,6 +86,12 @@
4396 short lastGetdbrType; /* last dbrType for DB or CA get */
4397 };
4398
4399+struct jlink;
4400+struct json_link {
4401+ char *string;
4402+ struct jlink *jlink;
4403+};
4404+
4405 /* structure of a VME io channel */
4406 struct vmeio {
4407 short card;
4408@@ -166,6 +172,7 @@
4409 union value {
4410 char *constantStr; /*constant string*/
4411 struct macro_link macro_link; /* link containing macro substitution*/
4412+ struct json_link json; /* JSON-encoded link */
4413 struct pv_link pv_link; /* link to process variable*/
4414 struct vmeio vmeio; /* vme io point */
4415 struct camacio camacio; /* camac io point */
4416
4417=== modified file 'src/ioc/dbtemplate/dbLoadTemplate_lex.l'
4418--- src/ioc/dbtemplate/dbLoadTemplate_lex.l 2010-08-20 22:59:38 +0000
4419+++ src/ioc/dbtemplate/dbLoadTemplate_lex.l 2017-03-03 18:23:23 +0000
4420@@ -24,13 +24,13 @@
4421
4422 {doublequote}({dstringchar}|{escape})*{doublequote} |
4423 {singlequote}({sstringchar}|{escape})*{singlequote} {
4424- yylval.Str = dbmfStrdup(yytext+1);
4425+ yylval.Str = dbmfStrdup((char *) yytext+1);
4426 yylval.Str[strlen(yylval.Str)-1] = '\0';
4427 return(QUOTE);
4428 }
4429
4430 {bareword}+ {
4431- yylval.Str = dbmfStrdup(yytext);
4432+ yylval.Str = dbmfStrdup((char *) yytext);
4433 return(WORD);
4434 }
4435
4436
4437=== modified file 'src/ioc/misc/iocInit.c'
4438--- src/ioc/misc/iocInit.c 2017-02-01 17:57:04 +0000
4439+++ src/ioc/misc/iocInit.c 2017-03-03 18:23:23 +0000
4440@@ -67,6 +67,7 @@
4441 #include "recSup.h"
4442 #include "registryDeviceSupport.h"
4443 #include "registryDriverSupport.h"
4444+#include "registryJLinks.h"
4445 #include "registryRecordType.h"
4446 #include "rsrv.h"
4447
4448@@ -655,18 +656,14 @@
4449 pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
4450 DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
4451
4452- if (plink->type == CA_LINK) {
4453+ if (plink->type == CA_LINK ||
4454+ plink->type == JSON_LINK ||
4455+ (plink->type == DB_LINK && iocBuildMode == buildIsolated)) {
4456 if (!locked) {
4457 dbScanLock(precord);
4458 locked = 1;
4459 }
4460- dbCaRemoveLink(NULL, plink);
4461-
4462- } else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) {
4463- /* free link, but don't split lockset like dbDbRemoveLink() */
4464- free(plink->value.pv_link.pvt);
4465- plink->type = PV_LINK;
4466- plink->lset = NULL;
4467+ dbRemoveLink(NULL, plink);
4468 }
4469 }
4470
4471
4472=== modified file 'src/ioc/registry/Makefile'
4473--- src/ioc/registry/Makefile 2012-08-10 16:40:50 +0000
4474+++ src/ioc/registry/Makefile 2017-03-03 18:23:23 +0000
4475@@ -14,6 +14,7 @@
4476 INC += registryRecordType.h
4477 INC += registryDeviceSupport.h
4478 INC += registryDriverSupport.h
4479+INC += registryJLinks.h
4480 INC += registryFunction.h
4481 INC += registryCommon.h
4482 INC += registryIocRegister.h
4483@@ -21,6 +22,7 @@
4484 dbCore_SRCS += registryRecordType.c
4485 dbCore_SRCS += registryDeviceSupport.c
4486 dbCore_SRCS += registryDriverSupport.c
4487+dbCore_SRCS += registryJLinks.c
4488 dbCore_SRCS += registryFunction.c
4489 dbCore_SRCS += registryCommon.c
4490 dbCore_SRCS += registryIocRegister.c
4491
4492=== modified file 'src/ioc/registry/registryCommon.c'
4493--- src/ioc/registry/registryCommon.c 2014-10-06 05:57:02 +0000
4494+++ src/ioc/registry/registryCommon.c 2017-03-03 18:23:23 +0000
4495@@ -19,6 +19,7 @@
4496 #include "registryCommon.h"
4497 #include "registryDeviceSupport.h"
4498 #include "registryDriverSupport.h"
4499+#include "registryJLinks.h"
4500
4501
4502 void registerRecordTypes(DBBASE *pbase, int nRecordTypes,
4503@@ -76,3 +77,15 @@
4504 }
4505 }
4506
4507+void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl)
4508+{
4509+ int i;
4510+ for (i = 0; i < nLinks; i++) {
4511+ if (!registryJLinkAdd(pbase, jlifsl[i])) {
4512+ errlogPrintf("registryJLinkAdd failed %s\n",
4513+ jlifsl[i]->name);
4514+ continue;
4515+ }
4516+ }
4517+}
4518+
4519
4520=== modified file 'src/ioc/registry/registryCommon.h'
4521--- src/ioc/registry/registryCommon.h 2014-10-06 05:57:02 +0000
4522+++ src/ioc/registry/registryCommon.h 2017-03-03 18:23:23 +0000
4523@@ -12,6 +12,7 @@
4524
4525 #include "dbStaticLib.h"
4526 #include "devSup.h"
4527+#include "dbJLink.h"
4528 #include "registryRecordType.h"
4529 #include "shareLib.h"
4530
4531@@ -28,6 +29,8 @@
4532 epicsShareFunc void registerDrivers(
4533 DBBASE *pbase, int nDrivers,
4534 const char * const *driverSupportNames, struct drvet * const *drvsl);
4535+epicsShareFunc void registerJLinks(
4536+ DBBASE *pbase, int nDrivers, jlif * const *jlifsl);
4537
4538 #ifdef __cplusplus
4539 }
4540
4541=== added file 'src/ioc/registry/registryJLinks.c'
4542--- src/ioc/registry/registryJLinks.c 1970-01-01 00:00:00 +0000
4543+++ src/ioc/registry/registryJLinks.c 2017-03-03 18:23:23 +0000
4544@@ -0,0 +1,23 @@
4545+/*************************************************************************\
4546+* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
4547+* National Laboratory.
4548+* EPICS BASE is distributed subject to a Software License Agreement found
4549+* in file LICENSE that is included with this distribution.
4550+\*************************************************************************/
4551+/* registryJLinks.c */
4552+
4553+#include "registry.h"
4554+#define epicsExportSharedSymbols
4555+#include "dbBase.h"
4556+#include "dbStaticLib.h"
4557+#include "registryJLinks.h"
4558+#include "dbJLink.h"
4559+
4560+epicsShareFunc int registryJLinkAdd(DBBASE *pbase, struct jlif *pjlif)
4561+{
4562+ linkSup *plinkSup = dbFindLinkSup(pbase, pjlif->name);
4563+
4564+ if (plinkSup)
4565+ plinkSup->pjlif = pjlif;
4566+ return !!plinkSup;
4567+}
4568
4569=== added file 'src/ioc/registry/registryJLinks.h'
4570--- src/ioc/registry/registryJLinks.h 1970-01-01 00:00:00 +0000
4571+++ src/ioc/registry/registryJLinks.h 2017-03-03 18:23:23 +0000
4572@@ -0,0 +1,28 @@
4573+/*************************************************************************\
4574+* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
4575+* National Laboratory.
4576+* Copyright (c) 2002 The Regents of the University of California, as
4577+* Operator of Los Alamos National Laboratory.
4578+* EPICS BASE is distributed subject to a Software License Agreement found
4579+* in file LICENSE that is included with this distribution.
4580+\*************************************************************************/
4581+
4582+#ifndef INC_registryJLinks_H
4583+#define INC_registryJLinks_H
4584+
4585+#include "dbBase.h"
4586+#include "dbJLink.h"
4587+#include "shareLib.h"
4588+
4589+#ifdef __cplusplus
4590+extern "C" {
4591+#endif
4592+
4593+epicsShareFunc int registryJLinkAdd(DBBASE *pbase, jlif *pjlif);
4594+
4595+#ifdef __cplusplus
4596+}
4597+#endif
4598+
4599+
4600+#endif /* INC_registryDriverSupport_H */
4601
4602=== modified file 'src/libCom/dbmf/dbmf.c'
4603--- src/libCom/dbmf/dbmf.c 2017-02-01 17:57:04 +0000
4604+++ src/libCom/dbmf/dbmf.c 2017-03-03 18:23:23 +0000
4605@@ -68,7 +68,7 @@
4606 static dbmfPrivate *pdbmfPvt = NULL;
4607 int dbmfDebug=0;
4608
4609
4610-int epicsShareAPI dbmfInit(size_t size, int chunkItems)
4611+int dbmfInit(size_t size, int chunkItems)
4612 {
4613 if(pdbmfPvt) {
4614 printf("dbmfInit: Already initialized\n");
4615@@ -94,7 +94,7 @@
4616 }
4617
4618
4619
4620-void* epicsShareAPI dbmfMalloc(size_t size)
4621+void* dbmfMalloc(size_t size)
4622 {
4623 void **pnextFree;
4624 void **pfreeList;
4625@@ -156,15 +156,23 @@
4626 return((void *)pmem);
4627 }
4628
4629-char * epicsShareAPI dbmfStrdup(unsigned char *str)
4630-{
4631- size_t len = strlen((char *) str);
4632- char *buf = dbmfMalloc(len + 1);
4633- strcpy(buf, (char *) str);
4634- return buf;
4635-}
4636-
4637-void epicsShareAPI dbmfFree(void* mem)
4638+char * dbmfStrdup(const char *str)
4639+{
4640+ size_t len = strlen(str);
4641+ char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
4642+
4643+ return strcpy(buf, str);
4644+}
4645+
4646+char * dbmfStrndup(const char *str, size_t len)
4647+{
4648+ char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
4649+
4650+ buf[len] = '\0';
4651+ return strncpy(buf, str, len);
4652+}
4653+
4654+void dbmfFree(void* mem)
4655 {
4656 char *pmem = (char *)mem;
4657 chunkNode *pchunkNode;
4658@@ -194,7 +202,7 @@
4659 epicsMutexUnlock(pdbmfPvt->lock);
4660 }
4661
4662
4663-int epicsShareAPI dbmfShow(int level)
4664+int dbmfShow(int level)
4665 {
4666 if(pdbmfPvt==NULL) {
4667 printf("Never initialized\n");
4668@@ -230,7 +238,7 @@
4669 return(0);
4670 }
4671
4672-void epicsShareAPI dbmfFreeChunks(void)
4673+void dbmfFreeChunks(void)
4674 {
4675 chunkNode *pchunkNode;
4676 chunkNode *pnext;;
4677@@ -259,21 +267,32 @@
4678
4679 #else /* DBMF_FREELIST_DEBUG */
4680
4681-int epicsShareAPI dbmfInit(size_t size, int chunkItems)
4682+int dbmfInit(size_t size, int chunkItems)
4683 { return 0; }
4684
4685-void* epicsShareAPI dbmfMalloc(size_t size)
4686+void* dbmfMalloc(size_t size)
4687 { return malloc(size); }
4688
4689-char * epicsShareAPI dbmfStrdup(unsigned char *str)
4690+char * dbmfStrdup(const char *str)
4691 { return strdup((char*)str); }
4692
4693-void epicsShareAPI dbmfFree(void* mem)
4694+void dbmfFree(void* mem)
4695 { free(mem); }
4696
4697-int epicsShareAPI dbmfShow(int level)
4698+int dbmfShow(int level)
4699 { return 0; }
4700
4701-void epicsShareAPI dbmfFreeChunks(void) {}
4702+void dbmfFreeChunks(void) {}
4703
4704 #endif /* DBMF_FREELIST_DEBUG */
4705+
4706+char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs)
4707+{
4708+ size_t len = strlen(lhs) + strlen(mid) + strlen(rhs) + 1;
4709+ char *buf = dbmfMalloc(len);
4710+ strcpy(buf, lhs);
4711+ strcat(buf, mid);
4712+ strcat(buf, rhs);
4713+ return buf;
4714+}
4715+
4716
4717=== modified file 'src/libCom/dbmf/dbmf.h'
4718--- src/libCom/dbmf/dbmf.h 2012-06-28 14:55:44 +0000
4719+++ src/libCom/dbmf/dbmf.h 2017-03-03 18:23:23 +0000
4720@@ -23,12 +23,15 @@
4721 extern "C" {
4722 #endif
4723
4724-epicsShareFunc int epicsShareAPI dbmfInit(size_t size, int chunkItems);
4725-epicsShareFunc void * epicsShareAPI dbmfMalloc(size_t bytes);
4726-epicsShareFunc char * epicsShareAPI dbmfStrdup(unsigned char *str);
4727-epicsShareFunc void epicsShareAPI dbmfFree(void* bytes);
4728-epicsShareFunc void epicsShareAPI dbmfFreeChunks(void);
4729-epicsShareFunc int epicsShareAPI dbmfShow(int level);
4730+epicsShareFunc int dbmfInit(size_t size, int chunkItems);
4731+epicsShareFunc void * dbmfMalloc(size_t bytes);
4732+epicsShareFunc char * dbmfStrdup(const char *str);
4733+epicsShareFunc char * dbmfStrndup(const char *str, size_t len);
4734+epicsShareFunc char * dbmfStrcat3(const char *lhs, const char *mid,
4735+ const char *rhs);
4736+epicsShareFunc void dbmfFree(void *bytes);
4737+epicsShareFunc void dbmfFreeChunks(void);
4738+epicsShareFunc int dbmfShow(int level);
4739
4740 /* Rules:
4741 * 1) Size is always made a multiple of 8.
4742
4743=== modified file 'src/libCom/error/errMdef.h'
4744--- src/libCom/error/errMdef.h 2017-02-01 17:57:04 +0000
4745+++ src/libCom/error/errMdef.h 2017-03-03 18:23:23 +0000
4746@@ -52,6 +52,7 @@
4747 #define M_time (529 <<16) /*epicsTime*/
4748
4749 epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
4750+epicsShareFunc const char* errSymMsg(long status);
4751 epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);
4752 epicsShareFunc void epicsShareAPI errSymTestPrint(long errNum);
4753 epicsShareFunc int epicsShareAPI errSymBld(void);
4754
4755=== modified file 'src/libCom/error/errSymLib.c'
4756--- src/libCom/error/errSymLib.c 2016-05-22 03:43:09 +0000
4757+++ src/libCom/error/errSymLib.c 2017-03-03 18:23:23 +0000
4758@@ -192,11 +192,9 @@
4759 assert ( nChar < bufLength );
4760 }
4761 }
4762-
4763
4764-/****************************************************************
4765- * errSymLookup
4766- ***************************************************************/
4767-void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
4768+
4769+static
4770+const char* errSymLookupInternal(long status)
4771 {
4772 unsigned modNum;
4773 unsigned hashInd;
4774@@ -211,9 +209,7 @@
4775 if ( modNum <= 500 ) {
4776 const char * pStr = strerror ((int) status);
4777 if ( pStr ) {
4778- strncpy(pBuf, pStr,bufLength);
4779- pBuf[bufLength-1] = '\0';
4780- return;
4781+ return pStr;
4782 }
4783 }
4784 else {
4785@@ -222,17 +218,35 @@
4786 pNextNode = *phashnode;
4787 while(pNextNode) {
4788 if(pNextNode->errNum==status){
4789- strncpy(pBuf, pNextNode->message, bufLength);
4790- pBuf[bufLength-1] = '\0';
4791- return;
4792+ return pNextNode->message;
4793 }
4794 phashnode = &pNextNode->hashnode;
4795 pNextNode = *phashnode;
4796 }
4797 }
4798+ return NULL;
4799+}
4800+
4801+const char* errSymMsg(long status)
4802+{
4803+ const char* msg = errSymLookupInternal(status);
4804+ return msg ? msg : "<Unknown code>";
4805+}
4806+
4807+/****************************************************************
4808+ * errSymLookup
4809+ ***************************************************************/
4810+void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
4811+{
4812+ const char* msg = errSymLookupInternal(status);
4813+ if(msg) {
4814+ strncpy(pBuf, msg, bufLength);
4815+ pBuf[bufLength-1] = '\0';
4816+ return;
4817+ }
4818 errRawCopy(status, pBuf, bufLength);
4819 }
4820-
4821
4822+
4823 /****************************************************************
4824 * errSymDump
4825 ***************************************************************/
4826
4827=== modified file 'src/libCom/error/makeStatTbl.pl'
4828--- src/libCom/error/makeStatTbl.pl 2009-07-09 20:11:02 +0000
4829+++ src/libCom/error/makeStatTbl.pl 2017-03-03 18:23:23 +0000
4830@@ -86,7 +86,7 @@
4831 {
4832 print OUT "$line\n";
4833 # define S_symbol /* comment */
4834- if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\*(.+)\*\/')
4835+ if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\* ?(.+?) ?\*\/')
4836 {
4837 $symbol[$count] = $1;
4838 $comment[$count]= $2;
4839
4840=== modified file 'src/libCom/misc/dbDefs.h'
4841--- src/libCom/misc/dbDefs.h 2016-05-22 03:43:09 +0000
4842+++ src/libCom/misc/dbDefs.h 2017-03-03 18:23:23 +0000
4843@@ -59,6 +59,10 @@
4844 #define PVNAME_STRINGSZ 61
4845 #define PVNAME_SZ (PVNAME_STRINGSZ - 1)
4846
4847+/* Buffer size for the string representation of a DBF_*LINK field */
4848+#define PVLINK_STRINGSZ 1024
4849+
4850+/* dbAccess enums/menus can have up to this many choices */
4851 #define DB_MAX_CHOICES 30
4852
4853 #endif /* INC_dbDefs_H */
4854
4855=== modified file 'src/libCom/misc/epicsString.c'
4856--- src/libCom/misc/epicsString.c 2016-12-07 22:36:58 +0000
4857+++ src/libCom/misc/epicsString.c 2017-03-03 18:23:23 +0000
4858@@ -222,6 +222,15 @@
4859 return 0;
4860 }
4861
4862+char * epicsStrnDup(const char *s, size_t len)
4863+{
4864+ char *buf = mallocMustSucceed(len + 1, "epicsStrnDup");
4865+
4866+ strncpy(buf, s, len);
4867+ buf[len] = '\0';
4868+ return buf;
4869+}
4870+
4871 char * epicsStrDup(const char *s)
4872 {
4873 return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s);
4874
4875=== modified file 'src/libCom/misc/epicsString.h'
4876--- src/libCom/misc/epicsString.h 2014-01-11 15:58:39 +0000
4877+++ src/libCom/misc/epicsString.h 2017-03-03 18:23:23 +0000
4878@@ -31,6 +31,7 @@
4879 epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2);
4880 epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len);
4881 epicsShareFunc char * epicsStrDup(const char *s);
4882+epicsShareFunc char * epicsStrnDup(const char *s, size_t len);
4883 epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n);
4884 #define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw
4885 epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen);
4886
4887=== modified file 'src/libCom/yacc/antelope.c'
4888--- src/libCom/yacc/antelope.c 2012-05-03 17:19:34 +0000
4889+++ src/libCom/yacc/antelope.c 2017-03-03 18:23:23 +0000
4890@@ -115,7 +115,11 @@
4891 int i;
4892 char *s;
4893
4894- if (argc > 0) myname = argv[0];
4895+ if (argc > 0) {
4896+ myname = strrchr(argv[0], '/');
4897+ if (myname) myname++;
4898+ else myname = argv[0];
4899+ }
4900 for (i = 1; i < argc; ++i)
4901 {
4902 s = argv[i];
4903
4904=== modified file 'src/libCom/yacc/error.c'
4905--- src/libCom/yacc/error.c 2013-11-20 23:28:41 +0000
4906+++ src/libCom/yacc/error.c 2017-03-03 18:23:23 +0000
4907@@ -14,7 +14,7 @@
4908 void
4909 fatal(char *msg)
4910 {
4911- fprintf(stderr, "%s: f - %s\n", myname, msg);
4912+ fprintf(stderr, "%s: fatal - %s\n", myname, msg);
4913 done(2);
4914 }
4915
4916@@ -22,7 +22,7 @@
4917 void
4918 no_space(void)
4919 {
4920- fprintf(stderr, "%s: f - out of space\n", myname);
4921+ fprintf(stderr, "%s: fatal - out of space\n", myname);
4922 done(2);
4923 }
4924
4925@@ -30,7 +30,7 @@
4926 void
4927 open_error(char *filename)
4928 {
4929- fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);
4930+ fprintf(stderr, "%s: fatal - cannot open \"%s\"\n", myname, filename);
4931 done(2);
4932 }
4933
4934@@ -38,7 +38,7 @@
4935 void
4936 unexpected_EOF(void)
4937 {
4938- fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n",
4939+ fprintf(stderr, "%s: error - line %d of \"%s\", unexpected end-of-file\n",
4940 myname, lineno, input_file_name);
4941 done(1);
4942 }
4943@@ -74,7 +74,7 @@
4944 void
4945 syntax_error(int st_lineno, char *st_line, char *st_cptr)
4946 {
4947- fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n",
4948+ fprintf(stderr, "%s: error - line %d of \"%s\", syntax error\n",
4949 myname, st_lineno, input_file_name);
4950 print_pos(st_line, st_cptr);
4951 done(1);
4952@@ -84,7 +84,7 @@
4953 void
4954 unterminated_comment(int c_lineno, char *c_line, char *c_cptr)
4955 {
4956- fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n",
4957+ fprintf(stderr, "%s: error - line %d of \"%s\", unmatched /*\n",
4958 myname, c_lineno, input_file_name);
4959 print_pos(c_line, c_cptr);
4960 done(1);
4961@@ -94,7 +94,7 @@
4962 void
4963 unterminated_string(int s_lineno, char *s_line, char *s_cptr)
4964 {
4965- fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n",
4966+ fprintf(stderr, "%s: error - line %d of \"%s\", unterminated string\n",
4967 myname, s_lineno, input_file_name);
4968 print_pos(s_line, s_cptr);
4969 done(1);
4970@@ -104,7 +104,7 @@
4971 void
4972 unterminated_text(int t_lineno, char *t_line, char *t_cptr)
4973 {
4974- fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n",
4975+ fprintf(stderr, "%s: error - line %d of \"%s\", unmatched %%{\n",
4976 myname, t_lineno, input_file_name);
4977 print_pos(t_line, t_cptr);
4978 done(1);
4979@@ -114,7 +114,7 @@
4980 void
4981 unterminated_union(int u_lineno, char *u_line, char *u_cptr)
4982 {
4983- fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \
4984+ fprintf(stderr, "%s: error - line %d of \"%s\", unterminated %%union \
4985 declaration\n", myname, u_lineno, input_file_name);
4986 print_pos(u_line, u_cptr);
4987 done(1);
4988@@ -124,7 +124,7 @@
4989 void
4990 over_unionized(char *u_cptr)
4991 {
4992- fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \
4993+ fprintf(stderr, "%s: error - line %d of \"%s\", too many %%union \
4994 declarations\n", myname, lineno, input_file_name);
4995 print_pos(line, u_cptr);
4996 done(1);
4997@@ -134,7 +134,7 @@
4998 void
4999 illegal_tag(int t_lineno, char *t_line, char *t_cptr)
5000 {
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches