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