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

Proposed by Andrew Johnson
Status: Rejected
Rejected by: Andrew Johnson
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
mdavidsaver Needs Fixing
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.
Revision history for this message
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
Revision history for this message
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} }}}")

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

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

12759. By mdavidsaver

iocInit: close CA_LINKs through lset

Revision history for this message
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

A cleaner way to close CA & DB links

12761. By Andrew Johnson

Fix issues related to const array initialization

12762. By Andrew Johnson

Undo a small & unnecessary behaviour change

Revision history for this message
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.

Revision history for this message
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

Add lset::dbPutLinkAsync and S_db_noLSET

12764. By Andrew Johnson

Use dbPutLinkAsync() in all output SoftCallback dev's

12765. By Andrew Johnson

Remove 2 obsolete dbStatic routines (forms)

12766. By Andrew Johnson

Missed a spot handling JSON_LINK field values

Revision history for this message
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

Add isConstant and isVolatile to LSET

12768. By Andrew Johnson

Use new dbLink APIs instead of checking link.type

12769. By Andrew Johnson

Start documenting changes

Revision history for this message
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

Undo buggy change

12771. By Andrew Johnson

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

Fix build warning from clang

Revision history for this message
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

Pass link dbfType to jlif allocator; needed!

Revision history for this message
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?

Revision history for this message
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

Add code for arrays of strings

12775. By Andrew Johnson

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

Split dbJLinkInit, JLinks are now parsed at load-time

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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?

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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

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.

Revision history for this message
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.

Revision history for this message
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...

Revision history for this message
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

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

Use new epicsStrnDup() API

12780. By Andrew Johnson

Fixes to dbJLink, added dbJLinkFree()

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

Revision history for this message
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).

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

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

Revision history for this message
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

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

Added calc link-type

Revision history for this message
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

Minor cleanups

12784. By Andrew Johnson

Clean up JSON_LINKs in doCloseLinks

12785. By Andrew Johnson

Minor fixes to the calc link-type

Revision history for this message
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

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

Add lnkConst_remove, fix debug messages

12788. By Andrew Johnson

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

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

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.

Revision history for this message
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!

Revision history for this message
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?

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

> Did we fix that problem already on another branch?

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

Revision history for this message
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

Some documentation updates

Revision history for this message
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

Minor updates

12793. By Andrew Johnson

Fix warnings from clang

Revision history for this message
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

Fixed a small memory leak in lnkCalc

Revision history for this message
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

Clean up memory leaks

12796. By Andrew Johnson

Added test link type, fix dbPutLinkTest for JSON_LINKs

Revision history for this message
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...

Revision history for this message
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

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

Revision history for this message
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.

Revision history for this message
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

Fixes for Windows builds

12799. By Andrew Johnson

Update comments in calcoutRecord

12800. By Andrew Johnson

Remove constant link checks from test device

12801. By Andrew Johnson

Fix HTML entities

12802. By mdavidsaver

add testing lset

12803. By mdavidsaver

libCom: add errSymMsg() error message lookup

Like errSymLookup() but always returns a static string.

12804. By mdavidsaver

dbUnitTest: more informative dbPutField*()

12805. By Andrew Johnson

dbUnitTest: Improve output slightly

12806. By mdavidsaver

db/test: dbPutLinkTest include json links and more

12807. By mdavidsaver

dbLock: add assert in dbScanLock

catch locking attempts before iocInit()

Revision history for this message
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

db/test: Fix warning from clang

Revision history for this message
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.

Revision history for this message
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

Revision history for this message
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")
> }

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

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

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

Revision history for this message
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.

Revision history for this message
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

Revision history for this message
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;
}

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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()).

Revision history for this message
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().

Revision history for this message
mdavidsaver (mdavidsaver) wrote :

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

Revision history for this message
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).

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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.

Revision history for this message
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: "!----------------------------------------------!"})
}

Revision history for this message
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).

Revision history for this message
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?

Revision history for this message
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

Revision history for this message
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.

Revision history for this message
Andrew Johnson (anj) wrote :

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

Revision history for this message
Andrew Johnson (anj) wrote :

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

Revision history for this message
Andrew Johnson (anj) wrote :

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

Revision history for this message
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.

Revision history for this message
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).

Revision history for this message
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

db/test: Fix warning from clang

12807. By mdavidsaver

dbLock: add assert in dbScanLock

catch locking attempts before iocInit()

12806. By mdavidsaver

db/test: dbPutLinkTest include json links and more

12805. By Andrew Johnson

dbUnitTest: Improve output slightly

12804. By mdavidsaver

dbUnitTest: more informative dbPutField*()

12803. By mdavidsaver

libCom: add errSymMsg() error message lookup

Like errSymLookup() but always returns a static string.

12802. By mdavidsaver

add testing lset

12801. By Andrew Johnson

Fix HTML entities

12800. By Andrew Johnson

Remove constant link checks from test device

12799. By Andrew Johnson

Update comments in calcoutRecord

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'documentation/RELEASE_NOTES.html'
--- documentation/RELEASE_NOTES.html 2017-02-01 17:57:04 +0000
+++ documentation/RELEASE_NOTES.html 2017-03-03 18:23:23 +0000
@@ -21,6 +21,152 @@
2121
22-->22-->
2323
24<h3>Device Support Address Type <tt>JSON_LINK</tt></h3>
25
26<p>Device support may be written to expect hardware addresses in the new
27<tt>JSON_LINK</tt> address type. Addresses loaded from a database file will be
28checked against the JSON rules, and a strict JSON representation of the result
29is provided as a C string pointed to by <tt>link.value.json.string</tt>.</p>
30
31<p>Currently the device support is responsible for parsing the JSON text itself.
32An event-driven JSON parser library has been included in libCom since Base-3.15,
33<a href="https://lloyd.github.io/yajl/">YAJL (Yet Another JSON Library)</a>
34Version 1.012, <a href="https://lloyd.github.io/yajl/yajl-1.0.12/">documented
35here</a>.</p>
36
37
38<h3>JSON Link Addressing</h3>
39
40<blockquote>
41
42<p><b style="color:red">FIXME</b> text missing here...</p>
43
44<h4>Support routine changes</h4>
45
46<p>For link fields in external record types and soft device support to be able
47to use the new JSON link types properly, the following changes are
48necessary:</p>
49
50<ul>
51
52<li>Make all calls to <tt>recGblInitConstantLink()</tt> unconditional on the
53link type, i.e. change this code:
54
55<pre>
56 if (prec->siml.type == CONSTANT) {
57 recGblInitConstantLink(&amp;prec->siml, DBF_USHORT, &amp;prec->simm);
58 }
59</pre>
60
61into this:
62
63<pre>
64 recGblInitConstantLink(&amp;prec->siml, DBF_USHORT, &amp;prec->simm);
65</pre>
66
67Note that <tt>recGblInitConstantLink()</tt> still returns true if the field was
68successfully initialized from the link.</li>
69
70<li>Code like this:
71
72<pre>
73 if ((prec->dol.type != CONSTANT) &amp;&amp;
74</pre>
75
76should usually become:
77
78<pre>
79 if (!dbLinkIsConstant(&amp;prec->dol) &amp;&amp;
80</pre>
81</li>
82
83<li>Other code that compares a link type with CONSTANT should be modified to
84use the new routine <tt>dbLinkIsConstant(plink)</tt> instead.</li>
85
86<li>Any code that calls dbCa routines directly or that explicitly checks if a
87link has been resolved as a CA link using code such as
88
89<pre>
90 if (plink->type == CA_LINK)
91</pre>
92
93should be modified to use the new generic routines defined in dbLink.h. As an
94example, the calcout record has been modified to use the new dbLink API.</li>
95
96<li>...</li>
97
98</ul>
99
100<p><b style="color:red">FIXME</b> text missing here...</p>
101
102</blockquote>
103
104
105<h3>Constant Link Values</h3>
106
107<p>Previously a constant link (i.e. a link that does not point to another PV,
108either local or over Channel Access) has only been able to provide a numeric
109value; any string found in a link field that was not recognized as a number was
110treated as a PV name. In this release, constant links may contain string values,
111arrays, or even arrays of strings. These are indicated by ...</p>
112
113<p><b style="color:red">FIXME</b> text missing here...</p>
114
115
116<h3>Database Parsing of "Relaxed JSON" Values</h3>
117
118<p>A database file can now provide a "relaxed JSON" value for a database field
119value or an info tag. Only a few field types can currently accept such values,
120but the capability is now available for use in other places in the future. If a
121JSON-capable field is written to at run-time though only strictly compliant JSON
122may be used (the dbStaticLib parser rewrites relaxed JSON values into strict
123JSON before passing them to the datase for interpretation, where the strict
124rules must be followed).</p>
125
126<p>"Relaxed JSON" was developed to maximize compatibility with the previous
127database parser rules and reduce the number of double-quotes that would be
128needed using strict JSON syntax. The parser will also accept strict JSON, which
129should be used when machine-generating database files. The differences are:</p>
130
131<ul>
132
133<li>Strings containing only the characters <tt><b>a-z A-Z 0-9 _ - + .</b></tt>
134do not have to be enclosed in double-quote characters.</li>
135
136<li>The above rule applies to map keys as well as to regular string values.</li>
137
138<li>The JSON keywords <tt>null</tt>, <tt>true</tt> and <tt>false</tt> (all
139lower-case) will be recognized as keywords, so must be quoted to use any of
140these single words as a string.</li>
141
142<li>Comments may be used, introduced as usual by the <tt><b>#</b></tt>
143character and extending to the end of the line.</li>
144
145</ul>
146
147<p>A JSON field or info value is only enclosed in quotes when the value being
148provided is a single string, and even here the quotes can be omitted in some
149cases as described above. The following shows both correct and incorrect
150excerpts from a database file:</p>
151
152<pre>
153 record(ai, math:pi) {
154 field(INP, {const: 3.14159265358979}) # Correct
155 field(SIOL, "{const: 3.142857}") # Wrong
156
157 info(autosave, { # White-space and comments are allowed
158 fields:[DESC, SIMM],
159 pass0:[VAL]
160 }) # Correct
161 }
162</pre>
163
164<p>Note that the record, field and info-tag names do <em>not</em> accept JSON
165values, so they follows the older bareword rules for quoting where the colon
166<tt><b>:</b></tt> and several additional characters are legal in a bareword
167string.</p>
168
169
24<h3>Echoless comments in iocsh</h3>170<h3>Echoless comments in iocsh</h3>
25171
26<p>The way comments are parsed by the iocsh interpreter has changed. The172<p>The way comments are parsed by the iocsh interpreter has changed. The
27173
=== modified file 'src/ioc/db/Makefile'
--- src/ioc/db/Makefile 2016-05-03 00:51:11 +0000
+++ src/ioc/db/Makefile 2017-03-03 18:23:23 +0000
@@ -14,14 +14,18 @@
14INC += callback.h14INC += callback.h
15INC += dbAccess.h15INC += dbAccess.h
16INC += dbAccessDefs.h16INC += dbAccessDefs.h
17INC += dbCa.h
18INC += dbAddr.h17INC += dbAddr.h
19INC += dbBkpt.h18INC += dbBkpt.h
19INC += dbCa.h
20INC += dbChannel.h20INC += dbChannel.h
21INC += dbConstLink.h
21INC += dbConvert.h22INC += dbConvert.h
22INC += dbConvertFast.h23INC += dbConvertFast.h
24INC += dbConvertJSON.h
25INC += dbDbLink.h
23INC += dbExtractArray.h26INC += dbExtractArray.h
24INC += dbEvent.h27INC += dbEvent.h
28INC += dbJLink.h
25INC += dbLink.h29INC += dbLink.h
26INC += dbLock.h30INC += dbLock.h
27INC += dbNotify.h31INC += dbNotify.h
@@ -64,9 +68,13 @@
64dbCore_SRCS += dbAccess.c68dbCore_SRCS += dbAccess.c
65dbCore_SRCS += dbBkpt.c69dbCore_SRCS += dbBkpt.c
66dbCore_SRCS += dbChannel.c70dbCore_SRCS += dbChannel.c
71dbCore_SRCS += dbConstLink.c
67dbCore_SRCS += dbConvert.c72dbCore_SRCS += dbConvert.c
73dbCore_SRCS += dbConvertJSON.c
74dbCore_SRCS += dbDbLink.c
68dbCore_SRCS += dbFastLinkConv.c75dbCore_SRCS += dbFastLinkConv.c
69dbCore_SRCS += dbExtractArray.c76dbCore_SRCS += dbExtractArray.c
77dbCore_SRCS += dbJLink.c
70dbCore_SRCS += dbLink.c78dbCore_SRCS += dbLink.c
71dbCore_SRCS += dbNotify.c79dbCore_SRCS += dbNotify.c
72dbCore_SRCS += dbScan.c80dbCore_SRCS += dbScan.c
7381
=== modified file 'src/ioc/db/dbAccess.c'
--- src/ioc/db/dbAccess.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbAccess.c 2017-03-03 18:23:23 +0000
@@ -41,7 +41,6 @@
41#include "dbAddr.h"41#include "dbAddr.h"
42#include "dbBase.h"42#include "dbBase.h"
43#include "dbBkpt.h"43#include "dbBkpt.h"
44#include "dbCa.h"
45#include "dbCommon.h"44#include "dbCommon.h"
46#include "dbConvertFast.h"45#include "dbConvertFast.h"
47#include "dbConvert.h"46#include "dbConvert.h"
@@ -668,7 +667,7 @@
668 paddr->dbr_field_type = DBR_CHAR;667 paddr->dbr_field_type = DBR_CHAR;
669 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {668 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
670 /* Clients see a char array, but keep original dbfType */669 /* Clients see a char array, but keep original dbfType */
671 paddr->no_elements = PVNAME_STRINGSZ + 12;670 paddr->no_elements = PVLINK_STRINGSZ;
672 paddr->field_size = 1;671 paddr->field_size = 1;
673 paddr->dbr_field_type = DBR_CHAR;672 paddr->dbr_field_type = DBR_CHAR;
674 } else {673 } else {
@@ -1059,23 +1058,12 @@
1059 }1058 }
1060 }1059 }
10611060
1062 switch (plink->type) { /* Old link type */1061 if (dbLinkIsDefined(plink)) {
1063 case DB_LINK:1062 dbRemoveLink(&locker, plink); /* Clear out old link */
1064 case CA_LINK:1063 }
1065 case CONSTANT:1064 else if (!isDevLink) {
1066 dbRemoveLink(&locker, plink); /* link type becomes PV_LINK */1065 status = S_db_badHWaddr;
1067 break;1066 goto restoreScan;
1068
1069 case PV_LINK:
1070 case MACRO_LINK:
1071 break; /* should never get here */
1072
1073 default: /* Hardware address */
1074 if (!isDevLink) {
1075 status = S_db_badHWaddr;
1076 goto restoreScan;
1077 }
1078 break;
1079 }1067 }
10801068
1081 if (special) status = dbPutSpecial(paddr, 0);1069 if (special) status = dbPutSpecial(paddr, 0);
@@ -1108,6 +1096,7 @@
1108 switch (plink->type) { /* New link type */1096 switch (plink->type) { /* New link type */
1109 case PV_LINK:1097 case PV_LINK:
1110 case CONSTANT:1098 case CONSTANT:
1099 case JSON_LINK:
1111 dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);1100 dbAddLink(&locker, plink, pfldDes->field_type, pdbaddr);
1112 break;1101 break;
11131102
11141103
=== modified file 'src/ioc/db/dbAccessDefs.h'
--- src/ioc/db/dbAccessDefs.h 2016-05-22 12:38:18 +0000
+++ src/ioc/db/dbAccessDefs.h 2017-03-03 18:23:23 +0000
@@ -181,6 +181,7 @@
181#define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/181#define S_db_badChoice (M_dbAccess|13) /*Illegal choice*/
182#define S_db_badField (M_dbAccess|15) /*Illegal field value*/182#define S_db_badField (M_dbAccess|15) /*Illegal field value*/
183#define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/183#define S_db_lsetLogic (M_dbAccess|17) /*Logic error generating lock sets*/
184#define S_db_noLSET (M_dbAccess|21) /*No link support table or entry*/
184#define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/185#define S_db_noRSET (M_dbAccess|31) /*missing record support entry table*/
185#define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/186#define S_db_noSupport (M_dbAccess|33) /*RSET or DSXT routine not defined*/
186#define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/187#define S_db_BadSub (M_dbAccess|35) /*Subroutine not found*/
187188
=== modified file 'src/ioc/db/dbCa.c'
--- src/ioc/db/dbCa.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbCa.c 2017-03-03 18:23:23 +0000
@@ -49,6 +49,7 @@
49#include "dbLock.h"49#include "dbLock.h"
50#include "dbScan.h"50#include "dbScan.h"
51#include "link.h"51#include "link.h"
52#include "recGbl.h"
52#include "recSup.h"53#include "recSup.h"
5354
54/* defined in dbContext.cpp55/* defined in dbContext.cpp
@@ -231,11 +232,8 @@
231void dbCaCallbackProcess(void *userPvt)232void dbCaCallbackProcess(void *userPvt)
232{233{
233 struct link *plink = (struct link *)userPvt;234 struct link *plink = (struct link *)userPvt;
234 dbCommon *pdbCommon = plink->precord;
235235
236 dbScanLock(pdbCommon);236 dbLinkAsyncComplete(plink);
237 pdbCommon->rset->process(pdbCommon);
238 dbScanUnlock(pdbCommon);
239}237}
240238
241void dbCaShutdown(void)239void dbCaShutdown(void)
@@ -339,8 +337,8 @@
339 addAction(pca, CA_CLEAR_CHANNEL);337 addAction(pca, CA_CLEAR_CHANNEL);
340}338}
341339
342long dbCaGetLink(struct link *plink,short dbrType, void *pdest,340long dbCaGetLink(struct link *plink, short dbrType, void *pdest,
343 epicsEnum16 *pstat, epicsEnum16 *psevr, long *nelements)341 long *nelements)
344{342{
345 caLink *pca = (caLink *)plink->value.pv_link.pvt;343 caLink *pca = (caLink *)plink->value.pv_link.pvt;
346 long status = 0;344 long status = 0;
@@ -412,13 +410,23 @@
412 aConvert(&dbAddr, pdest, ntoget, ntoget, 0);410 aConvert(&dbAddr, pdest, ntoget, ntoget, 0);
413 }411 }
414done:412done:
415 if (pstat) *pstat = pca->stat;413 if (link_action)
416 if (psevr) *psevr = pca->sevr;414 addAction(pca, link_action);
417 if (link_action) addAction(pca, link_action);415 if (!status)
416 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
417 plink->precord, pca->stat, pca->sevr);
418 epicsMutexUnlock(pca->lock);418 epicsMutexUnlock(pca->lock);
419
419 return status;420 return status;
420}421}
421422
422423
424static long dbCaPutAsync(struct link *plink,short dbrType,
425 const void *pbuffer,long nRequest)
426{
427 return dbCaPutLinkCallback(plink, dbrType, pbuffer, nRequest,
428 dbCaCallbackProcess, plink);
429}
430
423long dbCaPutLinkCallback(struct link *plink,short dbrType,431long dbCaPutLinkCallback(struct link *plink,short dbrType,
424 const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt)432 const void *pbuffer,long nRequest,dbCaCallback callback,void *userPvt)
425{433{
@@ -708,14 +716,16 @@
708}716}
709717
710static lset dbCa_lset = {718static lset dbCa_lset = {
711 dbCaRemoveLink,719 0, 1, /* not Constant, Volatile */
720 NULL, dbCaRemoveLink,
721 NULL, NULL, NULL,
712 isConnected,722 isConnected,
713 getDBFtype, getElements,723 getDBFtype, getElements,
714 dbCaGetLink,724 dbCaGetLink,
715 getControlLimits, getGraphicLimits, getAlarmLimits,725 getControlLimits, getGraphicLimits, getAlarmLimits,
716 getPrecision, getUnits,726 getPrecision, getUnits,
717 getAlarm, getTimeStamp,727 getAlarm, getTimeStamp,
718 dbCaPutLink,728 dbCaPutLink, dbCaPutAsync,
719 scanForward729 scanForward
720};730};
721731
722732
=== modified file 'src/ioc/db/dbCa.h'
--- src/ioc/db/dbCa.h 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbCa.h 2017-03-03 18:23:23 +0000
@@ -33,8 +33,7 @@
33epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);33epicsShareFunc void dbCaRemoveLink(struct dbLocker *locker, struct link *plink);
3434
35epicsShareFunc long dbCaGetLink(struct link *plink,35epicsShareFunc long dbCaGetLink(struct link *plink,
36 short dbrType, void *pbuffer, epicsEnum16 *pstat, epicsEnum16 *psevr,36 short dbrType, void *pbuffer, long *nRequest);
37 long *nRequest);
3837
39epicsShareFunc long dbCaGetAttributes(const struct link *plink,38epicsShareFunc long dbCaGetAttributes(const struct link *plink,
40 dbCaCallback callback, void *userPvt);39 dbCaCallback callback, void *userPvt);
4140
=== modified file 'src/ioc/db/dbChannel.c'
--- src/ioc/db/dbChannel.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbChannel.c 2017-03-03 18:23:23 +0000
@@ -531,7 +531,7 @@
531 paddr->dbr_field_type = DBR_CHAR;531 paddr->dbr_field_type = DBR_CHAR;
532 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {532 } else if (dbfType >= DBF_INLINK && dbfType <= DBF_FWDLINK) {
533 /* Clients see a char array, but keep original dbfType */533 /* Clients see a char array, but keep original dbfType */
534 paddr->no_elements = PVNAME_STRINGSZ + 12;534 paddr->no_elements = PVLINK_STRINGSZ;
535 paddr->field_size = 1;535 paddr->field_size = 1;
536 paddr->dbr_field_type = DBR_CHAR;536 paddr->dbr_field_type = DBR_CHAR;
537 } else {537 } else {
538538
=== added file 'src/ioc/db/dbConstLink.c'
--- src/ioc/db/dbConstLink.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbConstLink.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,140 @@
1/*************************************************************************\
2* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution.
8\*************************************************************************/
9/* dbConstLink.c
10 *
11 * Original Authors: Bob Dalesio, Marty Kraimer
12 * Current Author: Andrew Johnson
13 */
14
15#include <stdio.h>
16#include <string.h>
17
18#include "dbDefs.h"
19
20#define epicsExportSharedSymbols
21#include "dbAccessDefs.h"
22#include "dbAddr.h"
23#include "dbCommon.h"
24#include "dbConstLink.h"
25#include "dbConvertFast.h"
26#include "dbConvertJSON.h"
27#include "dbFldTypes.h"
28#include "dbLink.h"
29#include "link.h"
30
31/***************************** Constant Links *****************************/
32
33/* Forward definition */
34static lset dbConst_lset;
35
36void dbConstInitLink(struct link *plink)
37{
38 plink->lset = &dbConst_lset;
39}
40
41void dbConstAddLink(struct link *plink)
42{
43 plink->lset = &dbConst_lset;
44}
45
46/**************************** Member functions ****************************/
47
48static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
49{
50 const char *pstr = plink->value.constantStr;
51 size_t len;
52
53 if (!pstr)
54 return S_db_badField;
55 len = strlen(pstr);
56
57 /* Choice values must be numeric */
58 if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
59 dbrType = DBF_USHORT;
60
61 if (*pstr == '[' && pstr[len-1] == ']') {
62 /* Convert from JSON array */
63 long nReq = 1;
64
65 return dbPutConvertJSON(pstr, dbrType, pbuffer, &nReq);
66 }
67
68 return dbFastPutConvertRoutine[DBR_STRING][dbrType]
69 (pstr, pbuffer, NULL);
70}
71
72static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
73 epicsUInt32 *plen)
74{
75 const char *pstr = plink->value.constantStr;
76 size_t len;
77
78 if (!pstr)
79 return S_db_badField;
80 len = strlen(pstr);
81
82 /* FIXME This handles the common case, but not the general one... */
83 if (pstr[0] == '[' && pstr[1] == '"' &&
84 pstr[len-2] == '"' && pstr[len-1] == ']') {
85 pstr += 2;
86 len -= 4;
87 }
88 if (--size > len) size = len;
89
90 strncpy(pbuffer, pstr, size);
91 pbuffer[size] = 0;
92 *plen = (epicsUInt32) strlen(pbuffer) + 1;
93 return 0;
94}
95
96static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
97 long *pnReq)
98{
99 const char *pstr = plink->value.constantStr;
100
101 if (!pstr)
102 return S_db_badField;
103
104 /* Choice values must be numeric */
105 if (dbrType == DBF_MENU || dbrType == DBF_ENUM || dbrType == DBF_DEVICE)
106 dbrType = DBF_USHORT;
107
108 return dbPutConvertJSON(pstr, dbrType, pbuffer, pnReq);
109}
110
111static long dbConstGetNelements(const struct link *plink, long *nelements)
112{
113 *nelements = 0;
114 return 0;
115}
116
117static long dbConstGetValue(struct link *plink, short dbrType, void *pbuffer,
118 long *pnRequest)
119{
120 if (pnRequest)
121 *pnRequest = 0;
122 return 0;
123}
124
125static lset dbConst_lset = {
126 1, 0, /* Constant, not Volatile */
127 NULL, NULL,
128 dbConstLoadScalar,
129 dbConstLoadLS,
130 dbConstLoadArray,
131 NULL,
132 NULL, dbConstGetNelements,
133 dbConstGetValue,
134 NULL, NULL, NULL,
135 NULL, NULL,
136 NULL, NULL,
137 NULL, NULL,
138 NULL
139};
140
0141
=== added file 'src/ioc/db/dbConstLink.h'
--- src/ioc/db/dbConstLink.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbConstLink.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,34 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution.
8\*************************************************************************/
9/* dbConstLink.h
10 *
11 * Created on: April 3rd, 2016
12 * Author: Andrew Johnson
13 */
14
15#ifndef INC_dbConstLink_H
16#define INC_dbConstLink_H
17
18#include "shareLib.h"
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
24struct link;
25
26epicsShareFunc void dbConstInitLink(struct link *plink);
27epicsShareFunc void dbConstAddLink(struct link *plink);
28
29#ifdef __cplusplus
30}
31#endif
32
33#endif /* INC_dbConstLink_H */
34
035
=== added file 'src/ioc/db/dbConvertJSON.c'
--- src/ioc/db/dbConvertJSON.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbConvertJSON.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,172 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* dbConvertJSON.c */
8
9#include <string.h>
10#include <stdio.h>
11
12#include "dbDefs.h"
13#include "errlog.h"
14#include "yajl_alloc.h"
15#include "yajl_parse.h"
16
17#define epicsExportSharedSymbols
18#include "dbAccessDefs.h"
19#include "dbConvertFast.h"
20
21typedef long (*FASTCONVERT)();
22
23typedef struct parseContext {
24 int depth;
25 short dbrType;
26 short dbrSize;
27 char *pdest;
28 int elems;
29} parseContext;
30
31static int dbcj_null(void *ctx) {
32 return 0; /* Illegal */
33}
34
35static int dbcj_boolean(void *ctx, int val) {
36 return 0; /* Illegal */
37}
38
39static int dbcj_integer(void *ctx, long num) {
40 parseContext *parser = (parseContext *) ctx;
41 epicsInt32 val32 = num;
42 FASTCONVERT conv = dbFastPutConvertRoutine[DBF_LONG][parser->dbrType];
43
44 if (parser->elems > 0) {
45 conv(&val32, parser->pdest, NULL);
46 parser->pdest += parser->dbrSize;
47 parser->elems--;
48 }
49 return 1;
50}
51
52static int dbcj_double(void *ctx, double num) {
53 parseContext *parser = (parseContext *) ctx;
54 FASTCONVERT conv = dbFastPutConvertRoutine[DBF_DOUBLE][parser->dbrType];
55
56 if (parser->elems > 0) {
57 conv(&num, parser->pdest, NULL);
58 parser->pdest += parser->dbrSize;
59 parser->elems--;
60 }
61 return 1;
62}
63
64static int dbcj_string(void *ctx, const unsigned char *val, unsigned int len) {
65 parseContext *parser = (parseContext *) ctx;
66 char *pdest = parser->pdest;
67
68 /* Not attempting to handle char-array fields here, they need more
69 * metadata about the field than we have available at the moment.
70 */
71 if (parser->dbrType != DBF_STRING) {
72 errlogPrintf("dbPutConvertJSON: String provided, numeric value(s) expected\n");
73 return 0; /* Illegal */
74 }
75
76 if (parser->elems > 0) {
77 if (len > parser->dbrSize - 1)
78 len = parser->dbrSize - 1;
79 strncpy(pdest, (const char *) val, len);
80 pdest[len] = 0;
81 parser->pdest += parser->dbrSize;
82 parser->elems--;
83 }
84 return 1;
85}
86
87static int dbcj_start_map(void *ctx) {
88 errlogPrintf("dbPutConvertJSON: Map type not supported\n");
89 return 0; /* Illegal */
90}
91
92static int dbcj_map_key(void *ctx, const unsigned char *key, unsigned int len) {
93 return 0; /* Illegal */
94}
95
96static int dbcj_end_map(void *ctx) {
97 return 0; /* Illegal */
98}
99
100static int dbcj_start_array(void *ctx) {
101 parseContext *parser = (parseContext *) ctx;
102
103 if (++parser->depth > 1)
104 errlogPrintf("dbPutConvertJSON: Embedded arrays not supported\n");
105
106 return (parser->depth == 1);
107}
108
109static int dbcj_end_array(void *ctx) {
110 parseContext *parser = (parseContext *) ctx;
111
112 parser->depth--;
113 return (parser->depth == 0);
114}
115
116
117static yajl_callbacks dbcj_callbacks = {
118 dbcj_null, dbcj_boolean, dbcj_integer, dbcj_double, NULL, dbcj_string,
119 dbcj_start_map, dbcj_map_key, dbcj_end_map,
120 dbcj_start_array, dbcj_end_array
121};
122
123static const yajl_parser_config dbcj_config =
124 { 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
125
126long dbPutConvertJSON(const char *json, short dbrType,
127 void *pdest, long *pnRequest)
128{
129 parseContext context, *parser = &context;
130 yajl_alloc_funcs dbcj_alloc;
131 yajl_handle yh;
132 yajl_status ys;
133 size_t jlen = strlen(json);
134 long status;
135
136 parser->depth = 0;
137 parser->dbrType = dbrType;
138 parser->dbrSize = dbValueSize(dbrType);
139 parser->pdest = pdest;
140 parser->elems = *pnRequest;
141
142 yajl_set_default_alloc_funcs(&dbcj_alloc);
143 yh = yajl_alloc(&dbcj_callbacks, &dbcj_config, &dbcj_alloc, parser);
144 if (!yh)
145 return S_db_noMemory;
146
147 ys = yajl_parse(yh, (const unsigned char *) json, (unsigned int) jlen);
148 if (ys == yajl_status_insufficient_data)
149 ys = yajl_parse_complete(yh);
150
151 switch (ys) {
152 case yajl_status_ok:
153 *pnRequest -= parser->elems;
154 status = 0;
155 break;
156
157 case yajl_status_error: {
158 unsigned char *err = yajl_get_error(yh, 1,
159 (const unsigned char *) json, (unsigned int) jlen);
160 fprintf(stderr, "dbPutConvertJSON: %s\n", err);
161 yajl_free_error(yh, err);
162 }
163 /* fall through */
164 default:
165 status = S_db_badField;
166 }
167
168 yajl_free(yh);
169 return status;
170}
171
172
0173
=== added file 'src/ioc/db/dbConvertJSON.h'
--- src/ioc/db/dbConvertJSON.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbConvertJSON.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,27 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* dbConvertJSON.h */
8
9#ifndef INC_dbConvertJSON_H
10#define INC_dbConvertJSON_H
11
12#include <shareLib.h>
13
14#ifdef __cplusplus
15extern "C" {
16#endif
17
18/* This name should probably be changed to inclue "array" */
19epicsShareFunc long dbPutConvertJSON(const char *json, short dbrType,
20 void *pdest, long *psize);
21
22#ifdef __cplusplus
23}
24#endif
25
26#endif /* INC_dbConvertJSON_H */
27
028
=== added file 'src/ioc/db/dbDbLink.c'
--- src/ioc/db/dbDbLink.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbDbLink.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,353 @@
1/*************************************************************************\
2* Copyright (c) 2010 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution.
8\*************************************************************************/
9/* dbDbLink.c
10 *
11 * Original Authors: Bob Dalesio, Marty Kraimer
12 * Current Author: Andrew Johnson
13 */
14
15#include <stddef.h>
16#include <stdlib.h>
17#include <stdarg.h>
18#include <stdio.h>
19#include <string.h>
20
21#include "alarm.h"
22#include "cantProceed.h"
23#include "cvtFast.h"
24#include "dbDefs.h"
25#include "ellLib.h"
26#include "epicsTime.h"
27#include "errlog.h"
28
29#include "caeventmask.h"
30
31#define epicsExportSharedSymbols
32#include "dbAccessDefs.h"
33#include "dbAddr.h"
34#include "dbBase.h"
35#include "dbBkpt.h"
36#include "dbCommon.h"
37#include "dbConvertFast.h"
38#include "dbConvert.h"
39#include "db_field_log.h"
40#include "dbFldTypes.h"
41#include "dbLink.h"
42#include "dbLockPvt.h"
43#include "dbNotify.h"
44#include "dbScan.h"
45#include "dbStaticLib.h"
46#include "devSup.h"
47#include "link.h"
48#include "recGbl.h"
49#include "recSup.h"
50#include "special.h"
51
52/***************************** Database Links *****************************/
53
54/* Forward definition */
55static lset dbDb_lset;
56
57long dbDbInitLink(struct link *plink, short dbfType)
58{
59 DBADDR dbaddr;
60 long status;
61 DBADDR *pdbAddr;
62
63 status = dbNameToAddr(plink->value.pv_link.pvname, &dbaddr);
64 if (status)
65 return status;
66
67 plink->lset = &dbDb_lset;
68 plink->type = DB_LINK;
69 pdbAddr = dbCalloc(1, sizeof(struct dbAddr));
70 *pdbAddr = dbaddr; /* structure copy */
71 plink->value.pv_link.pvt = pdbAddr;
72 ellAdd(&dbaddr.precord->bklnk, &plink->value.pv_link.backlinknode);
73 /* merging into the same lockset is deferred to the caller.
74 * cf. initPVLinks()
75 */
76 dbLockSetMerge(NULL, plink->precord, dbaddr.precord);
77 assert(plink->precord->lset->plockSet == dbaddr.precord->lset->plockSet);
78 return 0;
79}
80
81void dbDbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
82 DBADDR *ptarget)
83{
84 plink->lset = &dbDb_lset;
85 plink->type = DB_LINK;
86 plink->value.pv_link.pvt = ptarget;
87 ellAdd(&ptarget->precord->bklnk, &plink->value.pv_link.backlinknode);
88
89 /* target record is already locked in dbPutFieldLink() */
90 dbLockSetMerge(locker, plink->precord, ptarget->precord);
91}
92
93static void dbDbRemoveLink(struct dbLocker *locker, struct link *plink)
94{
95 DBADDR *pdbAddr = (DBADDR *) plink->value.pv_link.pvt;
96
97 plink->type = PV_LINK;
98
99 /* locker is NULL when an isolated IOC is closing its links */
100 if (locker) {
101 plink->value.pv_link.pvt = 0;
102 plink->value.pv_link.getCvt = 0;
103 plink->value.pv_link.pvlMask = 0;
104 plink->value.pv_link.lastGetdbrType = 0;
105 ellDelete(&pdbAddr->precord->bklnk, &plink->value.pv_link.backlinknode);
106 dbLockSetSplit(locker, plink->precord, pdbAddr->precord);
107 }
108 free(pdbAddr);
109}
110
111static int dbDbIsConnected(const struct link *plink)
112{
113 return TRUE;
114}
115
116static int dbDbGetDBFtype(const struct link *plink)
117{
118 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
119
120 return paddr->field_type;
121}
122
123static long dbDbGetElements(const struct link *plink, long *nelements)
124{
125 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
126
127 *nelements = paddr->no_elements;
128 return 0;
129}
130
131static long dbDbGetValue(struct link *plink, short dbrType, void *pbuffer,
132 long *pnRequest)
133{
134 struct pv_link *ppv_link = &plink->value.pv_link;
135 DBADDR *paddr = ppv_link->pvt;
136 dbCommon *precord = plink->precord;
137 long status;
138
139 /* scan passive records if link is process passive */
140 if (ppv_link->pvlMask & pvlOptPP) {
141 unsigned char pact = precord->pact;
142
143 precord->pact = TRUE;
144 status = dbScanPassive(precord, paddr->precord);
145 precord->pact = pact;
146 if (status)
147 return status;
148 }
149
150 if (ppv_link->getCvt && ppv_link->lastGetdbrType == dbrType) {
151 status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
152 } else {
153 unsigned short dbfType = paddr->field_type;
154
155 if (dbrType < 0 || dbrType > DBR_ENUM || dbfType > DBF_DEVICE)
156 return S_db_badDbrtype;
157
158 if (paddr->no_elements == 1 && (!pnRequest || *pnRequest == 1)
159 && paddr->special != SPC_ATTRIBUTE) {
160 ppv_link->getCvt = dbFastGetConvertRoutine[dbfType][dbrType];
161 status = ppv_link->getCvt(paddr->pfield, pbuffer, paddr);
162 } else {
163 ppv_link->getCvt = NULL;
164 status = dbGet(paddr, dbrType, pbuffer, NULL, pnRequest, NULL);
165 }
166 ppv_link->lastGetdbrType = dbrType;
167 }
168
169 if (!status)
170 recGblInheritSevr(plink->value.pv_link.pvlMask & pvlOptMsMode,
171 plink->precord, paddr->precord->stat, paddr->precord->sevr);
172 return status;
173}
174
175static long dbDbGetControlLimits(const struct link *plink, double *low,
176 double *high)
177{
178 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
179 struct buffer {
180 DBRctrlDouble
181 double value;
182 } buffer;
183 long options = DBR_CTRL_DOUBLE;
184 long number_elements = 0;
185 long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
186 NULL);
187
188 if (status)
189 return status;
190
191 *low = buffer.lower_ctrl_limit;
192 *high = buffer.upper_ctrl_limit;
193 return 0;
194}
195
196static long dbDbGetGraphicLimits(const struct link *plink, double *low,
197 double *high)
198{
199 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
200 struct buffer {
201 DBRgrDouble
202 double value;
203 } buffer;
204 long options = DBR_GR_DOUBLE;
205 long number_elements = 0;
206 long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
207 NULL);
208
209 if (status)
210 return status;
211
212 *low = buffer.lower_disp_limit;
213 *high = buffer.upper_disp_limit;
214 return 0;
215}
216
217static long dbDbGetAlarmLimits(const struct link *plink, double *lolo,
218 double *low, double *high, double *hihi)
219{
220 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
221 struct buffer {
222 DBRalDouble
223 double value;
224 } buffer;
225 long options = DBR_AL_DOUBLE;
226 long number_elements = 0;
227 long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
228 0);
229
230 if (status)
231 return status;
232
233 *lolo = buffer.lower_alarm_limit;
234 *low = buffer.lower_warning_limit;
235 *high = buffer.upper_warning_limit;
236 *hihi = buffer.upper_alarm_limit;
237 return 0;
238}
239
240static long dbDbGetPrecision(const struct link *plink, short *precision)
241{
242 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
243 struct buffer {
244 DBRprecision
245 double value;
246 } buffer;
247 long options = DBR_PRECISION;
248 long number_elements = 0;
249 long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
250 0);
251
252 if (status)
253 return status;
254
255 *precision = (short) buffer.precision.dp;
256 return 0;
257}
258
259static long dbDbGetUnits(const struct link *plink, char *units, int unitsSize)
260{
261 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
262 struct buffer {
263 DBRunits
264 double value;
265 } buffer;
266 long options = DBR_UNITS;
267 long number_elements = 0;
268 long status = dbGet(paddr, DBR_DOUBLE, &buffer, &options, &number_elements,
269 0);
270
271 if (status)
272 return status;
273
274 strncpy(units, buffer.units, unitsSize);
275 return 0;
276}
277
278static long dbDbGetAlarm(const struct link *plink, epicsEnum16 *status,
279 epicsEnum16 *severity)
280{
281 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
282
283 if (status)
284 *status = paddr->precord->stat;
285 if (severity)
286 *severity = paddr->precord->sevr;
287 return 0;
288}
289
290static long dbDbGetTimeStamp(const struct link *plink, epicsTimeStamp *pstamp)
291{
292 DBADDR *paddr = (DBADDR *) plink->value.pv_link.pvt;
293
294 *pstamp = paddr->precord->time;
295 return 0;
296}
297
298static long dbDbPutValue(struct link *plink, short dbrType,
299 const void *pbuffer, long nRequest)
300{
301 struct pv_link *ppv_link = &plink->value.pv_link;
302 struct dbCommon *psrce = plink->precord;
303 DBADDR *paddr = (DBADDR *) ppv_link->pvt;
304 dbCommon *pdest = paddr->precord;
305 long status = dbPut(paddr, dbrType, pbuffer, nRequest);
306
307 recGblInheritSevr(ppv_link->pvlMask & pvlOptMsMode, pdest, psrce->nsta,
308 psrce->nsev);
309 if (status)
310 return status;
311
312 if (paddr->pfield == (void *) &pdest->proc ||
313 (ppv_link->pvlMask & pvlOptPP && pdest->scan == 0)) {
314 /* if dbPutField caused asyn record to process */
315 /* ask for reprocessing*/
316 if (pdest->putf) {
317 pdest->rpro = TRUE;
318 } else { /* process dest record with source's PACT true */
319 unsigned char pact;
320
321 if (psrce && psrce->ppn)
322 dbNotifyAdd(psrce, pdest);
323 pact = psrce->pact;
324 psrce->pact = TRUE;
325 status = dbProcess(pdest);
326 psrce->pact = pact;
327 }
328 }
329 return status;
330}
331
332static void dbDbScanFwdLink(struct link *plink)
333{
334 dbCommon *precord = plink->precord;
335 dbAddr *paddr = (dbAddr *) plink->value.pv_link.pvt;
336
337 dbScanPassive(precord, paddr->precord);
338}
339
340static lset dbDb_lset = {
341 0, 0, /* not Constant, not Volatile */
342 NULL, dbDbRemoveLink,
343 NULL, NULL, NULL,
344 dbDbIsConnected,
345 dbDbGetDBFtype, dbDbGetElements,
346 dbDbGetValue,
347 dbDbGetControlLimits, dbDbGetGraphicLimits, dbDbGetAlarmLimits,
348 dbDbGetPrecision, dbDbGetUnits,
349 dbDbGetAlarm, dbDbGetTimeStamp,
350 dbDbPutValue, NULL,
351 dbDbScanFwdLink
352};
353
0354
=== added file 'src/ioc/db/dbDbLink.h'
--- src/ioc/db/dbDbLink.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbDbLink.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,35 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution.
8\*************************************************************************/
9/* dbDbLink.h
10 *
11 * Created on: April 3rd, 2016
12 * Author: Andrew Johnson
13 */
14
15#ifndef INC_dbDbLink_H
16#define INC_dbDbLink_H
17
18#include "shareLib.h"
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
24struct link;
25struct dbLocker;
26
27epicsShareFunc long dbDbInitLink(struct link *plink, short dbfType);
28epicsShareFunc void dbDbAddLink(struct dbLocker *locker, struct link *plink,
29 short dbfType, DBADDR *ptarget);
30
31#ifdef __cplusplus
32}
33#endif
34
35#endif /* INC_dbDbLink_H */
036
=== modified file 'src/ioc/db/dbIocRegister.c'
--- src/ioc/db/dbIocRegister.c 2015-07-13 18:05:33 +0000
+++ src/ioc/db/dbIocRegister.c 2017-03-03 18:23:23 +0000
@@ -16,6 +16,7 @@
16#include "dbCaTest.h"16#include "dbCaTest.h"
17#include "dbEvent.h"17#include "dbEvent.h"
18#include "dbIocRegister.h"18#include "dbIocRegister.h"
19#include "dbJLink.h"
19#include "dbLock.h"20#include "dbLock.h"
20#include "dbNotify.h"21#include "dbNotify.h"
21#include "dbScan.h"22#include "dbScan.h"
@@ -109,6 +110,16 @@
109 dbcar(args[0].sval,args[1].ival);110 dbcar(args[0].sval,args[1].ival);
110}111}
111112
113/* dbjlr */
114static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
115static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
116static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
117static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs};
118static void dbjlrCallFunc(const iocshArgBuf *args)
119{
120 dbjlr(args[0].sval,args[1].ival);
121}
122
112/* dbel */123/* dbel */
113static const iocshArg dbelArg0 = { "record name",iocshArgString};124static const iocshArg dbelArg0 = { "record name",iocshArgString};
114static const iocshArg dbelArg1 = { "level",iocshArgInt};125static const iocshArg dbelArg1 = { "level",iocshArgInt};
@@ -395,6 +406,7 @@
395 iocshRegister(&dbsrFuncDef,dbsrCallFunc);406 iocshRegister(&dbsrFuncDef,dbsrCallFunc);
396 iocshRegister(&dbcarFuncDef,dbcarCallFunc);407 iocshRegister(&dbcarFuncDef,dbcarCallFunc);
397 iocshRegister(&dbelFuncDef,dbelCallFunc);408 iocshRegister(&dbelFuncDef,dbelCallFunc);
409 iocshRegister(&dbjlrFuncDef,dbjlrCallFunc);
398410
399 iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);411 iocshRegister(&dbLoadDatabaseFuncDef,dbLoadDatabaseCallFunc);
400 iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);412 iocshRegister(&dbLoadRecordsFuncDef,dbLoadRecordsCallFunc);
401413
=== added file 'src/ioc/db/dbJLink.c'
--- src/ioc/db/dbJLink.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbJLink.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,542 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* dbJLink.c */
8
9#include <stdio.h>
10#include <string.h>
11
12#include "epicsAssert.h"
13#include "dbmf.h"
14#include "errlog.h"
15#include "yajl_alloc.h"
16#include "yajl_parse.h"
17
18#define epicsExportSharedSymbols
19#include "dbAccessDefs.h"
20#include "dbCommon.h"
21#include "dbLink.h"
22#include "dbJLink.h"
23#include "dbLock.h"
24#include "dbStaticLib.h"
25#include "link.h"
26
27/* Change 'undef' to 'define' to turn on debug statements: */
28#undef DEBUG_JLINK
29
30#ifdef DEBUG_JLINK
31 int jlinkDebug = 10;
32# define IFDEBUG(n) \
33 if (jlinkDebug >= n) /* block or statement */
34#else
35# define IFDEBUG(n) \
36 if(0) /* Compiler will elide the block or statement */
37#endif
38
39
40typedef struct parseContext {
41 jlink *pjlink;
42 jlink *product;
43 short dbfType;
44 short jsonDepth;
45 unsigned key_is_link:1;
46} parseContext;
47
48#define CALL_OR_STOP(routine) !(routine) ? jlif_stop : (routine)
49
50static int dbjl_return(parseContext *parser, jlif_result result) {
51 jlink *pjlink = parser->pjlink;
52
53 IFDEBUG(10) {
54 printf("dbjl_return(%s@%p, %d)\t", pjlink->pif->name, pjlink, result);
55 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
56 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
57 }
58
59 if (result == jlif_stop && pjlink) {
60 jlink *parent;
61
62 while ((parent = pjlink->parent)) {
63 pjlink->pif->free_jlink(pjlink);
64 pjlink = parent;
65 }
66 pjlink->pif->free_jlink(pjlink);
67 }
68
69 return result;
70}
71
72static int dbjl_value(parseContext *parser, jlif_result result) {
73 jlink *pjlink = parser->pjlink;
74 jlink *parent;
75
76 IFDEBUG(10) {
77 printf("dbjl_value(%s@%p, %d)\t", pjlink->pif->name, pjlink, result);
78 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
79 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
80 }
81
82 if (result == jlif_stop || pjlink->parseDepth > 0)
83 return dbjl_return(parser, result);
84
85 parent = pjlink->parent;
86 if (!parent)
87 parser->product = pjlink;
88 else if (parent->pif->end_child)
89 parent->pif->end_child(parent, pjlink);
90
91 parser->pjlink = parent;
92
93 IFDEBUG(8)
94 printf("dbjl_value: product = %p\n", pjlink);
95
96 return jlif_continue;
97}
98
99static int dbjl_null(void *ctx) {
100 parseContext *parser = (parseContext *) ctx;
101 jlink *pjlink = parser->pjlink;
102
103 IFDEBUG(10)
104 printf("dbjl_null(%s@%p)\n", pjlink->pif->name, pjlink);
105
106 assert(pjlink);
107 return dbjl_value(parser,
108 CALL_OR_STOP(pjlink->pif->parse_null)(pjlink));
109}
110
111static int dbjl_boolean(void *ctx, int val) {
112 parseContext *parser = (parseContext *) ctx;
113 jlink *pjlink = parser->pjlink;
114
115 assert(pjlink);
116 return dbjl_value(parser,
117 CALL_OR_STOP(pjlink->pif->parse_boolean)(pjlink, val));
118}
119
120static int dbjl_integer(void *ctx, long num) {
121 parseContext *parser = (parseContext *) ctx;
122 jlink *pjlink = parser->pjlink;
123
124 IFDEBUG(10)
125 printf("dbjl_integer(%s@%p, %ld)\n",
126 pjlink->pif->name, pjlink, num);
127
128 assert(pjlink);
129 return dbjl_value(parser,
130 CALL_OR_STOP(pjlink->pif->parse_integer)(pjlink, num));
131}
132
133static int dbjl_double(void *ctx, double num) {
134 parseContext *parser = (parseContext *) ctx;
135 jlink *pjlink = parser->pjlink;
136
137 IFDEBUG(10)
138 printf("dbjl_double(%s@%p, %g)\n",
139 pjlink->pif->name, pjlink, num);
140
141 assert(pjlink);
142 return dbjl_value(parser,
143 CALL_OR_STOP(pjlink->pif->parse_double)(pjlink, num));
144}
145
146static int dbjl_string(void *ctx, const unsigned char *val, unsigned len) {
147 parseContext *parser = (parseContext *) ctx;
148 jlink *pjlink = parser->pjlink;
149
150 IFDEBUG(10)
151 printf("dbjl_string(%s@%p, \"%.*s\")\n",
152 pjlink->pif->name, pjlink, len, val);
153
154 assert(pjlink);
155 return dbjl_value(parser,
156 CALL_OR_STOP(pjlink->pif->parse_string)(pjlink, (const char *) val, len));
157}
158
159static int dbjl_start_map(void *ctx) {
160 parseContext *parser = (parseContext *) ctx;
161 jlink *pjlink = parser->pjlink;
162 int result;
163
164 if (!pjlink) {
165 IFDEBUG(10) {
166 printf("dbjl_start_map(NULL)\t");
167 printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
168 parser->jsonDepth, parser->key_is_link);
169 }
170
171 assert(parser->jsonDepth == 0);
172 parser->jsonDepth++;
173 parser->key_is_link = 1;
174 return jlif_continue; /* Opening '{' */
175 }
176
177 IFDEBUG(10) {
178 printf("dbjl_start_map(%s@%p)\t", pjlink->pif->name, pjlink);
179 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
180 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
181 }
182
183 pjlink->parseDepth++;
184 parser->jsonDepth++;
185
186 result = CALL_OR_STOP(pjlink->pif->parse_start_map)(pjlink);
187 if (result == jlif_key_child_link) {
188 parser->key_is_link = 1;
189 result = jlif_continue;
190 }
191
192 IFDEBUG(10)
193 printf("dbjl_start_map -> %d\n", result);
194
195 return dbjl_return(parser, result);
196}
197
198static int dbjl_map_key(void *ctx, const unsigned char *key, unsigned len) {
199 parseContext *parser = (parseContext *) ctx;
200 jlink *pjlink = parser->pjlink;
201 char *link_name;
202 linkSup *linkSup;
203 jlif *pjlif;
204
205 if (!parser->key_is_link) {
206 if (!pjlink) {
207 errlogPrintf("dbJLinkInit: Illegal second link key '%.*s'\n",
208 len, key);
209 return dbjl_return(parser, jlif_stop);
210 }
211
212 IFDEBUG(10) {
213 printf("dbjl_map_key(%s@%p, \"%.*s\")\t",
214 pjlink->pif->name, pjlink, len, key);
215 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
216 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
217 }
218
219 assert(pjlink->parseDepth > 0);
220 return dbjl_return(parser,
221 CALL_OR_STOP(pjlink->pif->parse_map_key)(pjlink,
222 (const char *) key, len));
223 }
224
225 IFDEBUG(10) {
226 printf("dbjl_map_key(NULL, \"%.*s\")\t", len, key);
227 printf(" jsonDepth=%d, parseDepth=00, key_is_link=%d\n",
228 parser->jsonDepth, parser->key_is_link);
229 }
230
231 link_name = dbmfStrndup((const char *) key, len);
232
233 linkSup = dbFindLinkSup(pdbbase, link_name);
234 if (!linkSup) {
235 errlogPrintf("dbJLinkInit: Link type '%s' not found\n",
236 link_name);
237 dbmfFree(link_name);
238 return dbjl_return(parser, jlif_stop);
239 }
240
241 pjlif = linkSup->pjlif;
242 if (!pjlif) {
243 errlogPrintf("dbJLinkInit: Support for Link type '%s' not loaded\n",
244 link_name);
245 dbmfFree(link_name);
246 return dbjl_return(parser, jlif_stop);
247 }
248
249 dbmfFree(link_name);
250
251 pjlink = pjlif->alloc_jlink(parser->dbfType);
252 if (!pjlink) {
253 errlogPrintf("dbJLinkInit: Out of memory\n");
254 return dbjl_return(parser, jlif_stop);
255 }
256 pjlink->pif = pjlif;
257 pjlink->parent = NULL;
258 pjlink->parseDepth = 0;
259
260 if (parser->pjlink) {
261 /* We're starting a child link, save its parent */
262 pjlink->parent = parser->pjlink;
263 }
264 parser->pjlink = pjlink;
265 parser->key_is_link = 0;
266
267 IFDEBUG(8)
268 printf("dbjl_map_key: New %s@%p\n", pjlink->pif->name, pjlink);
269
270 return jlif_continue;
271}
272
273static int dbjl_end_map(void *ctx) {
274 parseContext *parser = (parseContext *) ctx;
275 jlink *pjlink = parser->pjlink;
276 jlif_result result;
277
278 IFDEBUG(10) {
279 printf("dbjl_end_map(%s@%p)\t",
280 pjlink ? pjlink->pif->name : "NULL", pjlink);
281 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
282 parser->jsonDepth, pjlink ? pjlink->parseDepth : 0,
283 parser->key_is_link);
284 }
285
286 parser->jsonDepth--;
287 if (pjlink) {
288 pjlink->parseDepth--;
289
290 result = dbjl_value(parser,
291 CALL_OR_STOP(pjlink->pif->parse_end_map)(pjlink));
292 }
293 else {
294 result = jlif_continue;
295 }
296 return result;
297}
298
299static int dbjl_start_array(void *ctx) {
300 parseContext *parser = (parseContext *) ctx;
301 jlink *pjlink = parser->pjlink;
302
303 IFDEBUG(10) {
304 printf("dbjl_start_array(%s@%p)\t", pjlink->pif->name, pjlink);
305 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
306 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
307 }
308
309 assert(pjlink);
310 pjlink->parseDepth++;
311 parser->jsonDepth++;
312
313 return dbjl_return(parser,
314 CALL_OR_STOP(pjlink->pif->parse_start_array)(pjlink));
315}
316
317static int dbjl_end_array(void *ctx) {
318 parseContext *parser = (parseContext *) ctx;
319 jlink *pjlink = parser->pjlink;
320
321 IFDEBUG(10) {
322 printf("dbjl_end_array(%s@%p)\t", pjlink->pif->name, pjlink);
323 printf(" jsonDepth=%d, parseDepth=%d, key_is_link=%d\n",
324 parser->jsonDepth, pjlink->parseDepth, parser->key_is_link);
325 }
326
327 assert(pjlink);
328 pjlink->parseDepth--;
329 parser->jsonDepth--;
330
331 return dbjl_value(parser,
332 CALL_OR_STOP(pjlink->pif->parse_end_array)(pjlink));
333}
334
335
336static yajl_callbacks dbjl_callbacks = {
337 dbjl_null, dbjl_boolean, dbjl_integer, dbjl_double, NULL, dbjl_string,
338 dbjl_start_map, dbjl_map_key, dbjl_end_map, dbjl_start_array, dbjl_end_array
339};
340
341static const yajl_parser_config dbjl_config =
342 { 0, 0 }; /* allowComments = NO, checkUTF8 = NO */
343
344long dbJLinkParse(const char *json, size_t jlen, short dbfType,
345 jlink **ppjlink)
346{
347 parseContext context, *parser = &context;
348 yajl_alloc_funcs dbjl_allocs;
349 yajl_handle yh;
350 yajl_status ys;
351 long status;
352
353 IFDEBUG(10)
354 printf("dbJLinkInit(\"%.*s\", %d, %p)\n",
355 (int) jlen, json, dbfType, ppjlink);
356
357 parser->pjlink = NULL;
358 parser->product = NULL;
359 parser->dbfType = dbfType;
360 parser->jsonDepth = 0;
361 parser->key_is_link = 0;
362
363 IFDEBUG(10)
364 printf("dbJLinkInit: jsonDepth=%d, key_is_link=%d\n",
365 parser->jsonDepth, parser->key_is_link);
366
367 yajl_set_default_alloc_funcs(&dbjl_allocs);
368 yh = yajl_alloc(&dbjl_callbacks, &dbjl_config, &dbjl_allocs, parser);
369 if (!yh)
370 return S_db_noMemory;
371
372 ys = yajl_parse(yh, (const unsigned char *) json, (unsigned) jlen);
373 if (ys == yajl_status_insufficient_data)
374 ys = yajl_parse_complete(yh);
375
376 switch (ys) {
377 unsigned char *err;
378
379 case yajl_status_ok:
380 assert(parser->jsonDepth == 0);
381 *ppjlink = parser->product;
382 status = 0;
383 break;
384
385 case yajl_status_error:
386 err = yajl_get_error(yh, 1, (const unsigned char *) json, (unsigned) jlen);
387 errlogPrintf("dbJLinkInit: %s\n", err);
388 yajl_free_error(yh, err);
389 dbJLinkFree(parser->pjlink);
390 /* fall through */
391 default:
392 status = S_db_badField;
393 }
394
395 yajl_free(yh);
396 return status;
397}
398
399long dbJLinkInit(struct link *plink)
400{
401 jlink *pjlink;
402
403 assert(plink);
404 pjlink = plink->value.json.jlink;
405
406 if (pjlink)
407 plink->lset = pjlink->pif->get_lset(pjlink);
408
409 dbLinkOpen(plink);
410 return 0;
411}
412
413void dbJLinkFree(jlink *pjlink)
414{
415 if (pjlink)
416 pjlink->pif->free_jlink(pjlink);
417}
418
419void dbJLinkReport(jlink *pjlink, int level, int indent) {
420 if (pjlink && pjlink->pif->report)
421 pjlink->pif->report(pjlink, level, indent);
422}
423
424long dbJLinkMapChildren(struct link *plink, jlink_map_fn rtn, void *ctx)
425{
426 jlink *pjlink;
427 long status;
428
429 if (!plink || plink->type != JSON_LINK)
430 return 0;
431
432 pjlink = plink->value.json.jlink;
433 if (!pjlink)
434 return 0;
435
436 status = rtn(pjlink, ctx);
437 if (!status && pjlink->pif->map_children)
438 status = pjlink->pif->map_children(pjlink, rtn, ctx);
439
440 return status;
441}
442
443long dbjlr(const char *recname, int level)
444{
445 DBENTRY dbentry;
446 DBENTRY * const pdbentry = &dbentry;
447 long status;
448
449 if (!recname || recname[0] == '\0' || !strcmp(recname, "*")) {
450 recname = NULL;
451 printf("JSON links in all records\n\n");
452 }
453 else
454 printf("JSON links in record '%s'\n\n", recname);
455
456 dbInitEntry(pdbbase, pdbentry);
457 for (status = dbFirstRecordType(pdbentry);
458 status == 0;
459 status = dbNextRecordType(pdbentry)) {
460 for (status = dbFirstRecord(pdbentry);
461 status == 0;
462 status = dbNextRecord(pdbentry)) {
463 dbRecordType *pdbRecordType = pdbentry->precordType;
464 dbCommon *precord = pdbentry->precnode->precord;
465 char *prec = (char *) precord;
466 int i;
467
468 if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
469 continue;
470 if (dbIsAlias(pdbentry))
471 continue;
472
473 printf(" %s record '%s':\n", pdbRecordType->name, precord->name);
474
475 dbScanLock(precord);
476 for (i = 0; i < pdbRecordType->no_links; i++) {
477 int idx = pdbRecordType->link_ind[i];
478 dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
479 DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
480
481 if (plink->type != JSON_LINK)
482 continue;
483 if (!dbLinkIsDefined(plink))
484 continue;
485
486 printf(" Link field '%s':\n", pdbFldDes->name);
487 dbJLinkReport(plink->value.json.jlink, level, 6);
488 }
489 dbScanUnlock(precord);
490 if (recname)
491 goto done;
492 }
493 }
494done:
495 return 0;
496}
497
498long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx)
499{
500 DBENTRY dbentry;
501 DBENTRY * const pdbentry = &dbentry;
502 long status;
503
504 if (recname && (recname[0] = '\0' || !strcmp(recname, "*")))
505 recname = NULL;
506
507 dbInitEntry(pdbbase, pdbentry);
508 for (status = dbFirstRecordType(pdbentry);
509 status == 0;
510 status = dbNextRecordType(pdbentry)) {
511 for (status = dbFirstRecord(pdbentry);
512 status == 0;
513 status = dbNextRecord(pdbentry)) {
514 dbRecordType *pdbRecordType = pdbentry->precordType;
515 dbCommon *precord = pdbentry->precnode->precord;
516 char *prec = (char *) precord;
517 int i;
518
519 if (recname && strcmp(recname, dbGetRecordName(pdbentry)))
520 continue;
521 if (dbIsAlias(pdbentry))
522 continue;
523
524 dbScanLock(precord);
525 for (i = 0; i < pdbRecordType->no_links; i++) {
526 int idx = pdbRecordType->link_ind[i];
527 dbFldDes *pdbFldDes = pdbRecordType->papFldDes[idx];
528 DBLINK *plink = (DBLINK *) (prec + pdbFldDes->offset);
529
530 status = dbJLinkMapChildren(plink, rtn, ctx);
531 if (status)
532 goto unlock;
533 }
534unlock:
535 dbScanUnlock(precord);
536 if (status || recname)
537 goto done;
538 }
539 }
540done:
541 return status;
542}
0543
=== added file 'src/ioc/db/dbJLink.h'
--- src/ioc/db/dbJLink.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/dbJLink.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,132 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* dbJLink.h */
8
9#ifndef INC_dbJLink_H
10#define INC_dbJLink_H
11
12#include <stdlib.h>
13#include <shareLib.h>
14
15#ifdef __cplusplus
16extern "C" {
17#endif
18
19typedef enum {
20 jlif_stop = 0,
21 jlif_continue = 1
22} jlif_result;
23
24typedef enum {
25 jlif_key_stop = jlif_stop,
26 jlif_key_continue = jlif_continue,
27 jlif_key_child_link
28} jlif_key_result;
29
30struct link;
31struct lset;
32struct jlif;
33
34typedef struct jlink {
35 struct jlif *pif; /* Link methods */
36 struct jlink *parent; /* NULL for top-level links */
37 int parseDepth; /* Used by parser, unused afterwards */
38 /* Link types extend or embed this structure for private storage */
39} jlink;
40
41typedef long (*jlink_map_fn)(jlink *, void *ctx);
42
43typedef struct jlif {
44 /* Optional parser methods below given as NULL are equivalent to
45 * providing a routine that always returns jlif_stop, meaning that
46 * this JSON construct is not allowed at this point in the parse.
47 */
48
49 const char *name;
50 /* Name for the link type, used in link value */
51
52 jlink* (*alloc_jlink)(short dbfType);
53 /* Required, allocate new link structure */
54
55 void (*free_jlink)(jlink *);
56 /* Required, release all resources allocated for link */
57
58 jlif_result (*parse_null)(jlink *);
59 /* Optional, parser saw a null value */
60
61 jlif_result (*parse_boolean)(jlink *, int val);
62 /* Optional, parser saw a boolean value */
63
64 jlif_result (*parse_integer)(jlink *, long num);
65 /* Optional, parser saw an integer value */
66
67 jlif_result (*parse_double)(jlink *, double num);
68 /* Optional, parser saw a double value */
69
70 jlif_result (*parse_string)(jlink *, const char *val, size_t len);
71 /* Optional, parser saw a string value */
72
73 jlif_key_result (*parse_start_map)(jlink *);
74 /* Optional, parser saw an open-brace '{'. Return jlif_key_child_link
75 * to expect a child link next (extra key/value pairs may follow).
76 */
77
78 jlif_result (*parse_map_key)(jlink *, const char *key, size_t len);
79 /* Optional, parser saw a map key */
80
81 jlif_result (*parse_end_map)(jlink *);
82 /* Optional, parser saw a close-brace '}' */
83
84 jlif_result (*parse_start_array)(jlink *);
85 /* Optional, parser saw an open-bracket */
86
87 jlif_result (*parse_end_array)(jlink *);
88 /* Optional, parser saw a close-bracket */
89
90 void (*end_child)(jlink *parent, jlink *child);
91 /* Optional, called with pointer to the new child link after
92 * parse_start_map() returned jlif_key_child_link */
93
94 struct lset* (*get_lset)(const jlink *);
95 /* Required, return lset for this link instance */
96
97 void (*report)(const jlink *, int level, int indent);
98 /* Optional, print status information about this link instance, then
99 * if (level > 0) print a link identifier (at indent+2) and call
100 * dbJLinkReport(child, level-1, indent+4)
101 * for each child.
102 */
103
104 long (*map_children)(jlink *, jlink_map_fn rtn, void *ctx);
105 /* Optional, call dbJLinkMapChildren() on all embedded links.
106 * Stop immediately and return status if non-zero.
107 */
108
109 /* Link types must NOT extend this table with their own routines,
110 * this space is reserved for extensions to the jlink interface.
111 */
112} jlif;
113
114epicsShareFunc long dbJLinkParse(const char *json, size_t len, short dbfType,
115 jlink **ppjlink);
116epicsShareFunc long dbJLinkInit(struct link *plink);
117
118epicsShareFunc void dbJLinkFree(jlink *);
119epicsShareFunc void dbJLinkReport(jlink *, int level, int indent);
120
121epicsShareFunc long dbJLinkMapChildren(struct link *,
122 jlink_map_fn rtn, void *ctx);
123
124epicsShareFunc long dbjlr(const char *recname, int level);
125epicsShareFunc long dbJLinkMapAll(char *recname, jlink_map_fn rtn, void *ctx);
126
127#ifdef __cplusplus
128}
129#endif
130
131#endif /* INC_dbJLink_H */
132
0133
=== modified file 'src/ioc/db/dbLink.c'
--- src/ioc/db/dbLink.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbLink.c 2017-03-03 18:23:23 +0000
@@ -19,62 +19,35 @@
19#include <string.h>19#include <string.h>
2020
21#include "alarm.h"21#include "alarm.h"
22#include "cantProceed.h"
23#include "cvtFast.h"22#include "cvtFast.h"
24#include "dbDefs.h"23#include "dbDefs.h"
25#include "ellLib.h"24#include "ellLib.h"
26#include "epicsThread.h"
27#include "epicsTime.h"25#include "epicsTime.h"
28#include "errlog.h"26#include "errlog.h"
2927
30#include "caeventmask.h"28#include "caeventmask.h"
3129
32#define epicsExportSharedSymbols30#define epicsExportSharedSymbols
33#include "callback.h"
34#include "dbAccessDefs.h"31#include "dbAccessDefs.h"
35#include "dbAddr.h"32#include "dbAddr.h"
36#include "dbBase.h"33#include "dbBase.h"
37#include "dbBkpt.h"
38#include "dbCa.h"34#include "dbCa.h"
39#include "dbCommon.h"35#include "dbCommon.h"
40#include "dbConvertFast.h"36#include "dbConstLink.h"
41#include "dbConvert.h"37#include "dbDbLink.h"
42#include "dbEvent.h"
43#include "db_field_log.h"38#include "db_field_log.h"
44#include "dbFldTypes.h"39#include "dbFldTypes.h"
45#include "dbFldTypes.h"40#include "dbJLink.h"
46#include "dbLink.h"41#include "dbLink.h"
47#include "dbLockPvt.h"42#include "dbLock.h"
48#include "dbNotify.h"
49#include "dbScan.h"43#include "dbScan.h"
50#include "dbStaticLib.h"44#include "dbStaticLib.h"
51#include "devSup.h"45#include "devSup.h"
52#include "epicsEvent.h"
53#include "errMdef.h"
54#include "link.h"46#include "link.h"
55#include "recGbl.h"47#include "recGbl.h"
56#include "recSup.h"48#include "recSup.h"
57#include "special.h"49#include "special.h"
5850
59static void inherit_severity(const struct pv_link *ppv_link, dbCommon *pdest,
60 epicsEnum16 stat, epicsEnum16 sevr)
61{
62 switch (ppv_link->pvlMask & pvlOptMsMode) {
63 case pvlOptNMS:
64 break;
65 case pvlOptMSI:
66 if (sevr < INVALID_ALARM)
67 break;
68 /* Fall through */
69 case pvlOptMS:
70 recGblSetSevr(pdest, LINK_ALARM, sevr);
71 break;
72 case pvlOptMSS:
73 recGblSetSevr(pdest, stat, sevr);
74 break;
75 }
76}
77
78/* How to identify links in error messages */51/* How to identify links in error messages */
79static const char * link_field_name(const struct link *plink)52static const char * link_field_name(const struct link *plink)
80{53{
@@ -94,6 +67,7 @@
94}67}
9568
9669
70<<<<<<< TREE
97/***************************** Constant Links *****************************/71/***************************** Constant Links *****************************/
9872
99/* Forward definition */73/* Forward definition */
@@ -443,6 +417,8 @@
443 dbDbScanFwdLink417 dbDbScanFwdLink
444};418};
445419
420=======
421>>>>>>> MERGE-SOURCE
446/***************************** Generic Link API *****************************/422/***************************** Generic Link API *****************************/
447423
448void dbInitLink(struct link *plink, short dbfType)424void dbInitLink(struct link *plink, short dbfType)
@@ -454,6 +430,11 @@
454 return;430 return;
455 }431 }
456432
433 if (plink->type == JSON_LINK) {
434 dbJLinkInit(plink);
435 return;
436 }
437
457 if (plink->type != PV_LINK)438 if (plink->type != PV_LINK)
458 return;439 return;
459440
@@ -487,7 +468,8 @@
487 }468 }
488}469}
489470
490void dbAddLink(dbLocker *locker, struct link *plink, short dbfType, DBADDR *ptarget)471void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,
472 DBADDR *ptarget)
491{473{
492 struct dbCommon *precord = plink->precord;474 struct dbCommon *precord = plink->precord;
493475
@@ -496,6 +478,18 @@
496 return;478 return;
497 }479 }
498480
481 if (plink->type == JSON_LINK) {
482 /*
483 * FIXME: Can't create DB links as dbJLink types yet,
484 * dbLock.c doesn't have any way to find/track them.
485 */
486 dbJLinkInit(plink);
487 return;
488 }
489
490 if (plink->type != PV_LINK)
491 return;
492
499 if (plink == &precord->tsel)493 if (plink == &precord->tsel)
500 recGblTSELwasModified(plink);494 recGblTSELwasModified(plink);
501495
@@ -518,16 +512,15 @@
518 }512 }
519}513}
520514
521long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)515void dbLinkOpen(struct link *plink)
522{516{
523 if (plink->type == CONSTANT)517 lset *plset = plink->lset;
524 return dbConstLoadLink(plink, dbrType, pbuffer);
525518
526 /* Could pass a type hint to the other link types here */519 if (plset && plset->openLink)
527 return S_db_notFound;520 plset->openLink(plink);
528}521}
529522
530void dbRemoveLink(dbLocker *locker, struct link *plink)523void dbRemoveLink(struct dbLocker *locker, struct link *plink)
531{524{
532 lset *plset = plink->lset;525 lset *plset = plink->lset;
533526
@@ -536,6 +529,65 @@
536 plset->removeLink(locker, plink);529 plset->removeLink(locker, plink);
537 plink->lset = NULL;530 plink->lset = NULL;
538 }531 }
532 if (plink->type == JSON_LINK)
533 plink->value.json.jlink = NULL;
534}
535
536int dbLinkIsDefined(const struct link *plink)
537{
538 return (plink->lset != 0);
539}
540
541int dbLinkIsConstant(const struct link *plink)
542{
543 lset *plset = plink->lset;
544
545 if (plset)
546 return plset->isConstant;
547
548 return -1;
549}
550
551int dbLinkIsVolatile(const struct link *plink)
552{
553 lset *plset = plink->lset;
554
555 if (plset)
556 return plset->isVolatile;
557
558 return -1;
559}
560
561long dbLoadLink(struct link *plink, short dbrType, void *pbuffer)
562{
563 lset *plset = plink->lset;
564
565 if (plset && plset->loadScalar)
566 return plset->loadScalar(plink, dbrType, pbuffer);
567
568 return S_db_noLSET;
569}
570
571long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
572 epicsUInt32 *plen)
573{
574 lset *plset = plink->lset;
575
576 if (plset && plset->loadLS)
577 return plset->loadLS(plink, pbuffer, size, plen);
578
579 return S_db_noLSET;
580}
581
582long dbLoadLinkArray(struct link *plink, short dbrType, void *pbuffer,
583 long *pnRequest)
584{
585 lset *plset = plink->lset;
586
587 if (plset && plset->loadArray)
588 return plset->loadArray(plink, dbrType, pbuffer, pnRequest);
589
590 return S_db_noLSET;
539}591}
540592
541int dbIsLinkConnected(const struct link *plink)593int dbIsLinkConnected(const struct link *plink)
@@ -563,7 +615,7 @@
563 lset *plset = plink->lset;615 lset *plset = plink->lset;
564616
565 if (!plset || !plset->getElements)617 if (!plset || !plset->getElements)
566 return S_db_badField;618 return S_db_noLSET;
567619
568 return plset->getElements(plink, nelements);620 return plset->getElements(plink, nelements);
569}621}
@@ -572,24 +624,20 @@
572 long *poptions, long *pnRequest)624 long *poptions, long *pnRequest)
573{625{
574 struct dbCommon *precord = plink->precord;626 struct dbCommon *precord = plink->precord;
575 epicsEnum16 sevr = 0, stat = 0;
576 lset *plset = plink->lset;627 lset *plset = plink->lset;
577 long status;628 long status;
578629
579 if (poptions && *poptions) {630 if (poptions && *poptions) {
580 printf("dbGetLinkValue: Use of poptions no longer supported\n");631 printf("dbGetLink: Use of poptions no longer supported\n");
581 *poptions = 0;632 *poptions = 0;
582 }633 }
583634
584 if (!plset || !plset->getValue)635 if (!plset || !plset->getValue)
585 return -1;636 return -1;
586637
587 status = plset->getValue(plink, dbrType, pbuffer, &stat, &sevr, pnRequest);638 status = plset->getValue(plink, dbrType, pbuffer, pnRequest);
588 if (status) {639 if (status)
589 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);640 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
590 } else {
591 inherit_severity(&plink->value.pv_link, precord, stat, sevr);
592 }
593 return status;641 return status;
594}642}
595643
@@ -598,7 +646,7 @@
598 lset *plset = plink->lset;646 lset *plset = plink->lset;
599647
600 if (!plset || !plset->getControlLimits)648 if (!plset || !plset->getControlLimits)
601 return S_db_notFound;649 return S_db_noLSET;
602650
603 return plset->getControlLimits(plink, low, high);651 return plset->getControlLimits(plink, low, high);
604}652}
@@ -608,7 +656,7 @@
608 lset *plset = plink->lset;656 lset *plset = plink->lset;
609657
610 if (!plset || !plset->getGraphicLimits)658 if (!plset || !plset->getGraphicLimits)
611 return S_db_notFound;659 return S_db_noLSET;
612660
613 return plset->getGraphicLimits(plink, low, high);661 return plset->getGraphicLimits(plink, low, high);
614}662}
@@ -619,7 +667,7 @@
619 lset *plset = plink->lset;667 lset *plset = plink->lset;
620668
621 if (!plset || !plset->getAlarmLimits)669 if (!plset || !plset->getAlarmLimits)
622 return S_db_notFound;670 return S_db_noLSET;
623671
624 return plset->getAlarmLimits(plink, lolo, low, high, hihi);672 return plset->getAlarmLimits(plink, lolo, low, high, hihi);
625}673}
@@ -629,7 +677,7 @@
629 lset *plset = plink->lset;677 lset *plset = plink->lset;
630678
631 if (!plset || !plset->getPrecision)679 if (!plset || !plset->getPrecision)
632 return S_db_notFound;680 return S_db_noLSET;
633681
634 return plset->getPrecision(plink, precision);682 return plset->getPrecision(plink, precision);
635}683}
@@ -639,7 +687,7 @@
639 lset *plset = plink->lset;687 lset *plset = plink->lset;
640688
641 if (!plset || !plset->getUnits)689 if (!plset || !plset->getUnits)
642 return S_db_notFound;690 return S_db_noLSET;
643691
644 return plset->getUnits(plink, units, unitsSize);692 return plset->getUnits(plink, units, unitsSize);
645}693}
@@ -650,7 +698,7 @@
650 lset *plset = plink->lset;698 lset *plset = plink->lset;
651699
652 if (!plset || !plset->getAlarm)700 if (!plset || !plset->getAlarm)
653 return S_db_notFound;701 return S_db_noLSET;
654702
655 return plset->getAlarm(plink, status, severity);703 return plset->getAlarm(plink, status, severity);
656}704}
@@ -660,7 +708,7 @@
660 lset *plset = plink->lset;708 lset *plset = plink->lset;
661709
662 if (!plset || !plset->getTimeStamp)710 if (!plset || !plset->getTimeStamp)
663 return S_db_notFound;711 return S_db_noLSET;
664712
665 return plset->getTimeStamp(plink, pstamp);713 return plset->getTimeStamp(plink, pstamp);
666}714}
@@ -672,7 +720,7 @@
672 long status;720 long status;
673721
674 if (!plset || !plset->putValue)722 if (!plset || !plset->putValue)
675 return S_db_notFound;723 return S_db_noLSET;
676724
677 status = plset->putValue(plink, dbrType, pbuffer, nRequest);725 status = plset->putValue(plink, dbrType, pbuffer, nRequest);
678 if (status) {726 if (status) {
@@ -683,6 +731,33 @@
683 return status;731 return status;
684}732}
685733
734void dbLinkAsyncComplete(struct link *plink)
735{
736 dbCommon *pdbCommon = plink->precord;
737
738 dbScanLock(pdbCommon);
739 pdbCommon->rset->process(pdbCommon);
740 dbScanUnlock(pdbCommon);
741}
742
743long dbPutLinkAsync(struct link *plink, short dbrType, const void *pbuffer,
744 long nRequest)
745{
746 lset *plset = plink->lset;
747 long status;
748
749 if (!plset || !plset->putAsync)
750 return S_db_noLSET;
751
752 status = plset->putAsync(plink, dbrType, pbuffer, nRequest);
753 if (status) {
754 struct dbCommon *precord = plink->precord;
755
756 recGblSetSevr(precord, LINK_ALARM, INVALID_ALARM);
757 }
758 return status;
759}
760
686void dbScanFwdLink(struct link *plink)761void dbScanFwdLink(struct link *plink)
687{762{
688 lset *plset = plink->lset;763 lset *plset = plink->lset;
@@ -693,20 +768,6 @@
693768
694/* Helper functions for long string support */769/* Helper functions for long string support */
695770
696long dbLoadLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
697 epicsUInt32 *plen)
698{
699 if (plink->type == CONSTANT &&
700 plink->value.constantStr) {
701 strncpy(pbuffer, plink->value.constantStr, --size);
702 pbuffer[size] = 0;
703 *plen = (epicsUInt32) strlen(pbuffer) + 1;
704 return 0;
705 }
706
707 return S_db_notFound;
708}
709
710long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,771long dbGetLinkLS(struct link *plink, char *pbuffer, epicsUInt32 size,
711 epicsUInt32 *plen)772 epicsUInt32 *plen)
712{773{
713774
=== modified file 'src/ioc/db/dbLink.h'
--- src/ioc/db/dbLink.h 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbLink.h 2017-03-03 18:23:23 +0000
@@ -28,12 +28,31 @@
28struct dbLocker;28struct dbLocker;
2929
30typedef struct lset {30typedef struct lset {
31 /* Characteristics of the link type */
32 const unsigned isConstant:1;
33 const unsigned isVolatile:1;
34
35 /* Activation */
36 void (*openLink)(struct link *plink);
37
38 /* Destructor */
31 void (*removeLink)(struct dbLocker *locker, struct link *plink);39 void (*removeLink)(struct dbLocker *locker, struct link *plink);
40
41 /* Const init, data type hinting */
42 long (*loadScalar)(struct link *plink, short dbrType, void *pbuffer);
43 long (*loadLS)(struct link *plink, char *pbuffer, epicsUInt32 size,
44 epicsUInt32 *plen);
45 long (*loadArray)(struct link *plink, short dbrType, void *pbuffer,
46 long *pnRequest);
47
48 /* Metadata */
32 int (*isConnected)(const struct link *plink);49 int (*isConnected)(const struct link *plink);
33 int (*getDBFtype)(const struct link *plink);50 int (*getDBFtype)(const struct link *plink);
34 long (*getElements)(const struct link *plink, long *nelements);51 long (*getElements)(const struct link *plink, long *nelements);
52
53 /* Get data */
35 long (*getValue)(struct link *plink, short dbrType, void *pbuffer,54 long (*getValue)(struct link *plink, short dbrType, void *pbuffer,
36 epicsEnum16 *pstat, epicsEnum16 *psevr, long *pnRequest);55 long *pnRequest);
37 long (*getControlLimits)(const struct link *plink, double *lo, double *hi);56 long (*getControlLimits)(const struct link *plink, double *lo, double *hi);
38 long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);57 long (*getGraphicLimits)(const struct link *plink, double *lo, double *hi);
39 long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,58 long (*getAlarmLimits)(const struct link *plink, double *lolo, double *lo,
@@ -43,8 +62,14 @@
43 long (*getAlarm)(const struct link *plink, epicsEnum16 *status,62 long (*getAlarm)(const struct link *plink, epicsEnum16 *status,
44 epicsEnum16 *severity);63 epicsEnum16 *severity);
45 long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);64 long (*getTimeStamp)(const struct link *plink, epicsTimeStamp *pstamp);
65
66 /* Put data */
46 long (*putValue)(struct link *plink, short dbrType,67 long (*putValue)(struct link *plink, short dbrType,
47 const void *pbuffer, long nRequest);68 const void *pbuffer, long nRequest);
69 long (*putAsync)(struct link *plink, short dbrType,
70 const void *pbuffer, long nRequest);
71
72 /* Process */
48 void (*scanForward)(struct link *plink);73 void (*scanForward)(struct link *plink);
49} lset;74} lset;
5075
@@ -52,11 +77,20 @@
52 dbGetAlarm(link, NULL, sevr)77 dbGetAlarm(link, NULL, sevr)
5378
54epicsShareFunc void dbInitLink(struct link *plink, short dbfType);79epicsShareFunc void dbInitLink(struct link *plink, short dbfType);
55epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink, short dbfType,80epicsShareFunc void dbAddLink(struct dbLocker *locker, struct link *plink,
56 DBADDR *ptarget);81 short dbfType, DBADDR *ptarget);
82
83epicsShareFunc void dbLinkOpen(struct link *plink);
84epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);
85
86epicsShareFunc int dbLinkIsDefined(const struct link *plink); /* 0 or 1 */
87epicsShareFunc int dbLinkIsConstant(const struct link *plink); /* -1, 0 or 1 */
88epicsShareFunc int dbLinkIsVolatile(const struct link *plink); /* -1, 0 or 1 */
89
57epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,90epicsShareFunc long dbLoadLink(struct link *plink, short dbrType,
58 void *pbuffer);91 void *pbuffer);
59epicsShareFunc void dbRemoveLink(struct dbLocker *locker, struct link *plink);92epicsShareFunc long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
93 long *pnRequest);
6094
61epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);95epicsShareFunc long dbGetNelements(const struct link *plink, long *nelements);
62epicsShareFunc int dbIsLinkConnected(const struct link *plink);96epicsShareFunc int dbIsLinkConnected(const struct link *plink);
@@ -78,6 +112,9 @@
78 epicsTimeStamp *pstamp);112 epicsTimeStamp *pstamp);
79epicsShareFunc long dbPutLink(struct link *plink, short dbrType,113epicsShareFunc long dbPutLink(struct link *plink, short dbrType,
80 const void *pbuffer, long nRequest);114 const void *pbuffer, long nRequest);
115epicsShareFunc void dbLinkAsyncComplete(struct link *plink);
116epicsShareFunc long dbPutLinkAsync(struct link *plink, short dbrType,
117 const void *pbuffer, long nRequest);
81epicsShareFunc void dbScanFwdLink(struct link *plink);118epicsShareFunc void dbScanFwdLink(struct link *plink);
82119
83epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer,120epicsShareFunc long dbLoadLinkLS(struct link *plink, char *pbuffer,
84121
=== modified file 'src/ioc/db/dbLock.c'
--- src/ioc/db/dbLock.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbLock.c 2017-03-03 18:23:23 +0000
@@ -190,6 +190,8 @@
190 lockRecord * const lr = precord->lset;190 lockRecord * const lr = precord->lset;
191 lockSet *ls;191 lockSet *ls;
192192
193 assert(lr);
194
193 ls = dbLockGetRef(lr);195 ls = dbLockGetRef(lr);
194 assert(epicsAtomicGetIntT(&ls->refcount)>0);196 assert(epicsAtomicGetIntT(&ls->refcount)>0);
195197
196198
=== modified file 'src/ioc/db/dbTest.c'
=== modified file 'src/ioc/db/dbUnitTest.c'
--- src/ioc/db/dbUnitTest.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/dbUnitTest.c 2017-03-03 18:23:23 +0000
@@ -106,8 +106,8 @@
106 DBADDR addr;106 DBADDR addr;
107 union anybuf pod;107 union anybuf pod;
108108
109 if(dbNameToAddr(pv, &addr)) {109 if (dbNameToAddr(pv, &addr)) {
110 testFail("Missing PV %s", pv);110 testFail("Missing PV \"%s\"", pv);
111 return S_dbLib_recNotFound;111 return S_dbLib_recNotFound;
112 }112 }
113113
@@ -152,7 +152,7 @@
152 ret = testdbVPutField(pv, dbrType, ap);152 ret = testdbVPutField(pv, dbrType, ap);
153 va_end(ap);153 va_end(ap);
154154
155 testOk(ret==0, "dbPutField(%s, %d, ...) == %ld", pv, dbrType, ret);155 testOk(ret==0, "dbPutField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, ret, errSymMsg(ret));
156}156}
157157
158void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)158void testdbPutFieldFail(long status, const char* pv, short dbrType, ...)
@@ -164,10 +164,8 @@
164 ret = testdbVPutField(pv, dbrType, ap);164 ret = testdbVPutField(pv, dbrType, ap);
165 va_end(ap);165 va_end(ap);
166166
167 if(ret==status)167 testOk(ret==status, "dbPutField(\"%s\", %d, ...) -> %#lx (%s) == %#lx (%s)",
168 testPass("dbPutField(\"%s\", %d, ...) == %ld", pv, dbrType, status);168 pv, dbrType, status, errSymMsg(status), ret, errSymMsg(ret));
169 else
170 testFail("dbPutField(\"%s\", %d, ...) != %ld (%ld)", pv, dbrType, status, ret);
171}169}
172170
173void testdbGetFieldEqual(const char* pv, short dbrType, ...)171void testdbGetFieldEqual(const char* pv, short dbrType, ...)
@@ -187,13 +185,13 @@
187 long status;185 long status;
188186
189 if(dbNameToAddr(pv, &addr)) {187 if(dbNameToAddr(pv, &addr)) {
190 testFail("Missing PV %s", pv);188 testFail("Missing PV \"%s\"", pv);
191 return;189 return;
192 }190 }
193191
194 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);192 status = dbGetField(&addr, dbrType, pod.bytes, NULL, &nReq, NULL);
195 if(status) {193 if (status) {
196 testFail("dbGetField(\"%s\",%d,...) returns %ld", pv, dbrType, status);194 testFail("dbGetField(\"%s\", %d, ...) -> %#lx (%s)", pv, dbrType, status, errSymMsg(status));
197 return;195 return;
198 }196 }
199197
@@ -226,8 +224,8 @@
226{224{
227 DBADDR addr;225 DBADDR addr;
228226
229 if(dbNameToAddr(pv, &addr))227 if (dbNameToAddr(pv, &addr))
230 testAbort("Missing record %s", pv);228 testAbort("Missing record \"%s\"", pv);
231229
232 return addr.precord;230 return addr.precord;
233}231}
234232
=== modified file 'src/ioc/db/recGbl.c'
--- src/ioc/db/recGbl.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/recGbl.c 2017-03-03 18:23:23 +0000
@@ -17,6 +17,7 @@
17#include <string.h>17#include <string.h>
18#include <limits.h>18#include <limits.h>
1919
20#include "alarm.h"
20#include "dbDefs.h"21#include "dbDefs.h"
21#include "epicsMath.h"22#include "epicsMath.h"
22#include "epicsPrint.h"23#include "epicsPrint.h"
@@ -30,7 +31,6 @@
30#include "dbAccessDefs.h"31#include "dbAccessDefs.h"
31#include "dbAddr.h"32#include "dbAddr.h"
32#include "dbBase.h"33#include "dbBase.h"
33#include "dbCa.h"
34#include "dbCommon.h"34#include "dbCommon.h"
35#include "dbEvent.h"35#include "dbEvent.h"
36#include "db_field_log.h"36#include "db_field_log.h"
@@ -214,7 +214,27 @@
214 }214 }
215 return FALSE;215 return FALSE;
216}216}
217
218217
218
219void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
220 epicsEnum16 sevr)
221{
222 switch (msMode) {
223 case pvlOptNMS:
224 break;
225 case pvlOptMSI:
226 if (sevr < INVALID_ALARM)
227 break;
228 /* Fall through */
229 case pvlOptMS:
230 recGblSetSevr(precord, LINK_ALARM, sevr);
231 break;
232 case pvlOptMSS:
233 recGblSetSevr(precord, stat, sevr);
234 break;
235 }
236}
237
238
219void recGblFwdLink(void *precord)239void recGblFwdLink(void *precord)
220{240{
221 dbCommon *pdbc = precord;241 dbCommon *pdbc = precord;
@@ -236,7 +256,7 @@
236 dbCommon* prec = (dbCommon*)pvoid;256 dbCommon* prec = (dbCommon*)pvoid;
237 struct link *plink = &prec->tsel;257 struct link *plink = &prec->tsel;
238258
239 if (plink->type != CONSTANT) {259 if (!dbLinkIsConstant(plink)) {
240 struct pv_link *ppv_link = &plink->value.pv_link;260 struct pv_link *ppv_link = &plink->value.pv_link;
241261
242 if (ppv_link->pvlMask & pvlOptTSELisTime) {262 if (ppv_link->pvlMask & pvlOptTSELisTime) {
243263
=== modified file 'src/ioc/db/recGbl.h'
--- src/ioc/db/recGbl.h 2014-10-31 21:18:25 +0000
+++ src/ioc/db/recGbl.h 2017-03-03 18:23:23 +0000
@@ -59,6 +59,8 @@
59epicsShareFunc unsigned short recGblResetAlarms(void *precord);59epicsShareFunc unsigned short recGblResetAlarms(void *precord);
60epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat,60epicsShareFunc int recGblSetSevr(void *precord, epicsEnum16 new_stat,
61 epicsEnum16 new_sevr);61 epicsEnum16 new_sevr);
62epicsShareFunc void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
63 epicsEnum16 sevr);
62epicsShareFunc void recGblFwdLink(void *precord);64epicsShareFunc void recGblFwdLink(void *precord);
63epicsShareFunc void recGblGetTimeStamp(void *precord);65epicsShareFunc void recGblGetTimeStamp(void *precord);
64epicsShareFunc void recGblTSELwasModified(struct link *plink);66epicsShareFunc void recGblTSELwasModified(struct link *plink);
6567
=== modified file 'src/ioc/db/test/Makefile'
--- src/ioc/db/test/Makefile 2017-02-01 17:57:04 +0000
+++ src/ioc/db/test/Makefile 2017-03-03 18:23:23 +0000
@@ -18,7 +18,9 @@
18dbTestIoc_SRCS += arrRecord.c18dbTestIoc_SRCS += arrRecord.c
19dbTestIoc_SRCS += xRecord.c19dbTestIoc_SRCS += xRecord.c
20dbTestIoc_SRCS += dbLinkdset.c20dbTestIoc_SRCS += dbLinkdset.c
21dbTestIoc_SRCS += xLink.c
21dbTestIoc_SRCS += devx.c22dbTestIoc_SRCS += devx.c
23dbTestIoc_SRCS += jlinkz.c
22dbTestIoc_LIBS = dbCore ca Com24dbTestIoc_LIBS = dbCore ca Com
2325
24TARGETS += $(COMMON_DIR)/dbTestIoc.dbd26TARGETS += $(COMMON_DIR)/dbTestIoc.dbd
@@ -26,10 +28,11 @@
26dbTestIoc_DBD += menuGlobal.dbd28dbTestIoc_DBD += menuGlobal.dbd
27dbTestIoc_DBD += menuConvert.dbd29dbTestIoc_DBD += menuConvert.dbd
28dbTestIoc_DBD += menuScan.dbd30dbTestIoc_DBD += menuScan.dbd
29#dbTestIoc_DBD += arrRecord.dbd
30dbTestIoc_DBD += xRecord.dbd31dbTestIoc_DBD += xRecord.dbd
31dbTestIoc_DBD += arrRecord.dbd32dbTestIoc_DBD += arrRecord.dbd
33dbTestIoc_DBD += xLink.dbd
32dbTestIoc_DBD += devx.dbd34dbTestIoc_DBD += devx.dbd
35dbTestIoc_DBD += jlinkz.dbd
33dbTestIoc_DBD += dbLinkdset.dbd36dbTestIoc_DBD += dbLinkdset.dbd
34TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db37TESTFILES += $(COMMON_DIR)/dbTestIoc.dbd ../xRecord.db
3538
@@ -54,7 +57,7 @@
54dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp57dbPutLinkTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
55testHarness_SRCS += dbPutLinkTest.c58testHarness_SRCS += dbPutLinkTest.c
56TESTS += dbPutLinkTest59TESTS += dbPutLinkTest
57TESTFILES += ../dbPutLinkTest.db ../dbBadLink.db60TESTFILES += ../dbPutLinkTest.db ../dbPutLinkTestJ.db ../dbBadLink.db
5861
59TESTPROD_HOST += dbLockTest62TESTPROD_HOST += dbLockTest
60dbLockTest_SRCS += dbLockTest.c63dbLockTest_SRCS += dbLockTest.c
@@ -152,7 +155,7 @@
152testHarness_SRCS += recGblCheckDeadbandTest.c155testHarness_SRCS += recGblCheckDeadbandTest.c
153TESTS += recGblCheckDeadbandTest156TESTS += recGblCheckDeadbandTest
154157
155# The testHarness runs all the test programs in a known working order.158# This runs all the test programs in a known working order:
156testHarness_SRCS += epicsRunDbTests.c159testHarness_SRCS += epicsRunDbTests.c
157160
158dbTestHarness_SRCS += $(testHarness_SRCS)161dbTestHarness_SRCS += $(testHarness_SRCS)
159162
=== modified file 'src/ioc/db/test/dbLinkdset.c'
--- src/ioc/db/test/dbLinkdset.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/test/dbLinkdset.c 2017-03-03 18:23:23 +0000
@@ -29,6 +29,7 @@
29 static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \29 static dset devxLTest ## LTYPE = {4, NULL, &link_test_init, &link_test_noop, &link_test_noop}; \
30 epicsExportAddress(dset, devxLTest ## LTYPE);30 epicsExportAddress(dset, devxLTest ## LTYPE);
3131
32DEFDSET(JSON_LINK)
32DEFDSET(VME_IO)33DEFDSET(VME_IO)
33DEFDSET(CAMAC_IO)34DEFDSET(CAMAC_IO)
34DEFDSET(AB_IO)35DEFDSET(AB_IO)
3536
=== modified file 'src/ioc/db/test/dbLinkdset.dbd'
--- src/ioc/db/test/dbLinkdset.dbd 2017-02-01 17:57:04 +0000
+++ src/ioc/db/test/dbLinkdset.dbd 2017-03-03 18:23:23 +0000
@@ -1,3 +1,4 @@
1device(x, JSON_LINK, devxLTestJSON_LINK, "Unit Test JSON_LINK")
1device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")2device(x, VME_IO, devxLTestVME_IO, "Unit Test VME_IO")
2device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")3device(x, CAMAC_IO, devxLTestCAMAC_IO, "Unit Test CAMAC_IO")
3device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")4device(x, AB_IO, devxLTestAB_IO, "Unit Test AB_IO")
45
=== modified file 'src/ioc/db/test/dbPutLinkTest.c'
--- src/ioc/db/test/dbPutLinkTest.c 2017-02-01 17:57:04 +0000
+++ src/ioc/db/test/dbPutLinkTest.c 2017-03-03 18:23:23 +0000
@@ -24,8 +24,10 @@
24#include "osiFileName.h"24#include "osiFileName.h"
25#include "dbmf.h"25#include "dbmf.h"
26#include "errlog.h"26#include "errlog.h"
27#include <epicsAtomic.h>
2728
28#include "xRecord.h"29#include "xRecord.h"
30#include "jlinkz.h"
2931
30#include "testMain.h"32#include "testMain.h"
3133
@@ -60,6 +62,7 @@
60 {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},62 {"#B11 C12 N13 A14 F15 @cparam", {CAMAC_IO, "cparam", 0, "BCNAF", {11, 12, 13, 14, 15}}},
61 {" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},63 {" #B111 C112 N113 @cparam", {CAMAC_IO, "cparam", 0, "BCN", {111, 112, 113}}},
62 {" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},64 {" @hello world ", {INST_IO, "hello world", 0, "", /*{}*/}},
65 {" {\"x\":true} ", {JSON_LINK, "{\"x\":true}", 0, "", /*{}*/}},
63 {NULL}66 {NULL}
64};67};
6568
@@ -67,7 +70,7 @@
67{70{
68 const struct testParseDataT *td = testParseData;71 const struct testParseDataT *td = testParseData;
69 dbLinkInfo info;72 dbLinkInfo info;
70 testDiag("link parsing");73 testDiag("\n# Checking link parsing\n#");
71 testdbPrepare();74 testdbPrepare();
7275
73 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);76 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -80,27 +83,32 @@
80 testIocInitOk();83 testIocInitOk();
81 eltc(1);84 eltc(1);
8285
83 for(;td->str; td++) {86 for (;td->str; td++) {
84 int i, N;87 int i, N;
85 testDiag("Parse \"%s\"", td->str);88 testDiag("Parsing \"%s\"", td->str);
86 testOk1(dbParseLink(td->str, DBF_INLINK, &info)==0);89 testOk(dbParseLink(td->str, DBF_INLINK, &info) == 0, "Parser returned OK");
87 testOk1(info.ltype==td->info.ltype);90 if (!testOk(info.ltype == td->info.ltype, "Link type value"))
88 if(td->info.target)91 testDiag("Expected %d, got %d", td->info.ltype, info.ltype);
92 if (td->info.target)
89 testStrcmp(0, info.target, td->info.target);93 testStrcmp(0, info.target, td->info.target);
90 if(info.ltype==td->info.ltype) {94 if (info.ltype == td->info.ltype) {
91 switch(info.ltype) {95 switch (info.ltype) {
92 case PV_LINK:96 case PV_LINK:
93 testOk1(info.modifiers==td->info.modifiers);97 if (!testOk(info.modifiers == td->info.modifiers,
98 "PV Link modifier flags"))
99 testDiag("Expected %d, got %d", td->info.modifiers,
100 info.modifiers);
94 break;101 break;
95 case VME_IO:102 case VME_IO:
103 case CAMAC_IO:
96 testStrcmp(0, info.hwid, td->info.hwid);104 testStrcmp(0, info.hwid, td->info.hwid);
97 N = strlen(td->info.hwid);105 N = strlen(td->info.hwid);
98 for(i=0; i<N; i++)106 for (i=0; i<N; i++)
99 testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",107 testOk(info.hwnums[i]==td->info.hwnums[i], "%d == %d",
100 info.hwnums[i], td->info.hwnums[i]);108 info.hwnums[i], td->info.hwnums[i]);
101 }109 }
102 }110 }
103 free(info.target);111 dbFreeLinkInfo(&info);
104 }112 }
105113
106 testIocShutdownOk();114 testIocShutdownOk();
@@ -124,7 +132,7 @@
124{132{
125 const char * const *td = testParseFailData;133 const char * const *td = testParseFailData;
126 dbLinkInfo info;134 dbLinkInfo info;
127 testDiag("link parsing of invalid input");135 testDiag("\n# Check parsing of invalid inputs\n#");
128 testdbPrepare();136 testdbPrepare();
129137
130 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);138 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -138,8 +146,8 @@
138 eltc(1);146 eltc(1);
139147
140 for(;*td; td++) {148 for(;*td; td++) {
141 testDiag("Expect failure \"%s\"", *td);149 testOk(dbParseLink(*td, DBF_INLINK, &info) == S_dbLib_badField,
142 testOk1(dbParseLink(*td, DBF_INLINK, &info)==S_dbLib_badField);150 "dbParseLink correctly rejected \"%s\"", *td);
143 }151 }
144152
145 testIocShutdownOk();153 testIocShutdownOk();
@@ -179,7 +187,7 @@
179 const struct testDataT *td = testSetData;187 const struct testDataT *td = testSetData;
180 xRecord *prec;188 xRecord *prec;
181 DBLINK *plink;189 DBLINK *plink;
182 testDiag("DB/CA link retargeting");190 testDiag("\n# Checking DB/CA link retargeting\n#");
183 testdbPrepare();191 testdbPrepare();
184192
185 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);193 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -196,21 +204,22 @@
196 plink = &prec->lnk;204 plink = &prec->lnk;
197205
198 for (;td->linkstring;td++) {206 for (;td->linkstring;td++) {
199 testDiag("x1.LNK <- \"%s\"", td->linkstring);207 testDiag("Trying field value \"%s\"", td->linkstring);
200208
201 testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);209 testdbPutFieldOk("x1.LNK", DBF_STRING, td->linkstring);
202 if (td->linkback)210 if (td->linkback)
203 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);211 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkback);
204 else212 else
205 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);213 testdbGetFieldEqual("x1.LNK", DBF_STRING, td->linkstring);
206 testOk1(plink->type==td->linkType);214 if (!testOk(plink->type == td->linkType, "Link type"))
215 testDiag("Expected %d, got %d", td->linkType, plink->type);
207216
208 if (plink->type==td->linkType) {217 if (plink->type == td->linkType) {
209 switch(td->linkType) {218 switch (td->linkType) {
210 case CONSTANT:219 case CONSTANT:
211 if(plink->value.constantStr)220 if (plink->value.constantStr)
212 testOk1(strcmp(plink->value.constantStr,td->linkstring)==0);221 testOk1(strcmp(plink->value.constantStr, td->linkstring) == 0);
213 else if(td->linkstring[0]=='\0')222 else if (td->linkstring[0]=='\0')
214 testPass("Empty String");223 testPass("Empty String");
215 else224 else
216 testFail("oops");225 testFail("oops");
@@ -218,7 +227,7 @@
218227
219 case DB_LINK:228 case DB_LINK:
220 case CA_LINK:229 case CA_LINK:
221 testOk(plink->value.pv_link.pvlMask==td->pvlMask,230 testOk(plink->value.pv_link.pvlMask == td->pvlMask,
222 "pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask);231 "pvlMask %x == %x", plink->value.pv_link.pvlMask, td->pvlMask);
223 break;232 break;
224 }233 }
@@ -239,6 +248,7 @@
239} testHWDataT;248} testHWDataT;
240249
241static const testHWDataT testHWData[] = {250static const testHWDataT testHWData[] = {
251 {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
242 {"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"},252 {"rVME_IO", VME_IO, "#C100 S101 @parm VME_IO", {100, 101}, "parm VME_IO"},
243 {"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"},253 {"rCAMAC_IO", CAMAC_IO, "#B11 C12 N13 A14 F15 @parm CAMAC_IO", {11, 12, 13, 14, 15}, "parm CAMAC_IO"},
244 {"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"},254 {"rAB_IO", AB_IO, "#L21 A22 C23 S24 @parm AB_IO", {21, 22, 23, 24}, "parm AB_IO"},
@@ -255,63 +265,66 @@
255static void testLink(DBLINK *plink, const testHWDataT *td)265static void testLink(DBLINK *plink, const testHWDataT *td)
256{266{
257 switch(td->ltype) {267 switch(td->ltype) {
268 case JSON_LINK:
269 testOk1(strcmp(plink->value.json.string, td->parm) == 0);
270 break;
258 case VME_IO:271 case VME_IO:
259 testOk1(plink->value.vmeio.card==td->vals[0]);272 testOk1(plink->value.vmeio.card == td->vals[0]);
260 testOk1(plink->value.vmeio.signal==td->vals[1]);273 testOk1(plink->value.vmeio.signal == td->vals[1]);
261 testOk1(strcmp(plink->value.vmeio.parm, td->parm)==0);274 testOk1(strcmp(plink->value.vmeio.parm, td->parm) == 0);
262 break;275 break;
263 case CAMAC_IO:276 case CAMAC_IO:
264 testOk1(plink->value.camacio.b==td->vals[0]);277 testOk1(plink->value.camacio.b == td->vals[0]);
265 testOk1(plink->value.camacio.c==td->vals[1]);278 testOk1(plink->value.camacio.c == td->vals[1]);
266 testOk1(plink->value.camacio.n==td->vals[2]);279 testOk1(plink->value.camacio.n == td->vals[2]);
267 testOk1(plink->value.camacio.a==td->vals[3]);280 testOk1(plink->value.camacio.a == td->vals[3]);
268 testOk1(plink->value.camacio.f==td->vals[4]);281 testOk1(plink->value.camacio.f == td->vals[4]);
269 testOk1(strcmp(plink->value.camacio.parm, td->parm)==0);282 testOk1(strcmp(plink->value.camacio.parm, td->parm) == 0);
270 break;283 break;
271 case AB_IO:284 case AB_IO:
272 testOk1(plink->value.abio.link==td->vals[0]);285 testOk1(plink->value.abio.link == td->vals[0]);
273 testOk1(plink->value.abio.adapter==td->vals[1]);286 testOk1(plink->value.abio.adapter == td->vals[1]);
274 testOk1(plink->value.abio.card==td->vals[2]);287 testOk1(plink->value.abio.card == td->vals[2]);
275 testOk1(plink->value.abio.signal==td->vals[3]);288 testOk1(plink->value.abio.signal == td->vals[3]);
276 testOk1(strcmp(plink->value.abio.parm, td->parm)==0);289 testOk1(strcmp(plink->value.abio.parm, td->parm) == 0);
277 break;290 break;
278 case GPIB_IO:291 case GPIB_IO:
279 testOk1(plink->value.gpibio.link==td->vals[0]);292 testOk1(plink->value.gpibio.link == td->vals[0]);
280 testOk1(plink->value.gpibio.addr==td->vals[1]);293 testOk1(plink->value.gpibio.addr == td->vals[1]);
281 testOk1(strcmp(plink->value.gpibio.parm, td->parm)==0);294 testOk1(strcmp(plink->value.gpibio.parm, td->parm) == 0);
282 break;295 break;
283 case BITBUS_IO:296 case BITBUS_IO:
284 testOk1(plink->value.bitbusio.link==td->vals[0]);297 testOk1(plink->value.bitbusio.link == td->vals[0]);
285 testOk1(plink->value.bitbusio.node==td->vals[1]);298 testOk1(plink->value.bitbusio.node == td->vals[1]);
286 testOk1(plink->value.bitbusio.port==td->vals[2]);299 testOk1(plink->value.bitbusio.port == td->vals[2]);
287 testOk1(plink->value.bitbusio.signal==td->vals[3]);300 testOk1(plink->value.bitbusio.signal == td->vals[3]);
288 testOk1(strcmp(plink->value.bitbusio.parm, td->parm)==0);301 testOk1(strcmp(plink->value.bitbusio.parm, td->parm) == 0);
289 break;302 break;
290 case INST_IO:303 case INST_IO:
291 testOk1(strcmp(plink->value.instio.string, td->parm)==0);304 testOk1(strcmp(plink->value.instio.string, td->parm) == 0);
292 break;305 break;
293 case BBGPIB_IO:306 case BBGPIB_IO:
294 testOk1(plink->value.bbgpibio.link==td->vals[0]);307 testOk1(plink->value.bbgpibio.link == td->vals[0]);
295 testOk1(plink->value.bbgpibio.bbaddr==td->vals[1]);308 testOk1(plink->value.bbgpibio.bbaddr == td->vals[1]);
296 testOk1(plink->value.bbgpibio.gpibaddr==td->vals[2]);309 testOk1(plink->value.bbgpibio.gpibaddr == td->vals[2]);
297 testOk1(strcmp(plink->value.bbgpibio.parm, td->parm)==0);310 testOk1(strcmp(plink->value.bbgpibio.parm, td->parm) == 0);
298 break;311 break;
299 case RF_IO:312 case RF_IO:
300 testOk1(plink->value.rfio.cryo==td->vals[0]);313 testOk1(plink->value.rfio.cryo == td->vals[0]);
301 testOk1(plink->value.rfio.micro==td->vals[1]);314 testOk1(plink->value.rfio.micro == td->vals[1]);
302 testOk1(plink->value.rfio.dataset==td->vals[2]);315 testOk1(plink->value.rfio.dataset == td->vals[2]);
303 testOk1(plink->value.rfio.element==td->vals[3]);316 testOk1(plink->value.rfio.element == td->vals[3]);
304 break;317 break;
305 case VXI_IO:318 case VXI_IO:
306 if(plink->value.vxiio.flag==VXIDYNAMIC) {319 if(plink->value.vxiio.flag == VXIDYNAMIC) {
307 testOk1(plink->value.vxiio.frame==td->vals[0]);320 testOk1(plink->value.vxiio.frame == td->vals[0]);
308 testOk1(plink->value.vxiio.slot==td->vals[1]);321 testOk1(plink->value.vxiio.slot == td->vals[1]);
309 testOk1(plink->value.vxiio.signal==td->vals[2]);322 testOk1(plink->value.vxiio.signal == td->vals[2]);
310 } else {323 } else {
311 testOk1(plink->value.vxiio.la==td->vals[0]);324 testOk1(plink->value.vxiio.la == td->vals[0]);
312 testOk1(plink->value.vxiio.signal==td->vals[1]);325 testOk1(plink->value.vxiio.signal == td->vals[1]);
313 }326 }
314 testOk1(strcmp(plink->value.vxiio.parm, td->parm)==0);327 testOk1(strcmp(plink->value.vxiio.parm, td->parm) == 0);
315 break;328 break;
316 }329 }
317}330}
@@ -319,7 +332,7 @@
319static void testHWInitSet(void)332static void testHWInitSet(void)
320{333{
321 const testHWDataT *td = testHWData;334 const testHWDataT *td = testHWData;
322 testDiag("HW link parsing during initialization");335 testDiag("\n# Checking HW link parsing during initialization\n#");
323 testdbPrepare();336 testdbPrepare();
324337
325 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);338 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -332,13 +345,14 @@
332 testIocInitOk();345 testIocInitOk();
333 eltc(1);346 eltc(1);
334347
335 for(;td->recname;td++) {348 for (;td->recname; td++) {
336 char buf[MAX_STRING_SIZE];349 char buf[MAX_STRING_SIZE];
337 xRecord *prec;350 xRecord *prec;
338 DBLINK *plink;351 DBLINK *plink;
352
339 testDiag("%s == \"%s\"", td->recname, td->wval);353 testDiag("%s == \"%s\"", td->recname, td->wval);
340354
341 prec = (xRecord*)testdbRecordPtr(td->recname);355 prec = (xRecord *) testdbRecordPtr(td->recname);
342 plink = &prec->inp;356 plink = &prec->inp;
343357
344 strcpy(buf, td->recname);358 strcpy(buf, td->recname);
@@ -346,9 +360,11 @@
346360
347 testdbGetFieldEqual(buf, DBR_STRING, td->wval);361 testdbGetFieldEqual(buf, DBR_STRING, td->wval);
348362
349 testOk(plink->type==td->ltype, "link type %d == %d",363 if (!testOk(plink->type == td->ltype, "Link type")) {
350 plink->type, td->ltype);364 testDiag("Expected %d, got %d",
351 if(plink->type==td->ltype) {365 td->ltype, plink->type);
366 }
367 else {
352 testLink(plink, td);368 testLink(plink, td);
353 }369 }
354370
@@ -360,6 +376,7 @@
360}376}
361377
362static const testHWDataT testHWData2[] = {378static const testHWDataT testHWData2[] = {
379 {"rJSON_LINK", JSON_LINK, "{\"x\":true}", {0}, "{\"x\":true}"},
363 {"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"},380 {"rVME_IO", VME_IO, "#C200 S201 @another VME_IO", {200, 201}, "another VME_IO"},
364 {"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"},381 {"rCAMAC_IO", CAMAC_IO, "#B111 C112 N113 A114 F115 @CAMAC_IO", {111, 112, 113, 114, 115}, "CAMAC_IO"},
365 {"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"},382 {"rAB_IO", AB_IO, "#L121 A122 C123 S124 @another AB_IO", {121, 122, 123, 124}, "another AB_IO"},
@@ -376,7 +393,8 @@
376static void testHWMod(void)393static void testHWMod(void)
377{394{
378 const testHWDataT *td = testHWData2;395 const testHWDataT *td = testHWData2;
379 testDiag("HW link parsing during retarget");396
397 testDiag("\n# Checking HW link parsing during retarget\n#");
380 testdbPrepare();398 testdbPrepare();
381399
382 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);400 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -405,9 +423,11 @@
405423
406 testdbGetFieldEqual(buf, DBR_STRING, td->wval);424 testdbGetFieldEqual(buf, DBR_STRING, td->wval);
407425
408 testOk(plink->type==td->ltype, "link type %d == %d",426 if (!testOk(plink->type == td->ltype, "Link type")) {
409 plink->type, td->ltype);427 testDiag("Expected %d, got %d",
410 if(plink->type==td->ltype) {428 td->ltype, plink->type);
429 }
430 else {
411 testLink(plink, td);431 testLink(plink, td);
412 }432 }
413433
@@ -422,42 +442,42 @@
422{442{
423 xRecord *prec;443 xRecord *prec;
424 DBLINK *plink;444 DBLINK *plink;
425 testDiag("Link parsing failures during initialization");445 testDiag("\n# Checking link parse failures at iocInit\n#");
426 testdbPrepare();446 testdbPrepare();
427447
428 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);448 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
429449
430 dbTestIoc_registerRecordDeviceDriver(pdbbase);450 dbTestIoc_registerRecordDeviceDriver(pdbbase);
431451
432
433 /* this load will fail */452 /* this load will fail */
434 eltc(0);453 eltc(0);
435 testOk1(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR ".." OSI_PATH_LIST_SEPARATOR454 testOk(dbReadDatabase(&pdbbase, "dbBadLink.db", "." OSI_PATH_LIST_SEPARATOR
436 "../O.Common" OSI_PATH_LIST_SEPARATOR "O.Common", NULL)!=0);455 ".." OSI_PATH_LIST_SEPARATOR "../O.Common" OSI_PATH_LIST_SEPARATOR
456 "O.Common", NULL) != 0, "dbReadDatabase returned error (expected)");
437457
438 testIocInitOk();458 testIocInitOk();
439 eltc(1);459 eltc(1);
440460
441 testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @");461 testdbGetFieldEqual("eVME_IO1.INP", DBR_STRING, "#C0 S0 @");
442462
443 prec = (xRecord*)testdbRecordPtr("eVME_IO1");463 prec = (xRecord *) testdbRecordPtr("eVME_IO1");
444 plink = &prec->inp;464 plink = &prec->inp;
445 testOk1(plink->type==VME_IO);465 testOk1(plink->type == VME_IO);
446 testOk1(plink->value.vmeio.parm!=NULL);466 testOk1(plink->value.vmeio.parm != NULL);
447467
448 testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");468 testdbGetFieldEqual("eVME_IO2.INP", DBR_STRING, "#C0 S0 @");
449469
450 prec = (xRecord*)testdbRecordPtr("eVME_IO2");470 prec = (xRecord *) testdbRecordPtr("eVME_IO2");
451 plink = &prec->inp;471 plink = &prec->inp;
452 testOk1(plink->type==VME_IO);472 testOk1(plink->type == VME_IO);
453 testOk1(plink->value.vmeio.parm!=NULL);473 testOk1(plink->value.vmeio.parm != NULL);
454474
455 testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@");475 testdbGetFieldEqual("eINST_IO.INP", DBR_STRING, "@");
456476
457 prec = (xRecord*)testdbRecordPtr("eINST_IO");477 prec = (xRecord *) testdbRecordPtr("eINST_IO");
458 plink = &prec->inp;478 plink = &prec->inp;
459 testOk1(plink->type==INST_IO);479 testOk1(plink->type == INST_IO);
460 testOk1(plink->value.instio.string!=NULL);480 testOk1(plink->value.instio.string != NULL);
461481
462 testIocShutdownOk();482 testIocShutdownOk();
463483
@@ -466,7 +486,7 @@
466486
467static void testLinkFail(void)487static void testLinkFail(void)
468{488{
469 testDiag("Link parsing failures");489 testDiag("\n# Checking runtime link parse failures\n#");
470 testdbPrepare();490 testdbPrepare();
471491
472 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);492 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
@@ -482,9 +502,22 @@
482 /* INST_IO doesn't accept empty string */502 /* INST_IO doesn't accept empty string */
483 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");503 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "");
484504
485 /* INST_IO doesn't accept empty string */505 /* INST_IO doesn't accept string without @ */
486 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");506 testdbPutFieldFail(S_dbLib_badField, "rINST_IO.INP", DBR_STRING, "abc");
487507
508 /* JSON_LINK dies when expected */
509 testdbPutFieldOk("rJSON_LINK.INP", DBR_STRING, "{\"x\":true}");
510 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":false}");
511 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":null}");
512 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1}");
513 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":1.1}");
514 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":\"x\"}");
515 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":[]}");
516 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":{}}");
517
518 /* JSON_LINK syntax errors */
519 testdbPutFieldFail(S_dbLib_badField, "rJSON_LINK.INP", DBR_STRING, "{\"x\":bbbb}");
520
488 /* syntax errors */521 /* syntax errors */
489 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");522 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "#S201 C200 @another VME_IO");
490 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201");523 testdbPutFieldFail(S_dbLib_badField, "rVME_IO.INP", DBR_STRING, "C200 #S201");
@@ -502,9 +535,73 @@
502 testdbCleanup();535 testdbCleanup();
503}536}
504537
538static
539void testNumZ(int expect)
540{
541 int numz = epicsAtomicGetIntT(&numzalloc);
542 testOk(numz==expect, "numzalloc==%d (%d)", expect, numz);
543}
544
545static
546void testJLink(void)
547{
548 testDiag("Test json link setup/retarget");
549
550 testNumZ(0);
551
552 testDiag("Link parsing failures");
553 testdbPrepare();
554
555 testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
556
557 dbTestIoc_registerRecordDeviceDriver(pdbbase);
558
559 testdbReadDatabase("dbPutLinkTest.db", NULL, NULL);
560 testdbReadDatabase("dbPutLinkTestJ.db", NULL, NULL);
561
562 testNumZ(0);
563
564 eltc(0);
565 testIocInitOk();
566 eltc(1);
567
568 testNumZ(3);
569
570 testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
571 testdbPutFieldOk("j2.PROC", DBF_LONG, 1);
572 testdbPutFieldOk("j3.PROC", DBF_LONG, 1);
573
574 testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":1}}");
575 testdbGetFieldEqual("j1.VAL", DBF_LONG, 1);
576 testdbGetFieldEqual("j2.VAL", DBF_LONG, 2);
577 testdbGetFieldEqual("j3.VAL", DBF_LONG, 3);
578
579 testNumZ(3);
580
581 testdbPutFieldOk("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
582 testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
583 testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
584
585 testNumZ(3);
586
587 testdbPutFieldFail(S_dbLib_badField, "j1.INP", DBF_STRING, "{\"z\":{\"fail\":5}}");
588 testdbPutFieldOk("j1.PROC", DBF_LONG, 1);
589 testdbGetFieldEqual("j1.VAL", DBF_LONG, 4);
590 /* put failure in parsing stage doesn't modify link */
591 testdbGetFieldEqual("j1.INP", DBF_STRING, "{\"z\":{\"good\":4}}");
592
593 testNumZ(3);
594
595 testIocShutdownOk();
596
597 testNumZ(0);
598
599 testdbCleanup();
600}
601
505MAIN(dbPutLinkTest)602MAIN(dbPutLinkTest)
506{603{
507 testPlan(251);604 testPlan(301);
508 testLinkParse();605 testLinkParse();
509 testLinkFailParse();606 testLinkFailParse();
510 testCADBSet();607 testCADBSet();
@@ -512,5 +609,6 @@
512 testHWMod();609 testHWMod();
513 testLinkInitFail();610 testLinkInitFail();
514 testLinkFail();611 testLinkFail();
612 testJLink();
515 return testDone();613 return testDone();
516}614}
517615
=== modified file 'src/ioc/db/test/dbPutLinkTest.db'
--- src/ioc/db/test/dbPutLinkTest.db 2014-07-31 20:22:02 +0000
+++ src/ioc/db/test/dbPutLinkTest.db 2017-03-03 18:23:23 +0000
@@ -3,6 +3,10 @@
3record(x, "x3") {}3record(x, "x3") {}
4record(x, "x4") {}4record(x, "x4") {}
55
6record(x, "rJSON_LINK") {
7 field(DTYP, "Unit Test JSON_LINK")
8 field(INP, {x:true})
9}
6record(x, "rVME_IO") {10record(x, "rVME_IO") {
7 field(DTYP, "Unit Test VME_IO")11 field(DTYP, "Unit Test VME_IO")
8 field(INP, "#C100 S101 @parm VME_IO")12 field(INP, "#C100 S101 @parm VME_IO")
913
=== added file 'src/ioc/db/test/dbPutLinkTestJ.db'
--- src/ioc/db/test/dbPutLinkTestJ.db 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/dbPutLinkTestJ.db 2017-03-03 18:23:23 +0000
@@ -0,0 +1,12 @@
1
2record(x, "j1") {
3 field(INP, {z:{good:1}})
4}
5
6record(x, "j2") {
7 field(INP, {z:{good:2}})
8}
9
10record(x, "j3") {
11 field(INP, {z:{good:3}})
12}
013
=== modified file 'src/ioc/db/test/devx.c'
--- src/ioc/db/test/devx.c 2015-03-13 19:24:06 +0000
+++ src/ioc/db/test/devx.c 2017-03-03 18:23:23 +0000
@@ -141,17 +141,13 @@
141/* basic DTYP="Soft Channel" */141/* basic DTYP="Soft Channel" */
142static long xsoft_init_record(xRecord *prec)142static long xsoft_init_record(xRecord *prec)
143{143{
144 if(prec->inp.type==CONSTANT)144 recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
145 recGblInitConstantLink(&prec->inp, DBF_LONG, &prec->val);
146 return 0;145 return 0;
147}146}
148147
149static long xsoft_read(xRecord *prec)148static long xsoft_read(xRecord *prec)
150{149{
151 if(prec->inp.type==CONSTANT)150 return dbGetLink(&prec->inp, DBR_LONG, &prec->val, NULL, NULL);
152 return 0;
153 dbGetLink(&prec->inp, DBR_DOUBLE, &prec->val, NULL, NULL);
154 return 0;
155}151}
156152
157static struct xdset devxSoft = {153static struct xdset devxSoft = {
158154
=== added file 'src/ioc/db/test/jlinkz.c'
--- src/ioc/db/test/jlinkz.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/jlinkz.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,250 @@
1/*************************************************************************\
2* Copyright (c) 2016 Michael Davidsaver
3* EPICS BASE is distributed subject to a Software License Agreement found
4* in file LICENSE that is included with this distribution.
5 \*************************************************************************/
6
7#include <stdlib.h>
8#include <string.h>
9
10#include <epicsExport.h>
11#include <dbAccess.h>
12#include <link.h>
13#include <alarm.h>
14#include <dbJLink.h>
15#include <dbDefs.h>
16#include <dbConvertFast.h>
17#include <epicsMutex.h>
18#include <epicsAtomic.h>
19#include <epicsUnitTest.h>
20
21#define epicsExportSharedSymbols
22
23#include "jlinkz.h"
24
25int numzalloc;
26
27
28static
29void z_open(struct link *plink)
30{
31 zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
32
33 if(priv->isopen)
34 testDiag("lsetZ re-open");
35 priv->isopen = 1;
36 testDiag("Open jlinkz %p", priv);
37}
38
39static
40void z_remove(struct dbLocker *locker, struct link *plink)
41{
42 zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
43
44 epicsMutexLock(priv->lock);
45
46 if(!priv->isopen)
47 testDiag("lsetZ remove without open");
48
49 epicsMutexUnlock(priv->lock);
50
51 testDiag("Remove/free jlinkz %p", priv);
52
53 epicsAtomicDecrIntT(&numzalloc);
54
55 epicsMutexDestroy(priv->lock);
56 free(priv);
57 plink->value.json.jlink = NULL; /* paranoia */
58}
59
60static
61int z_connected(const struct link *plink)
62{
63 return 1; /* TODO: not provided should be connected */
64}
65
66static
67int z_dbftype(const struct link *plink)
68{
69 return DBF_LONG;
70}
71
72static
73long z_elements(const struct link *plink, long *nelements)
74{
75 *nelements = 1;
76 return 0;
77}
78
79static
80long z_getval(struct link *plink, short dbrType, void *pbuffer,
81 long *pnRequest)
82{
83 long ret;
84 long (*pconv)(const epicsInt32 *, void *, const dbAddr *) = dbFastGetConvertRoutine[DBF_LONG][dbrType];
85 zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
86
87 if(pnRequest && *pnRequest==0) return 0;
88
89 epicsMutexLock(priv->lock);
90 ret = (*pconv)(&priv->value, pbuffer, NULL);
91 epicsMutexUnlock(priv->lock);
92 if(ret==0 && pnRequest) *pnRequest = 1;
93 return ret;
94}
95
96/* TODO: atomicly get value and alarm */
97static
98long z_getalarm(const struct link *plink, epicsEnum16 *status,
99 epicsEnum16 *severity)
100{
101 zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
102 epicsEnum16 sevr, stat;
103
104 epicsMutexLock(priv->lock);
105 sevr = priv->isset ? 0 : INVALID_ALARM;
106 stat = priv->isset ? 0 : LINK_ALARM;
107 epicsMutexUnlock(priv->lock);
108
109 if(status) *status = stat;
110 if(severity) *severity = sevr;
111 return 0;
112}
113
114static
115long z_putval(struct link *plink, short dbrType,
116 const void *pbuffer, long nRequest)
117{
118 long ret;
119 long (*pconv)(epicsInt32 *, const void *, const dbAddr *) = dbFastPutConvertRoutine[DBF_LONG][dbrType];
120 zpriv *priv = CONTAINER(plink->value.json.jlink, zpriv, base);
121
122 if(nRequest==0) return 0;
123
124 epicsMutexLock(priv->lock);
125 ret = (*pconv)(&priv->value, pbuffer, NULL);
126 epicsMutexUnlock(priv->lock);
127 return ret;
128}
129
130static lset lsetZ = {
131 0, 0, /* non-const, non-volatile */
132 &z_open,
133 &z_remove,
134 NULL, NULL, NULL, /* load */
135 &z_connected,
136 &z_dbftype,
137 &z_elements,
138 &z_getval,
139 NULL, /* control limits */
140 NULL, /* display limits */
141 NULL, /* alarm limits */
142 NULL, /* prec */
143 NULL, /* units */
144 &z_getalarm,
145 NULL, /* time */
146 &z_putval,
147 NULL, /* putasync */
148 NULL, /* forward */
149};
150
151static
152jlink* z_alloc(short dbfType)
153{
154 zpriv *priv;
155 priv = calloc(1, sizeof(*priv));
156 if(!priv) goto fail;
157
158 priv->lock = epicsMutexCreate();
159 if(!priv->lock) goto fail;
160
161 epicsAtomicIncrIntT(&numzalloc);
162
163 testDiag("Alloc jlinkz %p", priv);
164
165 return &priv->base;
166fail:
167 if(priv && priv->lock) epicsMutexDestroy(priv->lock);
168 free(priv);
169 return NULL;
170}
171
172static
173void z_free(jlink *pj)
174{
175 zpriv *priv = CONTAINER(pj, zpriv, base);
176
177 if(priv->isopen)
178 testDiag("lsetZ jlink free after open()");
179
180 testDiag("Free jlinkz %p", priv);
181
182 epicsAtomicDecrIntT(&numzalloc);
183
184 epicsMutexDestroy(priv->lock);
185 free(priv);
186}
187
188static
189jlif_result z_int(jlink *pj, long num)
190{
191 zpriv *priv = CONTAINER(pj, zpriv, base);
192
193 priv->value = num;
194 priv->isset = 1;
195
196 return jlif_continue;
197}
198
199static
200jlif_key_result z_start(jlink *pj)
201{
202 return jlif_key_continue;
203}
204
205static
206jlif_result z_key(jlink *pj, const char *key, size_t len)
207{
208 zpriv *priv = CONTAINER(pj, zpriv, base);
209
210 if(len==4 && strncmp(key,"fail", len)==0) {
211 testDiag("Found fail key jlinkz %p", priv);
212 return jlif_stop;
213 } else {
214 return jlif_continue;
215 }
216}
217
218static
219jlif_result z_end(jlink *pj)
220{
221 return jlif_continue;
222}
223
224static
225struct lset* z_lset(const jlink *pj)
226{
227 return &lsetZ;
228}
229
230static jlif jlifZ = {
231 "z",
232 &z_alloc,
233 &z_free,
234 NULL, /* null */
235 NULL, /* bool */
236 &z_int,
237 NULL, /* double */
238 NULL, /* string */
239 &z_start,
240 &z_key,
241 &z_end,
242 NULL, /* start array */
243 NULL, /* end array */
244 NULL, /* end child */
245 &z_lset,
246 NULL, /* report */
247 NULL /* map child */
248};
249
250epicsExportAddress(jlif, jlifZ);
0251
=== added file 'src/ioc/db/test/jlinkz.dbd'
--- src/ioc/db/test/jlinkz.dbd 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/jlinkz.dbd 2017-03-03 18:23:23 +0000
@@ -0,0 +1,1 @@
1link("z", "jlifZ")
02
=== added file 'src/ioc/db/test/jlinkz.h'
--- src/ioc/db/test/jlinkz.h 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/jlinkz.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,27 @@
1/*************************************************************************\
2* Copyright (c) 2016 Michael Davidsaver
3* EPICS BASE is distributed subject to a Software License Agreement found
4* in file LICENSE that is included with this distribution.
5 \*************************************************************************/
6
7#ifndef JLINKZ_H
8#define JLINKZ_H
9
10#include <dbJLink.h>
11#include <epicsMutex.h>
12#include <epicsTypes.h>
13
14#include <shareLib.h>
15
16epicsShareExtern
17int numzalloc;
18
19typedef struct {
20 jlink base;
21 epicsMutexId lock;
22 unsigned isset:1;
23 unsigned isopen:1;
24 epicsInt32 value;
25} zpriv;
26
27#endif /* JLINKZ_H */
028
=== added file 'src/ioc/db/test/xLink.c'
--- src/ioc/db/test/xLink.c 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/xLink.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,88 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* xLink.c */
8
9#include "dbDefs.h"
10#include "dbLink.h"
11#include "dbJLink.h"
12#include "epicsExport.h"
13
14
15typedef struct xlink {
16 jlink jlink; /* embedded object */
17 /* ... */
18} xlink;
19
20static lset xlink_lset;
21
22static jlink* xlink_alloc(short dbfType)
23{
24 xlink *xlink = calloc(1, sizeof(struct xlink));
25
26 return &xlink->jlink;
27}
28
29static void xlink_free(jlink *pjlink)
30{
31 xlink *xlink = CONTAINER(pjlink, struct xlink, jlink);
32
33 free(xlink);
34}
35
36static jlif_result xlink_boolean(jlink *pjlink, int val)
37{
38 return val; /* False triggers a parse failure */
39}
40
41static struct lset* xlink_get_lset(const jlink *pjlink)
42{
43 return &xlink_lset;
44}
45
46
47static void xlink_remove(struct dbLocker *locker, struct link *plink)
48{
49 xlink_free(plink->value.json.jlink);
50}
51
52static long xlink_getNelements(const struct link *plink, long *nelements)
53{
54 *nelements = 0;
55 return 0;
56}
57
58static long xlink_getValue(struct link *plink, short dbrType, void *pbuffer,
59 long *pnRequest)
60{
61 if (pnRequest)
62 *pnRequest = 0;
63 return 0;
64}
65
66
67static lset xlink_lset = {
68 1, 0, /* Constant, not Volatile */
69 NULL, xlink_remove,
70 NULL, NULL, NULL, NULL,
71 NULL, xlink_getNelements, xlink_getValue,
72 NULL, NULL, NULL,
73 NULL, NULL,
74 NULL, NULL,
75 NULL, NULL,
76 NULL
77};
78
79static jlif xlinkIf = {
80 "x", xlink_alloc, xlink_free,
81 NULL, xlink_boolean, NULL, NULL, NULL,
82 NULL, NULL, NULL,
83 NULL, NULL,
84 NULL, xlink_get_lset,
85 NULL, NULL
86};
87epicsExportAddress(jlif, xlinkIf);
88
089
=== added file 'src/ioc/db/test/xLink.dbd'
--- src/ioc/db/test/xLink.dbd 1970-01-01 00:00:00 +0000
+++ src/ioc/db/test/xLink.dbd 2017-03-03 18:23:23 +0000
@@ -0,0 +1,1 @@
1link(x, xlinkIf)
02
=== modified file 'src/ioc/dbStatic/dbBase.h'
--- src/ioc/dbStatic/dbBase.h 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/dbBase.h 2017-03-03 18:23:23 +0000
@@ -43,6 +43,13 @@
43 struct dsxt *pdsxt; /* Extended device support */43 struct dsxt *pdsxt; /* Extended device support */
44}devSup;44}devSup;
4545
46typedef struct linkSup {
47 ELLNODE node;
48 char *name;
49 char *jlif_name;
50 struct jlif *pjlif;
51} linkSup;
52
46typedef struct dbDeviceMenu {53typedef struct dbDeviceMenu {
47 int nChoice;54 int nChoice;
48 char **papChoice;55 char **papChoice;
@@ -161,6 +168,7 @@
161 ELLLIST menuList;168 ELLLIST menuList;
162 ELLLIST recordTypeList;169 ELLLIST recordTypeList;
163 ELLLIST drvList;170 ELLLIST drvList;
171 ELLLIST linkList;
164 ELLLIST registrarList;172 ELLLIST registrarList;
165 ELLLIST functionList;173 ELLLIST functionList;
166 ELLLIST variableList;174 ELLLIST variableList;
167175
=== modified file 'src/ioc/dbStatic/dbLex.l'
--- src/ioc/dbStatic/dbLex.l 2015-10-13 17:03:32 +0000
+++ src/ioc/dbStatic/dbLex.l 2017-03-03 18:23:23 +0000
@@ -1,11 +1,12 @@
1/*************************************************************************\1/*************************************************************************\
2* Copyright (c) 2009 UChicago Argonne LLC, as Operator of Argonne2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution. 7* in file LICENSE that is included with this distribution.
8\*************************************************************************/8\*************************************************************************/
9
9newline "\n"10newline "\n"
10backslash "\\"11backslash "\\"
11doublequote "\""12doublequote "\""
@@ -15,6 +16,19 @@
15stringchar [^"\n\\]16stringchar [^"\n\\]
16bareword [a-zA-Z0-9_\-+:.\[\]<>;]17bareword [a-zA-Z0-9_\-+:.\[\]<>;]
1718
19punctuation [:,\[\]{}]
20normalchar [^"\\\0-\x1f]
21barechar [a-zA-Z0-9_\-+.]
22escapedchar ({backslash}["\\/bfnrt])
23hexdigit [0-9a-fA-F]
24unicodechar ({backslash}"u"{hexdigit}{4})
25jsonchar ({normalchar}|{escapedchar}|{unicodechar})
26jsondqstr ({doublequote}{jsonchar}*{doublequote})
27int ("-"?([0-9]|[1-9][0-9]+))
28frac ("."[0-9]+)
29exp ([eE][+-]?[0-9]+)
30number ({int}{frac}?{exp}?)
31
18%{32%{
19#undef YY_INPUT33#undef YY_INPUT
20#define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms))34#define YY_INPUT(b,r,ms) (r=(*db_yyinput)((char *)b,ms))
@@ -27,6 +41,8 @@
2741
28%}42%}
2943
44%x JSON
45
30%%46%%
3147
32"include" return(tokenINCLUDE);48"include" return(tokenINCLUDE);
@@ -38,6 +54,7 @@
38"field" return(tokenFIELD);54"field" return(tokenFIELD);
39"device" return(tokenDEVICE);55"device" return(tokenDEVICE);
40"driver" return(tokenDRIVER);56"driver" return(tokenDRIVER);
57"link" return(tokenLINK);
41"breaktable" return(tokenBREAKTABLE);58"breaktable" return(tokenBREAKTABLE);
42"record" return(tokenRECORD);59"record" return(tokenRECORD);
43"grecord" return(tokenGRECORD);60"grecord" return(tokenGRECORD);
@@ -48,18 +65,18 @@
48"variable" return(tokenVARIABLE);65"variable" return(tokenVARIABLE);
4966
50{bareword}+ { /* unquoted string or number */67{bareword}+ { /* unquoted string or number */
51 yylval.Str = dbmfStrdup(yytext);68 yylval.Str = dbmfStrdup((char *) yytext);
52 return(tokenSTRING); 69 return(tokenSTRING);
53}70}
5471
55{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */72{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
56 yylval.Str = dbmfStrdup(yytext+1);73 yylval.Str = dbmfStrdup((char *) yytext+1);
57 yylval.Str[strlen(yylval.Str)-1] = '\0';74 yylval.Str[strlen(yylval.Str)-1] = '\0';
58 return(tokenSTRING);75 return(tokenSTRING);
59}76}
6077
61%.* { /*C definition in recordtype*/78%.* { /*C definition in recordtype*/
62 yylval.Str = dbmfStrdup(yytext+1);79 yylval.Str = dbmfStrdup((char *) yytext+1);
63 return(tokenCDEFS);80 return(tokenCDEFS);
64}81}
6582
@@ -69,14 +86,36 @@
69")" return(yytext[0]);86")" return(yytext[0]);
70"," return(yytext[0]);87"," return(yytext[0]);
7188
72{comment}.* ;
73{whitespace} ;
74
75{doublequote}({stringchar}|{escape})*{newline} { /* bad string */89{doublequote}({stringchar}|{escape})*{newline} { /* bad string */
76 yyerrorAbort("Newline in string, closing quote missing");90 yyerrorAbort("Newline in string, closing quote missing");
77}91}
7892
79. {93<JSON>"null" return jsonNULL;
94<JSON>"true" return jsonTRUE;
95<JSON>"false" return jsonFALSE;
96
97<JSON>{punctuation} return yytext[0];
98
99<JSON>{jsondqstr} {
100 yylval.Str = dbmfStrdup((char *) yytext);
101 return jsonSTRING;
102}
103
104<JSON>{number} {
105 yylval.Str = dbmfStrdup((char *) yytext);
106 return jsonNUMBER;
107}
108
109<JSON>{barechar}+ {
110 yylval.Str = dbmfStrdup((char *) yytext);
111 return jsonBARE;
112}
113
114<INITIAL,JSON>{comment}.* ;
115
116<INITIAL,JSON>{whitespace} ;
117
118<INITIAL,JSON>. {
80 char message[40];119 char message[40];
81 YY_BUFFER_STATE *dummy=0;120 YY_BUFFER_STATE *dummy=0;
82121
83122
=== modified file 'src/ioc/dbStatic/dbLexRoutines.c'
--- src/ioc/dbStatic/dbLexRoutines.c 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/dbLexRoutines.c 2017-03-03 18:23:23 +0000
@@ -77,6 +77,7 @@
77static void dbDevice(char *recordtype,char *linktype,77static void dbDevice(char *recordtype,char *linktype,
78 char *dsetname,char *choicestring);78 char *dsetname,char *choicestring);
79static void dbDriver(char *name);79static void dbDriver(char *name);
80static void dbLinkType(char *name, char *jlif_name);
80static void dbRegistrar(char *name);81static void dbRegistrar(char *name);
81static void dbFunction(char *name);82static void dbFunction(char *name);
82static void dbVariable(char *name, char *type);83static void dbVariable(char *name, char *type);
@@ -815,6 +816,26 @@
815 ellAdd(&pdbbase->drvList,&pdrvSup->node);816 ellAdd(&pdbbase->drvList,&pdrvSup->node);
816}817}
817818
819static void dbLinkType(char *name, char *jlif_name)
820{
821 linkSup *pLinkSup;
822 GPHENTRY *pgphentry;
823
824 pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList);
825 if (pgphentry) {
826 return;
827 }
828 pLinkSup = dbCalloc(1,sizeof(linkSup));
829 pLinkSup->name = epicsStrDup(name);
830 pLinkSup->jlif_name = epicsStrDup(jlif_name);
831 pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
832 if (!pgphentry) {
833 yyerrorAbort("gphAdd failed");
834 }
835 pgphentry->userPvt = pLinkSup;
836 ellAdd(&pdbbase->linkList, &pLinkSup->node);
837}
838
818static void dbRegistrar(char *name)839static void dbRegistrar(char *name)
819{840{
820 dbText *ptext;841 dbText *ptext;
@@ -1057,7 +1078,12 @@
1057 yyerror(NULL);1078 yyerror(NULL);
1058 return;1079 return;
1059 }1080 }
1060 dbTranslateEscape(value, value); /* yuck: in-place, but safe */1081 if (*value == '"') {
1082 /* jsonSTRING values still have their quotes */
1083 value++;
1084 value[strlen(value) - 1] = 0;
1085 }
1086 dbTranslateEscape(value, value); /* in-place; safe & legal */
1061 status = dbPutString(pdbentry,value);1087 status = dbPutString(pdbentry,value);
1062 if(status) {1088 if(status) {
1063 epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",1089 epicsPrintf("Can't set \"%s.%s\" to \"%s\"\n",
@@ -1076,6 +1102,12 @@
1076 if(duplicate) return;1102 if(duplicate) return;
1077 ptempListNode = (tempListNode *)ellFirst(&tempList);1103 ptempListNode = (tempListNode *)ellFirst(&tempList);
1078 pdbentry = ptempListNode->item;1104 pdbentry = ptempListNode->item;
1105 if (*value == '"') {
1106 /* jsonSTRING values still have their quotes */
1107 value++;
1108 value[strlen(value) - 1] = 0;
1109 }
1110 dbTranslateEscape(value, value); /* yuck: in-place, but safe */
1079 status = dbPutInfo(pdbentry,name,value);1111 status = dbPutInfo(pdbentry,name,value);
1080 if(status) {1112 if(status) {
1081 epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",1113 epicsPrintf("Can't set \"%s\" info \"%s\" to \"%s\"\n",
10821114
=== modified file 'src/ioc/dbStatic/dbStaticIocRegister.c'
--- src/ioc/dbStatic/dbStaticIocRegister.c 2014-10-06 05:57:02 +0000
+++ src/ioc/dbStatic/dbStaticIocRegister.c 2017-03-03 18:23:23 +0000
@@ -86,6 +86,14 @@
86 dbDumpDriver(*iocshPpdbbase);86 dbDumpDriver(*iocshPpdbbase);
87}87}
8888
89/* dbDumpLink */
90static const iocshArg * const dbDumpLinkArgs[] = { &argPdbbase};
91static const iocshFuncDef dbDumpLinkFuncDef = {"dbDumpLink",1,dbDumpLinkArgs};
92static void dbDumpLinkCallFunc(const iocshArgBuf *args)
93{
94 dbDumpLink(*iocshPpdbbase);
95}
96
89/* dbDumpRegistrar */97/* dbDumpRegistrar */
90static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};98static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
91static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs};99static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs};
@@ -160,6 +168,7 @@
160 iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc);168 iocshRegister(&dbDumpFieldFuncDef, dbDumpFieldCallFunc);
161 iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc);169 iocshRegister(&dbDumpDeviceFuncDef, dbDumpDeviceCallFunc);
162 iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc);170 iocshRegister(&dbDumpDriverFuncDef, dbDumpDriverCallFunc);
171 iocshRegister(&dbDumpLinkFuncDef, dbDumpLinkCallFunc);
163 iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc);172 iocshRegister(&dbDumpRegistrarFuncDef,dbDumpRegistrarCallFunc);
164 iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc);173 iocshRegister(&dbDumpFunctionFuncDef, dbDumpFunctionCallFunc);
165 iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc);174 iocshRegister(&dbDumpVariableFuncDef, dbDumpVariableCallFunc);
166175
=== modified file 'src/ioc/dbStatic/dbStaticLib.c'
--- src/ioc/dbStatic/dbStaticLib.c 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/dbStaticLib.c 2017-03-03 18:23:23 +0000
@@ -43,6 +43,7 @@
43#include "special.h"43#include "special.h"
4444
45#include "dbCommon.h"45#include "dbCommon.h"
46#include "dbJLink.h"
4647
47int dbStaticDebug = 0;48int dbStaticDebug = 0;
48static char *pNullString = "";49static char *pNullString = "";
@@ -64,6 +65,7 @@
64 {"GPIB_IO",GPIB_IO},65 {"GPIB_IO",GPIB_IO},
65 {"BITBUS_IO",BITBUS_IO},66 {"BITBUS_IO",BITBUS_IO},
66 {"MACRO_LINK",MACRO_LINK},67 {"MACRO_LINK",MACRO_LINK},
68 {"JSON_LINK",JSON_LINK},
67 {"PN_LINK",PN_LINK},69 {"PN_LINK",PN_LINK},
68 {"DB_LINK",DB_LINK},70 {"DB_LINK",DB_LINK},
69 {"CA_LINK",CA_LINK},71 {"CA_LINK",CA_LINK},
@@ -118,6 +120,10 @@
118 case CONSTANT: free((void *)plink->value.constantStr); break;120 case CONSTANT: free((void *)plink->value.constantStr); break;
119 case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;121 case MACRO_LINK: free((void *)plink->value.macro_link.macroStr); break;
120 case PV_LINK: free((void *)plink->value.pv_link.pvname); break;122 case PV_LINK: free((void *)plink->value.pv_link.pvname); break;
123 case JSON_LINK:
124 dbJLinkFree(plink->value.json.jlink);
125 parm = plink->value.json.string;
126 break;
121 case VME_IO: parm = plink->value.vmeio.parm; break;127 case VME_IO: parm = plink->value.vmeio.parm; break;
122 case CAMAC_IO: parm = plink->value.camacio.parm; break;128 case CAMAC_IO: parm = plink->value.camacio.parm; break;
123 case AB_IO: parm = plink->value.abio.parm; break;129 case AB_IO: parm = plink->value.abio.parm; break;
@@ -454,6 +460,7 @@
454 dbVariableDef *pvarNext;460 dbVariableDef *pvarNext;
455 drvSup *pdrvSup;461 drvSup *pdrvSup;
456 drvSup *pdrvSupNext;462 drvSup *pdrvSupNext;
463 linkSup *plinkSup;
457 brkTable *pbrkTable;464 brkTable *pbrkTable;
458 brkTable *pbrkTableNext;465 brkTable *pbrkTableNext;
459 chFilterPlugin *pfilt;466 chFilterPlugin *pfilt;
@@ -556,6 +563,11 @@
556 free((void *)pdrvSup);563 free((void *)pdrvSup);
557 pdrvSup = pdrvSupNext;564 pdrvSup = pdrvSupNext;
558 }565 }
566 while ((plinkSup = (linkSup *) ellGet(&pdbbase->linkList))) {
567 free(plinkSup->jlif_name);
568 free(plinkSup->name);
569 free(plinkSup);
570 }
559 ptext = (dbText *)ellFirst(&pdbbase->registrarList);571 ptext = (dbText *)ellFirst(&pdbbase->registrarList);
560 while(ptext) {572 while(ptext) {
561 ptextNext = (dbText *)ellNext(&ptext->node);573 ptextNext = (dbText *)ellNext(&ptext->node);
@@ -1097,6 +1109,21 @@
1097 return(0);1109 return(0);
1098}1110}
10991111
1112long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp)
1113{
1114 linkSup *plinkSup;
1115
1116 if (!pdbbase) {
1117 fprintf(stderr, "pdbbase not specified\n");
1118 return -1;
1119 }
1120 for (plinkSup = (linkSup *) ellFirst(&pdbbase->linkList);
1121 plinkSup; plinkSup = (linkSup *) ellNext(&plinkSup->node)) {
1122 fprintf(fp, "link(%s,%s)\n", plinkSup->name, plinkSup->jlif_name);
1123 }
1124 return 0;
1125}
1126
1100long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp)1127long dbWriteRegistrarFP(DBBASE *pdbbase,FILE *fp)
1101{1128{
1102 dbText *ptext;1129 dbText *ptext;
@@ -1877,6 +1904,9 @@
1877 dbMsgCpy(pdbentry, "");1904 dbMsgCpy(pdbentry, "");
1878 }1905 }
1879 break;1906 break;
1907 case JSON_LINK:
1908 dbMsgCpy(pdbentry, plink->value.json.string);
1909 break;
1880 case PN_LINK:1910 case PN_LINK:
1881 dbMsgPrint(pdbentry, "%s%s",1911 dbMsgPrint(pdbentry, "%s%s",
1882 plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",1912 plink->value.pv_link.pvname ? plink->value.pv_link.pvname : "",
@@ -1972,6 +2002,9 @@
1972 dbMsgCpy(pdbentry, "");2002 dbMsgCpy(pdbentry, "");
1973 }2003 }
1974 break;2004 break;
2005 case JSON_LINK:
2006 dbMsgCpy(pdbentry, plink->value.json.string);
2007 break;
1975 case PV_LINK:2008 case PV_LINK:
1976 case CA_LINK:2009 case CA_LINK:
1977 case DB_LINK: {2010 case DB_LINK: {
@@ -2128,6 +2161,7 @@
2128 */2161 */
2129 case CONSTANT: plink->value.constantStr = NULL; break;2162 case CONSTANT: plink->value.constantStr = NULL; break;
2130 case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break;2163 case PV_LINK: plink->value.pv_link.pvname = callocMustSucceed(1, 1, "init PV_LINK"); break;
2164 case JSON_LINK: plink->value.json.string = pNullString; break;
2131 case VME_IO: plink->value.vmeio.parm = pNullString; break;2165 case VME_IO: plink->value.vmeio.parm = pNullString; break;
2132 case CAMAC_IO: plink->value.camacio.parm = pNullString; break;2166 case CAMAC_IO: plink->value.camacio.parm = pNullString; break;
2133 case AB_IO: plink->value.abio.parm = pNullString; break;2167 case AB_IO: plink->value.abio.parm = pNullString; break;
@@ -2160,6 +2194,16 @@
2160 return 0;2194 return 0;
2161}2195}
21622196
2197void dbFreeLinkInfo(dbLinkInfo *pinfo)
2198{
2199 if (pinfo->ltype == JSON_LINK) {
2200 dbJLinkFree(pinfo->jlink);
2201 pinfo->jlink = NULL;
2202 }
2203 free(pinfo->target);
2204 pinfo->target = NULL;
2205}
2206
2163long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)2207long dbParseLink(const char *str, short ftype, dbLinkInfo *pinfo)
2164{2208{
2165 char *pstr;2209 char *pstr;
@@ -2194,6 +2238,15 @@
2194 memcpy(pstr, str, len);2238 memcpy(pstr, str, len);
2195 pstr[len] = '\0';2239 pstr[len] = '\0';
21962240
2241 /* Check for braces => JSON */
2242 if (*str == '{' && str[len-1] == '}') {
2243 if (dbJLinkParse(str, len, ftype, &pinfo->jlink))
2244 goto fail;
2245
2246 pinfo->ltype = JSON_LINK;
2247 return 0;
2248 }
2249
2197 /* Check for other HW link types */2250 /* Check for other HW link types */
2198 if (*pstr == '#') {2251 if (*pstr == '#') {
2199 int ret;2252 int ret;
@@ -2241,12 +2294,9 @@
2241 /* RF_IO, the string isn't needed at all */2294 /* RF_IO, the string isn't needed at all */
2242 free(pinfo->target);2295 free(pinfo->target);
2243 pinfo->target = NULL;2296 pinfo->target = NULL;
2244 } else {
2245 /* missing parm when required, or found parm when not expected */
2246 free(pinfo->target);
2247 pinfo->target = NULL;
2248 return S_dbLib_badField;
2249 }2297 }
2298 else goto fail;
2299
2250 return 0;2300 return 0;
2251 }2301 }
22522302
@@ -2256,6 +2306,13 @@
2256 return 0;2306 return 0;
2257 }2307 }
22582308
2309 /* Link may be an array constant */
2310 if (pstr[0] == '[' && pstr[len-1] == ']' &&
2311 (strchr(pstr, ',') || strchr(pstr, '"'))) {
2312 pinfo->ltype = CONSTANT;
2313 return 0;
2314 }
2315
2259 pinfo->ltype = PV_LINK;2316 pinfo->ltype = PV_LINK;
2260 pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */2317 pstr = strchr(pstr, ' '); /* find start of link modifiers (can't be seperated by tabs) */
2261 if (pstr) {2318 if (pstr) {
@@ -2288,27 +2345,28 @@
22882345
2289 return 0;2346 return 0;
2290fail:2347fail:
2291 free(pinfo->target);2348 dbFreeLinkInfo(pinfo);
2292 return S_dbLib_badField;2349 return S_dbLib_badField;
2293}2350}
22942351
2295long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)2352long dbCanSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
2296{2353{
2297 /* consume allocated string pinfo->target on failure */2354 /* Release pinfo resources on failure */
2355 int expected_type = devsup ? devsup->link_type : CONSTANT;
22982356
2299 int link_type = CONSTANT;2357 if (pinfo->ltype == expected_type)
2300 if(devsup)
2301 link_type = devsup->link_type;
2302 if(link_type==pinfo->ltype)
2303 return 0;2358 return 0;
2304 switch(pinfo->ltype) {2359
2360 switch (pinfo->ltype) {
2305 case CONSTANT:2361 case CONSTANT:
2362 case JSON_LINK:
2306 case PV_LINK:2363 case PV_LINK:
2307 if(link_type==CONSTANT || link_type==PV_LINK)2364 if (expected_type == CONSTANT ||
2365 expected_type == JSON_LINK ||
2366 expected_type == PV_LINK)
2308 return 0;2367 return 0;
2309 default:2368 default:
2310 free(pinfo->target);2369 dbFreeLinkInfo(pinfo);
2311 pinfo->target = NULL;
2312 return 1;2370 return 1;
2313 }2371 }
2314}2372}
@@ -2333,10 +2391,23 @@
2333}2391}
23342392
2335static2393static
2394void dbSetLinkJSON(DBLINK *plink, dbLinkInfo *pinfo)
2395{
2396 plink->type = JSON_LINK;
2397 plink->value.json.string = pinfo->target;
2398 plink->value.json.jlink = pinfo->jlink;
2399
2400 pinfo->target = NULL;
2401 pinfo->jlink = NULL;
2402}
2403
2404static
2336void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)2405void dbSetLinkHW(DBLINK *plink, dbLinkInfo *pinfo)
2337{2406{
2338
2339 switch(pinfo->ltype) {2407 switch(pinfo->ltype) {
2408 case JSON_LINK:
2409 plink->value.json.string = pinfo->target;
2410 break;
2340 case INST_IO:2411 case INST_IO:
2341 plink->value.instio.string = pinfo->target;2412 plink->value.instio.string = pinfo->target;
2342 break;2413 break;
@@ -2412,36 +2483,39 @@
24122483
2413long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)2484long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *devsup)
2414{2485{
2415 int ret = 0;2486 int expected_type = devsup ? devsup->link_type : CONSTANT;
2416 int link_type = CONSTANT;2487
24172488 if (expected_type == CONSTANT ||
2418 if(devsup)2489 expected_type == JSON_LINK ||
2419 link_type = devsup->link_type;2490 expected_type == PV_LINK) {
24202491 switch (pinfo->ltype) {
2421 if(link_type==CONSTANT || link_type==PV_LINK) {
2422 switch(pinfo->ltype) {
2423 case CONSTANT:2492 case CONSTANT:
2424 dbFreeLinkContents(plink);2493 dbFreeLinkContents(plink);
2425 dbSetLinkConst(plink, pinfo); break;2494 dbSetLinkConst(plink, pinfo);
2495 break;
2426 case PV_LINK:2496 case PV_LINK:
2427 dbFreeLinkContents(plink);2497 dbFreeLinkContents(plink);
2428 dbSetLinkPV(plink, pinfo); break;2498 dbSetLinkPV(plink, pinfo);
2499 break;
2500 case JSON_LINK:
2501 dbFreeLinkContents(plink);
2502 dbSetLinkJSON(plink, pinfo);
2503 break;
2429 default:2504 default:
2430 errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error");2505 errlogMessage("Warning: dbSetLink: forgot to test with dbCanSetLink() or logic error");
2431 goto fail; /* can't assign HW link */2506 goto fail; /* can't assign HW link */
2432 }2507 }
24332508 }
2434 } else if(link_type==pinfo->ltype) {2509 else if (expected_type == pinfo->ltype) {
2435 dbFreeLinkContents(plink);2510 dbFreeLinkContents(plink);
2436 dbSetLinkHW(plink, pinfo);2511 dbSetLinkHW(plink, pinfo);
24372512 }
2438 } else2513 else
2439 goto fail;2514 goto fail;
24402515
2441 return ret;2516 return 0;
2442fail:2517fail:
2443 free(pinfo->target);2518 dbFreeLinkInfo(pinfo);
2444 pinfo->target = NULL;
2445 return S_dbLib_badField;2519 return S_dbLib_badField;
2446}2520}
24472521
@@ -2507,7 +2581,7 @@
2507 /* links not yet initialized by dbInitRecordLinks() */2581 /* links not yet initialized by dbInitRecordLinks() */
2508 free(plink->text);2582 free(plink->text);
2509 plink->text = epicsStrDup(pstring);2583 plink->text = epicsStrDup(pstring);
2510 free(link_info.target);2584 dbFreeLinkInfo(&link_info);
2511 } else {2585 } else {
2512 /* assignment after init (eg. autosave restore) */2586 /* assignment after init (eg. autosave restore) */
2513 struct dbCommon *prec = pdbentry->precnode->precord;2587 struct dbCommon *prec = pdbentry->precnode->precord;
@@ -3035,6 +3109,12 @@
3035 return(rtnval);3109 return(rtnval);
3036}3110}
30373111
3112linkSup* dbFindLinkSup(dbBase *pdbbase, const char *name) {
3113 GPHENTRY *pgph = gphFind(pdbbase->pgpHash,name,&pdbbase->linkList);
3114 if (!pgph) return NULL;
3115 return (linkSup *) pgph->userPvt;
3116}
3117
3038int dbGetNLinks(DBENTRY *pdbentry)3118int dbGetNLinks(DBENTRY *pdbentry)
3039{3119{
3040 dbRecordType *precordType = pdbentry->precordType;3120 dbRecordType *precordType = pdbentry->precordType;
@@ -3087,67 +3167,6 @@
3087 return(-1);3167 return(-1);
3088}3168}
30893169
3090long dbCvtLinkToConstant(DBENTRY *pdbentry)
3091{
3092 dbFldDes *pflddes;
3093 DBLINK *plink;
3094
3095 dbGetFieldAddress(pdbentry);
3096 pflddes = pdbentry->pflddes;
3097 if(!pflddes) return(-1);
3098 plink = (DBLINK *)pdbentry->pfield;
3099 if(!plink) return(-1);
3100 switch (pflddes->field_type) {
3101 case DBF_INLINK:
3102 case DBF_OUTLINK:
3103 case DBF_FWDLINK:
3104 if(plink->type == CONSTANT) return(0);
3105 if(plink->type != PV_LINK) return(S_dbLib_badLink);
3106 free((void *)plink->value.pv_link.pvname);
3107 plink->value.pv_link.pvname = NULL;
3108 plink->type = CONSTANT;
3109 if(pflddes->initial) {
3110 plink->value.constantStr =
3111 dbCalloc(strlen(pflddes->initial)+1,sizeof(char));
3112 strcpy(plink->value.constantStr,pflddes->initial);
3113 } else {
3114 plink->value.constantStr = NULL;
3115 }
3116 return(0);
3117 default:
3118 epicsPrintf("dbCvtLinkToConstant called for non link field\n");
3119 }
3120 return(S_dbLib_badLink);
3121}
3122
3123long dbCvtLinkToPvlink(DBENTRY *pdbentry)
3124{
3125 dbFldDes *pflddes;
3126 DBLINK *plink;
3127
3128 dbGetFieldAddress(pdbentry);
3129 pflddes = pdbentry->pflddes;
3130 if(!pflddes) return(-1);
3131 if(!pdbentry->precnode || !pdbentry->precnode->precord) return(-1);
3132 plink = (DBLINK *)pdbentry->pfield;
3133 if(!plink) return(-1);
3134 switch (pflddes->field_type) {
3135 case DBF_INLINK:
3136 case DBF_OUTLINK:
3137 case DBF_FWDLINK:
3138 if(plink->type == PV_LINK) return(0);
3139 if(plink->type != CONSTANT) return(S_dbLib_badLink);
3140 free(plink->value.constantStr);
3141 plink->type = PV_LINK;
3142 plink->value.pv_link.pvlMask = 0;
3143 plink->value.pv_link.pvname = 0;
3144 return(0);
3145 default:
3146 epicsPrintf("dbCvtLinkToPvlink called for non link field\n");
3147 }
3148 return(S_dbLib_badLink);
3149}
3150
3151void dbDumpPath(DBBASE *pdbbase)3170void dbDumpPath(DBBASE *pdbbase)
3152{3171{
3153 ELLLIST *ppathList;3172 ELLLIST *ppathList;
@@ -3383,6 +3402,15 @@
3383 dbWriteDriverFP(pdbbase,stdout);3402 dbWriteDriverFP(pdbbase,stdout);
3384}3403}
33853404
3405void dbDumpLink(DBBASE *pdbbase)
3406{
3407 if(!pdbbase) {
3408 fprintf(stderr,"pdbbase not specified\n");
3409 return;
3410 }
3411 dbWriteLinkFP(pdbbase,stdout);
3412}
3413
3386void dbDumpRegistrar(DBBASE *pdbbase)3414void dbDumpRegistrar(DBBASE *pdbbase)
3387{3415{
3388 if(!pdbbase) {3416 if(!pdbbase) {
33893417
=== modified file 'src/ioc/dbStatic/dbStaticLib.h'
--- src/ioc/dbStatic/dbStaticLib.h 2016-10-06 11:31:40 +0000
+++ src/ioc/dbStatic/dbStaticLib.h 2017-03-03 18:23:23 +0000
@@ -102,6 +102,7 @@
102epicsShareFunc long dbWriteDriver(DBBASE *pdbbase,102epicsShareFunc long dbWriteDriver(DBBASE *pdbbase,
103 const char *filename);103 const char *filename);
104epicsShareFunc long dbWriteDriverFP(DBBASE *pdbbase, FILE *fp);104epicsShareFunc long dbWriteDriverFP(DBBASE *pdbbase, FILE *fp);
105epicsShareFunc long dbWriteLinkFP(DBBASE *pdbbase, FILE *fp);
105epicsShareFunc long dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp);106epicsShareFunc long dbWriteRegistrarFP(DBBASE *pdbbase, FILE *fp);
106epicsShareFunc long dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp);107epicsShareFunc long dbWriteFunctionFP(DBBASE *pdbbase, FILE *fp);
107epicsShareFunc long dbWriteVariableFP(DBBASE *pdbbase, FILE *fp);108epicsShareFunc long dbWriteVariableFP(DBBASE *pdbbase, FILE *fp);
@@ -209,11 +210,12 @@
209 const char *name);210 const char *name);
210epicsShareFunc char * dbGetRelatedField(DBENTRY *pdbentry);211epicsShareFunc char * dbGetRelatedField(DBENTRY *pdbentry);
211212
213epicsShareFunc linkSup * dbFindLinkSup(dbBase *pdbbase,
214 const char *name);
215
212epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);216epicsShareFunc int dbGetNLinks(DBENTRY *pdbentry);
213epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);217epicsShareFunc long dbGetLinkField(DBENTRY *pdbentry, int index);
214epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);218epicsShareFunc int dbGetLinkType(DBENTRY *pdbentry);
215epicsShareFunc long dbCvtLinkToConstant(DBENTRY *pdbentry);
216epicsShareFunc long dbCvtLinkToPvlink(DBENTRY *pdbentry);
217219
218/* Dump routines */220/* Dump routines */
219epicsShareFunc void dbDumpPath(DBBASE *pdbbase);221epicsShareFunc void dbDumpPath(DBBASE *pdbbase);
@@ -228,6 +230,7 @@
228epicsShareFunc void dbDumpDevice(DBBASE *pdbbase,230epicsShareFunc void dbDumpDevice(DBBASE *pdbbase,
229 const char *recordTypeName);231 const char *recordTypeName);
230epicsShareFunc void dbDumpDriver(DBBASE *pdbbase);232epicsShareFunc void dbDumpDriver(DBBASE *pdbbase);
233epicsShareFunc void dbDumpLink(DBBASE *pdbbase);
231epicsShareFunc void dbDumpRegistrar(DBBASE *pdbbase);234epicsShareFunc void dbDumpRegistrar(DBBASE *pdbbase);
232epicsShareFunc void dbDumpFunction(DBBASE *pdbbase);235epicsShareFunc void dbDumpFunction(DBBASE *pdbbase);
233epicsShareFunc void dbDumpVariable(DBBASE *pdbbase);236epicsShareFunc void dbDumpVariable(DBBASE *pdbbase);
234237
=== modified file 'src/ioc/dbStatic/dbStaticPvt.h'
--- src/ioc/dbStatic/dbStaticPvt.h 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/dbStaticPvt.h 2017-03-03 18:23:23 +0000
@@ -36,19 +36,25 @@
36char *dbGetStringNum(DBENTRY *pdbentry);36char *dbGetStringNum(DBENTRY *pdbentry);
37long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);37long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
3838
39struct jlink;
40
39typedef struct dbLinkInfo {41typedef struct dbLinkInfo {
40 short ltype;42 short ltype;
4143
42 /* full link string for CONSTANT and PV_LINK,44 /* full link string for CONSTANT and PV_LINK,
43 * parm string for HW links*/45 * parm string for HW links, JSON for JSON_LINK
46 */
44 char *target;47 char *target;
4548
46 /* for PV_LINK */49 /* for PV_LINK */
47 short modifiers;50 short modifiers;
4851
49 /* HW links */52 /* for HW links */
50 char hwid[6]; /* one extra element for a nil */53 char hwid[6]; /* one extra element for a nil */
51 int hwnums[5];54 int hwnums[5];
55
56 /* for JSON_LINK */
57 struct jlink *jlink;
52} dbLinkInfo;58} dbLinkInfo;
5359
54long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);60long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec);
@@ -68,6 +74,8 @@
68 * Unconditionally takes ownership of pinfo->target74 * Unconditionally takes ownership of pinfo->target
69 */75 */
70long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);76long dbSetLink(DBLINK *plink, dbLinkInfo *pinfo, devSup *dset);
77/* Free dbLinkInfo storage */
78epicsShareFunc void dbFreeLinkInfo(dbLinkInfo *pinfo);
7179
72/* The following is for path */80/* The following is for path */
73typedef struct dbPathNode {81typedef struct dbPathNode {
7482
=== modified file 'src/ioc/dbStatic/dbYacc.y'
--- src/ioc/dbStatic/dbYacc.y 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/dbYacc.y 2017-03-03 18:23:23 +0000
@@ -17,17 +17,22 @@
1717
18%start database18%start database
1919
20%union
21{
22 char *Str;
23}
24
20%token tokenINCLUDE tokenPATH tokenADDPATH25%token tokenINCLUDE tokenPATH tokenADDPATH
21%token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE26%token tokenALIAS tokenMENU tokenCHOICE tokenRECORDTYPE
22%token tokenFIELD tokenINFO tokenREGISTRAR27%token tokenFIELD tokenINFO tokenREGISTRAR
23%token tokenDEVICE tokenDRIVER tokenBREAKTABLE28%token tokenDEVICE tokenDRIVER tokenLINK tokenBREAKTABLE
24%token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION29%token tokenRECORD tokenGRECORD tokenVARIABLE tokenFUNCTION
25%token <Str> tokenSTRING tokenCDEFS30%token <Str> tokenSTRING tokenCDEFS
2631
27%union32%token jsonNULL jsonTRUE jsonFALSE
28{33%token <Str> jsonNUMBER jsonSTRING jsonBARE
29 char *Str;34%type <Str> json_value json_object json_array
30}35%type <Str> json_members json_pair json_elements json_string
3136
32%%37%%
3338
@@ -46,6 +51,7 @@
46 | tokenRECORDTYPE recordtype_head recordtype_body51 | tokenRECORDTYPE recordtype_head recordtype_body
47 | device52 | device
48 | driver53 | driver
54 | link
49 | registrar55 | registrar
50 | function56 | function
51 | variable57 | variable
@@ -162,6 +168,13 @@
162 dbDriver($3); dbmfFree($3);168 dbDriver($3); dbmfFree($3);
163};169};
164170
171link: tokenLINK '(' tokenSTRING ',' tokenSTRING ')'
172{
173 if(dbStaticDebug>2) printf("link %s %s\n",$3,$5);
174 dbLinkType($3,$5);
175 dbmfFree($3); dbmfFree($5);
176};
177
165registrar: tokenREGISTRAR '(' tokenSTRING ')'178registrar: tokenREGISTRAR '(' tokenSTRING ')'
166{179{
167 if(dbStaticDebug>2) printf("registrar %s\n",$3);180 if(dbStaticDebug>2) printf("registrar %s\n",$3);
@@ -239,15 +252,17 @@
239record_field_list: record_field_list record_field252record_field_list: record_field_list record_field
240 | record_field;253 | record_field;
241254
242record_field: tokenFIELD '(' tokenSTRING ',' tokenSTRING ')'255record_field: tokenFIELD '(' tokenSTRING ','
256 { BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
243{257{
244 if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$5);258 if(dbStaticDebug>2) printf("record_field %s %s\n",$3,$6);
245 dbRecordField($3,$5); dbmfFree($3); dbmfFree($5);259 dbRecordField($3,$6); dbmfFree($3); dbmfFree($6);
246}260}
247 | tokenINFO '(' tokenSTRING ',' tokenSTRING ')'261 | tokenINFO '(' tokenSTRING ','
262 { BEGIN JSON; } json_value { BEGIN INITIAL; } ')'
248{263{
249 if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$5);264 if(dbStaticDebug>2) printf("record_info %s %s\n",$3,$6);
250 dbRecordInfo($3,$5); dbmfFree($3); dbmfFree($5);265 dbRecordInfo($3,$6); dbmfFree($3); dbmfFree($6);
251}266}
252 | tokenALIAS '(' tokenSTRING ')'267 | tokenALIAS '(' tokenSTRING ')'
253{268{
@@ -262,6 +277,69 @@
262 dbAlias($3,$5); dbmfFree($3); dbmfFree($5);277 dbAlias($3,$5); dbmfFree($3); dbmfFree($5);
263};278};
264279
280json_object: '{' '}'
281{
282 $$ = dbmfStrdup("{}");
283 if (dbStaticDebug>2) printf("json %s\n", $$);
284}
285 | '{' json_members '}'
286{
287 $$ = dbmfStrcat3("{", $2, "}");
288 dbmfFree($2);
289 if (dbStaticDebug>2) printf("json %s\n", $$);
290};
291
292json_members: json_pair
293 | json_pair ',' json_members
294{
295 $$ = dbmfStrcat3($1, ",", $3);
296 dbmfFree($1); dbmfFree($3);
297 if (dbStaticDebug>2) printf("json %s\n", $$);
298};
299
300json_pair: json_string ':' json_value
301{
302 $$ = dbmfStrcat3($1, ":", $3);
303 dbmfFree($1); dbmfFree($3);
304 if (dbStaticDebug>2) printf("json %s\n", $$);
305};
306
307json_string: jsonSTRING
308 | jsonBARE
309{
310 $$ = dbmfStrcat3("\"", $1, "\"");
311 dbmfFree($1);
312 if (dbStaticDebug>2) printf("json %s\n", $$);
313};
314
315json_array: '[' ']'
316{
317 $$ = dbmfStrdup("[]");
318 if (dbStaticDebug>2) printf("json %s\n", $$);
319}
320 | '[' json_elements ']'
321{
322 $$ = dbmfStrcat3("[", $2, "]");
323 dbmfFree($2);
324 if (dbStaticDebug>2) printf("json %s\n", $$);
325};
326
327json_elements: json_value
328 | json_value ',' json_elements
329{
330 $$ = dbmfStrcat3($1, ",", $3);
331 dbmfFree($1); dbmfFree($3);
332 if (dbStaticDebug>2) printf("json %s\n", $$);
333};
334
335json_value: jsonNULL { $$ = dbmfStrdup("null"); }
336 | jsonTRUE { $$ = dbmfStrdup("true"); }
337 | jsonFALSE { $$ = dbmfStrdup("false"); }
338 | jsonNUMBER
339 | json_string
340 | json_array
341 | json_object ;
342
265343
266%%344%%
267345
268346
269347
=== modified file 'src/ioc/dbStatic/link.h'
--- src/ioc/dbStatic/link.h 2017-02-01 17:57:04 +0000
+++ src/ioc/dbStatic/link.h 2017-03-03 18:23:23 +0000
@@ -32,7 +32,7 @@
32#define GPIB_IO 532#define GPIB_IO 5
33#define BITBUS_IO 633#define BITBUS_IO 6
34#define MACRO_LINK 734#define MACRO_LINK 7
3535#define JSON_LINK 8
36#define PN_LINK 936#define PN_LINK 9
37#define DB_LINK 1037#define DB_LINK 10
38#define CA_LINK 1138#define CA_LINK 11
@@ -40,7 +40,7 @@
40#define BBGPIB_IO 13 /* bitbus -> gpib */40#define BBGPIB_IO 13 /* bitbus -> gpib */
41#define RF_IO 1441#define RF_IO 14
42#define VXI_IO 1542#define VXI_IO 15
43#define LINK_NTYPES 1543#define LINK_NTYPES 16
44typedef struct maplinkType {44typedef struct maplinkType {
45 char *strvalue;45 char *strvalue;
46 int value;46 int value;
@@ -86,6 +86,12 @@
86 short lastGetdbrType; /* last dbrType for DB or CA get */86 short lastGetdbrType; /* last dbrType for DB or CA get */
87};87};
8888
89struct jlink;
90struct json_link {
91 char *string;
92 struct jlink *jlink;
93};
94
89/* structure of a VME io channel */95/* structure of a VME io channel */
90struct vmeio {96struct vmeio {
91 short card;97 short card;
@@ -166,6 +172,7 @@
166union value {172union value {
167 char *constantStr; /*constant string*/173 char *constantStr; /*constant string*/
168 struct macro_link macro_link; /* link containing macro substitution*/174 struct macro_link macro_link; /* link containing macro substitution*/
175 struct json_link json; /* JSON-encoded link */
169 struct pv_link pv_link; /* link to process variable*/176 struct pv_link pv_link; /* link to process variable*/
170 struct vmeio vmeio; /* vme io point */177 struct vmeio vmeio; /* vme io point */
171 struct camacio camacio; /* camac io point */178 struct camacio camacio; /* camac io point */
172179
=== modified file 'src/ioc/dbtemplate/dbLoadTemplate_lex.l'
--- src/ioc/dbtemplate/dbLoadTemplate_lex.l 2010-08-20 22:59:38 +0000
+++ src/ioc/dbtemplate/dbLoadTemplate_lex.l 2017-03-03 18:23:23 +0000
@@ -24,13 +24,13 @@
2424
25{doublequote}({dstringchar}|{escape})*{doublequote} |25{doublequote}({dstringchar}|{escape})*{doublequote} |
26{singlequote}({sstringchar}|{escape})*{singlequote} {26{singlequote}({sstringchar}|{escape})*{singlequote} {
27 yylval.Str = dbmfStrdup(yytext+1);27 yylval.Str = dbmfStrdup((char *) yytext+1);
28 yylval.Str[strlen(yylval.Str)-1] = '\0';28 yylval.Str[strlen(yylval.Str)-1] = '\0';
29 return(QUOTE);29 return(QUOTE);
30}30}
3131
32{bareword}+ {32{bareword}+ {
33 yylval.Str = dbmfStrdup(yytext);33 yylval.Str = dbmfStrdup((char *) yytext);
34 return(WORD);34 return(WORD);
35}35}
3636
3737
=== modified file 'src/ioc/misc/iocInit.c'
--- src/ioc/misc/iocInit.c 2017-02-01 17:57:04 +0000
+++ src/ioc/misc/iocInit.c 2017-03-03 18:23:23 +0000
@@ -67,6 +67,7 @@
67#include "recSup.h"67#include "recSup.h"
68#include "registryDeviceSupport.h"68#include "registryDeviceSupport.h"
69#include "registryDriverSupport.h"69#include "registryDriverSupport.h"
70#include "registryJLinks.h"
70#include "registryRecordType.h"71#include "registryRecordType.h"
71#include "rsrv.h"72#include "rsrv.h"
7273
@@ -655,18 +656,14 @@
655 pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];656 pdbRecordType->papFldDes[pdbRecordType->link_ind[j]];
656 DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);657 DBLINK *plink = (DBLINK *)((char *)precord + pdbFldDes->offset);
657658
658 if (plink->type == CA_LINK) {659 if (plink->type == CA_LINK ||
660 plink->type == JSON_LINK ||
661 (plink->type == DB_LINK && iocBuildMode == buildIsolated)) {
659 if (!locked) {662 if (!locked) {
660 dbScanLock(precord);663 dbScanLock(precord);
661 locked = 1;664 locked = 1;
662 }665 }
663 dbCaRemoveLink(NULL, plink);666 dbRemoveLink(NULL, plink);
664
665 } else if (iocBuildMode==buildIsolated && plink->type == DB_LINK) {
666 /* free link, but don't split lockset like dbDbRemoveLink() */
667 free(plink->value.pv_link.pvt);
668 plink->type = PV_LINK;
669 plink->lset = NULL;
670 }667 }
671 }668 }
672669
673670
=== modified file 'src/ioc/registry/Makefile'
--- src/ioc/registry/Makefile 2012-08-10 16:40:50 +0000
+++ src/ioc/registry/Makefile 2017-03-03 18:23:23 +0000
@@ -14,6 +14,7 @@
14INC += registryRecordType.h14INC += registryRecordType.h
15INC += registryDeviceSupport.h15INC += registryDeviceSupport.h
16INC += registryDriverSupport.h16INC += registryDriverSupport.h
17INC += registryJLinks.h
17INC += registryFunction.h18INC += registryFunction.h
18INC += registryCommon.h19INC += registryCommon.h
19INC += registryIocRegister.h20INC += registryIocRegister.h
@@ -21,6 +22,7 @@
21dbCore_SRCS += registryRecordType.c22dbCore_SRCS += registryRecordType.c
22dbCore_SRCS += registryDeviceSupport.c23dbCore_SRCS += registryDeviceSupport.c
23dbCore_SRCS += registryDriverSupport.c24dbCore_SRCS += registryDriverSupport.c
25dbCore_SRCS += registryJLinks.c
24dbCore_SRCS += registryFunction.c26dbCore_SRCS += registryFunction.c
25dbCore_SRCS += registryCommon.c27dbCore_SRCS += registryCommon.c
26dbCore_SRCS += registryIocRegister.c28dbCore_SRCS += registryIocRegister.c
2729
=== modified file 'src/ioc/registry/registryCommon.c'
--- src/ioc/registry/registryCommon.c 2014-10-06 05:57:02 +0000
+++ src/ioc/registry/registryCommon.c 2017-03-03 18:23:23 +0000
@@ -19,6 +19,7 @@
19#include "registryCommon.h"19#include "registryCommon.h"
20#include "registryDeviceSupport.h"20#include "registryDeviceSupport.h"
21#include "registryDriverSupport.h"21#include "registryDriverSupport.h"
22#include "registryJLinks.h"
2223
2324
24void registerRecordTypes(DBBASE *pbase, int nRecordTypes,25void registerRecordTypes(DBBASE *pbase, int nRecordTypes,
@@ -76,3 +77,15 @@
76 }77 }
77}78}
7879
80void registerJLinks(DBBASE *pbase, int nLinks, jlif * const *jlifsl)
81{
82 int i;
83 for (i = 0; i < nLinks; i++) {
84 if (!registryJLinkAdd(pbase, jlifsl[i])) {
85 errlogPrintf("registryJLinkAdd failed %s\n",
86 jlifsl[i]->name);
87 continue;
88 }
89 }
90}
91
7992
=== modified file 'src/ioc/registry/registryCommon.h'
--- src/ioc/registry/registryCommon.h 2014-10-06 05:57:02 +0000
+++ src/ioc/registry/registryCommon.h 2017-03-03 18:23:23 +0000
@@ -12,6 +12,7 @@
1212
13#include "dbStaticLib.h"13#include "dbStaticLib.h"
14#include "devSup.h"14#include "devSup.h"
15#include "dbJLink.h"
15#include "registryRecordType.h"16#include "registryRecordType.h"
16#include "shareLib.h"17#include "shareLib.h"
1718
@@ -28,6 +29,8 @@
28epicsShareFunc void registerDrivers(29epicsShareFunc void registerDrivers(
29 DBBASE *pbase, int nDrivers,30 DBBASE *pbase, int nDrivers,
30 const char * const *driverSupportNames, struct drvet * const *drvsl);31 const char * const *driverSupportNames, struct drvet * const *drvsl);
32epicsShareFunc void registerJLinks(
33 DBBASE *pbase, int nDrivers, jlif * const *jlifsl);
3134
32#ifdef __cplusplus35#ifdef __cplusplus
33}36}
3437
=== added file 'src/ioc/registry/registryJLinks.c'
--- src/ioc/registry/registryJLinks.c 1970-01-01 00:00:00 +0000
+++ src/ioc/registry/registryJLinks.c 2017-03-03 18:23:23 +0000
@@ -0,0 +1,23 @@
1/*************************************************************************\
2* Copyright (c) 2016 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* EPICS BASE is distributed subject to a Software License Agreement found
5* in file LICENSE that is included with this distribution.
6\*************************************************************************/
7/* registryJLinks.c */
8
9#include "registry.h"
10#define epicsExportSharedSymbols
11#include "dbBase.h"
12#include "dbStaticLib.h"
13#include "registryJLinks.h"
14#include "dbJLink.h"
15
16epicsShareFunc int registryJLinkAdd(DBBASE *pbase, struct jlif *pjlif)
17{
18 linkSup *plinkSup = dbFindLinkSup(pbase, pjlif->name);
19
20 if (plinkSup)
21 plinkSup->pjlif = pjlif;
22 return !!plinkSup;
23}
024
=== added file 'src/ioc/registry/registryJLinks.h'
--- src/ioc/registry/registryJLinks.h 1970-01-01 00:00:00 +0000
+++ src/ioc/registry/registryJLinks.h 2017-03-03 18:23:23 +0000
@@ -0,0 +1,28 @@
1/*************************************************************************\
2* Copyright (c) 2007 UChicago Argonne LLC, as Operator of Argonne
3* National Laboratory.
4* Copyright (c) 2002 The Regents of the University of California, as
5* Operator of Los Alamos National Laboratory.
6* EPICS BASE is distributed subject to a Software License Agreement found
7* in file LICENSE that is included with this distribution.
8\*************************************************************************/
9
10#ifndef INC_registryJLinks_H
11#define INC_registryJLinks_H
12
13#include "dbBase.h"
14#include "dbJLink.h"
15#include "shareLib.h"
16
17#ifdef __cplusplus
18extern "C" {
19#endif
20
21epicsShareFunc int registryJLinkAdd(DBBASE *pbase, jlif *pjlif);
22
23#ifdef __cplusplus
24}
25#endif
26
27
28#endif /* INC_registryDriverSupport_H */
029
=== modified file 'src/libCom/dbmf/dbmf.c'
--- src/libCom/dbmf/dbmf.c 2017-02-01 17:57:04 +0000
+++ src/libCom/dbmf/dbmf.c 2017-03-03 18:23:23 +0000
@@ -68,7 +68,7 @@
68static dbmfPrivate *pdbmfPvt = NULL;68static dbmfPrivate *pdbmfPvt = NULL;
69int dbmfDebug=0;69int dbmfDebug=0;
7070
7171
72int epicsShareAPI dbmfInit(size_t size, int chunkItems)72int dbmfInit(size_t size, int chunkItems)
73{73{
74 if(pdbmfPvt) {74 if(pdbmfPvt) {
75 printf("dbmfInit: Already initialized\n");75 printf("dbmfInit: Already initialized\n");
@@ -94,7 +94,7 @@
94}94}
9595
9696
9797
98void* epicsShareAPI dbmfMalloc(size_t size)98void* dbmfMalloc(size_t size)
99{99{
100 void **pnextFree;100 void **pnextFree;
101 void **pfreeList;101 void **pfreeList;
@@ -156,15 +156,23 @@
156 return((void *)pmem);156 return((void *)pmem);
157}157}
158158
159char * epicsShareAPI dbmfStrdup(unsigned char *str)159char * dbmfStrdup(const char *str)
160{160{
161 size_t len = strlen((char *) str);161 size_t len = strlen(str);
162 char *buf = dbmfMalloc(len + 1);162 char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
163 strcpy(buf, (char *) str);163
164 return buf;164 return strcpy(buf, str);
165}165}
166166
167void epicsShareAPI dbmfFree(void* mem)167char * dbmfStrndup(const char *str, size_t len)
168{
169 char *buf = dbmfMalloc(len + 1); /* FIXME Can return NULL */
170
171 buf[len] = '\0';
172 return strncpy(buf, str, len);
173}
174
175void dbmfFree(void* mem)
168{176{
169 char *pmem = (char *)mem;177 char *pmem = (char *)mem;
170 chunkNode *pchunkNode;178 chunkNode *pchunkNode;
@@ -194,7 +202,7 @@
194 epicsMutexUnlock(pdbmfPvt->lock);202 epicsMutexUnlock(pdbmfPvt->lock);
195}203}
196204
197205
198int epicsShareAPI dbmfShow(int level)206int dbmfShow(int level)
199{207{
200 if(pdbmfPvt==NULL) {208 if(pdbmfPvt==NULL) {
201 printf("Never initialized\n");209 printf("Never initialized\n");
@@ -230,7 +238,7 @@
230 return(0);238 return(0);
231}239}
232240
233void epicsShareAPI dbmfFreeChunks(void)241void dbmfFreeChunks(void)
234{242{
235 chunkNode *pchunkNode;243 chunkNode *pchunkNode;
236 chunkNode *pnext;;244 chunkNode *pnext;;
@@ -259,21 +267,32 @@
259267
260#else /* DBMF_FREELIST_DEBUG */268#else /* DBMF_FREELIST_DEBUG */
261269
262int epicsShareAPI dbmfInit(size_t size, int chunkItems)270int dbmfInit(size_t size, int chunkItems)
263{ return 0; }271{ return 0; }
264272
265void* epicsShareAPI dbmfMalloc(size_t size)273void* dbmfMalloc(size_t size)
266{ return malloc(size); }274{ return malloc(size); }
267275
268char * epicsShareAPI dbmfStrdup(unsigned char *str)276char * dbmfStrdup(const char *str)
269{ return strdup((char*)str); }277{ return strdup((char*)str); }
270278
271void epicsShareAPI dbmfFree(void* mem)279void dbmfFree(void* mem)
272{ free(mem); }280{ free(mem); }
273281
274int epicsShareAPI dbmfShow(int level)282int dbmfShow(int level)
275{ return 0; }283{ return 0; }
276284
277void epicsShareAPI dbmfFreeChunks(void) {}285void dbmfFreeChunks(void) {}
278286
279#endif /* DBMF_FREELIST_DEBUG */287#endif /* DBMF_FREELIST_DEBUG */
288
289char * dbmfStrcat3(const char *lhs, const char *mid, const char *rhs)
290{
291 size_t len = strlen(lhs) + strlen(mid) + strlen(rhs) + 1;
292 char *buf = dbmfMalloc(len);
293 strcpy(buf, lhs);
294 strcat(buf, mid);
295 strcat(buf, rhs);
296 return buf;
297}
298
280299
=== modified file 'src/libCom/dbmf/dbmf.h'
--- src/libCom/dbmf/dbmf.h 2012-06-28 14:55:44 +0000
+++ src/libCom/dbmf/dbmf.h 2017-03-03 18:23:23 +0000
@@ -23,12 +23,15 @@
23extern "C" {23extern "C" {
24#endif24#endif
2525
26epicsShareFunc int epicsShareAPI dbmfInit(size_t size, int chunkItems);26epicsShareFunc int dbmfInit(size_t size, int chunkItems);
27epicsShareFunc void * epicsShareAPI dbmfMalloc(size_t bytes);27epicsShareFunc void * dbmfMalloc(size_t bytes);
28epicsShareFunc char * epicsShareAPI dbmfStrdup(unsigned char *str);28epicsShareFunc char * dbmfStrdup(const char *str);
29epicsShareFunc void epicsShareAPI dbmfFree(void* bytes);29epicsShareFunc char * dbmfStrndup(const char *str, size_t len);
30epicsShareFunc void epicsShareAPI dbmfFreeChunks(void);30epicsShareFunc char * dbmfStrcat3(const char *lhs, const char *mid,
31epicsShareFunc int epicsShareAPI dbmfShow(int level);31 const char *rhs);
32epicsShareFunc void dbmfFree(void *bytes);
33epicsShareFunc void dbmfFreeChunks(void);
34epicsShareFunc int dbmfShow(int level);
3235
33/* Rules:36/* Rules:
34 * 1) Size is always made a multiple of 8.37 * 1) Size is always made a multiple of 8.
3538
=== modified file 'src/libCom/error/errMdef.h'
--- src/libCom/error/errMdef.h 2017-02-01 17:57:04 +0000
+++ src/libCom/error/errMdef.h 2017-03-03 18:23:23 +0000
@@ -52,6 +52,7 @@
52#define M_time (529 <<16) /*epicsTime*/52#define M_time (529 <<16) /*epicsTime*/
5353
54epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);54epicsShareFunc void epicsShareAPI errSymLookup(long status, char *pBuf, unsigned bufLength);
55epicsShareFunc const char* errSymMsg(long status);
55epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);56epicsShareFunc void epicsShareAPI errSymTest(unsigned short modnum, unsigned short begErrNum, unsigned short endErrNum);
56epicsShareFunc void epicsShareAPI errSymTestPrint(long errNum);57epicsShareFunc void epicsShareAPI errSymTestPrint(long errNum);
57epicsShareFunc int epicsShareAPI errSymBld(void);58epicsShareFunc int epicsShareAPI errSymBld(void);
5859
=== modified file 'src/libCom/error/errSymLib.c'
--- src/libCom/error/errSymLib.c 2016-05-22 03:43:09 +0000
+++ src/libCom/error/errSymLib.c 2017-03-03 18:23:23 +0000
@@ -192,11 +192,9 @@
192 assert ( nChar < bufLength );192 assert ( nChar < bufLength );
193 }193 }
194}194}
195
196195
197/****************************************************************196
198 * errSymLookup197static
199 ***************************************************************/198const char* errSymLookupInternal(long status)
200void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
201{199{
202 unsigned modNum;200 unsigned modNum;
203 unsigned hashInd;201 unsigned hashInd;
@@ -211,9 +209,7 @@
211 if ( modNum <= 500 ) {209 if ( modNum <= 500 ) {
212 const char * pStr = strerror ((int) status);210 const char * pStr = strerror ((int) status);
213 if ( pStr ) {211 if ( pStr ) {
214 strncpy(pBuf, pStr,bufLength);212 return pStr;
215 pBuf[bufLength-1] = '\0';
216 return;
217 }213 }
218 }214 }
219 else {215 else {
@@ -222,17 +218,35 @@
222 pNextNode = *phashnode;218 pNextNode = *phashnode;
223 while(pNextNode) {219 while(pNextNode) {
224 if(pNextNode->errNum==status){220 if(pNextNode->errNum==status){
225 strncpy(pBuf, pNextNode->message, bufLength);221 return pNextNode->message;
226 pBuf[bufLength-1] = '\0';
227 return;
228 }222 }
229 phashnode = &pNextNode->hashnode;223 phashnode = &pNextNode->hashnode;
230 pNextNode = *phashnode;224 pNextNode = *phashnode;
231 }225 }
232 }226 }
227 return NULL;
228}
229
230const char* errSymMsg(long status)
231{
232 const char* msg = errSymLookupInternal(status);
233 return msg ? msg : "<Unknown code>";
234}
235
236/****************************************************************
237 * errSymLookup
238 ***************************************************************/
239void epicsShareAPI errSymLookup (long status, char * pBuf, unsigned bufLength)
240{
241 const char* msg = errSymLookupInternal(status);
242 if(msg) {
243 strncpy(pBuf, msg, bufLength);
244 pBuf[bufLength-1] = '\0';
245 return;
246 }
233 errRawCopy(status, pBuf, bufLength);247 errRawCopy(status, pBuf, bufLength);
234}248}
235
236249
250
237/****************************************************************251/****************************************************************
238 * errSymDump252 * errSymDump
239 ***************************************************************/253 ***************************************************************/
240254
=== modified file 'src/libCom/error/makeStatTbl.pl'
--- src/libCom/error/makeStatTbl.pl 2009-07-09 20:11:02 +0000
+++ src/libCom/error/makeStatTbl.pl 2017-03-03 18:23:23 +0000
@@ -86,7 +86,7 @@
86{86{
87 print OUT "$line\n";87 print OUT "$line\n";
88 # define S_symbol /* comment */88 # define S_symbol /* comment */
89 if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\*(.+)\*\/')89 if ($line =~ m'[ \t#]define[ \t]*(S_[A-Za-z0-9_]+).*\/\* ?(.+?) ?\*\/')
90 {90 {
91 $symbol[$count] = $1;91 $symbol[$count] = $1;
92 $comment[$count]= $2;92 $comment[$count]= $2;
9393
=== modified file 'src/libCom/misc/dbDefs.h'
--- src/libCom/misc/dbDefs.h 2016-05-22 03:43:09 +0000
+++ src/libCom/misc/dbDefs.h 2017-03-03 18:23:23 +0000
@@ -59,6 +59,10 @@
59#define PVNAME_STRINGSZ 6159#define PVNAME_STRINGSZ 61
60#define PVNAME_SZ (PVNAME_STRINGSZ - 1)60#define PVNAME_SZ (PVNAME_STRINGSZ - 1)
6161
62/* Buffer size for the string representation of a DBF_*LINK field */
63#define PVLINK_STRINGSZ 1024
64
65/* dbAccess enums/menus can have up to this many choices */
62#define DB_MAX_CHOICES 3066#define DB_MAX_CHOICES 30
6367
64#endif /* INC_dbDefs_H */68#endif /* INC_dbDefs_H */
6569
=== modified file 'src/libCom/misc/epicsString.c'
--- src/libCom/misc/epicsString.c 2016-12-07 22:36:58 +0000
+++ src/libCom/misc/epicsString.c 2017-03-03 18:23:23 +0000
@@ -222,6 +222,15 @@
222 return 0;222 return 0;
223}223}
224224
225char * epicsStrnDup(const char *s, size_t len)
226{
227 char *buf = mallocMustSucceed(len + 1, "epicsStrnDup");
228
229 strncpy(buf, s, len);
230 buf[len] = '\0';
231 return buf;
232}
233
225char * epicsStrDup(const char *s)234char * epicsStrDup(const char *s)
226{235{
227 return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s);236 return strcpy(mallocMustSucceed(strlen(s)+1, "epicsStrDup"), s);
228237
=== modified file 'src/libCom/misc/epicsString.h'
--- src/libCom/misc/epicsString.h 2014-01-11 15:58:39 +0000
+++ src/libCom/misc/epicsString.h 2017-03-03 18:23:23 +0000
@@ -31,6 +31,7 @@
31epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2);31epicsShareFunc int epicsStrCaseCmp(const char *s1, const char *s2);
32epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len);32epicsShareFunc int epicsStrnCaseCmp(const char *s1, const char *s2, size_t len);
33epicsShareFunc char * epicsStrDup(const char *s);33epicsShareFunc char * epicsStrDup(const char *s);
34epicsShareFunc char * epicsStrnDup(const char *s, size_t len);
34epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n);35epicsShareFunc int epicsStrPrintEscaped(FILE *fp, const char *s, size_t n);
35#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw36#define epicsStrSnPrintEscaped epicsStrnEscapedFromRaw
36epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen);37epicsShareFunc size_t epicsStrnLen(const char *s, size_t maxlen);
3738
=== modified file 'src/libCom/yacc/antelope.c'
--- src/libCom/yacc/antelope.c 2012-05-03 17:19:34 +0000
+++ src/libCom/yacc/antelope.c 2017-03-03 18:23:23 +0000
@@ -115,7 +115,11 @@
115 int i;115 int i;
116 char *s;116 char *s;
117117
118 if (argc > 0) myname = argv[0];118 if (argc > 0) {
119 myname = strrchr(argv[0], '/');
120 if (myname) myname++;
121 else myname = argv[0];
122 }
119 for (i = 1; i < argc; ++i)123 for (i = 1; i < argc; ++i)
120 {124 {
121 s = argv[i];125 s = argv[i];
122126
=== modified file 'src/libCom/yacc/error.c'
--- src/libCom/yacc/error.c 2013-11-20 23:28:41 +0000
+++ src/libCom/yacc/error.c 2017-03-03 18:23:23 +0000
@@ -14,7 +14,7 @@
14void14void
15fatal(char *msg)15fatal(char *msg)
16{16{
17 fprintf(stderr, "%s: f - %s\n", myname, msg);17 fprintf(stderr, "%s: fatal - %s\n", myname, msg);
18 done(2);18 done(2);
19}19}
2020
@@ -22,7 +22,7 @@
22void22void
23no_space(void)23no_space(void)
24{24{
25 fprintf(stderr, "%s: f - out of space\n", myname);25 fprintf(stderr, "%s: fatal - out of space\n", myname);
26 done(2);26 done(2);
27}27}
2828
@@ -30,7 +30,7 @@
30void30void
31open_error(char *filename)31open_error(char *filename)
32{32{
33 fprintf(stderr, "%s: f - cannot open \"%s\"\n", myname, filename);33 fprintf(stderr, "%s: fatal - cannot open \"%s\"\n", myname, filename);
34 done(2);34 done(2);
35}35}
3636
@@ -38,7 +38,7 @@
38void38void
39unexpected_EOF(void)39unexpected_EOF(void)
40{40{
41 fprintf(stderr, "%s: e - line %d of \"%s\", unexpected end-of-file\n",41 fprintf(stderr, "%s: error - line %d of \"%s\", unexpected end-of-file\n",
42 myname, lineno, input_file_name);42 myname, lineno, input_file_name);
43 done(1);43 done(1);
44}44}
@@ -74,7 +74,7 @@
74void74void
75syntax_error(int st_lineno, char *st_line, char *st_cptr)75syntax_error(int st_lineno, char *st_line, char *st_cptr)
76{76{
77 fprintf(stderr, "%s: e - line %d of \"%s\", syntax error\n",77 fprintf(stderr, "%s: error - line %d of \"%s\", syntax error\n",
78 myname, st_lineno, input_file_name);78 myname, st_lineno, input_file_name);
79 print_pos(st_line, st_cptr);79 print_pos(st_line, st_cptr);
80 done(1);80 done(1);
@@ -84,7 +84,7 @@
84void84void
85unterminated_comment(int c_lineno, char *c_line, char *c_cptr)85unterminated_comment(int c_lineno, char *c_line, char *c_cptr)
86{86{
87 fprintf(stderr, "%s: e - line %d of \"%s\", unmatched /*\n",87 fprintf(stderr, "%s: error - line %d of \"%s\", unmatched /*\n",
88 myname, c_lineno, input_file_name);88 myname, c_lineno, input_file_name);
89 print_pos(c_line, c_cptr);89 print_pos(c_line, c_cptr);
90 done(1);90 done(1);
@@ -94,7 +94,7 @@
94void94void
95unterminated_string(int s_lineno, char *s_line, char *s_cptr)95unterminated_string(int s_lineno, char *s_line, char *s_cptr)
96{96{
97 fprintf(stderr, "%s: e - line %d of \"%s\", unterminated string\n",97 fprintf(stderr, "%s: error - line %d of \"%s\", unterminated string\n",
98 myname, s_lineno, input_file_name);98 myname, s_lineno, input_file_name);
99 print_pos(s_line, s_cptr);99 print_pos(s_line, s_cptr);
100 done(1);100 done(1);
@@ -104,7 +104,7 @@
104void104void
105unterminated_text(int t_lineno, char *t_line, char *t_cptr)105unterminated_text(int t_lineno, char *t_line, char *t_cptr)
106{106{
107 fprintf(stderr, "%s: e - line %d of \"%s\", unmatched %%{\n",107 fprintf(stderr, "%s: error - line %d of \"%s\", unmatched %%{\n",
108 myname, t_lineno, input_file_name);108 myname, t_lineno, input_file_name);
109 print_pos(t_line, t_cptr);109 print_pos(t_line, t_cptr);
110 done(1);110 done(1);
@@ -114,7 +114,7 @@
114void114void
115unterminated_union(int u_lineno, char *u_line, char *u_cptr)115unterminated_union(int u_lineno, char *u_line, char *u_cptr)
116{116{
117 fprintf(stderr, "%s: e - line %d of \"%s\", unterminated %%union \117 fprintf(stderr, "%s: error - line %d of \"%s\", unterminated %%union \
118declaration\n", myname, u_lineno, input_file_name);118declaration\n", myname, u_lineno, input_file_name);
119 print_pos(u_line, u_cptr);119 print_pos(u_line, u_cptr);
120 done(1);120 done(1);
@@ -124,7 +124,7 @@
124void124void
125over_unionized(char *u_cptr)125over_unionized(char *u_cptr)
126{126{
127 fprintf(stderr, "%s: e - line %d of \"%s\", too many %%union \127 fprintf(stderr, "%s: error - line %d of \"%s\", too many %%union \
128declarations\n", myname, lineno, input_file_name);128declarations\n", myname, lineno, input_file_name);
129 print_pos(line, u_cptr);129 print_pos(line, u_cptr);
130 done(1);130 done(1);
@@ -134,7 +134,7 @@
134void134void
135illegal_tag(int t_lineno, char *t_line, char *t_cptr)135illegal_tag(int t_lineno, char *t_line, char *t_cptr)
136{136{
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches