Compare commits

..

245 Commits

Author SHA1 Message Date
Andrew Johnson
57c930fbee Release R7.0.8.1
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 1s
Base / Cross linux-aarch64 (push) Failing after 2s
Base / Cross linux-arm gnueabi (push) Failing after 1s
Base / Cross linux-arm gnueabihf (push) Failing after 2s
Base / CentOS-7 (push) Failing after 1s
Base / Fedora-33 (push) Failing after 2s
Base / Fedora-latest (push) Failing after 1s
Base / Ub-20 clang-10 C++11 (push) Has been cancelled
Base / MacOS clang-12 (push) Has been cancelled
Base / Ub-20 clang-10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.9 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 pc686 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 uC5282 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW (push) Has been cancelled
Base / Ub-20 gcc-9 unsigned char (push) Has been cancelled
Base / Ub-20 gcc-9 C++11, static (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW, static (push) Has been cancelled
Base / Ub-22 gcc-12 c++20 Werror (push) Has been cancelled
Base / Win2019 MSC-19 (push) Has been cancelled
Base / Win2019 MSC-19, debug (push) Has been cancelled
Base / Win2019 MSC-19, static (push) Has been cancelled
2024-06-27 20:55:03 -05:00
Andrew Johnson
785b777baf Update Release Notes, set version numbers 2024-06-26 14:59:18 -05:00
Andrew Johnson
d0d15ee911 Replace UNRELEASED with 7.0.8.1 2024-06-26 14:59:18 -05:00
Andrew Johnson
5af9c7e50d Revert submodules to versions at R7.0.8 2024-06-26 14:59:18 -05:00
Andrew Johnson
5fe563bed8 Limit _FORTIFY_SOURCE <= 2 2024-06-26 14:58:53 -05:00
DW
11fba63d18 Fix histogram doc 2024-05-29 09:17:03 -05:00
Simon Rose
1db37bcd91 Update release notes 2024-05-29 09:15:35 -05:00
Simon Rose
beec00b403 Fix issue with compress record
The handling of N-to-M array compression was broken with the addition
of the partial buffer option, which broke the bounds check that was
being used.

Note that this also makes the partial buffer option more consistent;
if, for example, you have
```
record(compress, foo) {
  field(ALG, "N to 1 Average")
  field(INP, "bar NPP")
  field(NSAM, 2)
  field(N, 2)
  field(PBUF, YES)
}
```
(with `bar` having, e.g. length 3), then this will now behave as
expected on both of the samples.
2024-05-29 09:15:35 -05:00
DW
4966baf423 fix sizv for printf & fix doc 2024-05-20 09:18:26 -04:00
Michael Davidsaver
e5b4829074 bound lso/lsi to limit of dbAddr::field_size 2024-05-19 11:57:19 -04:00
Ralph Lange
d8b5616772 ci: bump checkout to v4 in check-editorconfig.yml
- fix node16.js usage warning
2024-05-16 10:07:12 +02:00
Ralph Lange
92615a77fe ci: fix last commit (GHA workaround) 2024-05-15 18:04:58 +02:00
Ralph Lange
b3f92d81db ci: add workaround for GHA node20@centos7 failures 2024-05-15 16:57:34 +02:00
Simon Rose
839f764bcb Clean up some potential memory leaks
The watchdog tasks are allocated, but not consistently removed. In
general this doesn't matter: they run in threads that will only
end when the process actually quits. For consistency and for the
purpose of future-proofing, I think there is value in having the
cleanup added in each case.
2024-05-15 09:26:09 -05:00
Simon Rose
4bb50fe664 Memory leak in caservertask.c 2024-05-15 09:26:09 -05:00
Ralph Lange
c77f32b19c Merge pull request #482 from ericonr/calcout-docs
Fix calcout doc typo.
2024-05-03 04:04:41 +09:00
Érico Nogueira
66ce1c2076 Fix calcout doc typo. 2024-05-02 15:29:22 -03:00
Érico Nogueira
1a9dc993c1 Fix gmtime messages in epicsTimeZoneTest. 2024-04-29 15:10:47 -07:00
Michael Davidsaver
cb1571783b link.h wrong type
Change to unsigned incorrectly increased size.

e88a186fc3
2024-04-02 08:35:25 -07:00
Freddie Akeroyd
5dfc6caf3c Accept should return SOCKET rather than int 2024-03-06 09:48:26 -06:00
Ralph Lange
cb49bd0133 Update ci-scripts to 3.4.1
Builds on AppVeyor (VS2019) started to fail
because of the Python version/distutils
2024-03-01 18:00:40 +01:00
Freddie Akeroyd
4720b61c1f Move call to setThreadName()
The call to setThreadName() is moved to avoid a race condition that
can happen with very short lived processes. If the process terminates
very quickly e.g. is a google test runner or the msi.exe command
called from a Makefile during a build, then very occasionally a
crash can occur during process termination if setThreadName() when called
from the newly created thread. This looks to be becauae the DLL it is
trying to call gets unloaded between it getting a handle to the DLL
and making the call. Moving the setThreadName() call to the creating
thread avoids this problem. The issue was only ever seen with statically
linked epics executables, I am unsure if the way a DLL based epics
program unloads might avoid this, or just make it less likely but
still possible. As mentioned above, the issue will only ever occur
to threads that are created during process termination and so would
not affect running IOCs
2024-02-21 09:59:36 -06:00
4383cf291e allow macros with defaults in dbLoadRecords without substitutions 2024-02-21 09:50:03 -06:00
Simon Rose
a6977ae731 Fix issue where VSCode makefile extension can delete files
The problem is that VSCode's make extension, in order to determine
some information about the project, runs
```
make --dry-run --always-make
```
which despite its name will actually try to remake the configure/*
files. Running `installEpics.pl` on these will delete them first,
then try copy them, resulting in an error.
2024-02-21 09:45:14 -06:00
Michael Davidsaver
07cbf00187 posix: warn on epicsSocketCreate() without osiSockAttach() 2023-12-22 10:26:28 -08:00
Michael Davidsaver
c75b9ad0be add dbRecordNode::order
Keep track of parse order of record instances.
2023-12-22 10:26:28 -08:00
Michael Davidsaver
87acb98d1e ca: remove hostname length limit when parsing address lists 2023-12-22 10:26:28 -08:00
Michael Davidsaver
403e203325 quieting clang-tidy, use unsigned
places where we shouldn't be negative anyway
2023-12-22 10:26:28 -08:00
Michael Davidsaver
a7a56912eb default/epicsMessageQueue: initialize threadNode 2023-12-22 10:26:28 -08:00
Michael Davidsaver
fe4a32e425 default/epicsMessageQueue: avoid volatile flag
Only one place where eventSent was accessed
without locking.  Move this load earlier.
2023-12-22 10:26:28 -08:00
Michael Davidsaver
823386573f ipAddrToAsciiGlobal::run() keep scratch buffer as local 2023-12-22 10:26:28 -08:00
Michael Davidsaver
ea8247586f adjustToWorstCaseAlignment() simplify
Add some STATIC_ASSERT to check assumptions.

Only in-tree use is freeListLib to ensure chunks in
a malloc()'d block are aligned.
2023-12-22 10:26:28 -08:00
Michael Davidsaver
e88a186fc3 make link::flags bit field unsigned 2023-12-22 10:26:28 -08:00
Michael Davidsaver
20f32068c3 gha add workflow_dispatch 2023-12-22 10:26:28 -08:00
Andrew Johnson
8998341588 Update version numbers and submodules after release 2023-12-15 13:03:57 -06:00
Andrew Johnson
448fde0671 Set release version numbers
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 2s
Base / Cross linux-aarch64 (push) Failing after 1s
Base / Cross linux-arm gnueabi (push) Failing after 2s
Base / Cross linux-arm gnueabihf (push) Failing after 1s
Base / CentOS-7 (push) Failing after 2s
Base / Fedora-33 (push) Failing after 1s
Base / Fedora-latest (push) Failing after 2s
Base / Ub-20 clang-10 C++11 (push) Has been cancelled
Base / MacOS clang-12 (push) Has been cancelled
Base / Ub-20 clang-10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.9 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 pc686 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 uC5282 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW (push) Has been cancelled
Base / Ub-20 gcc-9 unsigned char (push) Has been cancelled
Base / Ub-20 gcc-9 C++11, static (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW, static (push) Has been cancelled
Base / Ub-22 gcc-12 c++20 Werror (push) Has been cancelled
Base / Win2019 MSC-19 (push) Has been cancelled
Base / Win2019 MSC-19, debug (push) Has been cancelled
Base / Win2019 MSC-19, static (push) Has been cancelled
2023-12-14 16:42:10 -06:00
Andrew Johnson
477e36b1f0 Update submodules to tagged versions 2023-12-14 16:42:10 -06:00
Andrew Johnson
fad830bd14 Remove example sub-module 2023-12-14 16:42:10 -06:00
Andrew Johnson
331df3d7e4 UNRELEASED => 7.0.8 2023-12-14 16:42:10 -06:00
Andrew Johnson
4a53713f37 Update Release Procedures/Checklist 2023-12-14 16:31:42 -06:00
Andrew Johnson
2e6fd505d2 Use epicsSnprintf() for old MSVC compilers 2023-12-14 11:31:36 -06:00
Andrew Johnson
5ecf7d18a8 Clean up Clang 15 sprintf() warnings in libcom and ca 2023-12-13 13:34:25 -06:00
Andrew Johnson
56dbc949ff Add declarations to flex, cleans up Clang 15 warnings. 2023-12-12 13:44:08 -06:00
Simon Rose
6a369acd0b Add newline to help text 2023-12-05 08:05:12 -08:00
Simon Rose
d9d35a4eab Allow auto-declarations for dbdExpand.pl 2023-12-01 09:19:03 -06:00
Ralph Lange
116881ad87 Use split() for fetching last tag with darcs
(setting $/='' breaks reading multi-line into an array)
2023-11-29 09:53:24 -06:00
96857d92bc fix problem with commands returning multiple trailing newlines 2023-11-29 09:53:24 -06:00
Érico Nogueira
0cf8c934f9 Set ASL0 for mbboDirect Bx fields.
Since the record's VAL field is ASL0, it doesn't make sense to gate
writes into the Bx fields with ASL1.
2023-11-29 09:44:03 -06:00
Michael Davidsaver
69d05fe5b0 Add ERROR to error messages 2023-11-26 15:25:09 -08:00
Michael Davidsaver
511bf1ffca const-ify dbLink arrays
external code really should never be modifying pamaplinkType[]
2023-11-25 14:52:16 -08:00
Uchenna Ezeobi
7a7028de56 Config: Fixed Hard coded LDFLAGS in MVME2500 2023-11-17 15:20:32 -06:00
Michael Davidsaver
7a65c001ce update submodules 2023-11-15 07:31:53 -08:00
Michael Davidsaver
0bc6ff3d4c release notes 2023-11-07 16:00:46 -08:00
Michael Davidsaver
f2fe9d1203 bi "Raw Soft Channel" use MASK
If set, apply MASK to value read into RVAL.
2023-11-07 15:53:41 -08:00
Michael Davidsaver
ffc2d0f23a incorrect error check on GetStdHandle()
Likely inconsequential as GetConsoleMode() should
return 0 when given an invalid handle.
2023-11-07 15:53:41 -08:00
Michael Davidsaver
a352865df9 print ANSI escapes to stderr
unconditionally print ANSI some escapes (to colorize errors)
to the stderr stream.
2023-11-07 15:53:22 -08:00
Michael Davidsaver
63740f2edd colorize more errlog messages 2023-11-07 15:53:22 -08:00
Jeremy Lorelli
f4be9daf4d Null check callback function in callbackRequest
Previously, calling callbackRequest(pcallback), where pcallback->callback
is NULL, would result in a crash on one of the callback threads.
2023-11-07 15:17:04 -08:00
Michael Davidsaver
3fa1932345 update ci-scripts 2023-11-07 15:14:09 -08:00
Michael Davidsaver
95bd5453d9 dbRecordField() add "did you mean..." hint for unknown field 2023-11-01 09:52:32 -05:00
Michael Davidsaver
eb3f8a004c const-ify dbCopyEntry() and dbCopyEntryContents() 2023-11-01 09:52:32 -05:00
Michael Davidsaver
9f868a1074 avoid hang during concurrent db_cancel_event()
cf. fab8fd7102
2023-11-01 09:24:44 -05:00
Michael Davidsaver
b41787b6bf doc 2023-10-22 17:42:36 -07:00
19b232545c gha: turn most warnings into errors in github build 2023-10-22 17:42:36 -07:00
Michael Davidsaver
2ca70d3aa2 iocsh: keep history file 2023-10-22 17:42:36 -07:00
JJL772
395015aac4 Com: Make STATIC_ASSERT macro typedefs unique 2023-10-22 17:42:36 -07:00
Michael Davidsaver
92cae86ff2 dbRecordsOnceOnly allow append only with "*"
with

> record(ai, "myrec") {}

dbRecordsOnceOnly!=0 currently disallows appending fields with either form:

> record("*", "myrec") {} # error
> record(ai, "myrec") {}  # error

Change the meaning such that dbRecordsOnceOnly!=0
allways allows appending when explicitly intended (rtype "*").

> record("*", "myrec") {} # allowed
> record(ai, "myrec") {}  # error

Also clearly label this parse error.
2023-10-22 17:42:36 -07:00
Jack Harper
49ea46ee5e iocsh: add comment to cvtArg explaining default iocsharg behaviour 2023-10-22 17:42:36 -07:00
Michael Davidsaver
df908f299b remove unused local 2023-10-22 17:42:36 -07:00
AlexWells
6dec68554c iocsh: Add underline separator between help outputs
Also tweaks the overall format of the message a bit.
Add tests for new help output format
2023-10-22 17:42:36 -07:00
Michael Davidsaver
badd8f518d update modules/pvData 2023-10-22 17:42:35 -07:00
Michael Davidsaver
766c9906b5 update ci-scripts 2023-10-22 14:46:28 -07:00
Jeremy Lorelli
60fa2d31da libCom: Fix buggy pointer dereference in postfix() 2023-09-26 09:52:13 -07:00
Michael Davidsaver
88ea1507f4 Fix compile w/ vs2012 2023-09-22 08:20:27 -07:00
Emilio Perez
8c08c57247 Allow adding error symbols after early initialization
This was acomplished by making errSymbolAdd add the error symbol directly
into the global hash table and removing errnumlist which is not needed
anymore.

Unit tests were added for checking the following cases:
- Adding a valid symbol and checking that it exists (fixed by this change)
- Getting an existing error symbol
- Getting a non existing error symbol
- Adding an invalid error symbol (fixed by this change)
- Adding an error symbol with a code that already
  exists (fixed by this change)

Therefore, issue #268 was fixed

error: statically allocate error symbol hash table

This will allow calling errSymbolAdd before errSymBld, therefore, a
function adding error symbols can now be run before iocInit or errlogInit

error: add a constant for the minimum module number

Make adding an identical error symbol not fail

A test case was also added which test that adding an error symbol
with same error code and message as one added before will not fail

Add locking to error symbol table

This protects the cases of:
- simultaneously adding and requesting of an error symbol
- simultaneously adding many error symbols

Update release notes regarding adding error symbols
2023-09-22 08:20:19 -07:00
Michael Davidsaver
45b3bce515 epicsThreadShow() zombies
Flag when the thread has returned, but the tracking
struct is still around.  eg. in need of joining.
2023-09-22 08:19:25 -07:00
Érico Nogueira
7c4a21eab4 libCom: detect support for backtrace() with __has_include.
This is necessary in order to build epics-base with musl libc, for
example, and any other C libraries which don't include this
functionality. In order to not regress builds with older compilers, we
still support the uclibc check. Furthermore, it has been checked that
uclibc-ng (the maintained version of uclibc) doesn't install the
<execinfo.h> header when the functionality is disabled [1] [2].

To avoid repetition, we don't define HAS_EXECINFO to 0 when it is not
available.

[1] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n224
[2] https://cgit.uclibc-ng.org/cgi/cgit/uclibc-ng.git/tree/Makefile.in?id=cdb07d2cd52af39feb425e6d36c02b30916b9f0a#n277
2023-09-21 08:18:49 -07:00
Michael Davidsaver
fab8fd7102 dbEvent: handle multiple db_event_cancel()
Allow for multiple db_event_cancel() (concurrent or
self-cancel) prior to event_task wakeup.

In db_event_cancel(), immediate free() only if idle
(not queued or in progress).  Otherwise, defer free()
to event task.  Avoids need to immediately expunge
canceled event from queue.  Allow event task to
process canceled events as normal (except no user_sub)
until npend==0.
2023-09-21 08:18:49 -07:00
Michael Davidsaver
3d25756065 privatize evSubscrip 2023-09-21 08:18:49 -07:00
Michael Davidsaver
5aca4c684c dbEvent minor 2023-09-14 08:54:40 +02:00
Michael Davidsaver
39b5c01c5d minor 2023-09-14 08:54:40 +02:00
Michael Davidsaver
3b22e5f710 doc dbLock.h 2023-09-14 08:54:40 +02:00
Michael Davidsaver
9f660f2238 add initHookAtPrepare 2023-09-05 08:54:48 +02:00
Michael Davidsaver
ca9c957e62 doc add page for initHooks.h 2023-09-05 08:53:40 +02:00
Michael Davidsaver
8488c9e891 initHookName() assert length
basic compile time consistency check.
2023-09-05 08:53:07 +02:00
Michael Davidsaver
1f2edb69d2 silence -Wformat-security for printfRecord
we know what we are doing... right?
2023-09-05 08:53:03 +02:00
Michael Davidsaver
fe3ee85aee doc errSymTbl.h 2023-09-05 08:52:58 +02:00
Michael Davidsaver
a74789d9c0 db: decimate and sync filters don't drop DBE_PROPERTY 2023-08-28 14:17:00 +02:00
Michael Davidsaver
7830345e59 move IocshRegister() to iocshInit()
No need for both global ctor and lazy init
2023-08-28 14:17:00 +02:00
Michael Davidsaver
1595ed8860 quieting clang-tidy, non-functional changes
Changes to syntax which should not effect generated code
2023-08-28 14:17:00 +02:00
Michael Davidsaver
d97943b697 timerPrivate.h: mark override/final
Clarify the class hierarchy by decorating types and
method override and/or final whenever possible.
Does not change the hierarchy.
2023-08-28 14:17:00 +02:00
Michael Davidsaver
f8f4376594 ~fdRegForOldFdmgr() can't throw doubleDelete
Exceptions can't actually be thrown from a
C++ class destructor.
2023-08-28 14:17:00 +02:00
Érico Nogueira
df397f4ade fix typo in boRecord.dbd.pod 2023-08-28 14:17:00 +02:00
Zainab Olalekan
7fd690e53e Fix libcom test failure on RTEMS 2023-08-28 14:17:00 +02:00
Andrew Johnson
bc5d347bb2 Call perror() before close(), add detail to messages 2023-08-28 14:17:00 +02:00
Chris Johns
3ea29f581b rtems: Close NTP socket 2023-08-28 14:17:00 +02:00
AlexWells
6de82bb0fd Additional help messages for iocsh
Henrique Silva <henrique.silva@ess.eu>
2023-08-28 13:24:16 +02:00
e1a51e2839 extra parentheses around second sizeof to silence "does not compute number of elements" warning 2023-08-28 13:24:16 +02:00
Michael Davidsaver
0f59d823d3 appveyor remove skip_commits: 2023-08-28 13:24:16 +02:00
Ralph Lange
00dc55b8a2 Fix usage info in .tools/make-tar.sh 2023-08-08 10:29:09 +02:00
Rolf Keitel
524f81b8bd Doc updates to PINI, PHAS & EVNT 2023-07-25 11:36:44 -05:00
Michael Davidsaver
80e62031e9 doc 2023-07-23 08:43:32 -07:00
Minijackson
d87fd0db01 Fix MAKEFLAGS parsing with Make 4.4+
Since Make version 4.4, MAKEFLAGS also contains long options and
overridden variables on the command-line[1].

[1]: https://git.savannah.gnu.org/cgit/make.git/tree/NEWS?h=4.4#n67

This means that parsing by filtering out '--%' doesn't work reliably
anymore, since it doesn't remove overrides:

Running 'make VAR=quacks' gives 'MAKEFLAGS=" -- VAR=quacks"', and
'checkflags' would understand that flags -q, -s, ... were set.

This would get transmitted below into 'QUIET_FLAGS' and
'QUESTION_FLAG', then passed to the 'genVersionHeader.pl' as '-i' and
'-q'.

The result would be that 'genVersionHeader.pl' would never create the
version header (only check for its up-to-date status), leading to
confusing errors:

  ../misc/epicsRelease.c:25:32: error: expected ')' before 'EPICS_VCS_VERSION'
     25 |     printf ( "## %s\n", "Rev. " EPICS_VCS_VERSION );
        |            ~                   ^~~~~~~~~~~~~~~~~~

The NEWS file[1] recommends using 'firstword', but unfortunately this is
not compatible with GNUMake < 3.82.
2023-07-22 08:36:43 -07:00
Ralph Lange
6636b4b9e7 libCom: check calloc() failure in RTEMS-posix/osdMessageQueue.c 2023-07-22 08:35:59 -07:00
Ralph Lange
b51d1de283 libCom: fix possible memory leak in RTEMS-posix/osdMessageQueue.c
found by static code analysis (cppcheck @ sonarqube)
2023-07-22 08:35:59 -07:00
Ralph Lange
38c99df2e0 libCom: fix possible memory leaks in iocLogServer.c
found by static code analysis (cppcheck @ sonarqube)
2023-07-22 08:35:59 -07:00
Ralph Lange
688195a273 libCom: handle realloc() failures correctly in macEnv.c
found by static code analysis (cppcheck @ sonarqube)
2023-07-22 08:35:59 -07:00
Ralph Lange
d691acc001 ca: init local variable in catime.c
found by static code analysis (cppcheck @ sonarqube)
2023-07-22 08:35:59 -07:00
Ralph Lange
059d32a975 db: init struct members in dbChannel.c
found by static code analysis (cppcheck @ sonarqube)
2023-07-22 08:35:59 -07:00
Henrique Silva
5c99031157 Add missing AFTC documentation to records
Fixes #313
2023-07-22 08:33:19 -07:00
Henrique Silva
a01c671399 Add missing HYST field documentation to longin record 2023-07-22 08:33:19 -07:00
Minijackson
b2c80efd33 release notes: link release notes from submodules'
fixes #226
2023-07-22 08:33:08 -07:00
Karl Vestin
39b6fa26da Added unit test for binary output record type 2023-07-22 08:32:16 -07:00
Karl Vestin
3ee6097ab7 Fixes #361 2023-07-22 08:30:44 -07:00
Emilio Perez
500a57738b Validate target record name when creating an alias
This fixes issue #312 by printing an error when a field is specified
2023-07-22 08:29:09 -07:00
AlexWells
f488765631 Add tests for PR#310
Also add missing NULL/empty checks
2023-07-22 08:28:25 -07:00
Sebastian Marsching
3a2d225682 Detect error in fprintf and return (fixes #309).
fprintf returns a negative value in order to signal an error. We have to
detect this situation in epicsStrPrintEscaped and return a negative
when fprintf returns a negative value in order to give the calling code
a chance to detect this situation.

The old implementation (of simply accumulating the return values of
fprintf) was wrong anyway, because it would not only lead to an error in
fprintf to be lost but would also cause the returned number to be too
small (not representing the actual number of bytes written) in such a
case.

The only case where the old implementation would work correctly was when
all calls to fprintf succeeded or all these calls failed.
2023-07-22 08:27:32 -07:00
Doug Murray
1d056c6fe4 Add support for CA tools timeout from environment variable EPICS_CLI_TIMEOUT 2023-07-22 08:24:22 -07:00
Michael Davidsaver
42604fc794 Allow clang with GCC compilerSpecific.h
Some checks failed
Check EditorConfig / editorconfig (push) Failing after 1s
Base / Cross linux-aarch64 (push) Failing after 2s
Base / Cross linux-arm gnueabi (push) Failing after 1s
Base / Cross linux-arm gnueabihf (push) Failing after 2s
Base / CentOS-7 (push) Failing after 1s
Base / Fedora-33 (push) Failing after 2s
Base / Fedora-latest (push) Failing after 1s
Base / Ub-20 clang-10 C++11 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Win2019 mingw (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW (push) Has been cancelled
Base / Ub-20 gcc-9 unsigned char (push) Has been cancelled
Base / Ub-20 gcc-9 C++11, static (push) Has been cancelled
Base / Ub-20 gcc-9 + MinGW, static (push) Has been cancelled
Base / MacOS clang-12 (push) Has been cancelled
Base / Ub-20 clang-10 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 beatnik (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-4.9 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 pc686 (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 uC5282 (push) Has been cancelled
Base / Win2019 MSC-19 (push) Has been cancelled
Base / Win2019 MSC-19, debug (push) Has been cancelled
Base / Win2019 MSC-19, static (push) Has been cancelled
Makes it easier to run clang derivative analysis tools
on builds configured for GCC.
2023-06-13 08:47:13 -07:00
Michael Davidsaver
4ecc0daa79 make mapDBFToDBR[] const 2023-06-13 08:47:13 -07:00
Michael Davidsaver
5a1f3ecc8b doc: note when some record types were introduced 2023-05-18 11:57:41 -07:00
Michael Davidsaver
cb97d662a7 doc 2023-05-01 10:31:01 -07:00
Michael Davidsaver
d4fab0d20e iocsh: dbCompleteRecord() missing NULL check 2023-05-01 10:31:01 -07:00
Michael Davidsaver
0c13e6ba6c iocsh: tab completion handle iocshArgArgv 2023-05-01 10:31:01 -07:00
Michael Davidsaver
8f1243da40 epicsSingleton: eliminate global ctor with >= c++11 2023-05-01 10:25:29 -07:00
Michael Davidsaver
fe9995c0b5 Update recommendation for CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 2023-05-01 09:06:59 -07:00
Freddie Akeroyd
f56412d6a5 WIN32: use FlsAlloc() to cleanup epicsThreadOSD
Adjust macros for compiling for older MSVC/Win SDK versions

Try to cover missing fibres include in 7.0 SDK

Support Windows XP and above.
Also removed explicit define of _WIN32_WINNT in code if it has not
been passed on compile line. This is possibly a matter for
further discussion
2023-05-01 09:03:52 -07:00
Érico Nogueira
07d18c55ba Clean whitespace in makeBaseApp template README 2023-05-01 09:03:52 -07:00
Minijackson
9f97f25669 ci: add cross-compilation tests for aarch64, arm soft and hard float 2023-05-01 09:03:52 -07:00
912a82c0b5 replace deprecated decrementing volatile with atomic decrement 2023-05-01 09:03:52 -07:00
acf2241fd0 Some archs define ECHO in termios.h 2023-05-01 09:03:52 -07:00
Eva Lott
b878295d06 Added the new annotation EPICS_PRINTF_FMT 2023-05-01 09:03:45 -07:00
f41f11c7f6 fix compiler warning on 32 bit systems 2023-04-28 09:35:49 -05:00
Michael Davidsaver
216359974c update release notes 2023-04-18 13:11:59 -05:00
Torsten Bögershausen
17ad04505e Change compiler for FreeBSD 13: Use clang
FreeBSD 13 uses clang, not gcc, any more.
GNU_DIR must be set to /usr/local

Note: This change touches both the x86 and the x86_64 files.
It was tested on 'amd64' system only, which is x86_64
2023-04-18 09:51:33 -05:00
Michael Davidsaver
3500a02034 iocsh: expose dbCreateAlias 2023-04-18 09:51:28 -05:00
Michael Davidsaver
52b18d56a0 dbCreateAlias fixup error handling 2023-04-18 09:51:28 -05:00
Michael Davidsaver
5507646ce7 posix: optimize epicsThreadOnce()
Use atomic ops to short circuit when already initialized
2023-04-18 09:51:24 -05:00
Michael Davidsaver
625c2ef159 epicsThreadPerform: time epicsThreadOnce() 2023-04-18 09:51:24 -05:00
Jure Varlec
bdaca51d96 Update shareLib API docs, directing the reader to makeAPIheader.pl 2023-04-18 09:51:21 -05:00
Jure Varlec
9655b78e11 Update release notes: ts filter 2023-04-18 09:51:07 -05:00
Jure Varlec
e11f88017d ts filter: port to the new db_field_log 2023-04-18 09:51:07 -05:00
Jure Varlec
8a3020033e ts filter: replace cantProceed with a non-fatal error msg 2023-04-18 09:51:07 -05:00
Jure Varlec
bd1af9ac95 ts filter: fix unused variable warnings 2023-04-18 09:51:07 -05:00
Jure Varlec
d5959ca20a ts filter: handle calloc failures 2023-04-18 09:51:07 -05:00
Jure Varlec
e10dcede7d ts filter: clear the dtor field after destruction 2023-04-18 09:51:07 -05:00
Jure Varlec
c042b08ab0 Extend timestamp filter, giving access to the record timestamp 2023-04-18 09:51:07 -05:00
Michael Davidsaver
5eff3803a8 update release notes 2023-03-10 12:02:29 +00:00
Michael Davidsaver
151256533f renamed hidden pdbbase in dbd parser
avoid confusion with non-static pdbbase global
2023-03-10 11:00:58 +00:00
Michael Davidsaver
3b484f58d3 for links, treat "" the same as unset. 2023-03-10 11:00:58 +00:00
Michael Davidsaver
d3f93746a8 1<<31 upsets ubsan 2023-03-10 10:57:35 +00:00
Michael Davidsaver
f99a1cb0f3 dbdToRecordtypeH use offsetof()
quiets UB sanitizer
2023-03-10 10:57:35 +00:00
Michael Davidsaver
531a769007 fix rtems_ne2kpci_driver_attach prototype 2023-03-10 10:57:35 +00:00
Henrique Silva
3e51491628 Fix generalTimeReport interest_level argument type
Setting to iocshArgInt actually makes the function verbosity to be able
to be controlled by the value
2023-03-10 10:56:58 +00:00
0b01fb20db make buffer large enough for any argument value 2023-03-10 10:56:58 +00:00
Minijackson
c7a769e5da editorconfig: add workflow to check if config is upheld 2023-03-10 10:56:58 +00:00
Minijackson
a9fd57a865 editorconfig: add initial version forcing final newlines
See #337
2023-03-10 10:56:58 +00:00
e862f0e95f fix warning "if clause does not guard..." 2023-03-10 10:56:58 +00:00
172597e0e6 avoid accessing dbr_text[type] when type is out of range 2023-03-10 10:56:58 +00:00
bcdeeed206 fix missing newline at end of file 2023-03-10 10:56:58 +00:00
Michael Davidsaver
b6626e4f60 dbEvent: try to detect possible "stall"
event_read() should not return if the last callback
was delivered with eventsRemaining!=0
2023-03-10 10:56:58 +00:00
Michael Davidsaver
e1c1bb8b1b dbEvent: correct eventsRemaining
Only pass eventsRemaining when no queued events have been canceled.
Also possible race bt accessing ev_que with locking.
2023-03-10 10:56:58 +00:00
Torsten Bögershausen
90ae51e8f2 MacOs: Use readline from MacPorts
Commit b38ff09f6e and
commit d9ca8a70f0 introduced the TAB
completion in iocsh.

Commit 1f75813a4d enabled it for MacOs
having readline installed via HomeBrew.

This commit enables it for MacPorts.
2023-03-10 10:56:58 +00:00
Brendan Chandler
832abbd3b1 Return an error if subrecord processing fails due to bad INP links
If a sub record has an invalid INPx link, it was silently failing (and
not running the proc function).  This change plumbs in the error, so
the put fails and the user knows something went wrong.
2023-03-10 10:56:58 +00:00
Michael Davidsaver
52cc68433f COMMANDLINE_LIBRARY fallback to $(wildcard $(GNU_DIR) 2023-03-10 09:52:30 +00:00
Michael Davidsaver
f430389ee7 iocShutdown(): Always stop worker threads 2023-03-10 09:52:08 +00:00
Michael Davidsaver
bded79f14d dbScan join threads 2023-03-10 09:52:08 +00:00
Michael Davidsaver
2ff44cb386 callback join threads 2023-03-10 09:52:08 +00:00
Michael Davidsaver
a9ade9669a switch dbScan to epicsThreadCreateOpt() 2023-03-10 09:52:08 +00:00
Michael Davidsaver
f902d70006 switch callback to epicsThreadCreateOpt() 2023-03-10 09:52:08 +00:00
Michael Davidsaver
e22d74310b RTEMS: ensure epicsThreadMustJoin() short-circuits 2023-03-10 09:52:08 +00:00
Simon Rose
e5ad12e638 Updated RELEASE_NOTES 2023-03-10 09:51:08 +00:00
Simon Rose
b963a4564e Added commend about PBUF having no effect on 'Average' algorithm 2023-03-10 09:51:08 +00:00
Simon Rose
579c125b01 Updated documentation 2023-03-10 09:51:08 +00:00
Simon Rose
84d9617375 Added one more put/process to go over the buffer length
Note that it is not really a circular buffer in this case, but
a full reset of the buffer to the beginning. This matches the
documentation, but it seems valuable to add an explicit test for
this case.
2023-03-10 09:51:08 +00:00
Simon Rose
d66e90a016 Fixing 'error C2057: expected constant expression' error 2023-03-10 09:51:08 +00:00
Simon Rose
373e5440ac General cleanup 2023-03-10 09:51:08 +00:00
Simon Rose
dec23501e1 Added test for array_average 2023-03-10 09:51:08 +00:00
Simon Rose
11a4bed9aa compress_scalar for average works correctly now 2023-03-10 09:51:08 +00:00
Simon Rose
bf4a4c6b78 Added failing test for partial ai average 2023-03-10 09:51:08 +00:00
Simon Rose
b54d4b9a24 Added test for low value 2023-03-10 09:51:08 +00:00
Simon Rose
84f4771691 Single input data test passes 2023-03-10 09:51:08 +00:00
Simon Rose
1dc34a02e2 Add test path for single input data 2023-03-10 09:51:08 +00:00
Simon Rose
3ab22818da Added failing test for partially filled buffer average 2023-03-10 09:51:08 +00:00
Simon Rose
1ab474638d Initial test for averaging 2023-03-10 09:51:08 +00:00
Michael Davidsaver
eea361bf5e Com: Allow runtime bypass of freeListLib
By environment or iocsh variable.
2023-03-10 09:48:27 +00:00
Michael Davidsaver
d9052f7105 update ci-scripts 2023-03-09 14:26:45 +00:00
Michael Davidsaver
fa00572780 update pvData 2023-03-06 15:54:34 +00:00
Ralph Lange
636f5517b2 Add QT Creator 9.x dir to .gitignore 2023-03-06 09:30:02 +00:00
Andrew Johnson
718da5c9be Convert RecordReference.md to ComponentReference.pod
That .pod file will now be converted to .html using the same style as
the individual local documents that it links to.

The dbdToHtml output files now include a header and footer that link to
the ComponentReference.html file (they started pointing to the original
RecordReference.html but that was broken on most people's builds because
we weren't converting that from the original Markdown version).

This commit also adjusts documentation/Makefile to use the build system
properly, and reorders the Doxygen main page index.
2023-02-04 22:56:19 -06:00
Michael Davidsaver
e5335ce760 GHA: Add -funsigned-char build 2023-02-01 09:25:10 -08:00
3dbc9ea264 iocsh: fix argument splitting
Since commit 60128ee9 "Com: separate iocsh argument splitting",
iocsh is broken on VxWorks (tested with version 6.9.4.12)

Any command prints the error "Unbalanced quote."

> iocsh
epics> echo
Unbalanced quote.
epics> "echo"
Unbalanced quote.
epics> "echo
Unbalanced quote.
epics> echo bla
Unbalanced quote.
epics> echo 1 2 3
Unbalanced quote.
epics> exit
Unbalanced quote.
2023-02-01 09:25:10 -08:00
Michael Davidsaver
80da400f9c dbLock: avoid possibly lost log prior to cantProceed 2023-02-01 09:25:10 -08:00
Michael Davidsaver
5787125bbb doc 2023-02-01 09:25:10 -08:00
Andrew Johnson
bc54524270 Fix bad JSON char detection in dbStatic
Thanks to Dirk Zimoch for pointing this out.
2023-01-24 11:08:13 -06:00
Andrew Johnson
a2d53c05f6 Explain SNC registration in template 2023-01-24 11:06:48 -06:00
Andrew Johnson
3293a29d59 Darwin: Remove '-undefined dynamic_lookup' from SHRLIB_LDFLAGS
It's been deprecated by Apple in latest XCode and generates warning
messages when creating .dylib libraries, but they don't yet have a
solution for use when creating loadable libraries (.bundle files).
2023-01-19 11:05:21 -06:00
Andrew Johnson
550e902bf3 iocLogPrefix: skip "already set" warning if prefix identical 2023-01-19 11:00:14 -06:00
Andrew Johnson
84e5cc0b69 softIoc -v -x prefix should show loading exit_file 2023-01-19 10:56:46 -06:00
Andrew Johnson
4b63882f28 Appveyor config fixes 2023-01-06 15:28:15 -06:00
Andrew Johnson
fb742beae3 Fix epicsThreadOnceTest.c
Replace epicsThreadSleep() with proper thread synchronization.
The main thread was reading runCount without taking lock first.
Taking the lock before accessing doneCount might not be necessary
given the epicsEventMustWait(), but not 100% sure.
2023-01-06 15:24:56 -06:00
Andrew Johnson
7ef0c80630 errSymLib: status 0 => "Ok" 2022-12-31 19:14:57 -06:00
Andrew Johnson
1f75813a4d Have macOS builds use Homebrew's GNU readline if present 2022-12-31 19:12:49 -06:00
Andrew Johnson
34ad8e2347 Edits to Release Notes and postfix.h documentation 2022-12-31 19:10:49 -06:00
Andrew Johnson
413f14e4ae Remove now-unused pdset variable. 2022-12-29 18:10:38 -06:00
Andrew Johnson
c22895d499 Adjust documentation about longout feature 2022-12-29 18:04:48 -06:00
Andrew Johnson
e63184e518 Merge PR #63, longout.OOPT 2022-12-29 16:31:42 -06:00
Andrew Johnson
755a4541c5 Update the RecordReference.md index; add links and filters. 2022-12-28 15:53:48 -06:00
Andrew Johnson
05cd7edf71 Update channel filter documentation, adding $ syntax 2022-12-28 15:52:30 -06:00
Andrew Johnson
5759726a89 Update link documentation 2022-12-28 15:43:35 -06:00
Andrew Johnson
14e7926d22 Add RecRef links to the top & bottom of each reference page 2022-12-28 15:30:25 -06:00
Andrew Johnson
c068fe3525 capr.pl: Fetch link fields as long strings 2022-12-28 12:43:38 -06:00
Andrew Johnson
c2364d9d1c Add license header to new epicsReadlinePvt.h 2022-12-28 12:42:51 -06:00
Michael Davidsaver
6be0372257 doc 2022-12-21 09:50:07 -08:00
Michael Davidsaver
e994ad78db GHA update 2022-12-21 08:15:26 -08:00
Michael Davidsaver
49fddaa13e errlogRemoveListeners() handle self-removal
Handle errlogRemoveListeners() during a callback.
2022-12-20 20:12:04 -08:00
Michael Davidsaver
7448a8bfa9 errlog: worker exit when buffer is empty
Allow the worker to flush any buffered messages.
After pvt.atExit, further logging will be synchronous.
Ensure any flusher gets a final wakeup.
2022-12-20 20:12:04 -08:00
Michael Davidsaver
166267a32f ringPointerTest in thread 2022-12-20 20:12:04 -08:00
Andrew Johnson
b460c2659e Fix for GH issue #219, menu fields with non-choice values
It's rare, but menu fields may hold a value that does not correspond
to one of the menu choices; the default value of the SSCN fields is
the most common example (65535). Change the type conversion routines
to return a numeric string instead of giving an error.

DBF_DEVICE was fixed in dbFastLinkConv.c before the 7.0.7 release.
2022-12-20 20:12:04 -08:00
Michael Skoufis
7ccc3ab82d Modify pointer type in example to match options from epicsTypes.h 2022-12-20 20:12:04 -08:00
Michael Davidsaver
adb0c898a6 doc 2022-12-20 20:12:04 -08:00
Michael Davidsaver
b38ff09f6e Com: iocsh: Tab completion of variable names for "var" 2022-12-20 20:12:04 -08:00
Michael Davidsaver
d9ca8a70f0 Com: iocsh: Tab completion
Add tab completion for "help ...", record names, and "pdbbase"
2022-12-20 20:12:04 -08:00
Michael Davidsaver
60128ee924 Com: separate iocsh argument splitting 2022-12-20 20:12:04 -08:00
Michael Davidsaver
b189991f9d Com: iocsh: add tab completion for command names 2022-12-20 20:12:04 -08:00
Michael Davidsaver
07ffc1ffae Com: introduce epicsReadlinePvt.h
Helping IDEs understand gnuReadline.c and osdReadline.c
2022-12-20 20:11:45 -08:00
Michael Davidsaver
a6afef4850 calc: add FMOD 2022-12-20 20:11:44 -08:00
Michael Davidsaver
9c0c486111 appveyor: bypass auto-detect of readline 2022-12-08 09:12:45 -08:00
Michael Davidsaver
3f5cf61fb6 update pvData 2022-12-05 10:51:47 -08:00
Andrew Johnson
6222902688 Update pva2pva module 2022-12-01 12:43:18 -06:00
Andrew Johnson
7febee04fa More & better Perl script dependencies 2022-11-30 13:35:56 -06:00
Andrew Johnson
4640f0a8ae Fix dbdExpand.pl issue 2022-11-30 13:34:22 -06:00
Andrew Johnson
8969a952e4 configure: Make Perl-generated files dependent on their .pl script 2022-11-24 17:57:05 -06:00
Michael Davidsaver
bc9415bb10 epicsErrlogTest: osiSockAttach() 2022-11-15 10:07:26 -08:00
Andrew Johnson
cbd86ada20 regRecDevDrv.pl: Don't break long symbol names in DBD files
Makes a test symbol name long enough to trigger the problem.

Fixes lp: #1995728
2022-11-04 11:09:06 -05:00
Michael Davidsaver
0f8ea3aa36 doc update dbUnitTest.h 2022-09-25 09:34:13 -07:00
Andrew Johnson
f62f68fd66 Update all submodules for future development 2022-09-07 17:00:45 -05:00
Andrew Johnson
eddafd2827 Release Checklist used for 7.0.7 2022-09-07 16:58:21 -05:00
Andrew Johnson
e3ce9d7f1a Adjustments to the make-tar.sh script 2022-09-07 16:57:09 -05:00
Andrew Johnson
df96e6df0f Set next development versions 2022-09-07 13:54:12 -05:00
Joao Paulo Martins
6c573b496a longout rec: fix behaviour when record is processed for the first time and OOPT is On Change 2021-03-10 18:50:42 +01:00
Joao Paulo Martins
1d85bc7424 longout record: detect OUT link change using special function AFTER put, better documentation 2021-03-10 11:07:30 +01:00
Joao Paulo Martins
f4d94b9725 Longout OOPT field refactoring and updated documentation; Release notes additions 2021-03-09 16:31:54 +01:00
Joao Paulo Martins
4e7a18bfb4 Adding test routines for longout record with OOPT field 2021-03-09 16:31:54 +01:00
Joao Paulo Martins
c1ae5064b8 Added OOPT to longout record 2021-03-09 16:31:54 +01:00
269 changed files with 7122 additions and 2010 deletions

View File

@@ -25,18 +25,6 @@ init:
# Set clone depth (do not fetch complete history)
clone_depth: 5
# Skipping commits affecting only specific files
skip_commits:
files:
- 'documentation/*'
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.gitattributes'
- '.lgtm.yml'
- '**/*.html'
- '**/*.md'
#---------------------------------#
# build matrix configuration #
#---------------------------------#
@@ -68,6 +56,8 @@ environment:
- CMP: vs2010
- CMP: gcc
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
# TODO: static linking w/ readline isn't working. Bypass auto-detect
COMMANDLINE_LIBRARY: EPICS
# Platform: processor architecture
platform:

View File

@@ -32,18 +32,6 @@ init:
# Set clone depth (do not fetch complete history)
clone_depth: 5
# Skipping commits affecting only specific files
skip_commits:
files:
- 'documentation/*'
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.lgtm.yml'
- '.gitattributes'
- '**/*.html'
- '**/*.md'
#---------------------------------#
# build matrix configuration #
#---------------------------------#
@@ -74,6 +62,9 @@ environment:
- CMP: vs2012
- CMP: vs2010
- CMP: gcc
APPVEYOR_BUILD_WORKER_IMAGE: Visual Studio 2019
# TODO: static linking w/ readline isn't working. Bypass auto-detect
COMMANDLINE_LIBRARY: EPICS
# Platform: processor architecture
platform:

2
.ci

Submodule .ci updated: 8a2666a9de...20f8e05393

8
.editorconfig Normal file
View File

@@ -0,0 +1,8 @@
# Documentation for this file: https://EditorConfig.org
root = true
# Unix-style newlines ending every file,
# as some compilers complain about files not ending in newline
[*]
insert_final_newline = true

View File

@@ -0,0 +1,13 @@
name: Check EditorConfig
on:
push:
pull_request:
jobs:
editorconfig:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: EditorConfig-Action
uses: greut/eclint-action@v0

View File

@@ -29,6 +29,7 @@ on:
- '.gitattributes'
- '**/*.html'
- '**/*.md'
workflow_dispatch:
env:
SETUP_PATH: .ci-local:.ci
@@ -43,9 +44,7 @@ jobs:
env:
CMP: ${{ matrix.cmp }}
BCFG: ${{ matrix.configuration }}
WINE: ${{ matrix.wine }}
RTEMS: ${{ matrix.rtems }}
RTEMS_TARGET: ${{ matrix.rtems_target }}
CI_CROSS_TARGETS: ${{ matrix.cross }}
EXTRA: ${{ matrix.extra }}
TEST: ${{ matrix.test }}
strategy:
@@ -53,16 +52,35 @@ jobs:
matrix:
# Job names also name artifacts, character limitations apply
include:
- os: ubuntu-22.04
cmp: gcc-12
name: "Ub-22 gcc-12 c++20 Werror"
# Turn all warnings into errors,
# except for those we could not fix (yet).
# Remove respective -Wno-error=... flag once it is fixed.
extra: "CMD_CXXFLAGS=-std=c++20
CMD_CPPFLAGS='-fdiagnostics-color
-Werror
-Wno-error=deprecated-declarations
-Wno-error=stringop-truncation
-Wno-error=restrict
-Wno-error=sizeof-pointer-memaccess
-Wno-error=nonnull
-Wno-error=dangling-pointer
-Wno-error=format-overflow
-Wno-error=format-security
-Wno-error=stringop-overread'"
- os: ubuntu-20.04
cmp: gcc
configuration: default
wine: "64"
cross: "windows-x64-mingw"
name: "Ub-20 gcc-9 + MinGW"
- os: ubuntu-20.04
cmp: gcc
configuration: static
wine: "64"
cross: "windows-x64-mingw"
name: "Ub-20 gcc-9 + MinGW, static"
- os: ubuntu-20.04
@@ -71,6 +89,12 @@ jobs:
extra: "CMD_CXXFLAGS=-std=c++11"
name: "Ub-20 gcc-9 C++11, static"
- os: ubuntu-20.04
cmp: gcc
configuration: static
extra: "CMD_CFLAGS=-funsigned-char CMD_CXXFLAGS=-funsigned-char"
name: "Ub-20 gcc-9 unsigned char"
- os: ubuntu-20.04
cmp: clang
configuration: default
@@ -85,67 +109,42 @@ jobs:
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "5"
rtems_target: RTEMS-pc686-qemu
cross: "RTEMS-pc686-qemu@5"
name: "Ub-20 gcc-9 + RT-5.1 pc686"
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "5"
rtems_target: RTEMS-beatnik
cross: "RTEMS-beatnik@5"
test: NO
name: "Ub-20 gcc-9 + RT-5.1 beatnik"
# Only build one RTEMS target per CPU family
# unless it's running the tests
#
# - os: ubuntu-20.04
# cmp: gcc
# configuration: default
# rtems: "5"
# rtems_target: RTEMS-mvme3100
# test: NO
# name: "Ub-20 gcc-9 + RT-5.1 mvme3100"
#
# - os: ubuntu-20.04
# cmp: gcc
# configuration: default
# rtems: "5"
# rtems_target: RTEMS-qoriq_e500
# test: NO
# name: "Ub-20 gcc-9 + RT-5.1 qoriq_e500"
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "5"
rtems_target: RTEMS-xilinx_zynq_a9_qemu
cross: "RTEMS-xilinx_zynq_a9_qemu@5"
test: NO
name: "Ub-20 gcc-9 + RT-5.1 xilinx_zynq_a9_qemu"
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "5"
rtems_target: RTEMS-uC5282
cross: "RTEMS-uC5282@5"
test: NO
name: "Ub-20 gcc-9 + RT-5.1 uC5282"
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "4.10"
name: "Ub-20 gcc-9 + RT-4.10"
rtems_target: RTEMS-pc386-qemu
cross: "RTEMS-pc386-qemu@4.10"
test: NO
- os: ubuntu-20.04
cmp: gcc
configuration: default
rtems: "4.9"
name: "Ub-20 gcc-9 + RT-4.9"
rtems_target: RTEMS-pc386-qemu
cross: "RTEMS-pc386-qemu@4.9"
- os: macos-latest
cmp: clang
@@ -154,13 +153,15 @@ jobs:
- os: windows-2019
cmp: vs2019
configuration: default
configuration: debug
name: "Win2019 MSC-19"
extra: "CMD_CXXFLAGS=-analysis"
- os: windows-2019
cmp: vs2019
configuration: static
configuration: static-debug
name: "Win2019 MSC-19, static"
extra: "CMD_CXXFLAGS=-analysis"
- os: windows-2019
cmp: vs2019
@@ -172,8 +173,28 @@ jobs:
configuration: default
name: "Win2019 mingw"
# Cross builds
- os: ubuntu-latest
cmp: gcc
configuration: default
name: "Cross linux-aarch64"
cross: linux-aarch64
- os: ubuntu-latest
cmp: gcc
configuration: default
name: "Cross linux-arm gnueabi"
cross: linux-arm@arm-linux-gnueabi
- os: ubuntu-latest
cmp: gcc
configuration: default
name: "Cross linux-arm gnueabihf"
cross: linux-arm@arm-linux-gnueabihf
steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: true
- name: Automatic core dumper analysis
@@ -191,7 +212,7 @@ jobs:
run: python .ci/cue.py -T 60M test
- name: Upload tapfiles Artifact
if: ${{ always() }}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: tapfiles ${{ matrix.name }}
path: '**/O.*/*.tap'
@@ -258,11 +279,15 @@ jobs:
# people would rather just break all existing scripts...
[ -e /usr/bin/python ] || ln -sf python3 /usr/bin/python
python --version
- uses: actions/checkout@v2
- uses: actions/checkout@v3
with:
submodules: true
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@master
if: matrix.image!='centos:7'
- name: Automatic core dumper analysis
uses: mdavidsaver/ci-core-dumper@node16
if: matrix.image=='centos:7'
- name: Prepare and compile dependencies
run: python .ci/cue.py prepare
- name: Build main module
@@ -271,7 +296,7 @@ jobs:
run: python .ci/cue.py -T 20M test
- name: Upload tapfiles Artifact
if: ${{ always() }}
uses: actions/upload-artifact@v2
uses: actions/upload-artifact@v3
with:
name: tapfiles ${{ matrix.name }}
path: '**/O.*/*.tap'

2
.gitignore vendored
View File

@@ -11,8 +11,10 @@
/modules/Makefile.local
O.*/
/QtC-*
/.qtc_*
/.vscode/
*.orig
*.log
.*.swp
.DS_Store
.iocsh_history

View File

@@ -20,9 +20,9 @@ maybedie() {
usage() {
cat <<EOF >&2
usage: $0 [-v] [-s] <rev> [<outfile> [<prefix>]]
usage: $0 [-v] [-d] <rev> [<outfile> [<prefix>]]
<rev> may be any git revision spec. (tag, branch, or commit id).
<rev> may be any git revision spec. (tag or, using -d, branch or commit id).
If provided, <outfile> must end with ".tar", ".tar.gz" or ".tar.bz2".
If <outfile> is omitted, "base-<rev>.tar.gz" will be used.
@@ -140,10 +140,12 @@ sed \
-e '/\/\.ci-local\//d' \
-e '/\/\.tools\//d' \
-e '/\/jenkins\//d' \
-e '/\/\.git/d' \
-e '/\/\.github\//d' \
-e '/\/\.gitmodules$/d' \
-e '/\/\.hgtags$/d' \
-e '/\/\.cproject$/d' \
-e '/\/\.project$/d' \
-e '/\/\.lgtm\.yml$/d' \
-e '/\/\.travis\.yml$/d' \
-e '/\/\.appveyor\.yml$/d' \
-e '/\/\.readthedocs\.yml$/d' \

View File

@@ -38,10 +38,16 @@ TOOLS = $(abspath $(EPICS_BASE_HOST_BIN))
FIND_TOOL = $(firstword $(wildcard $(TOOLS)/$(1) \
$(TOP)/src/tools/$(1)) $(EPICS_BASE)/src/tools/$(1))
#---------------------------------------------------------------
# Find Perl modules for dependencies
FIND_PM = $(wildcard $(EPICS_BASE)/lib/perl/$(1))
#---------------------------------------------------------------
# EPICS Base build tools and tool flags
PODTOHTML = $(PERL) $(TOOLS)/podToHtml.pl
PODTOHTML_pl = $(TOOLS)/podToHtml.pl
PODTOHTML_dep = $(PODTOHTML_pl) $(call FIND_PM,EPICS/PodHtml.pm)
PODTOHTML = $(PERL) $(PODTOHTML_pl)
CONVERTRELEASE = $(PERL) $(call FIND_TOOL,convertRelease.pl)
FULLPATHNAME = $(PERL) $(TOOLS)/fullPathName.pl
GENVERSIONHEADER = $(PERL) $(TOOLS)/genVersionHeader.pl $(QUIET_FLAG) $(QUESTION_FLAG)

View File

@@ -48,11 +48,11 @@ EPICS_VERSION = 7
EPICS_REVISION = 0
# EPICS_MODIFICATION must be a number >=0 and <256
EPICS_MODIFICATION = 7
EPICS_MODIFICATION = 8
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
# Not included in the official EPICS version number if zero
EPICS_PATCH_LEVEL = 0
EPICS_PATCH_LEVEL = 1
# Immediately after an official release the EPICS_PATCH_LEVEL is incremented
# and the -DEV suffix is added (similar to the Maven -SNAPSHOT versions)

View File

@@ -2,7 +2,7 @@
EPICS_CA_MAJOR_VERSION = 4
EPICS_CA_MINOR_VERSION = 14
EPICS_CA_MAINTENANCE_VERSION = 2
EPICS_CA_MAINTENANCE_VERSION = 4
# Development flag, set to zero for release versions

View File

@@ -83,8 +83,9 @@ IOCS_APPL_TOP = $(INSTALL_ABSOLUTE)
#-------------------------------------------------------
# How to portably check the flags to make
makeflags := $(firstword $(filter-out -,$(filter-out --%,$(MAKEFLAGS))))
define checkflags
make-$1 = $(findstring $1,$(filter-out --%,$(MAKEFLAGS)))
make-$1 := $(findstring $1,$(makeflags))
endef
# This is extensible to most single letter flags:
$(foreach flag,s q, $(eval $(call checkflags,$(flag))))

View File

@@ -5,14 +5,26 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
# Our locally-built tools
DBEXPAND = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdExpand.pl
DBTORECORDTYPEH = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl
DBTOMENUH = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl
DBDTOHTML = $(PERL) $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl
REGISTERRECORDDEVICEDRIVER = $(PERL) $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
# Installed perl scripts and dependent modules that have
# a significant effect on the script's output
DBDEXPAND_pl = $(EPICS_BASE_HOST_BIN)/dbdExpand.pl
DBDTORECTYPEH_pl = $(EPICS_BASE_HOST_BIN)/dbdToRecordtypeH.pl
DBDTORECTYPEH_dep = $(DBDTORECTYPEH_pl) $(call FIND_PM,DBD/Rec*.pm)
DBDTOMENUH_pl = $(EPICS_BASE_HOST_BIN)/dbdToMenuH.pl
DBDTOMENUH_dep = $(DBDTOMENUH_pl) $(call FIND_PM,DBD/Menu.pm)
DBDTOHTML_pl = $(EPICS_BASE_HOST_BIN)/dbdToHtml.pl
DBDTOHTML_dep = $(DBDTOHTML_pl) $(call FIND_PM,EPICS/Pod*Html.pm)
REGRECDEVDRV_pl = $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
REGRECDEVDRV_dep = $(REGRECDEVDRV_pl)
# Windows can need these paths to be quoted
# Commands for running scripts in recipes
DBEXPAND = $(PERL) $(DBDEXPAND_pl)
DBTORECORDTYPEH = $(PERL) $(DBDTORECTYPEH_pl)
DBTOMENUH = $(PERL) $(DBDTOMENUH_pl)
DBDTOHTML = $(PERL) $(DBDTOHTML_pl)
REGISTERRECORDDEVICEDRIVER = $(PERL) $(REGRECDEVDRV_pl)
# Installed binary executables, quoted for running on Windows
MAKEBPT = "$(EPICS_BASE_HOST_BIN)/makeBpt$(HOSTEXE)"
MSI3_15 = "$(EPICS_BASE_HOST_BIN)/msi$(HOSTEXE)"

View File

@@ -1,8 +1,8 @@
# Version number for the database APIs and shared library
EPICS_DATABASE_MAJOR_VERSION = 3
EPICS_DATABASE_MINOR_VERSION = 22
EPICS_DATABASE_MAINTENANCE_VERSION = 0
EPICS_DATABASE_MINOR_VERSION = 23
EPICS_DATABASE_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions

View File

@@ -1,8 +1,8 @@
# Version number for the libcom APIs and shared library
EPICS_LIBCOM_MAJOR_VERSION = 3
EPICS_LIBCOM_MINOR_VERSION = 22
EPICS_LIBCOM_MAINTENANCE_VERSION = 0
EPICS_LIBCOM_MINOR_VERSION = 23
EPICS_LIBCOM_MAINTENANCE_VERSION = 1
# Development flag, set to zero for release versions

View File

@@ -34,5 +34,9 @@ CFG += TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A)
include $(TOP)/configure/RULES
ifeq ($(GNU),YES)
# Pass compiler flags to preprocessor to enable _FORTIFY_SOURCE
TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A): CPPFLAGS += $(CFLAGS)
endif
TOOLCHAIN.$(EPICS_HOST_ARCH).$(T_A): toolchain.c
$(PREPROCESS.cpp)

View File

@@ -17,7 +17,7 @@ TEMPL_SUFFIX ?= .template
# vpath
vpath %.pm $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.pod $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.pod $(USR_VPATH) $(SRC_DIRS) .. $(dir $(DBD))
vpath %.dbd $(USR_VPATH) $(SRC_DIRS) $(dir $(DBD))
vpath %.db $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
vpath %.vdb $(USR_VPATH) $(SRC_DIRS) $(dir $(DB))
@@ -217,32 +217,32 @@ realclean: clean
#---------------------------------------------------------------
# Dependency files
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd
%Record.h$(DEP): $(COMMON_DIR)/%Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $@
@$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
%Record.h$(DEP): %Record.dbd
%Record.h$(DEP): %Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $@
@$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
%Record.h$(DEP): ../%Record.dbd
%Record.h$(DEP): ../%Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $@
@$(DBTORECORDTYPEH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
menu%.h$(DEP): $(COMMON_DIR)/menu%.dbd
menu%.h$(DEP): $(COMMON_DIR)/menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $@
@$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
menu%.h$(DEP): menu%.dbd
menu%.h$(DEP): menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $@
@$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
menu%.h$(DEP): ../menu%.dbd
menu%.h$(DEP): ../menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $@
@$(DBTOMENUH) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
@@ -261,7 +261,7 @@ menu%.h$(DEP): ../menu%.dbd
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $< > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
%.dbd$(DEP):
%.dbd$(DEP): $($*_DBD)
@$(RM) $@
@$(DBEXPAND) -D $(DBDFLAGS) -o $(COMMONDEP_TARGET) $($*_DBD) > $@
@echo "$(COMMONDEP_TARGET): ../Makefile" >> $@
@@ -330,32 +330,32 @@ $(INSTALL_DB)/%$(TEMPL_SUFFIX): ../%$(TEMPL_SUFFIX)
#---------------------------------------------------------------
# INC files
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd
$(COMMON_DIR)/%Record.h: $(COMMON_DIR)/%Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $(notdir $@)
$(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%Record.h: %Record.dbd
$(COMMON_DIR)/%Record.h: %Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $(notdir $@)
$(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%Record.h: ../%Record.dbd
$(COMMON_DIR)/%Record.h: ../%Record.dbd $(DBDTORECTYPEH_dep)
@$(RM) $(notdir $@)
$(DBTORECORDTYPEH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd
$(COMMON_DIR)/menu%.h: $(COMMON_DIR)/menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $(notdir $@)
$(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/menu%.h: menu%.dbd
$(COMMON_DIR)/menu%.h: menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $(notdir $@)
$(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/menu%.h: ../menu%.dbd
$(COMMON_DIR)/menu%.h: ../menu%.dbd $(DBDTOMENUH_dep)
@$(RM) $(notdir $@)
$(DBTOMENUH) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
@@ -400,7 +400,7 @@ $(COMMON_DBDCATS):$(COMMON_DIR)/%.dbd:
$(DBDCAT_COMMAND)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.dbd:
$(COMMON_DIR)/%.dbd: $($*_DBD)
$(ECHO) "Creating dbd file $(notdir $@)"
@$(RM) $(notdir $@)
$(DBEXPAND) $(DBDFLAGS) -o $(notdir $@) $($*_DBD)
@@ -430,28 +430,28 @@ $(foreach file, $(DBD_INSTALLS), $(eval $(call DBD_INSTALLS_template, $(file))))
#---------------------------------------------------------------
# HTML files
$(COMMON_DIR)/%.html: %.dbd.pod
$(COMMON_DIR)/%.html: %.dbd.pod $(DBDTOHTML_pl)
@$(RM) $(notdir $@)
$(DBDTOHTML) $(DBDFLAGS) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.html: %.pod
$(COMMON_DIR)/%.html: %.pod $(PODTOHTML_dep)
@$(RM) $(notdir $@)
$(PODTOHTML) -s -s -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.html: %.pm
$(COMMON_DIR)/%.html: %.pm $(PODTOHTML_dep)
@$(RM) $(notdir $@)
$(PODTOHTML) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.html: ../%.pm
$(COMMON_DIR)/%.html: ../%.pm $(PODTOHTML_dep)
@$(RM) $(notdir $@)
$(PODTOHTML) -s -o $(notdir $@) $<
@$(MKDIR) $(dir $@)
@$(MV) $(notdir $@) $@
$(COMMON_DIR)/%.html: ../%.pl
$(COMMON_DIR)/%.html: ../%.pl $(PODTOHTML_dep)
@$(RM) $(notdir $@)
$(PODTOHTML) -s -o $(notdir $@) $<
@$(MV) $(notdir $@) $@
@@ -527,16 +527,19 @@ $(foreach file, $(DB_INSTALLS), $(eval $(call DB_INSTALLS_template, $(file))))
#---------------------------------------------------------------
# register record,device,driver support
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
%_registerRecordDeviceDriver.cpp: $(COMMON_DIR)/%.dbd $(REGRECDEVDRV_dep)
@$(RM) $@
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \
$< $(basename $@) $(IOCS_APPL_TOP)
%_registerRecordDeviceDriver.cpp: %.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
%_registerRecordDeviceDriver.cpp: %.dbd $(REGRECDEVDRV_dep)
@$(RM) $@
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \
$< $(basename $@) $(IOCS_APPL_TOP)
%_registerRecordDeviceDriver.cpp: ../%.dbd $(EPICS_BASE_HOST_BIN)/registerRecordDeviceDriver.pl
%_registerRecordDeviceDriver.cpp: ../%.dbd $(REGRECDEVDRV_dep)
@$(RM) $@
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ $< $(basename $@) $(IOCS_APPL_TOP)
$(REGISTERRECORDDEVICEDRIVER) $(REGRDDFLAGS) -o $@ \
$< $(basename $@) $(IOCS_APPL_TOP)
.PRECIOUS: %_registerRecordDeviceDriver.cpp

View File

@@ -532,11 +532,11 @@ endif # LOADABLE_SHRLIB_SUFFIX
ifneq ($(INSTALL_CONFIGS),)
$(INSTALL_CONFIG)/%: %
$(ECHO) "Installing config file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $(abspath $< $(@D))
$(INSTALL_CONFIG)/%: ../%
$(ECHO) "Installing config file $@"
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $< $(@D)
@$(INSTALL) -d -m $(INSTALL_PERMISSIONS) $(abspath $< $(@D))
endif
$(INSTALL_INCLUDE)/%: $(COMMON_DIR)/%

View File

@@ -22,7 +22,7 @@ ARCH_DEP_CFLAGS += -DRTEMS_HAS_ALTIVEC
#OP_SYS_LDLIBS += -lbspExt #does not use posix stuff ... want to ignore
OP_SYS_LDLIBS += -Wl,--gc-sections
#ARCH_DEP_LDFLAGS = -mcpu=8540 -meabi -msdata=sysv -mstrict-align -mspe -mabi=spe -mfloat-gprs=double
ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/powerpc-rtems5/qoriq_e500/lib
ARCH_DEP_LDFLAGS = -L$(RTEMS_BASE)/$(GNU_TARGET)$(RTEMS_VERSION)/$(RTEMS_BSP)/lib
MUNCH_SUFFIX = .img
MUNCHNAME = $(PRODNAME:%$(EXE)=%$(MUNCH_SUFFIX))

View File

@@ -7,6 +7,10 @@
# Include definitions common to all Unix targets
include $(CONFIG)/os/CONFIG.Common.UnixCommon
GNU = NO
CMPLR_CLASS = clang
CC = clang
CCC = clang++
OS_CLASS = freebsd

View File

@@ -66,7 +66,7 @@ GNU = NO
#
# Darwin shared libraries
#
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace -undefined dynamic_lookup \
SHRLIB_LDFLAGS = -dynamiclib -flat_namespace \
-install_name $(abspath $(INSTALL_LIB))/$@ \
$(addprefix -compatibility_version , $(SHRLIB_VERSION)) \
$(addprefix -current_version , $(SHRLIB_VERSION))

View File

@@ -5,3 +5,4 @@
#Include definitions common to unix hosts
include $(CONFIG)/os/CONFIG.UnixCommon.Common
CMPLR_CLASS = clang

View File

@@ -2,6 +2,7 @@
# Definitions for freebsd-x86 host - freebsd-x86 target builds
# Sites may override these definitions in CONFIG_SITE.freebsd-x86.freebsd-x86
#-------------------------------------------------------
GNU_DIR=/usr/local
# Include common gnu compiler definitions
include $(CONFIG)/CONFIG.gnuCommon

View File

@@ -2,6 +2,7 @@
# Definitions for freebsd-x86_64 host - freebsd-x86_64 target builds
# Sites may override these definitions in CONFIG_SITE.freebsd-x86_64.freebsd-x86_64
#-------------------------------------------------------
GNU_DIR=/usr/local
# Include common gnu compiler definitions
include $(CONFIG)/CONFIG.gnuCommon

View File

@@ -3,21 +3,27 @@
# Site specific definitions for darwin builds
#-------------------------------------------------------
# Note the dir/firstword/wildcard functions below are used
# to avoid warnings about missing directories.
# These settings are designed for users of Homebrew.
# Users of other third-party package managers are welcome to
# provide patches appropriate for their manager.
ifneq (,$(wildcard /opt/homebrew))
# Default location on aarch64
HOMEBREW_DIR = /opt/homebrew
else ifneq (,$(wildcard /usr/local/Homebrew))
# Default location on x86_64
HOMEBREW_DIR = /usr/local
else ifneq (,$(wildcard /opt/local/include/readline))
# MacPorts
READLINE_DIR = /opt/local
endif
# Mix-and-match of different package systems is probably not advisable,
# but you can try that if you like...
# Uncomment these definitions when using Homebrew packages:
#OP_SYS_INCLUDES += -I/usr/local/include
#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /usr/local/lib/*))))
# Uncomment these definitions when using DarwinPorts packages:
#OP_SYS_INCLUDES += -I/opt/local/include
#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /opt/local/lib/*))))
# Uncomment these definitions when using Fink packages:
#OP_SYS_INCLUDES += -I/sw/include
#OP_SYS_LDFLAGS += $(addprefix -L,$(dir $(firstword $(wildcard /sw/lib/*))))
# Look for Homebrew's readline
ifneq (,$(wildcard $(HOMEBREW_DIR)/opt/readline))
READLINE_DIR = $(HOMEBREW_DIR)/opt/readline
endif
# Use GNU readline if it's avaiilable
ifneq (,$(wildcard $(READLINE_DIR)/include/readline/readline.h))
INCLUDES_READLINE = -I$(READLINE_DIR)/include
LDFLAGS_READLINE = -L$(READLINE_DIR)/lib
endif

View File

@@ -45,5 +45,10 @@ COMMANDLINE_LIBRARY ?= READLINE
COMMANDLINE_LIBRARY ?= EPICS
# endif
#else
COMMANDLINE_LIBRARY ?= EPICS
COMMANDLINE_LIBRARY ?= $(strip $(if $(wildcard $(if $(GNU_DIR),$(GNU_DIR)/include/readline/readline.h)), READLINE, EPICS))
#endif
#if defined(_FORTIFY_SOURCE) && _FORTIFY_SOURCE>2
OP_SYS_CPPFLAGS += -U_FORTIFY_SOURCE -D_FORTIFY_SOURCE=2
#endif

View File

@@ -0,0 +1,170 @@
=head1 EPICS Component Reference Manual
This document provides reference information about the record types,
menus, link types and channel filters included with EPICS Base.
Many details about the record and menu definitions are derived automatically
from the source code at build time.
=head2 Introduction and IOC Concepts
These links point to an external website where introductory and overview
documentation is now being published.
=over
=item * L<Introduction to EPICS|https://docs.epics-controls.org/en/latest/guides/EPICS_Intro.html>
=item * L<Process Database Concepts|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html>
=back
=head2 Record Type Definitions
These sections describe common aspects of the record types:
=over
=item * L<Fields Common to All Record Types|dbCommonRecord>
=item * L<Fields Common to Input Record Types|dbCommonInput>
=item * L<Fields Common to Output Record Types|dbCommonOutput>
=back
These are the record types supplied with EPICS Base:
=over
=item * L<Analog Array Input Record (aai)|aaiRecord>
=item * L<Analog Array Output Record (aao)|aaoRecord>
=item * L<Analog Input Record (ai)|aiRecord>
=item * L<Analog Output Record (ao)|aoRecord>
=item * L<Array Subroutine Record (aSub)|aSubRecord>
=item * L<Binary Input Record (bi)|biRecord>
=item * L<Binary Output Record (bo)|boRecord>
=item * L<Calculation Output Record (calcout)|calcoutRecord>
=item * L<Calculation Record (calc)|calcRecord>
=item * L<Compression Record (compress)|compressRecord>
=item * L<Data Fanout Record (dfanout)|dfanoutRecord>
=item * L<Event Record (event)|eventRecord>
=item * L<Fanout Record (fanout)|fanoutRecord>
=item * L<Histogram Record (histogram)|histogramRecord>
=item * L<64-bit Integer Input Record (int64in)|int64inRecord>
=item * L<64-bit Integer Output Record (int64out)|int64outRecord>
=item * L<Long Input Record (longin)|longinRecord>
=item * L<Long Output Record (longout)|longoutRecord>
=item * L<Long String Input Record (lsi)|lsiRecord>
=item * L<Long String Output Record (lso)|lsoRecord>
=item * L<Multi-Bit Binary Input Direct Record (mbbiDirect)|mbbiDirectRecord>
=item * L<Multi-Bit Binary Input Record (mbbi)|mbbiRecord>
=item * L<Multi-Bit Binary Output Direct Record (mbboDirect)|mbboDirectRecord>
=item * L<Multi-Bit Binary Output Record (mbbo)|mbboRecord>
=item * L<Permissive Record (permissive)|permissiveRecord>
=item * L<Printf Record (printf)|printfRecord>
=item * L<Select Record (sel)|selRecord>
=item * L<Sequence Record (seq)|seqRecord>
=item * L<State Record (state)|stateRecord>
=item * L<String Input Record (stringin)|stringinRecord>
=item * L<String Output Record (stringout)|stringoutRecord>
=item * L<Sub-Array Record (subArray)|subArrayRecord>
=item * L<Subroutine Record (sub)|subRecord>
=item * L<Waveform Record (waveform)|waveformRecord>
=back
=head2 Menu Definitions
Menu field choices are documented with the record type that defines them, or
here for the global menus that are used by multiple record types:
=over
=item * L<Alarm Severity Menu|menuAlarmSevr>
=item * L<Alarm Status Menu|menuAlarmStat>
=item * L<Analog Conversions Menu|menuConvert>
=item * L<Field Type Menu|menuFtype>
=item * L<Invalid Value Output Action Menu|menuIvoa>
=item * L<Output Mode Select Menu|menuOmsl>
=item * L<Process at iocInit Menu|menuPini>
=item * L<Post Monitors Menu|menuPost>
=item * L<Priority Menu|menuPriority>
=item * L<Scan Menu|menuScan>
=item * L<Simulation Mode Menu|menuSimm>
=item * L<YesE<sol>No Menu|menuYesNo>
=back
=head2 Other Components
EPICS Base also comes with extensible sets of server Channel Filters and IOC
Database Link types, which are documented here:
=over
=item * L<Channel Filters|filters>
=item * L<IOC Database Link Types|links>
=back
=head2 Corrections and Updates
Corrections to these documents can be submitted as patch files to the EPICS
core developers, or as GitHub pull requests to the 7.0 branch of Base.
These document sources can be found under `modules/database/src` tree, mostly
in the `std/rec` and `ioc/db` directories in files with extension `.dbd.pod`.
The documentation source format is a combination of the EPICS DBD file format
with an extended version of Perl's POD (plain old documentation); run `perldoc
pod` for details of POD.

View File

@@ -762,7 +762,6 @@ WARN_LOGFILE =
INPUT = ../mainpage.dox \
../RELEASE_NOTES.md \
../README.md \
../RecordReference.md \
@TOP@/include
# This tag can be used to specify the character encoding of the source files

View File

@@ -1,24 +1,24 @@
TOP = ..
include $(TOP)/configure/CONFIG
ifeq ($(T_A),$(EPICS_HOST_ARCH))
DOXYGEN=doxygen
EXPAND = Doxyfile@
EXPAND_ME += EPICS_VERSION
EXPAND_ME += EPICS_REVISION
EXPAND_ME += EPICS_MODIFICATION
EXPAND_ME += EPICS_PATCH_LEVEL
EXPAND_ME += OS_CLASS CMPLR_CLASS
HTMLS += ComponentReference.html
TARGETS += doxygen
DOXYGEN = doxygen
ME = documentation/O.$(T_A)/html
GH_FILES = $(ME)/ $(ME)/.nojekyll $(ME)/*.* $(ME)/*/*.*
install: doxygen
include $(TOP)/configure/RULES
doxygen: Doxyfile ../mainpage.dox
doxygen: Doxyfile ../mainpage.dox $(INSTALL_HTMLS)
$(DOXYGEN)
rsync -av $(TOP)/html/ html/
@@ -29,7 +29,3 @@ commit: doxygen
(cd $(TOP) && $(CURDIR)/../commit-gh.sh $(GH_FILES))
.PHONY: commit
endif # EPICS_HOST_ARCH
include $(TOP)/configure/RULES

View File

@@ -9,8 +9,253 @@ important to read more than just the first section to understand everything that
has changed in each release.
The PVA submodules each have their own individual sets of release notes which
should also be read to understand what has changed since earlier releases.
should also be read to understand what has changed since earlier releases:
- [normativeTypes](https://github.com/epics-base/normativeTypesCPP/blob/master/documentation/RELEASE_NOTES.md)
- [pvAccess](http://epics-base.github.io/pvAccessCPP/pvarelease_notes.html)
- [pvData](http://epics-base.github.io/pvDataCPP/release_notes.html)
- [pvDatabase](https://github.com/epics-base/pvDatabaseCPP/blob/master/documentation/RELEASE_NOTES.md)
- [pva2pva](https://epics-base.github.io/pva2pva/release_notes.html)
- [pvaClient](https://github.com/epics-base/pvaClientCPP/blob/master/documentation/RELEASE_NOTES.md)
## EPICS Release 7.0.8.1
### Limit to `_FORTIFY_SOURCE=2`
GCC versions 12 and beyond and glibc have added some aggressive runtime
checks for buffer overflows in libc functions at runtime, and the
[Ubuntu 2024.04](https://wiki.ubuntu.com/ToolChain/CompilerFlags) release
increased their default gcc fortification level from 2 to 3.
This has started causing EPICS Base builds to fail on that version, and
other OS releases may make that configuration change with similar results.
This release detects a compiler configured with `_FORTIFY_SOURCE=3` and
overrides it to 2.
Later releases of Base will adjust the code, providing information to the
compiler to avoid triggering these incorrect protections.
### Fix issue with compress record
In Base 7.0.8, an update to the compress record was added to allow for certain
algorithms to use partially filled buffers in their computations. Unfortunately,
this broke the behaviour of the records in certain cases. This has been fixed.
### Various minor changes
These included fixing minor memory leaks and documentation corrections. The
`SIZV` field of lsi, lso and printf record VAL fields now can't exceed 32767
characters, to match an internal limit.
### `epicsSocketAccept()` now returns `SOCKET`, not `int`
This might have some effect on downstream modules still using `int`, but the
OS-specific osdSock.h headers which osiSock.h includes have all declared
`SOCKET` (in most casese as a typedef for `int`) for many releases.
This change removes a compiler warning on WIN32.
Further details and the discussion about this change can be found
[here](https://github.com/epics-base/epics-base/pull/458).
### `dbLoadRecords` allows macros with default values
Previously the parser assumed that files containing macro substitutions were
bad if no macro definitions were provided; that assumption was made incorrect
once macro substitutions were allowed to provide a default value.
### Hostname length limit in CA removed
Before this release, the CA client library only handled hostnames in address
list environment variables up to 255 characters long.
This limit has been removed.
-----
## EPICS Release 7.0.8
### bi "Raw Soft Channel" use MASK
If MASK is non-zero, The raw device support will now apply MASK to the
value read into RVAL.
eg. allows extraction of a bit from an input integer.
```
record(longin, "integer") {
field(VAL, "0xff")
}
record(bi, "bit1") {
field(DESC, "extract bit 1")
field(DTYP, "Raw Soft Channel")
field(INP , "integer")
field(MASK, "0x2")
field(ZNAM, "Clear")
field(ONAM, "Set")
}
```
### ANSI escapes in stderr
ANSI escape charactor sequences may now be printed to the stderr stream.
These escapes will appear in logs captured from that stream.
Tools which parse and/or render these logs may need to be adjusted to
either strip out the escapes, or to translate them into markup.
(see [ansi2html](https://pypi.org/project/ansi2html/) for example)
### Allow explicit append with `dbRecordsOnceOnly!=0`
Previously setting `dbRecordsOnceOnly!=0` prevented any further changes to a record via a .db file. eg.
```
record(ai, "myrec") {}
```
`dbRecordsOnceOnly!=0` previously disallowed appending fields with either form:
```
record("*", "myrec") {} # error
record(ai, "myrec") {} # error
```
Beginning with this release, `dbRecordsOnceOnly!=0` allows appending when explicitly intended (when record type is `"*"`).
```
record("*", "myrec") {} # allowed
record(ai, "myrec") {} # error
```
### Add `$EPICS_CLI_TIMEOUT`
Add support for CA tools timeout from environment variable `$EPICS_CLI_TIMEOUT`
which sets the default the default timeout for `caget` et al.
The `-w` argument continues to take precedence.
### Fixed leak from a non-EPICS thread on WIN32
On Windows targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated. Prior to this release the allocation would not be `free()`d,
resulting in a memory leak.
A similar issue on POSIX targets was previously fixed.
### Change compiler for FreeBSD to clang
The default compiler for FreeBSD targets changes from GCC to clang.
### Expose `dbCreateAlias` in IOC shell
Add a new IOC shell command `dbCreateAlias` allow record aliases to be added.
Intended for use before `iocInit`. eg. to add an alias "bar" for a record "foo".
```
dbLoadRecords("some.db") # includes: record(ai, "foo") { ...
dbCreateAlias("foo", "bar")
iocInit()
```
### dbEvent eventsRemaining missed on cancel
In some cases, RSRV may queue a subscription update, but not flush it.
This partially addresses this issue.
### subRecord on bad INP links
Previously, if a subRecord has an invalid `INP*` link, it was silently failing
(and not running the proc function). Now the the status code returned by the
subroutine is returned from `dbProcess()`.
### COMMANDLINE\_LIBRARY fallback to GNU\_DIR
Fall back to the previous behavior when searching for `readline.h` with older compilers.
### Search for readline installed via HomeBrew.
Look for `/opt/local/include/readline` on OSX.
### Always stop worker threads
The SCAN and callback threads are now stopped during normal IOC shutdown.
### Allow runtime bypass of free list allocator
The environment variable `$EPICS_FREELIST_BYPASS` may be set to `YES` to cause the `freeListLib` functions to always call directly to `malloc()`/`free()`. May be useful when troubleshooting some kinds of memory allocation bugs which would otherwise be "hidden". eg. use-after-free data races. This may also improve the results of dynamic analysis tools which are not aware of this internal free list.
### `compress` record enhancement
The compress record now supports the use of partially-filled buffers when using
any of the N-to-one algorithms. This is achieved by setting the new field `PBUF`
to `YES`.
### Extended timestamp channel filter
The `"ts"` filter can now retrieve the record's timestamp in several numeric
and string formats, some of which support full nanosecond precision.
Hal$ caget -a test:channel
test:channel 2021-03-11 18:23:48.265386 42
Hal$ caget -f9 'test:channel.{"ts": {"num": "dbl"}}'
test:channel.{"ts": {"num": "dbl"}} 984331428.265386105
Hal$ caget 'test:channel.{"ts": {"str": "iso"}}'
test:channel.{"ts": {"str": "iso"}} 2021-03-11T18:23:48.265386+0100
Hal$ caget -f1 'test:channel.{"ts": {"num": "ts"}}'
test:channel.{"ts": {"num": "ts"}} 2 984331428.0 265386163.0
More information is included in the filters documentation, which can be found in
the `html/filters.html` document that is generated during the build
### Allow adding new error symbols at any time
`errSymbolAdd` can now be called after early initialization.
### Add conditional output (OOPT) to the longout record
The longout record can now be configured using its new OOPT and OOCH fields
to (not) write to its output link depending on the contents of VAL, in a
similar manner to the calcout record. More information can be found on the
reference page for the longout record type that accompanies this release.
This functionality was suggested in
[lp# 1398215](https://bugs.launchpad.net/epics-base/+bug/1398215) and may
be added to other output record types if the community finds it useful,
please send feedback about the feature to tech-talk.
### IOC Shell
#### Tab completion
When built with optional GNU libreadline support, the interactive IOC shell
will perform tab completion for command names as well as for some arguments
of the built-in commands. For example, the record name argument of `dbpr`,
and the path name argument of `cd`.
Externally defined commands have a limited ability to opt into completion by
using the new `iocshArgStringRecord` and `iocshArgStringPath` argument types.
Both function identically to `iocshArgString` but indicate how to suggest
completion strings.
Builds on macOS (`darwin-x86` or `darwin-aarch64` targets) normally use Apple's
libedit library in readline compatibility mode, which doesn't support the tab
completion API that GNU readline provides. You can use Homebrew or some other
third-party package manager to install the GNU readline package, then edit the
`configure/os/CONFIG_SITE.darwinCommon.darwinCommon` file to have EPICS use the
real thing to get tab completion in the IOC shell. The default settings in that
file currently look for and use a Homebrew-installed readline if present.
#### Persist history
Attempt to read and write command to a file (`./.iocsh_history` by default).
Name may be overwritten with by setting `$EPICS_IOCSH_HISTFILE` to an
alternate path, or disabled by setting to an empty string.
#### Changes to help output
Rework the `help` command output to improve formatting and readability,
and include a visual marker (a line of underlines) between different help commands.
### Add FMOD as CALC Expression
The floating point modulo function `FMOD(NUM,DEN)` has been added to the CALC
expression engine and is available to all software using that (calc and calcout
record types, access security library and some extensions).
-----
## EPICS Release 7.0.7
@@ -151,6 +396,32 @@ This release fixed the leak on POSIX targets.
See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241)
for WIN32 status.
### Fixed leak from a non-EPICS thread
On some targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated.
Prior to this release, on POSIX and WIN32 targets, this
allocation would not be `free()`d, resulting in a memory leak.
This release fixed the leak on POSIX and WIN32 targets (excluding
MSVC before vs2012, and the WINE runtime).
### Fixed leak from a non-EPICS thread
On some targets, if a thread not created by `epicsThreadCreate*()` directly
or indirectly calls an `epicsThread*()` function, a specific tracking struct
is allocated.
Prior to this release, on POSIX and WIN32 targets, this
struct would not be `free()`d, resulting in a memory leak.
This release fixed the leak on POSIX targets.
See the associated github [issue 241](https://github.com/epics-base/epics-base/issues/241)
for WIN32 status.
### Fix `CHECK_RELEASE = WARN`
This now works again, it was broken in 2019 (7.0.3.1) by an errant commit.

View File

@@ -1,74 +0,0 @@
# Record Reference Documentation {#recordrefmanual}
The documentation below for the record types and menus included with Base was
converted from the old EPICS Wiki pages and updated. This list only includes the
record types supplied with Base. The first two links below are to an external
website where these original reference chapters are now being published.
* [Introduction to EPICS](https://docs.epics-controls.org/en/latest/guides/EPICS_Intro.html)
* [Process Database Concepts](https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html)
* [Fields Common to All Record Types](dbCommonRecord.html)
* [Fields Common to Input Record Types](dbCommonInput.html)
* [Fields Common to Output Record Types](dbCommonOutput.html)
## Record Types
* [Analog Array Input Record (aai)](aaiRecord.html)
* [Analog Array Output Record (aao)](aaoRecord.html)
* [Analog Input Record (ai)](aiRecord.html)
* [Analog Output Record (ao)](aoRecord.html)
* [Array Subroutine Record (aSub)](aSubRecord.html)
* [Binary Input Record (bi)](biRecord.html)
* [Binary Output Record (bo)](boRecord.html)
* [Calculation Output Record (calcout)](calcoutRecord.html)
* [Calculation Record (calc)](calcRecord.html)
* [Compression Record (compress)](compressRecord.html)
* [Data Fanout Record (dfanout)](dfanoutRecord.html)
* [Event Record (event)](eventRecord.html)
* [Fanout Record (fanout)](fanoutRecord.html)
* [Histogram Record (histogram)](histogramRecord.html)
* [64bit Integer Input Record (int64in)](int64inRecord.html)
* [64bit Integer Output Record (int64out)](int64outRecord.html)
* [Long Input Record (longin)](longinRecord.html)
* [Long Output Record (longout)](longoutRecord.html)
* [Long String Input Record (lsi)](lsiRecord.html)
* [Long String Output Record (lso)](lsoRecord.html)
* [Multi-Bit Binary Input Direct Record (mbbiDirect)](mbbiDirectRecord.html)
* [Multi-Bit Binary Input Record (mbbi)](mbbiRecord.html)
* [Multi-Bit Binary Output Direct Record (mbboDirect)](mbboDirectRecord.html)
* [Multi-Bit Binary Output Record (mbbo)](mbboRecord.html)
* [Permissive Record (permissive)](permissiveRecord.html)
* [Printf Record (printf)](printfRecord.html)
* [Select Record (sel)](selRecord.html)
* [Sequence Record (seq)](seqRecord.html)
* [State Record (state)](stateRecord.html)
* [String Input Record (stringin)](stringinRecord.html)
* [String Output Record (stringout)](stringoutRecord.html)
* [Sub-Array Record (subArray)](subArrayRecord.html)
* [Subroutine Record (sub)](subRecord.html)
* [Waveform Record (waveform)](waveformRecord.html)
## Menu Definitions
* [Alarm Severity Menu](menuAlarmSevr.html)
* [Alarm Status Menu](menuAlarmStat.html)
* [Analog Conversions Menu](menuConvert.html)
* [Field Type Menu](menuFtype.html)
* [Invalid Value Output Action Menu](menuIvoa.html)
* [Output Mode Select Menu](menuOmsl.html)
* [Process at iocInit Menu](menuPini.html)
* [Post Monitors Menu](menuPost.html)
* [Priority Menu](menuPriority.html)
* [Scan Menu](menuScan.html)
* [Simulation Mode Menu](menuSimm.html)
* [Yes/No Menu](menuYesNo.html)
## Corrections and Updates
Corrections to these documents can be submitted as patch files to the EPICS core
developers, or as merge requests or pull requests to the 7.0 branch of Base.
The document sources can be found in the `modules/database/src/std/rec` and
`modules/database/src/ioc/db` directories in files with extension `.dbd.pod`.
The documentation source format is a combination of the EPICS DBD file format
with an extended version of Perl's POD (plain old documentation); run `perldoc
pod` for details of POD.

View File

@@ -48,14 +48,14 @@ everything that has to be done since it's so easy to miss steps.</p>
<p>The following roles are used below:</p>
<dl>
<dt><strong>Release Manager</strong> ()</dt>
<dt><strong>Release Manager</strong></dt>
<dd>Responsible for managing and tagging the release</dd>
<dt><strong>Platform Developers</strong> (informal)</dt>
<dd>Responsible for individual operating system platforms</dd>
<dt><strong>Core Developers</strong></dt>
<dd>Responsible for maintaining the EPICS software</dd>
<dt><strong>Application Developers</strong></dt>
<dd>Responsible for support modules that depend on EPICS Base.</dd>
<dt><strong>Website Editor</strong> (Andrew Johnson)</dt>
<dd>Responsible for the EPICS website</dd>
<dt><strong>Website Editors</strong></dt>
<dd>Responsible for the EPICS websites</dd>
</dl>
<form>
@@ -72,23 +72,22 @@ everything that has to be done since it's so easy to miss steps.</p>
<tr>
<td>&nbsp;</td>
<td>Release Manager</td>
<td>Email all developers about the upcoming release and ask for a list
of remaining tasks that must be finished.</td>
<td>Notify core developers about the upcoming release and ask about any
remaining tasks that must be finished.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>All developers</td>
<td>Check the bug tracker for any outstanding items and handle
appropriately. All bugs that have been fixed should have been marked
as Fix Committed.</td>
appropriately.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>Release Manager</td>
<td>Set the Feature Freeze date, by which time all Git commits for
enhancements and new functionality should have been completed. After
this date, commits should only be made to fix problems that show up
during testing.</td>
<td>Set a Feature Freeze date, by which time all Git branches for
enhancements and new functionality should have been merged. After this
date, commits and merges should only be made to fix problems that show
up during testing.</td>
</tr>
<tr>
<td>&nbsp;</td>
@@ -97,6 +96,7 @@ everything that has to be done since it's so easy to miss steps.</p>
<td>Ensure that documentation will be updated before the release date:
<ul>
<li>Release Notes</li>
<li>Doxygen annotations</li>
<li>Other documents</li>
</ul>
</td>
@@ -104,13 +104,8 @@ everything that has to be done since it's so easy to miss steps.</p>
<tr>
<td>&nbsp;</td>
<td>Release Manager</td>
<td>Review and update this checklist for the upcoming release.</td>
</tr>
<tr>
<td>&nbsp;</td>
<td>Release Manager</td>
<td>Create a release milestone on Launchpad. If a target release date is
known set "Date Targeted" to the expected release date.</td>
<td>Review and update this checklist for the upcoming release.
Update the release version number in the tags and messages below.</td>
</tr>
<tr>
<th colspan="3">Testing</th>
@@ -118,7 +113,7 @@ everything that has to be done since it's so easy to miss steps.</p>
<tr>
<td>&nbsp;</td>
<td>Platform Developers</td>
<td>Run the built-in test programs on all available host platforms using
<td>Run the internal test programs on all available host platforms using
<blockquote><tt>
make -s runtests
</tt></blockquote></td>
@@ -156,6 +151,7 @@ everything that has to be done since it's so easy to miss steps.</p>
<td>Check that documentation has been updated:
<ul>
<li>Release Notes</li>
<li>Doxygen annotations</li>
<li>Other documents</li>
</ul>
</td>
@@ -167,8 +163,8 @@ everything that has to be done since it's so easy to miss steps.</p>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Obtain a positive <q>Ok to release</q> from developers.</td>
<td>Core Developers</td>
<td>Reach a consensus that the software is ready to release.</td>
</tr>
<tr>
<th colspan="3">Creating the final release version</th>
@@ -180,6 +176,9 @@ everything that has to be done since it's so easy to miss steps.</p>
<p><b>For each external submodule</b> in turn (assuming it has not been
tagged yet):</p>
<ol>
<li><tt>git grep UNRELEASED</tt> and insert the module version to any
doxygen annotations that have a <tt>@since UNRELEASED</tt> comment.
Commit (don't push yet).</li>
<li>Check that the module's Release Notes have been updated to cover
all changes; add items as necessary, and set the module version
number and release date if appropriate. Convert to HTML and view in
@@ -188,27 +187,29 @@ everything that has to be done since it's so easy to miss steps.</p>
cd base-7.0/modules/&lt;module&gt;/documentation<br />
pandoc -f gfm -t html -o RELEASE_NOTES.html RELEASE_NOTES.md
</tt></blockquote>
Commit changes (don't push yet).</li>
Commit these changes (don't push).</li>
<li>Edit the module's release version file
<tt>configure/CONFIG_<i>module</i>_VERSION</tt> and its top-level
<tt>Doxyfile</tt>; set the <tt>DEVELOPMENT_FLAG</tt> value to 0 and
remove <tt>-dev</tt> from the <tt>PROJECT_NUMBER</tt> string.
Commit changes (don't push).</li>
<tt>configure/CONFIG_<i>module</i>_VERSION</tt> and the
<tt>Doxyfile</tt>s in the top-level and/or documentation
directories. In these, set <tt>DEVELOPMENT_FLAG</tt> to 0 and remove
<tt>-dev</tt> from the <tt>PROJECT_NUMBER</tt> string. Commit these
changes (don't push).</li>
<li>Tag the module:
<blockquote><tt>
git tag -m 'ANJ: Tag for EPICS 7.0.6.1' &lt;module-version&gt;
git tag -m 'ANJ: Tag for EPICS 7.0.8.1' &lt;module-version&gt;
</tt></blockquote>
</li>
<li>Update the git submodule on the Base-7.0 branch to the
newly-tagged version, but don't commit yet:
newly-tagged version, check the module's status matches the tag:
<blockquote><tt>
cd base-7.0/modules<br />
git add &lt;module&gt;<br />
git submodule status --cached
</tt></blockquote>
Don't commit the submodule updates yet.
</li>
<li>Edit the module's release version file
@@ -218,7 +219,8 @@ everything that has to be done since it's so easy to miss steps.</p>
<tt>PROJECT_NUMBER</tt> string, appending <tt>-dev</tt> to the new
module version number. Commit changes.</li>
<li>Push commits and the new tag to the submodule's GitHub repository:
<li>Push commits and the new tag to the submodule's GitHub repository
(assumed to be the <tt>upstream</tt> remote):
<blockquote><tt>
cd base-7.0/modules/&lt;module&gt;<br />
git push --follow-tags upstream master
@@ -267,10 +269,9 @@ everything that has to be done since it's so easy to miss steps.</p>
<td>Tag the epics-base module in Git:
<blockquote><tt>
cd base-7.0<br />
git tag -m 'ANJ: Tagged for release' R7.0.6.1
git tag -m 'ANJ: Tagged for release' R7.0.8.1
</tt></blockquote>
<p>Don't push anything to the Launchpad repository
yet.</p>
<p>Don't push to GitHub yet.</p>
</td>
</tr>
<tr>
@@ -302,12 +303,12 @@ everything that has to be done since it's so easy to miss steps.</p>
files and directories that are only used for continuous integration:
<blockquote><tt>
cd base-7.0<br />
./.tools/make-tar.sh R7.0.6.1 ../base-7.0.6.1.tar.gz base-7.0.6.1/
./.tools/make-tar.sh R7.0.8.1 ../base-7.0.8.1.tar.gz base-7.0.8.1/
</tt></blockquote>
Create a GPG signature file of the tarfile as follows:
<blockquote><tt>
cd ..<br />
gpg --armor --sign --detach-sig base-7.0.6.1.tar.gz
gpg --armor --sign --detach-sig base-7.0.8.1.tar.gz
</tt></blockquote>
</td>
</tr>
@@ -315,8 +316,9 @@ everything that has to be done since it's so easy to miss steps.</p>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Test the tar file by extracting its contents and building it on at
least one supported platform. When this succeeds the commits and new git
tag can be pushed to the Launchpad repository:
least one supported platform. If this succeeds the commits and new git
tag can be pushed to the GitHub repository's 7.0 branch (assumed to be
the <tt>upstream</tt> remote):
<blockquote><tt>
git push --follow-tags upstream 7.0
</tt></blockquote>
@@ -364,7 +366,7 @@ everything that has to be done since it's so easy to miss steps.</p>
</tr>
<tr>
<th colspan="3">Publish to epics-controls</th>
<th colspan="3">Publish to epics-controls.org</th>
</tr>
<tr>
<td><input type="checkbox"></td>
@@ -372,7 +374,7 @@ everything that has to be done since it's so easy to miss steps.</p>
<td>Upload the tar file and its <tt>.asc</tt> signature file to the
epics-controls web-server.
<blockquote><tt>
scp base-7.0.6.1.tar.gz base-7.0.6.1.tar.gz.asc epics-controls:download/base<br />
scp base-7.0.8.1.tar.gz base-7.0.8.1.tar.gz.asc epics-controls:download/base<br />
</tt></blockquote>
</td>
</tr>
@@ -389,22 +391,22 @@ everything that has to be done since it's so easy to miss steps.</p>
</tr>
<tr>
<th colspan="3">Publish to Launchpad</th>
<th colspan="3">Publish to GitHub</th>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Go to the Launchpad milestone for this release. Click the Create
release button and add the release date. Put a URL for the release page
in the Release notes box, and click the Create release button. Upload
the tar file and its <tt>.asc</tt> signature file to the new Launchpad
release page.</td>
<td>Go to the GitHub
<a href="https://github.com/epics-base/epics-base/releases/new?tag=R7.0.8.1">
Create release from tag R7.0.8.1</a> page.
Upload the tar file and its <tt>.asc</tt> signature file to the new
GitHub release page.</td>
</tr>
<tr>
<td><input type="checkbox"></td>
<td>Release Manager</td>
<td>Find all Launchpad bug reports with the status Fix Committed which
have been fixed in this release and mark them Fix Released.</td>
<td>We used to close out bug reports in Launchpad at release-time, this
would be the time to do that if we have an equivalent on GitHub.</td>
</tr>
<tr>

View File

@@ -5,11 +5,11 @@ Documentation index
@li @ref releasenotes
@li @ref install
@li @ref recordrefmanual
@li <a href="ComponentReference.html">EPICS Component Reference Manual</a>
@li <a href="filters.html">Field Modifiers and Channel Filters</a>
@li <a href="links.html">Extensible IOC Database Links</a>
@li <a href="CAref.html">Channel Access Reference Manual</a>
@li <a href="filters.html">Server Side Filters Reference</a>
@li <a href="msi.html">msi: Macro Substitution and Include Tool</a>
@li <a href="links.html">JSON Link Types</a>
@li <a href="CA.html">Perl 5 Interface to Channel Access</a>
*/

View File

@@ -34,9 +34,6 @@ pvDatabase_DEPEND_DIRS = pvAccess
SUBMODULES += pva2pva
pva2pva_DEPEND_DIRS = pvAccess
SUBMODULES += example
example_DEPEND_DIRS = pva2pva pvaClient
# Allow sites to add extra submodules
-include Makefile.local

View File

@@ -2575,7 +2575,8 @@ void monitorUpdateTest ( chid chan, unsigned interestLevel )
SEVCHK ( ca_get ( DBR_FLOAT, chan, &temp ), NULL );
SEVCHK ( ca_pend_io ( timeoutToPendIO ), NULL );
/* printf ( "flow control bypassed %u events\n", flowCtrlCount ); */
if (0)
printf ( "flow control bypassed %u events\n", flowCtrlCount );
showProgressEnd ( interestLevel );
}

View File

@@ -25,6 +25,7 @@
#include <stdexcept>
#include <string> // vxWorks 6.0 requires this include
#include "epicsStdio.h"
#include "dbDefs.h"
#include "epicsGuard.h"
#include "epicsVersion.h"
@@ -1008,7 +1009,7 @@ bool cac::defaultExcep (
char buf[512];
char hostName[64];
iiu.getHostName ( guard, hostName, sizeof ( hostName ) );
sprintf ( buf, "host=%s ctx=%.400s", hostName, pCtx );
epicsSnprintf( buf, sizeof(buf), "host=%s ctx=%.400s", hostName, pCtx );
this->notify.exception ( guard, status, buf, 0, 0u );
return true;
}
@@ -1312,7 +1313,7 @@ void cac::pvMultiplyDefinedNotify ( msgForMultiplyDefinedPV & mfmdpv,
const char * pChannelName, const char * pAcc, const char * pRej )
{
char buf[256];
sprintf ( buf, "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s",
epicsSnprintf( buf, sizeof(buf), "Channel: \"%.64s\", Connecting to: %.64s, Ignored: %.64s",
pChannelName, pAcc, pRej );
{
callbackManager mgr ( this->notify, this->cbMutex );

View File

@@ -450,7 +450,7 @@ void timeIt ( tf *pfunc, ti *pItems, unsigned iterations,
epicsTimeStamp end_time;
epicsTimeStamp start_time;
double delay;
unsigned inlineIter;
unsigned inlineIter = 0;
epicsTimeGetCurrent ( &start_time );
(*pfunc) ( pItems, iterations, &inlineIter );

View File

@@ -21,6 +21,9 @@
#define epicsAssertAuthor "Jeff Hill johill@lanl.gov"
#include <vector>
#include <exception>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>
@@ -28,6 +31,7 @@
#include "envDefs.h"
#include "epicsAssert.h"
#include "epicsString.h"
#include "epicsStdioRedirect.h"
#include "errlog.h"
#include "osiWireFormat.h"
@@ -35,39 +39,6 @@
#include "addrList.h"
#include "iocinf.h"
/*
* getToken()
*/
static char *getToken ( const char **ppString, char *pBuf, unsigned bufSIze )
{
bool tokenFound = false;
const char *pToken;
unsigned i;
pToken = *ppString;
while ( isspace (*pToken) && *pToken ){
pToken++;
}
for ( i=0u; i<bufSIze; i++ ) {
if ( isspace (pToken[i]) || pToken[i]=='\0' ) {
pBuf[i] = '\0';
*ppString = &pToken[i];
if ( i != 0 ) {
tokenFound = true;
}
break;
}
pBuf[i] = pToken[i];
}
if ( tokenFound ) {
pBuf[bufSIze-1] = '\0';
return pBuf;
}
return NULL;
}
/*
* addAddrToChannelAccessAddressList ()
*/
@@ -77,9 +48,7 @@ extern "C" int epicsStdCall addAddrToChannelAccessAddressList
{
osiSockAddrNode *pNewNode;
const char *pStr;
const char *pToken;
struct sockaddr_in addr;
char buf[256u]; /* large enough to hold an IP address or hostname */
int status, ret = -1;
pStr = envGetConfigParamPtr (pEnv);
@@ -87,31 +56,45 @@ extern "C" int epicsStdCall addAddrToChannelAccessAddressList
return ret;
}
while ( ( pToken = getToken (&pStr, buf, sizeof (buf) ) ) ) {
status = aToIPAddr ( pToken, port, &addr );
if (status<0) {
fprintf ( stderr, "%s: Parsing '%s'\n", __FILE__, pEnv->name);
fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken);
continue;
try {
std::vector<char> scratch(pStr, pStr+strlen(pStr)+1); // copy chars and trailing nil
char *save = NULL;
for(const char *pToken = epicsStrtok_r(&scratch[0], " \t\n\r", &save);
pToken;
pToken = epicsStrtok_r(NULL, " \t\n\r", &save))
{
if(!pToken[0]) {
continue;
}
status = aToIPAddr ( pToken, port, &addr );
if (status<0) {
fprintf ( stderr, "%s: Parsing '%s'\n", __FILE__, pEnv->name);
fprintf ( stderr, "\tBad internet address or host name: '%s'\n", pToken);
continue;
}
if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) {
continue;
}
pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
if (pNewNode==NULL) {
fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
break;
}
pNewNode->addr.ia = addr;
/*
* LOCK applied externally
*/
ellAdd (pList, &pNewNode->node);
ret = 0; /* success if anything is added to the list */
}
if ( ignoreNonDefaultPort && ntohs ( addr.sin_port ) != port ) {
continue;
}
pNewNode = (osiSockAddrNode *) calloc (1, sizeof(*pNewNode));
if (pNewNode==NULL) {
fprintf ( stderr, "addAddrToChannelAccessAddressList(): no memory available for configuration\n");
break;
}
pNewNode->addr.ia = addr;
/*
* LOCK applied externally
*/
ellAdd (pList, &pNewNode->node);
ret = 0; /* success if anything is added to the list */
} catch(std::exception&) { // only bad_alloc currently possible
ret = -1;
}
return ret;

View File

@@ -56,6 +56,7 @@ extern "C" void epicsStdCall ca_dump_dbr (
if ( INVALID_DB_REQ ( type ) ) {
printf ( "bad DBR type %ld\n", type );
return;
}
printf ( "%s\t", dbr_text[type] );

View File

@@ -549,7 +549,7 @@ void epicsStdCall caRepeaterRegistrationMessage (
char sockErrBuf[64];
epicsSocketConvertErrnoToString (
sockErrBuf, sizeof ( sockErrBuf ) );
fprintf ( stderr, "error sending registration message to CA repeater daemon was \"%s\"\n",
fprintf ( stderr, ERL_ERROR " sending registration message to CA repeater daemon was \"%s\"\n",
sockErrBuf );
}
}
@@ -813,13 +813,13 @@ bool udpiiu::exceptionRespAction (
if ( msg.m_postsize > sizeof ( caHdr ) ){
errlogPrintf (
"error condition \"%s\" detected by %s with context \"%s\" at %s\n",
ERL_ERROR " condition \"%s\" detected by %s with context \"%s\" at %s\n",
ca_message ( msg.m_available ),
name, reinterpret_cast <const char *> ( &reqMsg + 1 ), date );
}
else{
errlogPrintf (
"error condition \"%s\" detected by %s at %s\n",
ERL_ERROR " condition \"%s\" detected by %s at %s\n",
ca_message ( msg.m_available ), name, date );
}

View File

@@ -150,6 +150,7 @@ sub parseDbd {
$thisField = $1;
$thisType = $2;
$isAfield = 1;
$thisSize = 1024 if $thisType =~ m/^ DBF_(IN|OUT|FWD)LINK $/x;
}
elsif ( m/interest \s* \( \s* (\w+) \s* \)/x ) {
die "File format error at line $i of file\n $opt_d\n"

View File

@@ -392,6 +392,8 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
use_ca_timeout_env ( &caTimeout);
while ((opt = getopt(argc, argv, ":taicnhsSVe:f:g:l:#:d:0:w:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
@@ -437,8 +439,8 @@ int main (int argc, char *argv[])
if(epicsScanDouble(optarg, &caTimeout) != 1)
{
fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('caget -h' for help.)\n", optarg);
caTimeout = DEFAULT_TIMEOUT;
"- ignored, using '%.1f'. ('caget -h' for help.)\n",
optarg, caTimeout);
}
break;
case '#': /* Array count */

View File

@@ -141,6 +141,8 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
use_ca_timeout_env ( &caTimeout);
while ((opt = getopt(argc, argv, ":nhVw:s:p:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
@@ -150,11 +152,16 @@ int main (int argc, char *argv[])
printf( "\nEPICS Version %s, CA Protocol version %s\n", EPICS_VERSION_STRING, ca_version() );
return 0;
case 'w': /* Set CA timeout value */
/*
* epicsScanDouble is a macro defined as epicsParseDouble,
* (found in modules/libcom/src/misc) which will only
* change caTimeout here if it finds an acceptable value.
*/
if(epicsScanDouble(optarg, &caTimeout) != 1)
{
fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('cainfo -h' for help.)\n", optarg);
caTimeout = DEFAULT_TIMEOUT;
"- ignored, using '%.1f'. ('cainfo -h' for help.)\n",
optarg, caTimeout);
}
break;
case 's': /* ca_client_status interest level */

View File

@@ -219,6 +219,8 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
use_ca_timeout_env ( &caTimeout);
while ((opt = getopt(argc, argv, ":nhVm:sSe:f:g:l:#:0:w:t:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
@@ -251,11 +253,16 @@ int main (int argc, char *argv[])
}
break;
case 'w': /* Set CA timeout value */
/*
* epicsScanDouble is a macro defined as epicsParseDouble,
* (found in modules/libcom/src/misc) which will only
* change caTimeout here if it finds an acceptable value.
*/
if(epicsScanDouble(optarg, &caTimeout) != 1)
{
fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('camonitor -h' for help.)\n", optarg);
caTimeout = DEFAULT_TIMEOUT;
"- ignored, using '%.1f'. ('camonitor -h' for help.)\n",
optarg, caTimeout);
}
break;
case '#': /* Array count */

View File

@@ -35,6 +35,7 @@
#include <epicsStdlib.h>
#include <cadef.h>
#include <errlog.h>
#include <epicsGetopt.h>
#include <epicsEvent.h>
#include <epicsString.h>
@@ -284,6 +285,8 @@ int main (int argc, char *argv[])
LINE_BUFFER(stdout); /* Configure stdout buffering */
putenv("POSIXLY_CORRECT="); /* Behave correct on GNU getopt systems */
use_ca_timeout_env ( &caTimeout);
while ((opt = getopt(argc, argv, ":cnlhatsVS#:w:p:F:")) != -1) {
switch (opt) {
case 'h': /* Print usage */
@@ -318,11 +321,16 @@ int main (int argc, char *argv[])
request = callback;
break;
case 'w': /* Set CA timeout value */
/*
* epicsScanDouble is a macro defined as epicsParseDouble,
* (found in modules/libcom/src/misc) which will only
* change caTimeout here if it finds an acceptable value.
*/
if(epicsScanDouble(optarg, &caTimeout) != 1)
{
fprintf(stderr, "'%s' is not a valid timeout value "
"- ignored. ('caput -h' for help.)\n", optarg);
caTimeout = DEFAULT_TIMEOUT;
"- ignored, using '%.1f'. ('caput -h' for help.)\n",
optarg, caTimeout);
}
break;
case '#': /* Array count */
@@ -337,7 +345,7 @@ int main (int argc, char *argv[])
if (sscanf(optarg,"%u", &caPriority) != 1)
{
fprintf(stderr, "'%s' is not a valid CA priority "
"- ignored. ('caget -h' for help.)\n", optarg);
"- ignored. ('caput -h' for help.)\n", optarg);
caPriority = DEFAULT_CA_PRIORITY;
}
if (caPriority > CA_PRIORITY_MAX) caPriority = CA_PRIORITY_MAX;
@@ -542,7 +550,7 @@ int main (int argc, char *argv[])
result = ca_array_put (dbrType, count, pvs[0].chid, pbuf);
}
if (result != ECA_NORMAL) {
fprintf(stderr, "Error from put operation: %s\n", ca_message(result));
fprintf(stderr, ERL_ERROR " from put operation: %s\n", ca_message(result));
free(sbuf); free(dbuf); free(ebuf);
return 1;
}
@@ -563,7 +571,7 @@ int main (int argc, char *argv[])
}
if (result != ECA_NORMAL) {
fprintf(stderr, "Error occured writing data: %s\n", ca_message(result));
fprintf(stderr, ERL_ERROR " occured writing data: %s\n", ca_message(result));
free(sbuf); free(dbuf); free(ebuf);
return 1;
}

View File

@@ -28,6 +28,7 @@
#include <alarm.h>
#include <epicsTime.h>
#include <epicsStdlib.h>
#include <epicsString.h>
#include <cadef.h>
@@ -53,7 +54,7 @@ char fieldSeparator = ' '; /* OFS default is whitespace */
int enumAsNr = 0; /* used for -n option - get DBF_ENUM as number */
int charArrAsStr = 0; /* used for -S option - treat char array as (long) string */
double caTimeout = 1.0; /* wait time default (see -w option) */
double caTimeout = DEFAULT_TIMEOUT; /* wait time default (see -w option) */
capri caPriority = DEFAULT_CA_PRIORITY; /* CA Priority */
#define TIMETEXTLEN 28 /* Length of timestamp text buffer */
@@ -639,3 +640,22 @@ int connect_pvs (pv* pvs, int nPvs)
}
return returncode;
}
/* Set the timeout to EPICS_CLI_TIMEOUT */
void use_ca_timeout_env ( double* timeout)
{
const char* tmoStr; /* contents of environment var */
if ((tmoStr = getenv("EPICS_CLI_TIMEOUT")) != NULL && timeout != NULL)
{
if(epicsScanDouble(tmoStr, timeout) != 1)
{
fprintf(stderr, "'%s' is not a valid timeout value "
"(from 'EPICS_CLI_TIMEOUT' in the environment) - "
"ignored. (use '-h' for help.)\n", tmoStr);
*timeout = DEFAULT_TIMEOUT;
}
}
}

View File

@@ -100,6 +100,7 @@ extern char *dbr2str (const void *value, unsigned type);
extern void print_time_val_sts (pv *pv, unsigned long reqElems);
extern int create_pvs (pv *pvs, int nPvs, caCh *pCB );
extern int connect_pvs (pv *pvs, int nPvs );
extern void use_ca_timeout_env (double* timeout);
/*
* no additions below this endif

View File

@@ -225,6 +225,11 @@ static void asCaTask(void)
if(asCaDebug) printf("asCaTask has cleared all channels\n");
epicsEventSignal(asCaTaskWait);
}
/* ATM never reached, just a placeholder */
cantProceed("Unreachable. Perpetual thread.");
taskwdRemove(0);
}
void asCaStart(void)

View File

@@ -16,12 +16,13 @@
#include "asIocRegister.h"
/* asSetFilename */
static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgString};
static const iocshArg asSetFilenameArg0 = { "ascf",iocshArgStringPath};
static const iocshArg * const asSetFilenameArgs[] = {&asSetFilenameArg0};
static const iocshFuncDef asSetFilenameFuncDef =
{"asSetFilename",1,asSetFilenameArgs,
"Set path+file name of ACF file.\n"
"No immediate effect. Run as asInit() to (re)load.\n"};
"No immediate effect. Run asInit to (re)load.\n"
"Example: asSetFilename /full/path/to/accessSecurityFile\n"};
static void asSetFilenameCallFunc(const iocshArgBuf *args)
{
asSetFilename(args[0].sval);
@@ -33,7 +34,8 @@ static const iocshArg * const asSetSubstitutionsArgs[] = {&asSetSubstitutionsArg
static const iocshFuncDef asSetSubstitutionsFuncDef =
{"asSetSubstitutions",1,asSetSubstitutionsArgs,
"Set subtitutions used when reading ACF file.\n"
"No immediate effect. Run as asInit() to (re)load.\n"};
"No immediate effect. Run asInit to (re)load.\n"
"Example: asSetSubstitutions var1=5,var2=hello\n"};
static void asSetSubstitutionsCallFunc(const iocshArgBuf *args)
{
asSetSubstitutions(args[0].sval);
@@ -59,7 +61,10 @@ static void asdbdumpCallFunc(const iocshArgBuf *args)
static const iocshArg aspuagArg0 = { "uagname",iocshArgString};
static const iocshArg * const aspuagArgs[] = {&aspuagArg0};
static const iocshFuncDef aspuagFuncDef = {"aspuag",1,aspuagArgs,
"Show members of User Access Group.\n"};
"Show members of the User Access Group.\n"
"If no Group is specified then the members\n"
"of all user access groups are displayed.\n"
"Example: aspuag mygroup\n"};
static void aspuagCallFunc(const iocshArgBuf *args)
{
aspuag(args[0].sval);
@@ -69,7 +74,10 @@ static void aspuagCallFunc(const iocshArgBuf *args)
static const iocshArg asphagArg0 = { "hagname",iocshArgString};
static const iocshArg * const asphagArgs[] = {&asphagArg0};
static const iocshFuncDef asphagFuncDef = {"asphag",1,asphagArgs,
"Show members of Host Access Group.\n"};
"Show members of the Host Access Group.\n"
"If no Group is specified then the members\n"
"of all host access groups are displayed\n"
"Example: asphag mygroup\n"};
static void asphagCallFunc(const iocshArgBuf *args)
{
asphag(args[0].sval);
@@ -78,8 +86,12 @@ static void asphagCallFunc(const iocshArgBuf *args)
/* asprules */
static const iocshArg asprulesArg0 = { "asgname",iocshArgString};
static const iocshArg * const asprulesArgs[] = {&asprulesArg0};
static const iocshFuncDef asprulesFuncDef = {"asprules",1,asprulesArgs,
"List rules of an Access Security Group.\n"};
static const iocshFuncDef asprulesFuncDef = {
"asprules",1,asprulesArgs,
"List rules of an Access Security Group.\n"
"If no Group is specified then list the rules for all groups\n"
"Example: asprules mygroup\n"
};
static void asprulesCallFunc(const iocshArgBuf *args)
{
asprules(args[0].sval);
@@ -89,20 +101,28 @@ static void asprulesCallFunc(const iocshArgBuf *args)
static const iocshArg aspmemArg0 = { "asgname",iocshArgString};
static const iocshArg aspmemArg1 = { "clients",iocshArgInt};
static const iocshArg * const aspmemArgs[] = {&aspmemArg0,&aspmemArg1};
static const iocshFuncDef aspmemFuncDef = {"aspmem",2,aspmemArgs,
"List members of Access Security Group.\n"};
static const iocshFuncDef aspmemFuncDef = {
"aspmem",2,aspmemArgs,
"List members of Access Security Group.\n"
"If no Group is specified then print the members for all Groups.\n"
"If clients is (0, 1) then Channel Access clients attached to each member\n"
"(are not, are) shown\n"
"Example: aspmem mygroup 1\n",
};
static void aspmemCallFunc(const iocshArgBuf *args)
{
aspmem(args[0].sval,args[1].ival);
}
/* astac */
static const iocshArg astacArg0 = { "recordname",iocshArgString};
static const iocshArg astacArg0 = { "recordname",iocshArgStringRecord};
static const iocshArg astacArg1 = { "user",iocshArgString};
static const iocshArg astacArg2 = { "host",iocshArgString};
static const iocshArg * const astacArgs[] = {&astacArg0,&astacArg1,&astacArg2};
static const iocshFuncDef astacFuncDef = {"astac",3,astacArgs,
"Test Access Security privlages granted to user+host.\n"};
static const iocshFuncDef astacFuncDef = {
"astac",3,astacArgs,
"Show what read/write permissions the user:host would have when\n"
"accessing a certain PV.\n"};
static void astacCallFunc(const iocshArgBuf *args)
{
astac(args[0].sval,args[1].sval,args[2].sval);
@@ -111,8 +131,14 @@ static void astacCallFunc(const iocshArgBuf *args)
/* ascar */
static const iocshArg ascarArg0 = { "level",iocshArgInt};
static const iocshArg * const ascarArgs[] = {&ascarArg0};
static const iocshFuncDef ascarFuncDef = {"ascar",1,ascarArgs,
"Report status of PVs used in INP*() Access Security rules.\n"};
static const iocshFuncDef ascarFuncDef = {
"ascar",1,ascarArgs,
"Report status of PVs used in INP*() Access Security rules.\n"
"Level 0 - Summary report\n"
" 1 - Summary report plus details on unconnected channels\n"
" 2 - Summary report plus detail report on each channel\n"
"Example: ascar 1\n"
};
static void ascarCallFunc(const iocshArgBuf *args)
{
ascar(args[0].ival);

View File

@@ -21,6 +21,7 @@
#include <ctype.h>
#include "dbDefs.h"
#include "errlog.h"
#include "ellLib.h"
#include "cvtTable.h"
@@ -125,12 +126,12 @@ int main(int argc, char **argv)
}
inFile = fopen(argv[1],"r");
if(!inFile) {
fprintf(stderr,"Error opening %s\n",argv[1]);
fprintf(stderr,ERL_ERROR " opening %s\n",argv[1]);
exit(-1);
}
outFile = fopen(outFilename,"w");
if(!outFile) {
fprintf(stderr,"Error opening %s\n",outFilename);
fprintf(stderr,ERL_ERROR " opening %s\n",outFilename);
exit(-1);
}
while(fgets(inbuf,MAX_LINE_SIZE,inFile)) {

View File

@@ -13,13 +13,15 @@
THESE_RULES := $(IOCDIR)/db/RULES
dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES)
dbCommon.h$(DEP): $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \
$(DBDTORECTYPEH_dep)
@$(RM) $@
@$(DBTORECORDTYPEH) -D -I ../db -I $(COMMON_DIR) -o $(COMMONDEP_TARGET) $< > $@
$(COMMON_DIR)/dbCommonRecord.html: ../db/dbCommon.dbd.pod
$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES)
$(COMMON_DIR)/dbCommon.h: $(COMMON_DIR)/dbCommonRecord.dbd $(THESE_RULES) \
$(DBDTORECTYPEH_dep)
@$(RM) $(notdir $@)
$(DBTORECORDTYPEH) -I ../db -I $(COMMON_DIR) -o $(notdir $@) $<
@$(MV) $(notdir $@) $@

View File

@@ -58,6 +58,7 @@ typedef struct cbQueueSet {
int shutdown; // use atomic
int threadsConfigured;
int threadsRunning;
epicsThreadId *threads;
} cbQueueSet;
static cbQueueSet callbackQueue[NUM_CALLBACK_PRIORITIES];
@@ -242,11 +243,15 @@ void callbackStop(void)
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
cbQueueSet *mySet = &callbackQueue[i];
int j;
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
epicsEventSignal(mySet->semWakeUp);
epicsEventWaitWithTimeout(startStopEvent, 0.1);
}
for(j=0; j<mySet->threadsConfigured; j++) {
epicsThreadMustJoin(mySet->threads[j]);
}
}
}
@@ -266,6 +271,8 @@ void callbackCleanup(void)
mySet->semWakeUp = NULL;
epicsRingPointerDelete(mySet->queue);
mySet->queue = NULL;
free(mySet->threads);
mySet->threads = NULL;
}
epicsTimerQueueRelease(timerQueue);
@@ -297,17 +304,25 @@ void callbackInit(void)
cantProceed("epicsRingPointerLockedCreate failed for %s\n",
threadNamePrefix[i]);
callbackQueue[i].queueOverflow = FALSE;
if (callbackQueue[i].threadsConfigured == 0)
callbackQueue[i].threadsConfigured = callbackThreadsDefault;
callbackQueue[i].threads = callocMustSucceed(callbackQueue[i].threadsConfigured,
sizeof(*callbackQueue[i].threads),
"callbackInit");
for (j = 0; j < callbackQueue[i].threadsConfigured; j++) {
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
opts.joinable = 1;
opts.priority = threadPriority[i];
opts.stackSize = epicsThreadStackBig;
if (callbackQueue[i].threadsConfigured > 1 )
sprintf(threadName, "%s-%d", threadNamePrefix[i], j);
else
strcpy(threadName, threadNamePrefix[i]);
tid = epicsThreadCreate(threadName, threadPriority[i],
epicsThreadGetStackSize(epicsThreadStackBig),
(EPICSTHREADFUNC)callbackTask, &priorityValue[i]);
callbackQueue[i].threads[j] = tid = epicsThreadCreateOpt(threadName,
(EPICSTHREADFUNC)callbackTask, &priorityValue[i], &opts);
if (tid == 0) {
cantProceed("Failed to spawn callback thread %s\n", threadName);
} else {
@@ -329,6 +344,10 @@ int callbackRequest(epicsCallback *pcallback)
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback was NULL\n");
return S_db_notInit;
}
if (!pcallback->callback) {
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " pcallback->callback was NULL\n");
return S_db_notInit;
}
priority = pcallback->priority;
if (priority < 0 || priority >= NUM_CALLBACK_PRIORITIES) {
epicsInterruptContextMessage("callbackRequest: " ERL_ERROR " Bad priority\n");

View File

@@ -73,7 +73,7 @@ epicsExportAddress(int, dbAccessDebugPUTF);
DB_LOAD_RECORDS_HOOK_ROUTINE dbLoadRecordsHook = NULL;
static short mapDBFToDBR[DBF_NTYPES] = {
static const short mapDBFToDBR[DBF_NTYPES] = {
/* DBF_STRING => */ DBR_STRING,
/* DBF_CHAR => */ DBR_CHAR,
/* DBF_UCHAR => */ DBR_UCHAR,
@@ -798,18 +798,13 @@ int dbLoadRecords(const char* file, const char* subs)
return -1;
}
status = dbReadDatabase(&pdbbase, file, 0, subs);
switch(status)
{
case 0:
if(status==0) {
if(dbLoadRecordsHook)
dbLoadRecordsHook(file, subs);
break;
case -2:
errlogPrintf("dbLoadRecords: failed to load '%s'\n"
" Records cannot be loaded after iocInit!\n", file);
break;
default:
errlogPrintf("dbLoadRecords: failed to load '%s'\n", file);
} else {
fprintf(stderr, ERL_ERROR " failed to load '%s'\n", file);
if(status==-2)
fprintf(stderr, " Records cannot be loaded after iocInit!\n");
}
return status;
}

View File

@@ -220,6 +220,8 @@ DBCORE_API long dbNameToAddr(const char *pname, struct dbAddr *paddr);
/** Initialize DBADDR from a dbEntry
* Also handles SPC_DBADDR processing. This is really an internal
* routine for use by dbNameToAddr() and dbChannelCreate().
*
* \since 7.0.2.1
*/
DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry,
struct dbAddr *paddr);
@@ -227,6 +229,8 @@ DBCORE_API long dbEntryToAddr(const struct dbEntry *pdbentry,
/** Initialize DBENTRY from a valid dbAddr*
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias().
*
* \since 3.16.1
*/
DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr,
struct dbEntry *pdbentry);
@@ -234,6 +238,8 @@ DBCORE_API void dbInitEntryFromAddr(struct dbAddr *paddr,
/** Initialize DBENTRY from a valid record (dbCommon*)
* Constant time equivalent of dbInitEntry() then dbFindRecord(),
* and finally dbFollowAlias() when no field is specified.
*
* \since 3.16.1
*/
DBCORE_API void dbInitEntryFromRecord(struct dbCommon *prec,
struct dbEntry *pdbentry);

View File

@@ -575,15 +575,10 @@ long dbChannelOpen(dbChannel *chan)
}
/* Set up type probe */
probe.type = dbfl_type_val;
probe.ctx = dbfl_context_read;
memset(&probe, 0, sizeof(probe));
probe.field_type = dbChannelExportType(chan);
probe.no_elements = dbChannelElements(chan);
probe.field_size = dbChannelFieldSize(chan);
probe.sevr = NO_ALARM;
probe.stat = NO_ALARM;
probe.time.secPastEpoch = 0;
probe.time.nsec = 0;
p = probe;

View File

@@ -41,20 +41,35 @@ extern "C" {
/**
* event subscription
*/
typedef struct evSubscrip {
struct evSubscrip;
typedef struct evSubscrip evSubscrip;
#ifdef EPICS_PRIVATE_API
struct evSubscrip {
ELLNODE node;
struct dbChannel * chan;
/* user_sub==NULL used to indicate db_cancel_event() */
EVENTFUNC * user_sub;
void * user_arg;
/* associated queue, may be shared with other evSubscrip */
struct event_que * ev_que;
/* NULL if !npend. if npend!=0, pointer to last event added to event_que::valque */
db_field_log ** pLastLog;
unsigned long npend; /**< n times this event is on the queue */
unsigned long nreplace; /**< n times replacing event on the queue */
/* n times this event is on the queue */
unsigned long npend;
/* n times replacing event on the queue */
unsigned long nreplace;
/* DBE mask */
unsigned char select;
/* if set, subscription will yield dbfl_type_val */
char useValque;
/* event_task is handling this subscription */
char callBackInProgress;
/* this node added to dbCommon::mlis */
char enabled;
} evSubscrip;
};
#endif
typedef struct chFilter chFilter;

View File

@@ -91,20 +91,25 @@ For example these rates are all valid:
1 second
2 Hertz
The B<PINI> field specifies record processing at initialization. If it is set
to YES during database configuration, the record is processed once at IOC
initialization (before the normal scan tasks are started).
The B<PINI> field specifies record processing at initialization. It can have the
values NO, YES, RUN, RUNNING, PAUSE, and PAUSED. If it is set to YES during
database configuration, the record is processed once at IOC initialization
(before the normal scan tasks are started). For the other values see
L<menuPini.dbd|menuPini> for more details.
The B<PHAS> field orders the records within a specific SCAN group. This is not
meaningful for passive records. All records of a specified phase are processed
before those with higher phase number. It is generally better practice to use
linked passive records to enforce the order of processing rather than a phase
number.
The B<PHAS> field orders the records processed within a specific SCAN group or
PINI processing phase. All records of a specified phase are processed before
those with higher phase number. It is generally better practice to use linked
passive records to enforce the order of processing rather than a phase number.
If the PINI field is set to NO, the PHAS field is not meaningful for passive
records.
The B<EVNT> field specifies an event number. This event number is used if the
SCAN field is set to C<Event>. All records with scan type C<Event> and the
same EVNT value will be processed when a call to post_event for EVNT is made.
The call to post_event is: post_event(short event_number).
The B<EVNT> field specifies an event name or number. This event identifier is
used if the SCAN field is set to C<Event>. All records with scan type C<Event>
and the same EVNT value will be processed when that event is signalled by other
software running in the IOC, either by calling C<post_event(int event)> for
numeric events, or by calling C<postEvent()> with an event handle previously
looked up using C<eventNameToHandle()> for named events.
The B<PRIO> field specifies the scheduling priority for processing records
with SCAN=C<I/O Event> and asynchronous record completion tasks.

View File

@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
=head1 Fields Common to Input Record Types
=title Fields Common to Input Record Types
This section describes fields that are found in many input record types.
These fields usually have the same meaning whenever they are used.
@@ -206,3 +206,5 @@ If SIMM is not YES, NO or RAW, a SOFT alarm with a severity of INVALID is
raised, and return status is set to -1.
=back
=cut

View File

@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
=head1 Fields Common to Output Record Types
=title Fields Common to Output Record Types
This section describes fields that are found in many output record types.
These fields usually have the same meaning whenever they are used.
@@ -237,3 +237,5 @@ If SIMM is not YES or NO, a SOFT alarm with a severity of INVALID is
raised, and return status is set to -1.
=back
=cut

View File

@@ -3,7 +3,7 @@
# in file LICENSE that is included with this distribution.
#*************************************************************************
=head1 Fields Common to All Record Types
=title Fields Common to All Record Types
This section contains a description of the fields that are common to all record
types. These fields are defined in dbCommon.dbd.

View File

@@ -154,7 +154,7 @@ static long dbConstLoadScalar(struct link *plink, short dbrType, void *pbuffer)
const char *pstr = plink->value.constantStr;
size_t len;
if (!pstr)
if (!pstr || !pstr[0])
return S_db_badField;
len = strlen(pstr);
@@ -181,7 +181,7 @@ static long dbConstLoadLS(struct link *plink, char *pbuffer, epicsUInt32 size,
const char *pstr = plink->value.constantStr;
long status;
if (!pstr)
if (!pstr || !pstr[0])
return S_db_badField;
status = dbLSConvertJSON(pstr, pbuffer, size, plen);
@@ -197,7 +197,7 @@ static long dbConstLoadArray(struct link *plink, short dbrType, void *pbuffer,
const char *pstr = plink->value.constantStr;
long status;
if (!pstr)
if (!pstr || !pstr[0])
return S_db_badField;
/* Choice values must be numeric */

View File

@@ -18,6 +18,7 @@
* Ralph Lange <Ralph.Lange@bessy.de>
*/
#define EPICS_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -75,22 +76,23 @@ struct event_que {
unsigned short getix;
unsigned short quota; /* the number of assigned entries*/
unsigned short nDuplicates; /* N events duplicated on this q */
unsigned short nCanceled; /* the number of canceled entries */
unsigned possibleStall;
};
struct event_user {
struct event_que firstque; /* the first event que */
ELLLIST waiters; /* event_waiter::node */
epicsMutexId lock;
epicsEventId ppendsem; /* Wait while empty */
epicsEventId pflush_sem; /* wait for flush */
epicsEventId pexitsem; /* wait for event task to join */
EXTRALABORFUNC *extralabor_sub;/* off load to event task */
void *extralabor_arg;/* parameter to above */
epicsThreadId taskid; /* event handler task id */
struct evSubscrip *pSuicideEvent; /* event that is deleting itself */
epicsUInt32 pflush_seq; /* worker cycle count for synchronization */
unsigned queovr; /* event que overflow count */
unsigned char pendexit; /* exit pend task */
unsigned char extra_labor; /* if set call extra labor func */
@@ -100,6 +102,11 @@ struct event_user {
epicsThreadId init_func_arg;
};
typedef struct {
ELLNODE node; /* event_user::waiters */
epicsEventId wake;
} event_waiter;
/*
* Reliable intertask communication requires copying the current value of the
* channel for later queuing so 3 stepper motor steps of 10 each do not turn
@@ -121,10 +128,9 @@ static void *dbevFieldLogFreeList;
static char *EVENT_PEND_NAME = "eventTask";
static struct evSubscrip canceledEvent;
static epicsMutexId stopSync;
/* unused space in queue (EVENTQUESIZE when empty) */
static unsigned short ringSpace ( const struct event_que *pevq )
{
if ( pevq->evque[pevq->putix] == EVENTQEMPTY ) {
@@ -138,17 +144,11 @@ static unsigned short ringSpace ( const struct event_que *pevq )
return 0;
}
/*
* db_event_list ()
*/
int db_event_list ( const char *pname, unsigned level )
{
return dbel ( pname, level );
}
/*
* dbel ()
*/
int dbel ( const char *pname, unsigned level )
{
DBADDR addr;
@@ -216,7 +216,6 @@ int dbel ( const char *pname, unsigned level )
if ( level > 2 ) {
unsigned nDuplicates;
unsigned nCanceled;
if ( pevent->nreplace ) {
printf (", discarded by replacement=%ld", pevent->nreplace);
}
@@ -225,14 +224,10 @@ int dbel ( const char *pname, unsigned level )
}
LOCKEVQUE(pevent->ev_que);
nDuplicates = pevent->ev_que->nDuplicates;
nCanceled = pevent->ev_que->nCanceled;
UNLOCKEVQUE(pevent->ev_que);
if ( nDuplicates ) {
printf (", duplicate count =%u\n", nDuplicates );
}
if ( nCanceled ) {
printf (", canceled count =%u\n", nCanceled );
}
}
if ( level > 3 ) {
@@ -317,9 +312,6 @@ dbEventCtx db_init_events (void)
evUser->ppendsem = epicsEventCreate(epicsEventEmpty);
if (!evUser->ppendsem)
goto fail;
evUser->pflush_sem = epicsEventCreate(epicsEventEmpty);
if (!evUser->pflush_sem)
goto fail;
evUser->lock = epicsMutexCreate();
if (!evUser->lock)
goto fail;
@@ -329,7 +321,6 @@ dbEventCtx db_init_events (void)
evUser->flowCtrlMode = FALSE;
evUser->extraLaborBusy = FALSE;
evUser->pSuicideEvent = NULL;
return (dbEventCtx) evUser;
fail:
if(evUser->lock)
@@ -338,8 +329,6 @@ fail:
epicsMutexDestroy (evUser->firstque.writelock);
if(evUser->ppendsem)
epicsEventDestroy (evUser->ppendsem);
if(evUser->pflush_sem)
epicsEventDestroy (evUser->pflush_sem);
if(evUser->pexitsem)
epicsEventDestroy (evUser->pexitsem);
freeListFree(dbevEventUserFreeList,evUser);
@@ -403,7 +392,6 @@ void db_close_events (dbEventCtx ctx)
epicsEventDestroy(evUser->pexitsem);
epicsEventDestroy(evUser->ppendsem);
epicsEventDestroy(evUser->pflush_sem);
epicsMutexDestroy(evUser->lock);
epicsMutexUnlock (stopSync);
@@ -460,8 +448,7 @@ dbEventSubscription db_add_event (
while ( TRUE ) {
int success = 0;
LOCKEVQUE ( ev_que );
success = ( ev_que->quota + ev_que->nCanceled <
EVENTQUESIZE - EVENTENTRIES );
success = ( ev_que->quota < EVENTQUESIZE - EVENTENTRIES );
if ( success ) {
ev_que->quota += EVENTENTRIES;
}
@@ -578,62 +565,62 @@ static void event_remove ( struct event_que *ev_que,
void db_cancel_event (dbEventSubscription event)
{
struct evSubscrip * const pevent = (struct evSubscrip *) event;
unsigned short getix;
struct event_que *que = pevent->ev_que;
char sync = 0;
db_event_disable ( event );
/*
* flag the event as canceled by NULLing out the callback handler
*
* make certain that the event isn't being accessed while
* its call back changes
*/
LOCKEVQUE (pevent->ev_que);
LOCKEVQUE (que);
pevent->user_sub = NULL;
pevent->user_sub = NULL; /* callback pointer doubles as canceled flag */
/*
* purge this event from the queue
*
* Its better to take this approach rather than waiting
* for the event thread to finish removing this event
* from the queue because the event thread will not
* process if we are in flow control mode. Since blocking
* here will block CA's TCP input queue then a dead lock
* would be possible.
*/
for ( getix = pevent->ev_que->getix;
pevent->ev_que->evque[getix] != EVENTQEMPTY; ) {
if ( pevent->ev_que->evque[getix] == pevent ) {
assert ( pevent->ev_que->nCanceled < USHRT_MAX );
pevent->ev_que->nCanceled++;
event_remove ( pevent->ev_que, getix, &canceledEvent );
}
getix = RNGINC ( getix );
if ( getix == pevent->ev_que->getix ) {
break;
}
}
assert ( pevent->npend == 0u );
if(pevent->callBackInProgress) {
/* this event callback is pending or in-progress in event_task. */
if(pevent->ev_que->evUser->taskid != epicsThreadGetIdSelf())
sync = 1; /* concurrent to event_task, so wait */
if ( pevent->ev_que->evUser->taskid == epicsThreadGetIdSelf() ) {
pevent->ev_que->evUser->pSuicideEvent = pevent;
}
else {
while ( pevent->callBackInProgress ) {
UNLOCKEVQUE (pevent->ev_que);
epicsEventMustWait ( pevent->ev_que->evUser->pflush_sem );
LOCKEVQUE (pevent->ev_que);
}
} else if(pevent->npend) {
/* some (now defunct) events in the queue, defer free() to event_task */
} else {
/* no other references, cleanup now */
pevent->ev_que->quota -= EVENTENTRIES;
freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
pevent->ev_que->quota -= EVENTENTRIES;
UNLOCKEVQUE (que);
UNLOCKEVQUE (pevent->ev_que);
if(sync) {
/* cycle through worker */
struct event_user *evUser = que->evUser;
epicsUInt32 curSeq;
event_waiter wait;
wait.wake = epicsEventCreate(epicsEventEmpty); /* may fail */
freeListFree ( dbevEventSubscriptionFreeList, pevent );
epicsMutexMustLock ( evUser->lock );
ellAdd(&evUser->waiters, &wait.node);
/* grab current cycle counter, then wait for it to change */
curSeq = evUser->pflush_seq;
do {
epicsMutexUnlock( evUser->lock );
/* ensure worker will cycle at least once */
epicsEventMustTrigger(evUser->ppendsem);
return;
if(wait.wake) {
epicsEventMustWait(wait.wake);
} else {
epicsThreadSleep(0.01); /* ick. but better than cantProceed() */
}
epicsMutexMustLock ( evUser->lock );
} while(curSeq == evUser->pflush_seq);
ellDelete(&evUser->waiters, &wait.node);
/* destroy under lock to ensure epicsEventMustTrigger() has returned */
if(wait.wake)
epicsEventDestroy(wait.wake);
epicsMutexUnlock( evUser->lock );
}
}
/*
@@ -933,9 +920,7 @@ void db_post_single_event (dbEventSubscription event)
*/
static int event_read ( struct event_que *ev_que )
{
db_field_log *pfl;
void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
int eventsRemaining, db_field_log *pfl );
int notifiedRemaining = 0;
/*
* evUser ring buffer must be locked for the multiple
@@ -955,19 +940,8 @@ static int event_read ( struct event_que *ev_que )
while ( ev_que->evque[ev_que->getix] != EVENTQEMPTY ) {
struct evSubscrip *pevent = ev_que->evque[ev_que->getix];
pfl = ev_que->valque[ev_que->getix];
if ( pevent == &canceledEvent ) {
ev_que->evque[ev_que->getix] = EVENTQEMPTY;
if (ev_que->valque[ev_que->getix]) {
db_delete_field_log(ev_que->valque[ev_que->getix]);
ev_que->valque[ev_que->getix] = NULL;
}
ev_que->getix = RNGINC ( ev_que->getix );
assert ( ev_que->nCanceled > 0 );
ev_que->nCanceled--;
continue;
}
int eventsRemaining;
db_field_log *pfl = ev_que->valque[ev_que->getix];
/*
* Simple type values queued up for reliable interprocess
@@ -977,12 +951,7 @@ static int event_read ( struct event_que *ev_que )
event_remove ( ev_que, ev_que->getix, EVENTQEMPTY );
ev_que->getix = RNGINC ( ev_que->getix );
/*
* create a local copy of the call back parameters while
* we still have the lock
*/
user_sub = pevent->user_sub;
eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY;
/*
* Next event pointer can be used by event tasks to determine
@@ -994,14 +963,12 @@ static int event_read ( struct event_que *ev_que )
* record lock, and it is calling db_post_events() waiting
* for the event queue lock (which this thread now has).
*/
if ( user_sub ) {
/*
* This provides a way to test to see if an event is in use
* despite the fact that the event queue does not point to
* it.
*/
if ( pevent->user_sub ) {
EVENTFUNC* user_sub = pevent->user_sub;
pevent->callBackInProgress = TRUE;
UNLOCKEVQUE (ev_que);
/* Run post-event-queue filter chain */
if (ellCount(&pevent->chan->post_chain)) {
pfl = dbChannelRunPostChain(pevent->chan, pfl);
@@ -1009,41 +976,32 @@ static int event_read ( struct event_que *ev_que )
if (pfl) {
/* Issue user callback */
( *user_sub ) ( pevent->user_arg, pevent->chan,
ev_que->evque[ev_que->getix] != EVENTQEMPTY, pfl );
eventsRemaining, pfl );
notifiedRemaining = eventsRemaining;
}
LOCKEVQUE (ev_que);
/*
* check to see if this event has been canceled each
* time that the callBackInProgress flag is set to false
* while we have the event queue lock, and post the flush
* complete sem if there are no longer any events on the
* queue
*/
if ( ev_que->evUser->pSuicideEvent == pevent ) {
ev_que->evUser->pSuicideEvent = NULL;
}
else {
if ( pevent->user_sub==NULL && pevent->npend==0u ) {
pevent->callBackInProgress = FALSE;
epicsEventSignal ( ev_que->evUser->pflush_sem );
}
else {
pevent->callBackInProgress = FALSE;
}
}
pevent->callBackInProgress = FALSE;
}
/* callback may have called db_cancel_event(), so must check user_sub again */
if(!pevent->user_sub && !pevent->npend) {
pevent->ev_que->quota -= EVENTENTRIES;
freeListFree ( dbevEventSubscriptionFreeList, pevent );
}
db_delete_field_log(pfl);
}
if(notifiedRemaining && !ev_que->possibleStall) {
ev_que->possibleStall = 1;
errlogPrintf(ERL_WARNING " dbEvent possible queue stall\n");
}
UNLOCKEVQUE (ev_que);
return DB_EVENT_OK;
}
/*
* EVENT_TASK()
*/
static void event_task (void *pParm)
{
struct event_user * const evUser = (struct event_user *) pParm;
@@ -1084,13 +1042,25 @@ static void event_task (void *pParm)
}
evUser->extraLaborBusy = FALSE;
for ( ev_que = &evUser->firstque; ev_que;
ev_que = ev_que->nextque ) {
for ( ev_que = &evUser->firstque; ev_que; ev_que = ev_que->nextque ) {
/* unlock during iteration is safe as event_que will not be free'd */
epicsMutexUnlock ( evUser->lock );
event_read (ev_que);
epicsMutexMustLock ( evUser->lock );
}
pendexit = evUser->pendexit;
evUser->pflush_seq++;
if(ellCount(&evUser->waiters)) {
/* hold lock throughout to avoid race between event trigger and destroy */
ELLNODE *cur;
for(cur = ellFirst(&evUser->waiters); cur; cur = ellNext(cur)) {
event_waiter *w = CONTAINER(cur, event_waiter, node);
if(w->wake)
epicsEventMustTrigger(w->wake);
}
}
epicsMutexUnlock ( evUser->lock );
} while( ! pendexit );

View File

@@ -14,7 +14,6 @@
*/
#include <stddef.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <float.h>
@@ -24,6 +23,7 @@
#include "dbDefs.h"
#include "epicsConvert.h"
#include "epicsStdlib.h"
#include "epicsStdio.h"
#include "errlog.h"
#include "errMdef.h"
@@ -1335,24 +1335,26 @@ static long cvt_menu_st(
epicsEnum16 *from,
char *to,
const dbAddr *paddr)
{
dbFldDes *pdbFldDes;
dbMenu *pdbMenu;
char **papChoiceValue;
char *pchoice;
{
dbFldDes *pdbFldDes;
dbMenu *pdbMenu;
if(! paddr
|| !(pdbFldDes = paddr->pfldDes)
|| !(pdbMenu = (dbMenu *)pdbFldDes->ftPvt)
|| *from>=pdbMenu->nChoice
|| !(papChoiceValue = pdbMenu->papChoiceValue)
|| !(pchoice=papChoiceValue[*from])) {
recGblDbaddrError(S_db_badChoice,paddr,"dbFastLinkConv(cvt_menu_st)");
return(S_db_badChoice);
if (!paddr ||
!(pdbFldDes = paddr->pfldDes) ||
!(pdbMenu = (dbMenu *)pdbFldDes->ftPvt)) {
recGblDbaddrError(S_db_badChoice, paddr, "dbFastLinkConv(cvt_menu_st)");
return S_db_badChoice;
}
strncpy(to,pchoice,MAX_STRING_SIZE);
return(0);
}
if (*from < pdbMenu->nChoice) {
strncpy(to, pdbMenu->papChoiceValue[*from], MAX_STRING_SIZE);
}
else {
/* Convert out-of-range values to numeric strings */
epicsSnprintf(to, MAX_STRING_SIZE, "%u", *from);
}
return 0;
}
/* Get Device to String */

View File

@@ -8,10 +8,13 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#define EPICS_PRIVATE_API
#include "iocsh.h"
#include "callback.h"
#include "dbAccess.h"
#include "dbStaticPvt.h"
#include "dbBkpt.h"
#include "dbCaTest.h"
#include "dbEvent.h"
@@ -28,8 +31,8 @@
DBCORE_API extern int callbackParallelThreadsDefault;
/* dbLoadDatabase */
static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgString};
static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgString};
static const iocshArg dbLoadDatabaseArg0 = { "file name",iocshArgStringPath};
static const iocshArg dbLoadDatabaseArg1 = { "path",iocshArgStringPath};
static const iocshArg dbLoadDatabaseArg2 = { "substitutions",iocshArgString};
static const iocshArg * const dbLoadDatabaseArgs[3] =
{
@@ -49,7 +52,7 @@ static void dbLoadDatabaseCallFunc(const iocshArgBuf *args)
}
/* dbLoadRecords */
static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgString};
static const iocshArg dbLoadRecordsArg0 = { "file name",iocshArgStringPath};
static const iocshArg dbLoadRecordsArg1 = { "substitutions",iocshArgString};
static const iocshArg * const dbLoadRecordsArgs[2] = {&dbLoadRecordsArg0,&dbLoadRecordsArg1};
static const iocshFuncDef dbLoadRecordsFuncDef = {
@@ -66,104 +69,124 @@ static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
}
/* dbb */
static const iocshArg dbbArg0 = { "record name",iocshArgString};
static const iocshArg dbbArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbbArgs[1] = {&dbbArg0};
static const iocshFuncDef dbbFuncDef = {"dbb",1,dbbArgs,
"Add breakpoint to a lock set.\n"};
"Set Breakpoint on a record\n"
"This command spawns one breakpoint continuation task per lockset,"
" in which further record execution is run\n"};
static void dbbCallFunc(const iocshArgBuf *args) { dbb(args[0].sval);}
/* dbd */
static const iocshArg dbdArg0 = { "record name",iocshArgString};
static const iocshArg dbdArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbdArgs[1] = {&dbdArg0};
static const iocshFuncDef dbdFuncDef = {"dbd",1,dbdArgs,
"Remove breakpoint from a record.\n"};
static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
/* dbc */
static const iocshArg dbcArg0 = { "record name",iocshArgString};
static const iocshArg dbcArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbcArgs[1] = {&dbcArg0};
static const iocshFuncDef dbcFuncDef = {"dbc",1,dbcArgs,
"Continue processing in a lock set.\n"};
"Continue processing in a lockset until next breakpoint is found.\n"};
static void dbcCallFunc(const iocshArgBuf *args) { dbc(args[0].sval);}
/* dbs */
static const iocshArg dbsArg0 = { "record name",iocshArgString};
static const iocshArg dbsArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbsArgs[1] = {&dbsArg0};
static const iocshFuncDef dbsFuncDef = {"dbs",1,dbsArgs,
"Step through record processing.\n"};
"Step through record processing within a lockset.\n"
"If called without an argument, automatically steps with the last breakpoint.\n"};
static void dbsCallFunc(const iocshArgBuf *args) { dbs(args[0].sval);}
/* dbstat */
static const iocshFuncDef dbstatFuncDef = {"dbstat",0,0,
"print list of stopped records, and breakpoints set in locksets.\n"};
"Print list of suspended records, and breakpoints set in locksets.\n"};
static void dbstatCallFunc(const iocshArgBuf *args) { dbstat();}
/* dbp */
static const iocshArg dbpArg0 = { "record name",iocshArgString};
static const iocshArg dbpArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbpArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbpArgs[2] = {&dbpArg0,&dbpArg1};
static const iocshFuncDef dbpFuncDef = {"dbp",2,dbpArgs,
"print stopped record.\n"};
static const iocshFuncDef dbpFuncDef = {
"dbp",2,dbpArgs,
"Print Fields of a currently suspended record by a breakpoint.\n"
"interest level 0 - Fields of interest to an Application developer and\n"
" that can be changed as a result of record processing.\n"
" 1 - Fields of interest to an Application developer and\n"
" that do not change during record processing.\n"
" 2 - Fields of major interest to a System developer.\n"
" 3 - Fields of minor interest to a System developer.\n"
" 4 - Internal record fields.\n"};
static void dbpCallFunc(const iocshArgBuf *args)
{ dbp(args[0].sval,args[1].ival);}
/* dbap */
static const iocshArg dbapArg0 = { "record name",iocshArgString};
static const iocshArg dbapArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbapArgs[1] = {&dbapArg0};
static const iocshFuncDef dbapFuncDef = {"dbap",1,dbapArgs,
"toggle printing after processing a certain record.\n"};
"Auto Print.\n"
"Toggle automatic printing after processing a record that has a breakpoint.\n"};
static void dbapCallFunc(const iocshArgBuf *args) { dbap(args[0].sval);}
/* dbsr */
static const iocshArg dbsrArg0 = { "interest level",iocshArgInt};
static const iocshArg * const dbsrArgs[1] = {&dbsrArg0};
static const iocshFuncDef dbsrFuncDef = {"dbsr",1,dbsrArgs,
"Database Server Report.\n"};
"Database Server Report.\n"
"Print current status of server and number of connected clients.\n"
"Level 0 prints summary information. Higher levels print more.\n"};
static void dbsrCallFunc(const iocshArgBuf *args) { dbsr(args[0].ival);}
/* dbcar */
static const iocshArg dbcarArg0 = { "record name",iocshArgString};
static const iocshArg dbcarArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbcarArg1 = { "level",iocshArgInt};
static const iocshArg * const dbcarArgs[2] = {&dbcarArg0,&dbcarArg1};
static const iocshFuncDef dbcarFuncDef = {"dbcar",2,dbcarArgs,
"Database Channel Access Report.\n"
"Shows status of Channel Access links (CA_LINK).\n"
"interest level 0 - Shows statistics for all links.\n"
" 1 - Shows info. of only disconnected links.\n"
" 2 - Shows info. for all links.\n"};
" level 0 - Shows statistics for all links.\n"
" 1 - Shows info. of only disconnected links.\n"
" 2 - Shows info. for all links.\n"};
static void dbcarCallFunc(const iocshArgBuf *args)
{
dbcar(args[0].sval,args[1].ival);
}
/* dbjlr */
static const iocshArg dbjlrArg0 = { "record name",iocshArgString};
static const iocshArg dbjlrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbjlrArg1 = { "level",iocshArgInt};
static const iocshArg * const dbjlrArgs[2] = {&dbjlrArg0,&dbjlrArg1};
static const iocshFuncDef dbjlrFuncDef = {"dbjlr",2,dbjlrArgs,
"Database JSON link Report.\n"};
"Database JSON link Report.\n"
"List all JSON links in a record. If no record is specified, print for all\n"};
static void dbjlrCallFunc(const iocshArgBuf *args)
{
dbjlr(args[0].sval,args[1].ival);
}
/* dbel */
static const iocshArg dbelArg0 = { "record name",iocshArgString};
static const iocshArg dbelArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbelArg1 = { "level",iocshArgInt};
static const iocshArg * const dbelArgs[2] = {&dbelArg0,&dbelArg1};
static const iocshFuncDef dbelFuncDef = {"dbel",2,dbelArgs,
"Database event list.\n"
"Show information on dbEvent subscriptions.\n"};
"Show information on dbEvent subscriptions.\n"
"Higher level shows more information (0 - 4)\n"
"Example: dbel aitest 2\n"};
static void dbelCallFunc(const iocshArgBuf *args)
{
dbel(args[0].sval, args[1].ival);
}
/* dba */
static const iocshArg dbaArg0 = { "record name",iocshArgString};
static const iocshArg dbaArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbaArgs[1] = {&dbaArg0};
static const iocshFuncDef dbaFuncDef = {"dba",1,dbaArgs,
"dbAddr info.\n"};
"Database Address.\n"
"Print information in the dbAddr structure for a specific field.\n"
"If no field is specified, VAL is assumed.\n\n"
"Example: dba(\"aitest.HIGH\")\n"};
static void dbaCallFunc(const iocshArgBuf *args) { dba(args[0].sval);}
/* dbl */
@@ -173,7 +196,12 @@ static const iocshArg * const dblArgs[] = {&dblArg0,&dblArg1};
static const iocshFuncDef dblFuncDef = {"dbl",2,dblArgs,
"Database list.\n"
"List record/field names.\n"
"With no arguments, lists all record names.\n"};
"With no arguments, lists all record names.\n"
"If record type is given, then only the names of records maching the type are printed\n"
"If a field list is given, then their values are also printed\n\n"
"Example: dbl(\"\")\n"
" dbl(\"ai\")\n"
" dbl(\"ai\",\"HIGH LOW VAL PREC\")\n"};
static void dblCallFunc(const iocshArgBuf *args)
{
dbl(args[0].sval,args[1].sval);
@@ -183,79 +211,102 @@ static void dblCallFunc(const iocshArgBuf *args)
static const iocshArg dbnrArg0 = { "verbose",iocshArgInt};
static const iocshArg * const dbnrArgs[1] = {&dbnrArg0};
static const iocshFuncDef dbnrFuncDef = {"dbnr",1,dbnrArgs,
"List stats on record alias()s.\n"};
"List number of records and aliases by type.\n"
"If verbose, list all record types regardless of being instanced\n"};
static void dbnrCallFunc(const iocshArgBuf *args) { dbnr(args[0].ival);}
/* dbli */
static const iocshArg dbliArg0 = { "pattern",iocshArgString};
static const iocshArg * const dbliArgs[1] = {&dbliArg0};
static const iocshFuncDef dbliFuncDef = {"dbli",1,dbliArgs,
"List info() tags with names matching pattern.\n"};
"List info() tags with names matching pattern.\n\n"
"Example: dbli(\"autosave*\")\n"};
static void dbliCallFunc(const iocshArgBuf *args) { dbli(args[0].sval);}
/* dbla */
static const iocshArg dblaArg0 = { "pattern",iocshArgString};
static const iocshArg dblaArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dblaArgs[1] = {&dblaArg0};
static const iocshFuncDef dblaFuncDef = {"dbla",1,dblaArgs,
"List record alias()s by alias name pattern.\n"};
"List record alias()s by alias name pattern.\n\n"
"Example: dbla(\"alia*\")\n"};
static void dblaCallFunc(const iocshArgBuf *args) { dbla(args[0].sval);}
/* dbgrep */
static const iocshArg dbgrepArg0 = { "pattern",iocshArgString};
static const iocshArg dbgrepArg0 = { "pattern",iocshArgStringRecord};
static const iocshArg * const dbgrepArgs[1] = {&dbgrepArg0};
static const iocshFuncDef dbgrepFuncDef = {"dbgrep",1,dbgrepArgs,
"List record names matching pattern.\n"};
"List record names matching pattern.\n"
"The pattern can contain any characters that are legal in record names as well as:\n"
" - \"?\", which matches 0 or one characters.\n"
" - \"*\", which matches 0 or more characters.\n\n"
"Example: dbgrep(\"*gpibAi*\")\n"};
static void dbgrepCallFunc(const iocshArgBuf *args) { dbgrep(args[0].sval);}
/* dbgf */
static const iocshArg dbgfArg0 = { "record name",iocshArgString};
static const iocshArg dbgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbgfArgs[1] = {&dbgfArg0};
static const iocshFuncDef dbgfFuncDef = {"dbgf",1,dbgfArgs,
"Database Get Field.\n"
"Print current value of record field.\n"};
"Print current value of record field.\n"
"If no field name is specified, VAL is assumed.\n\n"
"Example: dbgf(\"aitest.VAL\")\n"};
static void dbgfCallFunc(const iocshArgBuf *args) { dbgf(args[0].sval);}
/* dbpf */
static const iocshArg dbpfArg0 = { "record name",iocshArgString};
static const iocshArg dbpfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbpfArgs[2] = {&dbpfArg0,&dbpfArg1};
static const iocshFuncDef dbpfFuncDef = {"dbpf",2,dbpfArgs,
"Database Put Field.\n"
"Change value of record field.\n"};
"Change value of record field and read it back with dbgf.\n"
"If no field is specified, VAL is assumed\n"};
static void dbpfCallFunc(const iocshArgBuf *args)
{ dbpf(args[0].sval,args[1].sval);}
/* dbpr */
static const iocshArg dbprArg0 = { "record name",iocshArgString};
static const iocshArg dbprArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbprArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dbprArgs[2] = {&dbprArg0,&dbprArg1};
static const iocshFuncDef dbprFuncDef = {"dbpr",2,dbprArgs,
"Database Print Record.\n"
"Print values of record fields.\n"};
static const iocshFuncDef dbprFuncDef = {
"dbpr",2,dbprArgs,
"Database Print Record.\n"
"Print values of record fields for given interest level.\n"
"interest level 0 - Fields that can be changed as a result of record processing.\n"
" 1 - Fields that do not change during record processing.\n"
" 2 - Fields of major interest to a System developer.\n"
" 3 - Fields of minor interest to a System developer.\n"
" 4 - Internal record fields.\n\n"
"Example: dbpr aitest 3\n"
};
static void dbprCallFunc(const iocshArgBuf *args)
{ dbpr(args[0].sval,args[1].ival);}
/* dbtr */
static const iocshArg dbtrArg0 = { "record name",iocshArgString};
static const iocshArg dbtrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtrArgs[1] = {&dbtrArg0};
static const iocshFuncDef dbtrFuncDef = {"dbtr",1,dbtrArgs,
"Process record and then some fields.\n"};
static void dbtrCallFunc(const iocshArgBuf *args) { dbtr(args[0].sval);}
/* dbtgf */
static const iocshArg dbtgfArg0 = { "record name",iocshArgString};
static const iocshArg dbtgfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const dbtgfArgs[1] = {&dbtgfArg0};
static const iocshFuncDef dbtgfFuncDef = {"dbtgf",1,dbtgfArgs,
"Database Test Get Field.\n"
"Get field with different DBR_* types\n"};
"Get and print the specified field with all possible DBR_* types\n"
"Example: dbtgf aitest\n"
"Example: dbtgf aitest.VAL\n"};
static void dbtgfCallFunc(const iocshArgBuf *args) { dbtgf(args[0].sval);}
/* dbtpf */
static const iocshArg dbtpfArg0 = { "record name",iocshArgString};
static const iocshArg dbtpfArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpfArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpfArgs[2] = {&dbtpfArg0,&dbtpfArg1};
static const iocshFuncDef dbtpfFuncDef = {"dbtpf",2,dbtpfArgs,
"Database Test Put Field.\n"};
"Database Test Put Field.\n"
"Put the given value to the given PV, then get the value\n"
"for all possible DBR_* types\n\n"
"Example: dbtpf aitest 5.0\n"};
static void dbtpfCallFunc(const iocshArgBuf *args)
{ dbtpf(args[0].sval,args[1].sval);}
@@ -270,33 +321,43 @@ static void dbiorCallFunc(const iocshArgBuf *args)
/* dbhcr */
static const iocshFuncDef dbhcrFuncDef = {"dbhcr",0,0,
"Database Report Device Config.\n"};
"Database Hardware Configuration Report.\n"
"Produce a report of all hardware links.\n"
"The produced report will probably not be in the sort order desired.\n"
"Use the UNIX sort command:\n"
"dbhcr > report\n"
"sort report > report.sorted\n"};
static void dbhcrCallFunc(const iocshArgBuf *args) { dbhcr();}
/* gft */
static const iocshArg gftArg0 = { "record name",iocshArgString};
static const iocshArg gftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg * const gftArgs[1] = {&gftArg0};
static const iocshFuncDef gftFuncDef = {"gft",1,gftArgs,
"Report dbChannel info and value.\n"};
"Report dbChannel info and value.\n"
"Example: gft aitest\n"
"Example: gft aitest.VAL\n"};
static void gftCallFunc(const iocshArgBuf *args) { gft(args[0].sval);}
/* pft */
static const iocshArg pftArg0 = { "record name",iocshArgString};
static const iocshArg pftArg0 = { "record name",iocshArgStringRecord};
static const iocshArg pftArg1 = { "value",iocshArgString};
static const iocshArg * const pftArgs[2] = {&pftArg0,&pftArg1};
static const iocshFuncDef pftFuncDef = {"pft",2,pftArgs,
"dbChannel put value.\n"};
"dbChannel put value.\n"
"Example: pft aitest 5.0\n"};
static void pftCallFunc(const iocshArgBuf *args)
{ pft(args[0].sval,args[1].sval);}
/* dbtpn */
static const iocshArg dbtpnArg0 = { "record name",iocshArgString};
static const iocshArg dbtpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dbtpnArg1 = { "value",iocshArgString};
static const iocshArg * const dbtpnArgs[2] = {&dbtpnArg0,&dbtpnArg1};
static const iocshFuncDef dbtpnFuncDef = {"dbtpn",2,dbtpnArgs,
"Database Put Notify\n"
"Database Test Process Notify\n"
"Without value, begin async. processing and get\n"
"With value, begin put, process, and get\n"};
"With value, begin put, process, and get\n"
"Example: dbtpn aitest\n"
"Example: dbtpn aitest 5.0\n"};
static void dbtpnCallFunc(const iocshArgBuf *args)
{ dbtpn(args[0].sval,args[1].sval);}
@@ -311,36 +372,45 @@ static const iocshArg dbPutAttrArg1 = { "attribute name",iocshArgString};
static const iocshArg dbPutAttrArg2 = { "value",iocshArgString};
static const iocshArg * const dbPutAttrArgs[] =
{&dbPutAttrArg0, &dbPutAttrArg1, &dbPutAttrArg2};
static const iocshFuncDef dbPutAttrFuncDef =
{"dbPutAttribute",3,dbPutAttrArgs,
"Set/Create record attribute.\n"};
static const iocshFuncDef dbPutAttrFuncDef = {"dbPutAttribute",3,dbPutAttrArgs,
"Set/Create record attribute.\n"};
static void dbPutAttrCallFunc(const iocshArgBuf *args)
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
/* tpn */
static const iocshArg tpnArg0 = { "record name",iocshArgString};
static const iocshArg tpnArg0 = { "record name",iocshArgStringRecord};
static const iocshArg tpnArg1 = { "value",iocshArgString};
static const iocshArg * const tpnArgs[2] = {&tpnArg0,&tpnArg1};
static const iocshFuncDef tpnFuncDef = {"tpn",2,tpnArgs,
"Begin async. process and get.\n"};
"Test Process Notify.\n\n"
"Example: tpn aitest 5.0\n"};
static void tpnCallFunc(const iocshArgBuf *args)
{ tpn(args[0].sval,args[1].sval);}
/* dblsr */
static const iocshArg dblsrArg0 = { "record name",iocshArgString};
static const iocshArg dblsrArg0 = { "record name",iocshArgStringRecord};
static const iocshArg dblsrArg1 = { "interest level",iocshArgInt};
static const iocshArg * const dblsrArgs[2] = {&dblsrArg0,&dblsrArg1};
static const iocshFuncDef dblsrFuncDef = {"dblsr",2,dblsrArgs,
"Database Lockset report.\n"};
"Database Lockset report.\n"
"Generate a report showing the lock set to which each record belongs.\n"
"interest level 0 - Show lock set information only.\n"
" 1 - Show each record in the lock set.\n"
" 2 - Show each record and all database links in the lock set.\n\n"
"Example: dblsr aitest 2\n"};
static void dblsrCallFunc(const iocshArgBuf *args)
{ dblsr(args[0].sval,args[1].ival);}
/* dbLockShowLocked */
static const iocshArg dbLockShowLockedArg0 = { "interest level",iocshArgInt};
static const iocshArg * const dbLockShowLockedArgs[1] = {&dbLockShowLockedArg0};
static const iocshFuncDef dbLockShowLockedFuncDef =
{"dbLockShowLocked",1,dbLockShowLockedArgs,
"Show Locksets which are currently locked.\n"};
static const iocshFuncDef dbLockShowLockedFuncDef = {
"dbLockShowLocked",1,dbLockShowLockedArgs,
"Show Locksets which are currently locked.\n"
"interest level argument is passed to epicsMutexShow to adjust reported\n"
"information.\n\n"
"Example: dbLockShowLocked 0\n"
};
static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
{ dbLockShowLocked(args[0].ival);}
@@ -348,10 +418,9 @@ static void dbLockShowLockedCallFunc(const iocshArgBuf *args)
static const iocshArg scanOnceSetQueueSizeArg0 = { "size",iocshArgInt};
static const iocshArg * const scanOnceSetQueueSizeArgs[1] =
{&scanOnceSetQueueSizeArg0};
static const iocshFuncDef scanOnceSetQueueSizeFuncDef =
{"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs,
"Change size of Scan once queue.\n"
"Must be called before iocInit().\n"};
static const iocshFuncDef scanOnceSetQueueSizeFuncDef = {"scanOnceSetQueueSize",1,scanOnceSetQueueSizeArgs,
"Change size of Scan once queue.\n"
"Must be called before iocInit().\n"};
static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
{
scanOnceSetQueueSize(args[0].ival);
@@ -361,9 +430,8 @@ static void scanOnceSetQueueSizeCallFunc(const iocshArgBuf *args)
static const iocshArg scanOnceQueueShowArg0 = { "reset",iocshArgInt};
static const iocshArg * const scanOnceQueueShowArgs[1] =
{&scanOnceQueueShowArg0};
static const iocshFuncDef scanOnceQueueShowFuncDef =
{"scanOnceQueueShow",1,scanOnceQueueShowArgs,
"Show details and statitics of scan once queue processing.\n"};
static const iocshFuncDef scanOnceQueueShowFuncDef = {"scanOnceQueueShow",1,scanOnceQueueShowArgs,
"Show details and statitics of scan once queue processing.\n"};
static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
{
scanOnceQueueShow(args[0].ival);
@@ -373,7 +441,8 @@ static void scanOnceQueueShowCallFunc(const iocshArgBuf *args)
static const iocshArg scanpplArg0 = { "rate",iocshArgDouble};
static const iocshArg * const scanpplArgs[1] = {&scanpplArg0};
static const iocshFuncDef scanpplFuncDef = {"scanppl",1,scanpplArgs,
"print periodic scan lists.\n"};
"Print info for records with periodic scan.\n"
"If rate == 0.0, all periods are shown.\n"};
static void scanpplCallFunc(const iocshArgBuf *args)
{ scanppl(args[0].dval);}
@@ -405,10 +474,9 @@ static void scanpiolCallFunc(const iocshArgBuf *args) { scanpiol();}
static const iocshArg callbackSetQueueSizeArg0 = { "bufsize",iocshArgInt};
static const iocshArg * const callbackSetQueueSizeArgs[1] =
{&callbackSetQueueSizeArg0};
static const iocshFuncDef callbackSetQueueSizeFuncDef =
{"callbackSetQueueSize",1,callbackSetQueueSizeArgs,
"Change depth of queue for callback workers.\n"
"Must be called before iocInit().\n"};
static const iocshFuncDef callbackSetQueueSizeFuncDef = {"callbackSetQueueSize",1,callbackSetQueueSizeArgs,
"Change depth of queue for callback workers.\n"
"Must be called before iocInit().\n"};
static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
{
callbackSetQueueSize(args[0].ival);
@@ -418,9 +486,8 @@ static void callbackSetQueueSizeCallFunc(const iocshArgBuf *args)
static const iocshArg callbackQueueShowArg0 = { "reset", iocshArgInt};
static const iocshArg * const callbackQueueShowArgs[1] =
{&callbackQueueShowArg0};
static const iocshFuncDef callbackQueueShowFuncDef =
{"callbackQueueShow",1,callbackQueueShowArgs,
"Show status of callback thread processing queue.\n"};
static const iocshFuncDef callbackQueueShowFuncDef = {"callbackQueueShow",1,callbackQueueShowArgs,
"Show status of callback thread processing queue.\n"};
static void callbackQueueShowCallFunc(const iocshArgBuf *args)
{
callbackQueueShow(args[0].ival);
@@ -431,11 +498,10 @@ static const iocshArg callbackParallelThreadsArg0 = { "no of threads", iocshArgI
static const iocshArg callbackParallelThreadsArg1 = { "priority", iocshArgString};
static const iocshArg * const callbackParallelThreadsArgs[2] =
{&callbackParallelThreadsArg0,&callbackParallelThreadsArg1};
static const iocshFuncDef callbackParallelThreadsFuncDef =
{"callbackParallelThreads",2,callbackParallelThreadsArgs,
"Configure multiple workers for a given callback queue priority level.\n"
"priority may be omitted or \"*\" to act on all priorities\n"
"or one of LOW, MEDIUM, or HIGH.\n"};
static const iocshFuncDef callbackParallelThreadsFuncDef = {"callbackParallelThreads",2,callbackParallelThreadsArgs,
"Configure multiple workers for a given callback queue priority level.\n"
"priority may be omitted or \"*\" to act on all priorities\n"
"or one of LOW, MEDIUM, or HIGH.\n"};
static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
{
callbackParallelThreads(args[0].ival, args[1].sval);
@@ -444,8 +510,8 @@ static void callbackParallelThreadsCallFunc(const iocshArgBuf *args)
/* dbStateCreate */
static const iocshArg dbStateArgName = { "name", iocshArgString };
static const iocshArg * const dbStateCreateArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateCreateFuncDef = { "dbStateCreate", 1, dbStateCreateArgs,
"Allocate new state name for \"state\" filter.\n"};
static const iocshFuncDef dbStateCreateFuncDef = {"dbStateCreate", 1, dbStateCreateArgs,
"Allocate new state name for \"state\" filter.\n"};
static void dbStateCreateCallFunc (const iocshArgBuf *args)
{
dbStateCreate(args[0].sval);
@@ -453,8 +519,8 @@ static void dbStateCreateCallFunc (const iocshArgBuf *args)
/* dbStateSet */
static const iocshArg * const dbStateSetArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateSetFuncDef = { "dbStateSet", 1, dbStateSetArgs,
"Change state to set for \"state\" filter.\n"};
static const iocshFuncDef dbStateSetFuncDef = {"dbStateSet", 1, dbStateSetArgs,
"Change state to set for \"state\" filter.\n"};
static void dbStateSetCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -465,8 +531,8 @@ static void dbStateSetCallFunc (const iocshArgBuf *args)
/* dbStateClear */
static const iocshArg * const dbStateClearArgs[] = { &dbStateArgName };
static const iocshFuncDef dbStateClearFuncDef = { "dbStateClear", 1, dbStateClearArgs,
"Change state to clear for \"state\" filter.\n" };
static const iocshFuncDef dbStateClearFuncDef = {"dbStateClear", 1, dbStateClearArgs,
"Change state to clear for \"state\" filter.\n"};
static void dbStateClearCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -478,8 +544,8 @@ static void dbStateClearCallFunc (const iocshArgBuf *args)
/* dbStateShow */
static const iocshArg dbStateShowArg1 = { "level", iocshArgInt };
static const iocshArg * const dbStateShowArgs[] = { &dbStateArgName, &dbStateShowArg1 };
static const iocshFuncDef dbStateShowFuncDef = { "dbStateShow", 2, dbStateShowArgs,
"Show set/clear status of named state. (cf. \"state\" filter)\n" };
static const iocshFuncDef dbStateShowFuncDef = {"dbStateShow", 2, dbStateShowArgs,
"Show set/clear status of named state. (cf. \"state\" filter)\n"};
static void dbStateShowCallFunc (const iocshArgBuf *args)
{
dbStateId sid = dbStateFind(args[0].sval);
@@ -491,8 +557,8 @@ static void dbStateShowCallFunc (const iocshArgBuf *args)
/* dbStateShowAll */
static const iocshArg dbStateShowAllArg0 = { "level", iocshArgInt };
static const iocshArg * const dbStateShowAllArgs[] = { &dbStateShowAllArg0 };
static const iocshFuncDef dbStateShowAllFuncDef = { "dbStateShowAll", 1, dbStateShowAllArgs,
"Show set/clear status of all named states. (cf. \"state\" filter)\n" };
static const iocshFuncDef dbStateShowAllFuncDef = {"dbStateShowAll", 1, dbStateShowAllArgs,
"Show set/clear status of all named states. (cf. \"state\" filter)\n"};
static void dbStateShowAllCallFunc (const iocshArgBuf *args)
{
dbStateShowAll(args[0].ival);
@@ -500,6 +566,8 @@ static void dbStateShowAllCallFunc (const iocshArgBuf *args)
void dbIocRegister(void)
{
iocshCompleteRecord = &dbCompleteRecord;
iocshRegister(&dbbFuncDef,dbbCallFunc);
iocshRegister(&dbdFuncDef,dbdCallFunc);
iocshRegister(&dbcFuncDef,dbcCallFunc);

View File

@@ -359,6 +359,8 @@ typedef struct lset {
* @param plink the link
* @param rtn routine to execute
* @returns status value
*
* @since 3.16.1
*/
long (*doLocked)(struct link *plink, dbLinkUserCallback rtn, void *priv);
@@ -410,8 +412,21 @@ DBCORE_API long dbLoadLinkArray(struct link *, short dbrType, void *pbuffer,
DBCORE_API long dbGetNelements(const struct link *plink, long *pnElements);
DBCORE_API int dbIsLinkConnected(const struct link *plink); /* 0 or 1 */
DBCORE_API int dbGetLinkDBFtype(const struct link *plink);
/** \brief Fetch current value from link.
* \param dbrType Database DBR code
* \param pbuffer Destination buffer
* \param nRequest If !NULL. Caller initializes with number of elements requested,
* On success, set to number of elements written to pbuffer.
* \return 0 on success
*
* When called with `nRequest==NULL`, treated as a request for one (1)
* element.
*
* see lset::getValue
*/
DBCORE_API long dbTryGetLink(struct link *, short dbrType, void *pbuffer,
long *nRequest);
/** see dbTryGetLink() */
DBCORE_API long dbGetLink(struct link *, short dbrType, void *pbuffer,
long *options, long *nRequest);
DBCORE_API long dbGetControlLimits(const struct link *plink, double *low,

View File

@@ -121,8 +121,7 @@ void dbLockIncRef(lockSet* ls)
{
int cnt = epicsAtomicIncrIntT(&ls->refcount);
if(cnt<=1) {
errlogPrintf("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt);
cantProceed(NULL);
cantProceed("dbLockIncRef(%p) on dead lockSet. refs: %d\n", ls, cnt);
}
}
@@ -145,9 +144,8 @@ void dbLockDecRef(lockSet *ls)
epicsMutexMustLock(ls->lock);
if(ellCount(&ls->lockRecordList)!=0) {
errlogPrintf("dbLockDecRef(%p) would free lockSet with %d records\n",
ls, ellCount(&ls->lockRecordList));
cantProceed(NULL);
cantProceed("dbLockDecRef(%p) would free lockSet with %d records\n",
ls, ellCount(&ls->lockRecordList));
}
epicsMutexUnlock(ls->lock);
@@ -421,9 +419,8 @@ retry:
#ifdef LOCKSET_DEBUG
if(plock->owner) {
if(plock->owner!=myself || plock->ownercount<1) {
errlogPrintf("dbScanLockMany(%p) ownership violation %p (%p) %u\n",
locker, plock->owner, myself, plock->ownercount);
cantProceed(NULL);
cantProceed("dbScanLockMany(%p) ownership violation %p (%p) %u\n",
locker, plock->owner, myself, plock->ownercount);
}
plock->ownercount++;
} else {
@@ -444,8 +441,7 @@ retry:
/* if we have at least one lockRecord, then we will always lock
* at least its present lockSet
*/
errlogPrintf("dbScanLockMany(%p) didn't lock anything\n", locker);
cantProceed(NULL);
cantProceed("dbScanLockMany(%p) didn't lock anything\n", locker);
}
}
@@ -602,17 +598,15 @@ void dbLockSetMerge(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
#ifdef LOCKSET_DEBUG
if(locker && (A->owner!=myself || B->owner!=myself)) {
errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
A->owner, B->owner, myself);
cantProceed(NULL);
cantProceed("dbLockSetMerge(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
A->owner, B->owner, myself);
}
#endif
if(locker && (A->ownerlocker!=locker || B->ownerlocker!=locker)) {
errlogPrintf("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
A->ownerlocker, B->ownerlocker, locker);
cantProceed(NULL);
cantProceed("dbLockSetMerge(%p,\"%s\",\"%s\") locker ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
A->ownerlocker, B->ownerlocker, locker);
}
if(A==B)
@@ -688,19 +682,17 @@ void dbLockSetSplit(dbLocker *locker, dbCommon *pfirst, dbCommon *psecond)
#ifdef LOCKSET_DEBUG
if(ls->owner!=myself || psecond->lset->plockSet->owner!=myself) {
errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
ls->owner, psecond->lset->plockSet->owner, myself);
cantProceed(NULL);
cantProceed("dbLockSetSplit(%p,\"%s\",\"%s\") ownership violation %p %p (%p)\n",
locker, pfirst->name, psecond->name,
ls->owner, psecond->lset->plockSet->owner, myself);
}
#endif
/* lockset consistency violation */
if(ls!=psecond->lset->plockSet) {
errlogPrintf("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n",
locker, pfirst->name, psecond->name,
pfirst->lset->plockSet, psecond->lset->plockSet);
cantProceed(NULL);
cantProceed("dbLockSetSplit(%p,\"%s\",\"%s\") consistency violation %p %p\n",
locker, pfirst->name, psecond->name,
pfirst->lset->plockSet, psecond->lset->plockSet);
}
@@ -877,7 +869,7 @@ nosplit:
}
}
static char *msstring[4]={"NMS","MS","MSI","MSS"};
static const char *msstring[4]={"NMS","MS","MSI","MSS"};
long dblsr(char *recordname,int level)
{

View File

@@ -24,18 +24,64 @@ extern "C" {
struct dbCommon;
struct dbBase;
/** @brief Lock multiple records.
*
* A dbLocker allows a caller to simultaneously lock multiple records.
* The list of records is provided to dbLockerAlloc().
* And the resulting dbLocker can be locked/unlocked repeatedly.
*
* Each thread can only lock one dbLocker at a time.
* While locked, dbScanLock() may be called only on those records
* included in the dbLocker.
*
* @since 3.16.0.1
*/
struct dbLocker;
typedef struct dbLocker dbLocker;
/** @brief Lock a record for modification.
*
* While locked, caller may access record using eg. dbGet() or dbPut(),
* but not dbGetField() or dbPutField().
* The caller must later call dbScanUnlock().
* dbScanLock() may be called again as the record lock behaves as a recursive mutex.
*/
DBCORE_API void dbScanLock(struct dbCommon *precord);
/** @brief Unlock a record.
*
* Reverse the action of dbScanLock()
*/
DBCORE_API void dbScanUnlock(struct dbCommon *precord);
/** @brief Prepare to lock a set of records.
* @param precs Array of nrecs dbCommon pointers.
* @param nrecs Length of precs array
* @param flags Set to 0
* @return NULL on error
* @since 3.16.0.1
*/
DBCORE_API dbLocker *dbLockerAlloc(struct dbCommon * const *precs,
size_t nrecs,
unsigned int flags);
DBCORE_API void dbLockerFree(dbLocker *);
/** @brief Free dbLocker allocated by dbLockerAlloc()
* @param plocker Must not be NULL
* @since 3.16.0.1
*/
DBCORE_API void dbLockerFree(dbLocker *plocker);
/** @brief Lock all records of dbLocker
*
* While locked, caller may access any associated record passed to dbLockerAlloc() .
* dbScanLockMany() may not be called again (multi-lock is not recursive).
* dbScanLock()/dbScanUnlock() may be called on individual record.
* The caller must later call dbScanUnlockMany().
* @since 3.16.0.1
*/
DBCORE_API void dbScanLockMany(dbLocker*);
/** @brief Unlock all records of dbLocker
* @since 3.16.0.1
*/
DBCORE_API void dbScanUnlockMany(dbLocker*);
DBCORE_API unsigned long dbLockGetLockId(

View File

@@ -10,6 +10,7 @@
#define DBLOCKPVT_H
#include "dbLock.h"
#include "epicsMutex.h"
#include "epicsSpin.h"
/* Define to enable additional error checking */

View File

@@ -168,9 +168,13 @@ void scanStop(void)
epicsEventSignal(ppsl->loopEvent);
epicsEventWait(startStopEvent);
}
for (i = 0; i < nPeriodic; i++) {
epicsThreadMustJoin(periodicTaskId[i]);
}
scanOnce((dbCommon *)&exitOnce);
epicsEventWait(startStopEvent);
epicsThreadMustJoin(onceTaskId);
}
void scanCleanup(void)
@@ -761,14 +765,16 @@ void scanOnceQueueShow(const int reset)
static void initOnce(void)
{
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
opts.joinable = 1;
opts.priority = epicsThreadPriorityScanLow + nPeriodic;
opts.stackSize = epicsThreadStackBig;
if ((onceQ = epicsRingBytesLockedCreate(sizeof(onceEntry)*onceQueueSize)) == NULL) {
cantProceed("initOnce: Ring buffer create failed\n");
}
if(!onceSem)
onceSem = epicsEventMustCreate(epicsEventEmpty);
onceTaskId = epicsThreadCreate("scanOnce",
epicsThreadPriorityScanLow + nPeriodic,
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
onceTaskId = epicsThreadCreateOpt("scanOnce", onceTask, 0, &opts);
epicsEventWait(startStopEvent);
}
@@ -932,14 +938,16 @@ static void spawnPeriodic(int ind)
{
periodic_scan_list *ppsl = papPeriodic[ind];
char taskName[20];
epicsThreadOpts opts = EPICS_THREAD_OPTS_INIT;
opts.joinable = 1;
opts.priority = epicsThreadPriorityScanLow + ind;
opts.stackSize = epicsThreadStackBig;
if (!ppsl) return;
sprintf(taskName, "scan-%g", ppsl->period);
periodicTaskId[ind] = epicsThreadCreate(
taskName, epicsThreadPriorityScanLow + ind,
epicsThreadGetStackSize(epicsThreadStackBig),
periodicTask, (void *)ppsl);
periodicTaskId[ind] = epicsThreadCreateOpt(
taskName, periodicTask, (void *)ppsl, &opts);
epicsEventWait(startStopEvent);
}

View File

@@ -506,62 +506,62 @@ long dbtgf(const char *pname)
ret_options=0;
dbr_type = DBR_STRING;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/MAX_STRING_SIZE));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/MAX_STRING_SIZE));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_CHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt8)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt8))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UCHAR;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt8)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt8))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_SHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_USHORT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_LONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ULONG;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_INT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsInt64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsInt64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_UINT64;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsUInt64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsUInt64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_FLOAT;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat32)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsFloat32))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_DOUBLE;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsFloat64)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsFloat64))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);
dbr_type = DBR_ENUM;
no_elements = MIN(addr.no_elements,((sizeof(buffer))/sizeof(epicsEnum16)));
no_elements = MIN(addr.no_elements,(sizeof(buffer)/(sizeof(epicsEnum16))));
status = dbGetField(&addr,dbr_type,pbuffer,&ret_options,&no_elements,NULL);
printBuffer(status,dbr_type,pbuffer,0L,0L,no_elements,pMsgBuff,tab_size);

View File

@@ -50,6 +50,7 @@ void testdbPrepare(void)
{
if(!testEvtLock)
testEvtLock = epicsMutexMustCreate();
initHookAnnounce(initHookAfterPrepareDatabase);
}
void testdbReadDatabase(const char* file,
@@ -94,6 +95,7 @@ void testIocShutdownOk(void)
void testdbCleanup(void)
{
initHookAnnounce(initHookBeforeCleanupDatabase);
dbFreeBase(pdbbase);
db_cleanup_events();
initHookFree();
@@ -339,7 +341,7 @@ void testdbGetArrFieldEqual(const char* pv, short dbfType, long nRequest, unsign
break;
}
#define OP(DBR,Type,pat) case DBR: {Type expect = *(Type*)pbuf, actual = *(Type*)gbuf; assert(vSize==sizeof(Type)); match &= expect==actual; \
if(expect!=actual) testDiag("[%lu] expected=" pat " actual=" pat, n, expect, actual); break;}
if(expect!=actual) {testDiag("[%lu] expected=" pat " actual=" pat, n, expect, actual);} break;}
OP(DBR_CHAR, char, "%c");
OP(DBR_UCHAR, unsigned char, "%u");

View File

@@ -105,7 +105,7 @@ DBCORE_API void testdbVGetFieldEqual(const char* pv, short dbrType, va_list ap);
*
* @code
* static const epicsUInt32 putval[] = {1,2,3};
* testdbVGetFieldEqual("some:wf", DBF_ULONG, NELEMENTS(putval), putval);
* testdbPutArrFieldOk("some:wf", DBF_ULONG, NELEMENTS(putval), putval);
* @endcode
*
* @see @ref dbtestactions
@@ -190,6 +190,7 @@ DBCORE_API void testGlobalUnlock(void);
*
* @code
* #include <dbUnitTest.h>
* #include <testMain.h>
*
* int mytest_registerRecordDeviceDriver(DBBASE *pbase);
* void testCase(void) {
@@ -204,7 +205,7 @@ DBCORE_API void testGlobalUnlock(void);
* }
*
* MAIN(mytestmain) {
* testPlan(0);
* testPlan(0); // adjust number of tests
* testCase();
* testCase(); // may be repeated if desirable.
* return testDone();
@@ -212,6 +213,9 @@ DBCORE_API void testGlobalUnlock(void);
* @endcode
*
* @code
* TOP = ..
* include $(TOP)/configure/CONFIG
*
* TARGETS += $(COMMON_DIR)/mytest.dbd
* DBDDEPENDS_FILES += mytest.dbd$(DEP)
* TESTFILES += $(COMMON_DIR)/mytest.dbd
@@ -222,6 +226,8 @@ DBCORE_API void testGlobalUnlock(void);
* mytest_SRCS += mytestmain.c # see above
* mytest_SRCS += mytest_registerRecordDeviceDriver.cpp
* TESTFILES += some.db
*
* include $(TOP)/configure/RULES
* @endcode
*
* @section dbtestactions Actions

View File

@@ -39,12 +39,17 @@ extern "C" {
* will adjust automatically, it just compares field sizes.
*/
union native_value {
epicsInt8 dbf_char;
epicsInt16 dbf_short;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
epicsInt8 dbf_char;
epicsUInt8 dbf_uchar;
epicsInt16 dbf_short;
epicsUInt16 dbf_ushort;
epicsEnum16 dbf_enum;
epicsInt32 dbf_long;
epicsUInt32 dbf_ulong;
epicsInt64 dbf_int64;
epicsUInt64 dbf_uint64;
epicsFloat32 dbf_float;
epicsFloat64 dbf_double;
#ifdef DB_EVENT_LOG_STRINGS
char dbf_string[MAX_STRING_SIZE];
#endif

View File

@@ -75,7 +75,7 @@ DBCORE_API void recGblInheritSevr(int msMode, void *precord, epicsEnum16 stat,
epicsEnum16 sevr);
DBCORE_API int recGblSetSevrMsg(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr,
const char *msg, ...) EPICS_PRINTF_STYLE(4,5);
EPICS_PRINTF_FMT(const char *msg), ...) EPICS_PRINTF_STYLE(4,5);
DBCORE_API int recGblSetSevrVMsg(void *precord, epicsEnum16 new_stat,
epicsEnum16 new_sevr,
const char *msg, va_list args);

View File

@@ -28,5 +28,6 @@ dbCore_SRCS += dbYacc.c
dbCore_SRCS += dbPvdLib.c
dbCore_SRCS += dbStaticRun.c
dbCore_SRCS += dbStaticIocRegister.c
dbCore_SRCS += dbCompleteRecord.cpp
CLEANS += dbLex.c dbYacc.c

View File

@@ -118,6 +118,10 @@ typedef struct dbRecordNode {
char *recordname;
ELLLIST infoList; /*LIST head of info nodes*/
int flags;
/** Parse order of this record()
* @since 7.0.8.1
*/
unsigned order;
struct dbRecordNode *aliasedRecnode; /* NULL unless flags|DBRN_FLAGS_ISALIAS */
}dbRecordNode;
@@ -184,5 +188,9 @@ typedef struct dbBase {
struct gphPvt *pgpHash;
short ignoreMissingMenus;
short loadCdefs;
/** Total number of records.
* @since 7.0.8.1
*/
unsigned no_records;
}dbBase;
#endif

View File

@@ -0,0 +1,163 @@
/*************************************************************************\
* Copyright (c) 2022 Michael Davidsaver
* SPDX-License-Identifier: EPICS
* EPICS Base is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include <algorithm>
#include <limits>
#include <string>
#include <set>
#include <string.h>
#include <epicsStdio.h>
#include <dbAccess.h>
#include "dbStaticPvt.h"
namespace {
// immutable C string slice. (avoid allocating many temporary std::string)
class CStr {
const char* p;
size_t l;
public:
CStr() :p(NULL), l(0u) {}
CStr(const CStr& o) :p(o.p), l(o.l) {}
explicit CStr(const char* p) :p(p), l(p ? strlen(p) : 0u) {}
CStr(const char* p, size_t n) :p(p), l(n) {}
CStr& operator=(const CStr& o) {
p = o.p;
l = o.l;
return *this;
}
bool operator==(const CStr& o) const {
return l==o.l && (p==o.p || memcmp(p, o.p, l)==0);
}
bool operator!=(const CStr& o) const {
return !(*this==o);
}
bool operator<(const CStr& o) const {
size_t pl = std::min(l, o.l);
int cmp = memcmp(p, o.p, pl);
return cmp<0 || (cmp==0 && l < o.l);
}
bool empty() const { return !l; }
size_t size() const { return l; }
bool prefixOf(const CStr& full) const {
return full.l >= l && memcmp(full.p, p, l)==0;
}
CStr commonPrefix(const CStr& o, size_t startFrom=0u) const {
size_t n, N;
for(n=startFrom, N=std::min(l, o.l); n<N; n++) {
if(p[n]!=o.p[n])
break;
}
return CStr(p, n);
}
void chop_at_first_of(const char* sep, size_t startFrom=0u) {
size_t n;
for(n=startFrom; n<l; n++) {
char c = p[n];
for(const char *s = sep; *s; s++) {
if(c == *s) {
l = n+1u; // include trailing separator
return;
}
}
}
}
char* dup() const {
char* ret = (char*)malloc(l+1u);
if(ret) {
memcpy(ret, p, l);
ret[l] = '\0';
}
return ret;
}
};
} // namespace
char** dbCompleteRecord(const char *cword)
{
const CStr word(cword);
DBENTRY ent;
dbInitEntry(pdbbase, &ent);
try{
// iterating all record twice...
CStr prefix;
bool first = true;
// find longest prefix match
for(long status = dbFirstRecordType(&ent); !status; status = dbNextRecordType(&ent)) {
for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) {
const CStr name(ent.precnode->recordname);
if(!word.prefixOf(name))
continue;
if(first) { // first match
prefix = name;
first = false;
} else {
prefix = prefix.commonPrefix(name, word.size());
}
}
}
// with prefix size known, iterate again to find suggestions
typedef std::set<CStr> suggestions_t;
suggestions_t suggestions;
for(long status = dbFirstRecordType(&ent); !status; status = dbNextRecordType(&ent)) {
for(status = dbFirstRecord(&ent); !status; status = dbNextRecord(&ent)) {
CStr name(ent.precnode->recordname);
if(!prefix.prefixOf(name))
continue;
name.chop_at_first_of(":<>{}-", prefix.size());
suggestions.insert(name);
}
}
dbFinishEntry(&ent);
char** ret = NULL;
if(!prefix.empty() || !suggestions.empty()) {
ret = (char**)malloc(sizeof(*ret)*(2u + suggestions.size()));
if(ret) {
ret[0] = prefix.dup();
size_t n=1u;
for(suggestions_t::iterator it(suggestions.begin()), end(suggestions.end());
it!=end; ++it)
{
ret[n++] = it->dup();
}
ret[n] = NULL;
}
}
return ret;
} catch(std::exception& e){
fprintf(stderr, "dbCompleteRecord error: %s\n", e.what());
dbFinishEntry(&ent);
return NULL;
}
}

View File

@@ -15,7 +15,8 @@ doublequote "\""
comment "#"
whitespace [ \t\r\n]
escape {backslash}.
stringchar [^"\n\\]
sqschar [^'\n\\]
dqschar [^"\n\\]
bareword [a-zA-Z0-9_\-+:.\[\]<>;]
punctuation [:,\[\]{}]
@@ -84,7 +85,7 @@ static int yyreset(void)
return(tokenSTRING);
}
{doublequote}({stringchar}|{escape})*{doublequote} { /* quoted string */
{doublequote}({dqschar}|{escape})*{doublequote} { /* quoted string */
yylval.Str = dbmfStrdup((char *) yytext+1);
yylval.Str[strlen(yylval.Str)-1] = '\0';
return(tokenSTRING);
@@ -129,14 +130,14 @@ static int yyreset(void)
/* Error patterns */
{doublequote}({stringchar}|{escape})*{newline} {
{doublequote}({dqschar}|{escape})*{newline} {
yyerrorAbort("Newline in string, closing quote missing");
}
<JSON>{doublequote}({stringchar}|{escape})*{doublequote} {
<JSON>{doublequote}({dqschar}|{escape})*{doublequote} {
yyerrorAbort("Bad character in JSON string");
}
<JSON>{singlequote}({stringchar}|{escape})*{singlequote} {
<JSON>{singlequote}({sqschar}|{escape})*{singlequote} {
yyerrorAbort("Bad character in JSON string");
}

View File

@@ -111,7 +111,8 @@ typedef struct inputFile{
static ELLLIST inputFileList = ELLLIST_INIT;
static inputFile *pinputFileNow = NULL;
static DBBASE *pdbbase = NULL;
/* The DBBASE most recently allocated/used by dbReadCOM() */
static DBBASE *savedPdbbase = NULL;
typedef struct tempListNode {
ELLNODE node;
@@ -233,36 +234,36 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
}
if(*ppdbbase == 0) *ppdbbase = dbAllocBase();
pdbbase = *ppdbbase;
savedPdbbase = *ppdbbase;
if(path && strlen(path)>0) {
dbPath(pdbbase,path);
dbPath(savedPdbbase,path);
} else {
penv = getenv("EPICS_DB_INCLUDE_PATH");
if(penv) {
dbPath(pdbbase,penv);
dbPath(savedPdbbase,penv);
} else {
dbPath(pdbbase,".");
dbPath(savedPdbbase,".");
}
}
my_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
freeListInitPvt(&freeListPvt,sizeof(tempListNode),100);
if(substitutions) {
if(macCreateHandle(&macHandle,NULL)) {
epicsPrintf("macCreateHandle error\n");
status = -1;
goto cleanup;
}
macParseDefns(macHandle,(char *)substitutions,&macPairs);
if(macPairs ==NULL) {
macDeleteHandle(macHandle);
macHandle = NULL;
} else {
macInstallMacros(macHandle,macPairs);
free((void *)macPairs);
mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
}
macSuppressWarning(macHandle,dbQuietMacroWarnings);
if (substitutions == NULL)
substitutions = "";
if(macCreateHandle(&macHandle,NULL)) {
epicsPrintf("macCreateHandle error\n");
status = -1;
goto cleanup;
}
macParseDefns(macHandle,substitutions,&macPairs);
if(macPairs == NULL) {
macDeleteHandle(macHandle);
macHandle = NULL;
} else {
macInstallMacros(macHandle,macPairs);
free(macPairs);
mac_input_buffer = dbCalloc(MY_BUFFER_SIZE,sizeof(char));
}
macSuppressWarning(macHandle,dbQuietMacroWarnings);
pinputFile = dbCalloc(1,sizeof(inputFile));
if (filename) {
pinputFile->filename = macEnvExpand(filename);
@@ -271,7 +272,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
FILE *fp1 = 0;
if (pinputFile->filename)
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp1);
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp1);
if (!pinputFile->filename || !fp1) {
errPrintf(0, __FILE__, __LINE__,
"dbRead opening file %s\n",pinputFile->filename);
@@ -297,13 +298,13 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
while (ellCount(&tempList))
popFirstTemp(); /* Memory leak on parser failure */
dbFreePath(pdbbase);
dbFreePath(savedPdbbase);
if(!status) { /*add RTYP and VERS as an attribute */
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
long localStatus;
dbInitEntry(pdbbase,pdbEntry);
dbInitEntry(savedPdbbase,pdbEntry);
localStatus = dbFirstRecordType(pdbEntry);
while(!localStatus) {
localStatus = dbPutRecordAttribute(pdbEntry,"RTYP",
@@ -323,7 +324,7 @@ static long dbReadCOM(DBBASE **ppdbbase,const char *filename, FILE *fp,
cleanup:
if(dbRecordsAbcSorted) {
ELLNODE *cur;
for(cur = ellFirst(&pdbbase->recordTypeList); cur; cur=ellNext(cur))
for(cur = ellFirst(&savedPdbbase->recordTypeList); cur; cur=ellNext(cur))
{
dbRecordType *rtype = CONTAINER(cur, dbRecordType, node);
@@ -416,12 +417,12 @@ static void dbIncludePrint(void)
static void dbPathCmd(char *path)
{
dbPath(pdbbase,path);
dbPath(savedPdbbase,path);
}
static void dbAddPathCmd(char *path)
{
dbAddPath(pdbbase,path);
dbAddPath(savedPdbbase,path);
}
static void dbIncludeNew(char *filename)
@@ -431,7 +432,7 @@ static void dbIncludeNew(char *filename)
pinputFile = dbCalloc(1,sizeof(inputFile));
pinputFile->filename = macEnvExpand(filename);
pinputFile->path = dbOpenFile(pdbbase, pinputFile->filename, &fp);
pinputFile->path = dbOpenFile(savedPdbbase, pinputFile->filename, &fp);
if (!fp) {
epicsPrintf("Can't open include file \"%s\"\n", filename);
yyerror(NULL);
@@ -453,7 +454,7 @@ static void dbMenuHead(char *name)
yyerrorAbort("dbMenuHead: Menu name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->menuList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->menuList);
if(pgphentry) {
duplicate = TRUE;
return;
@@ -501,14 +502,14 @@ static void dbMenuBody(void)
}
if(ellCount(&tempList)) yyerrorAbort("dbMenuBody: tempList not empty");
/* Add menu in sorted order */
pMenu = (dbMenu *)ellFirst(&pdbbase->menuList);
pMenu = (dbMenu *)ellFirst(&savedPdbbase->menuList);
while(pMenu && strcmp(pMenu->name,pnewMenu->name) >0 )
pMenu = (dbMenu *)ellNext(&pMenu->node);
if(pMenu)
ellInsert(&pdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node);
ellInsert(&savedPdbbase->menuList,ellPrevious(&pMenu->node),&pnewMenu->node);
else
ellAdd(&pdbbase->menuList,&pnewMenu->node);
pgphentry = gphAdd(pdbbase->pgpHash,pnewMenu->name,&pdbbase->menuList);
ellAdd(&savedPdbbase->menuList,&pnewMenu->node);
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewMenu->name,&savedPdbbase->menuList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
} else {
@@ -525,14 +526,14 @@ static void dbRecordtypeHead(char *name)
yyerrorAbort("dbRecordtypeHead: Recordtype name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->recordTypeList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->recordTypeList);
if(pgphentry) {
duplicate = TRUE;
return;
}
pdbRecordType = dbCalloc(1,sizeof(dbRecordType));
pdbRecordType->name = epicsStrDup(name);
if (pdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList);
if (savedPdbbase->loadCdefs) ellInit(&pdbRecordType->cdefList);
if(ellCount(&tempList))
yyerrorAbort("dbRecordtypeHead tempList not empty");
allocTemp(pdbRecordType);
@@ -564,13 +565,13 @@ static short findOrAddGuiGroup(const char *name)
{
dbGuiGroup *pdbGuiGroup;
GPHENTRY *pgphentry;
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->guiGroupList);
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->guiGroupList);
if (!pgphentry) {
pdbGuiGroup = dbCalloc(1,sizeof(dbGuiGroup));
pdbGuiGroup->name = epicsStrDup(name);
ellAdd(&pdbbase->guiGroupList, &pdbGuiGroup->node);
pdbGuiGroup->key = ellCount(&pdbbase->guiGroupList);
pgphentry = gphAdd(pdbbase->pgpHash, pdbGuiGroup->name, &pdbbase->guiGroupList);
ellAdd(&savedPdbbase->guiGroupList, &pdbGuiGroup->node);
pdbGuiGroup->key = ellCount(&savedPdbbase->guiGroupList);
pgphentry = gphAdd(savedPdbbase->pgpHash, pdbGuiGroup->name, &savedPdbbase->guiGroupList);
pgphentry->userPvt = pdbGuiGroup;
}
return ((dbGuiGroup *)pgphentry->userPvt)->key;
@@ -653,8 +654,8 @@ static void dbRecordtypeFieldItem(char *name,char *value)
return;
}
if(strcmp(name,"menu")==0) {
pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(pdbbase,value);
if(!pdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt)
pdbFldDes->ftPvt = (dbMenu *)dbFindMenu(savedPdbbase,value);
if(!savedPdbbase->ignoreMissingMenus && !pdbFldDes->ftPvt)
yyerrorAbort("menu not found");
return;
}
@@ -672,7 +673,7 @@ static void dbRecordtypeCdef(char *text) {
tempListNode *ptempListNode;
dbRecordType *pdbRecordType;
if (!pdbbase->loadCdefs || duplicate) return;
if (!savedPdbbase->loadCdefs || duplicate) return;
ptempListNode = (tempListNode *)ellFirst(&tempList);
pdbRecordType = ptempListNode->item;
@@ -781,14 +782,14 @@ static void dbRecordtypeBody(void)
ellInit(&pdbRecordType->attributeList);
ellInit(&pdbRecordType->recList);
ellInit(&pdbRecordType->devList);
pgphentry = gphAdd(pdbbase->pgpHash,pdbRecordType->name,
&pdbbase->recordTypeList);
pgphentry = gphAdd(savedPdbbase->pgpHash,pdbRecordType->name,
&savedPdbbase->recordTypeList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
} else {
pgphentry->userPvt = pdbRecordType;
}
ellAdd(&pdbbase->recordTypeList,&pdbRecordType->node);
ellAdd(&savedPdbbase->recordTypeList,&pdbRecordType->node);
}
static void dbDevice(char *recordtype,char *linktype,
@@ -798,7 +799,7 @@ static void dbDevice(char *recordtype,char *linktype,
dbRecordType *pdbRecordType;
GPHENTRY *pgphentry;
int i,link_type;
pgphentry = gphFind(pdbbase->pgpHash,recordtype,&pdbbase->recordTypeList);
pgphentry = gphFind(savedPdbbase->pgpHash,recordtype,&savedPdbbase->recordTypeList);
if(!pgphentry) {
epicsPrintf("Record type \"%s\" not found for device \"%s\"\n",
recordtype, choicestring);
@@ -819,7 +820,7 @@ static void dbDevice(char *recordtype,char *linktype,
return;
}
pdbRecordType = (dbRecordType *)pgphentry->userPvt;
pgphentry = gphFind(pdbbase->pgpHash,choicestring,&pdbRecordType->devList);
pgphentry = gphFind(savedPdbbase->pgpHash,choicestring,&pdbRecordType->devList);
if(pgphentry) {
return;
}
@@ -827,7 +828,7 @@ static void dbDevice(char *recordtype,char *linktype,
pdevSup->name = epicsStrDup(dsetname);
pdevSup->choice = epicsStrDup(choicestring);
pdevSup->link_type = link_type;
pgphentry = gphAdd(pdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList);
pgphentry = gphAdd(savedPdbbase->pgpHash,pdevSup->choice,&pdbRecordType->devList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
} else {
@@ -845,18 +846,18 @@ static void dbDriver(char *name)
yyerrorAbort("dbDriver: Driver name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->drvList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->drvList);
if(pgphentry) {
return;
}
pdrvSup = dbCalloc(1,sizeof(drvSup));
pdrvSup->name = epicsStrDup(name);
pgphentry = gphAdd(pdbbase->pgpHash,pdrvSup->name,&pdbbase->drvList);
pgphentry = gphAdd(savedPdbbase->pgpHash,pdrvSup->name,&savedPdbbase->drvList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
}
pgphentry->userPvt = pdrvSup;
ellAdd(&pdbbase->drvList,&pdrvSup->node);
ellAdd(&savedPdbbase->drvList,&pdrvSup->node);
}
static void dbLinkType(char *name, char *jlif_name)
@@ -864,19 +865,19 @@ static void dbLinkType(char *name, char *jlif_name)
linkSup *pLinkSup;
GPHENTRY *pgphentry;
pgphentry = gphFind(pdbbase->pgpHash, name, &pdbbase->linkList);
pgphentry = gphFind(savedPdbbase->pgpHash, name, &savedPdbbase->linkList);
if (pgphentry) {
return;
}
pLinkSup = dbCalloc(1,sizeof(linkSup));
pLinkSup->name = epicsStrDup(name);
pLinkSup->jlif_name = epicsStrDup(jlif_name);
pgphentry = gphAdd(pdbbase->pgpHash, pLinkSup->name, &pdbbase->linkList);
pgphentry = gphAdd(savedPdbbase->pgpHash, pLinkSup->name, &savedPdbbase->linkList);
if (!pgphentry) {
yyerrorAbort("gphAdd failed");
}
pgphentry->userPvt = pLinkSup;
ellAdd(&pdbbase->linkList, &pLinkSup->node);
ellAdd(&savedPdbbase->linkList, &pLinkSup->node);
}
static void dbRegistrar(char *name)
@@ -888,18 +889,18 @@ static void dbRegistrar(char *name)
yyerrorAbort("dbRegistrar: Registrar name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->registrarList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->registrarList);
if(pgphentry) {
return;
}
ptext = dbCalloc(1,sizeof(dbText));
ptext->text = epicsStrDup(name);
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->registrarList);
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->registrarList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
}
pgphentry->userPvt = ptext;
ellAdd(&pdbbase->registrarList,&ptext->node);
ellAdd(&savedPdbbase->registrarList,&ptext->node);
}
static void dbFunction(char *name)
@@ -911,18 +912,18 @@ static void dbFunction(char *name)
yyerrorAbort("dbFunction: Function name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->functionList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->functionList);
if(pgphentry) {
return;
}
ptext = dbCalloc(1,sizeof(dbText));
ptext->text = epicsStrDup(name);
pgphentry = gphAdd(pdbbase->pgpHash,ptext->text,&pdbbase->functionList);
pgphentry = gphAdd(savedPdbbase->pgpHash,ptext->text,&savedPdbbase->functionList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
}
pgphentry->userPvt = ptext;
ellAdd(&pdbbase->functionList,&ptext->node);
ellAdd(&savedPdbbase->functionList,&ptext->node);
}
static void dbVariable(char *name, char *type)
@@ -934,19 +935,19 @@ static void dbVariable(char *name, char *type)
yyerrorAbort("dbVariable: Variable name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->variableList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->variableList);
if(pgphentry) {
return;
}
pvar = dbCalloc(1,sizeof(dbVariableDef));
pvar->name = epicsStrDup(name);
pvar->type = epicsStrDup(type);
pgphentry = gphAdd(pdbbase->pgpHash,pvar->name,&pdbbase->variableList);
pgphentry = gphAdd(savedPdbbase->pgpHash,pvar->name,&savedPdbbase->variableList);
if(!pgphentry) {
yyerrorAbort("gphAdd failed");
}
pgphentry->userPvt = pvar;
ellAdd(&pdbbase->variableList,&pvar->node);
ellAdd(&savedPdbbase->variableList,&pvar->node);
}
static void dbBreakHead(char *name)
@@ -958,7 +959,7 @@ static void dbBreakHead(char *name)
yyerrorAbort("dbBreakHead: Breaktable name can't be empty");
return;
}
pgphentry = gphFind(pdbbase->pgpHash,name,&pdbbase->bptList);
pgphentry = gphFind(savedPdbbase->pgpHash,name,&savedPdbbase->bptList);
if(pgphentry) {
duplicate = TRUE;
return;
@@ -1042,17 +1043,17 @@ static void dbBreakBody(void)
/* Continue with last slope beyond the final point */
paBrkInt[number-1].slope = paBrkInt[number-2].slope;
/* Add brkTable in sorted order */
pbrkTable = (brkTable *)ellFirst(&pdbbase->bptList);
pbrkTable = (brkTable *)ellFirst(&savedPdbbase->bptList);
while (pbrkTable) {
if (strcmp(pbrkTable->name, pnewbrkTable->name) > 0) {
ellInsert(&pdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable),
ellInsert(&savedPdbbase->bptList, ellPrevious((ELLNODE *)pbrkTable),
(ELLNODE *)pnewbrkTable);
break;
}
pbrkTable = (brkTable *)ellNext(&pbrkTable->node);
}
if (!pbrkTable) ellAdd(&pdbbase->bptList, &pnewbrkTable->node);
pgphentry = gphAdd(pdbbase->pgpHash,pnewbrkTable->name,&pdbbase->bptList);
if (!pbrkTable) ellAdd(&savedPdbbase->bptList, &pnewbrkTable->node);
pgphentry = gphAdd(savedPdbbase->pgpHash,pnewbrkTable->name,&savedPdbbase->bptList);
if (!pgphentry) {
yyerrorAbort("dbBreakBody: gphAdd failed");
return;
@@ -1067,7 +1068,7 @@ int dbRecordNameValidate(const char *name)
const char *pos = name;
if (!*name) {
yyerrorAbort("Error: Record/Alias name can't be empty");
yyerrorAbort(ERL_ERROR ": Record/Alias name can't be empty");
return 1;
}
@@ -1085,7 +1086,7 @@ int dbRecordNameValidate(const char *name)
name, c);
} else if(c==' ' || c=='\t' || c=='"' || c=='\'' || c=='.' || c=='$') {
epicsPrintf("Error: Bad character '%c' in Record/Alias name \"%s\"\n",
epicsPrintf(ERL_ERROR ": Bad character '%c' in Record/Alias name \"%s\"\n",
c, name);
yyerrorAbort(NULL);
return 1;
@@ -1103,20 +1104,16 @@ static void dbRecordHead(char *recordType, char *name, int visible)
if(dbRecordNameValidate(name))
return;
pdbentry = dbAllocEntry(pdbbase);
pdbentry = dbAllocEntry(savedPdbbase);
if (ellCount(&tempList))
yyerrorAbort("dbRecordHead: tempList not empty");
allocTemp(pdbentry);
if (recordType[0] == '*' && recordType[1] == 0) {
if (dbRecordsOnceOnly)
epicsPrintf("Record-type \"*\" not valid with dbRecordsOnceOnly\n");
else {
status = dbFindRecord(pdbentry, name);
if (status == 0)
return; /* done */
epicsPrintf("Record \"%s\" not found\n", name);
}
status = dbFindRecord(pdbentry, name);
if (status == 0)
return; /* done */
epicsPrintf(ERL_ERROR ": Record \"%s\" not found\n", name);
yyerror(NULL);
duplicate = TRUE;
return;
@@ -1135,15 +1132,16 @@ static void dbRecordHead(char *recordType, char *name, int visible)
status = dbCreateRecord(pdbentry,name);
if (status == S_dbLib_recExists) {
if (strcmp(recordType, dbGetRecordTypeName(pdbentry)) != 0) {
epicsPrintf("Record \"%s\" of type \"%s\" redefined with new type "
epicsPrintf(ERL_ERROR ": Record \"%s\" of type \"%s\" redefined with new type "
"\"%s\"\n", name, dbGetRecordTypeName(pdbentry), recordType);
yyerror(NULL);
duplicate = TRUE;
return;
}
else if (dbRecordsOnceOnly) {
epicsPrintf("Record \"%s\" already defined (dbRecordsOnceOnly is "
"set)\n", name);
epicsPrintf(ERL_ERROR ": Record \"%s\" already defined and dbRecordsOnceOnly set.\n"
"Used record type \"*\" to append.\n",
name);
yyerror(NULL);
duplicate = TRUE;
}
@@ -1169,8 +1167,28 @@ static void dbRecordField(char *name,char *value)
pdbentry = ptempListNode->item;
status = dbFindField(pdbentry,name);
if (status) {
epicsPrintf("Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordName(pdbentry), name);
epicsPrintf("%s Record \"%s\" does not have a field \"%s\"\n",
dbGetRecordTypeName(pdbentry), dbGetRecordName(pdbentry), name);
if(dbGetRecordName(pdbentry)) {
DBENTRY temp;
double bestSim = -1.0;
const dbFldDes *bestFld = NULL;
dbCopyEntryContents(pdbentry, &temp);
for(status = dbFirstField(&temp, 0); !status; status = dbNextField(&temp, 0)) {
double sim = epicsStrSimilarity(name, temp.pflddes->name);
if(!bestFld || sim > bestSim) {
bestSim = sim;
bestFld = temp.pflddes;
}
}
dbFinishEntry(&temp);
if(bestSim>0.0) {
epicsPrintf(" Did you mean \"%s\"?", bestFld->name);
if(bestFld->prompt)
epicsPrintf(" (%s)", bestFld->prompt);
epicsPrintf("\n");
}
}
yyerror(NULL);
return;
}
@@ -1257,10 +1275,10 @@ static void dbAlias(char *name, char *alias)
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
if(dbRecordNameValidate(alias))
if(dbRecordNameValidate(alias) || dbRecordNameValidate(name))
return;
dbInitEntry(pdbbase, pdbEntry);
dbInitEntry(savedPdbbase, pdbEntry);
if (dbFindRecord(pdbEntry, name)) {
epicsPrintf("Alias \"%s\" refers to unknown record \"%s\"\n",
alias, name);

View File

@@ -9,6 +9,8 @@
\*************************************************************************/
#include "iocsh.h"
#include "errSymTbl.h"
#include "errlog.h"
#include "dbStaticIocRegister.h"
#include "dbStaticLib.h"
@@ -23,7 +25,8 @@ static const iocshArg argRecType = { "recordTypeName", iocshArgString};
/* dbDumpPath */
static const iocshArg * const dbDumpPathArgs[] = {&argPdbbase};
static const iocshFuncDef dbDumpPathFuncDef = {"dbDumpPath",1,dbDumpPathArgs,
"Dump .db/.dbd file search path.\n"};
"Dump .db/.dbd file search path.\n"
"Example: dbDumpPath pdbbase\n"};
static void dbDumpPathCallFunc(const iocshArgBuf *args)
{
dbDumpPath(*iocshPpdbbase);
@@ -98,7 +101,8 @@ static void dbDumpDeviceCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpDriverArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpDriverFuncDef = {"dbDumpDriver",1,dbDumpDriverArgs,
"Dump device support information.\n"
"Example: dbDumpDriver pdbbase\n"};
"Example: dbDumpDriver pdbbase\n"
"If the last argument(s) are missing, dump all device support information.\n",};
static void dbDumpDriverCallFunc(const iocshArgBuf *args)
{
dbDumpDriver(*iocshPpdbbase);
@@ -123,7 +127,8 @@ static const iocshArg * const dbDumpRegistrarArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpRegistrarFuncDef = {"dbDumpRegistrar",1,dbDumpRegistrarArgs,
"Dump list of registered functions including ones for subroutine records,\n"
"and ones that can be invoked from iocsh.\n"
"Example: dbDumpRegistrar pdbbase\n"};
"Example: dbDumpRegistrar pdbbase\n"
"If last argument(s) are missing, dump all registered functions\n"};
static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
{
dbDumpRegistrar(*iocshPpdbbase);
@@ -133,7 +138,8 @@ static void dbDumpRegistrarCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpFunctionArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpFunctionFuncDef = {"dbDumpFunction",1,dbDumpFunctionArgs,
"Dump list of registered subroutine functions.\n"
"Example: dbDumpFunction pddbase\n"};
"Example: dbDumpFunction pddbase\n"
"If last argument(s) are missing, dump all registered subroutine functions\n"};
static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
{
dbDumpFunction(*iocshPpdbbase);
@@ -143,7 +149,8 @@ static void dbDumpFunctionCallFunc(const iocshArgBuf *args)
static const iocshArg * const dbDumpVariableArgs[] = { &argPdbbase};
static const iocshFuncDef dbDumpVariableFuncDef = {"dbDumpVariable",1,dbDumpVariableArgs,
"Dump list of variables used in the database.\n"
"Example: dbDumpVariable pddbase\n"};
"Example: dbDumpVariable pddbase\n"
"If last argument(s) are missing, dump all variables.\n"};
static void dbDumpVariableCallFunc(const iocshArgBuf *args)
{
dbDumpVariable(*iocshPpdbbase);
@@ -158,7 +165,8 @@ static const iocshFuncDef dbDumpBreaktableFuncDef = {
2,
dbDumpBreaktableArgs,
"Dump the given break table\n"
"Example: dbDumpBreaktable pdbbase typeKdegC\n",
"Example: dbDumpBreaktable pdbbase typeKdegC\n"
"If last argument(s) are missing, dump all breakpoint tables.\n",
};
static void dbDumpBreaktableCallFunc(const iocshArgBuf *args)
{
@@ -175,7 +183,8 @@ static const iocshFuncDef dbPvdDumpFuncDef = {
dbPvdDumpArgs,
"Dump the various buckets of the process variable directory.\n"
"If verbose is greater than 0, also print the process variables in each bucket.\n"
"Example: dbPvdDump pdbbase 1\n",
"Example: dbPvdDump pdbbase 1\n"
"If the last argument(s) are missing, dump all buckets as though verbose is 0.\n",
};
static void dbPvdDumpCallFunc(const iocshArgBuf *args)
{
@@ -217,6 +226,40 @@ static void dbReportDeviceConfigCallFunc(const iocshArgBuf *args)
dbReportDeviceConfig(*iocshPpdbbase,stdout);
}
static const iocshArg dbCreateAliasArg0 = { "record",iocshArgStringRecord};
static const iocshArg dbCreateAliasArg1 = { "alias",iocshArgStringRecord};
static const iocshArg * const dbCreateAliasArgs[] = {&argPdbbase,&dbCreateAliasArg0, &dbCreateAliasArg1};
static const iocshFuncDef dbCreateAliasFuncDef = {
"dbCreateAlias",
3,
dbCreateAliasArgs,
"Add a new record alias.\n"
"\n"
"Example: dbCreateAlias pdbbase record:name new:alias\n",
};
static void dbCreateAliasCallFunc(const iocshArgBuf *args)
{
DBENTRY ent;
long status;
dbInitEntry(*iocshPpdbbase, &ent);
if(!args[1].sval || !args[2].sval) {
status = S_dbLib_recNotFound;
} else {
status = dbFindRecord(&ent, args[1].sval);
if(!status) {
status = dbCreateAlias(&ent, args[2].sval);
}
}
dbFinishEntry(&ent);
if(status) {
fprintf(stderr, ERL_ERROR ": %ld %s\n", status, errSymMsg(status));
iocshSetError(1);
}
}
void dbStaticIocRegister(void)
{
iocshRegister(&dbDumpPathFuncDef, dbDumpPathCallFunc);
@@ -234,4 +277,5 @@ void dbStaticIocRegister(void)
iocshRegister(&dbPvdDumpFuncDef, dbPvdDumpCallFunc);
iocshRegister(&dbPvdTableSizeFuncDef,dbPvdTableSizeCallFunc);
iocshRegister(&dbReportDeviceConfigFuncDef, dbReportDeviceConfigCallFunc);
iocshRegister(&dbCreateAliasFuncDef, dbCreateAliasCallFunc);
}

View File

@@ -57,10 +57,10 @@ static char *pNullString = "";
*/
STATIC_ASSERT(messagesize >= 21);
static char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
static char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
static const char *ppstring[5]={" NPP"," PP"," CA"," CP"," CPP"};
static const char *msstring[4]={" NMS"," MS"," MSI"," MSS"};
maplinkType pamaplinkType[LINK_NTYPES] = {
const maplinkType pamaplinkType[LINK_NTYPES] = {
{"CONSTANT",CONSTANT},
{"PV_LINK",PV_LINK},
{"VME_IO",VME_IO},
@@ -89,7 +89,7 @@ static FILE *openOutstream(const char *filename)
errno = 0;
stream = fopen(filename,"w");
if(!stream) {
fprintf(stderr,"error opening %s %s\n",filename,strerror(errno));
fprintf(stderr,ERL_ERROR " opening %s %s\n",filename,strerror(errno));
return 0;
}
return stream;
@@ -637,7 +637,7 @@ void dbFinishEntry(DBENTRY *pdbentry)
}
}
DBENTRY * dbCopyEntry(DBENTRY *pdbentry)
DBENTRY * dbCopyEntry(const DBENTRY *pdbentry)
{
DBENTRY *pnew;
@@ -647,7 +647,7 @@ DBENTRY * dbCopyEntry(DBENTRY *pdbentry)
return(pnew);
}
void dbCopyEntryContents(DBENTRY *pfrom,DBENTRY *pto)
void dbCopyEntryContents(const DBENTRY *pfrom,DBENTRY *pto)
{
*pto = *pfrom;
pto->message = NULL;
@@ -1445,6 +1445,7 @@ long dbCreateRecord(DBENTRY *pdbentry,const char *precordName)
pdbentry->precnode = pNewRecNode;
ppvd = dbPvdAdd(pdbentry->pdbbase,precordType,pNewRecNode);
if(!ppvd) {errMessage(-1,"Logic Err: Could not add to PVD");return(-1);}
pNewRecNode->order = pdbentry->pdbbase->no_records++;
return(0);
}
@@ -1652,6 +1653,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
dbRecordNode *pnewnode;
DBENTRY tempEntry;
PVDENTRY *ppvd;
long status;
if (!precordType)
return S_dbLib_recordTypeNotFound;
@@ -1664,9 +1666,10 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
return S_dbLib_recNotFound;
dbInitEntry(pdbentry->pdbbase, &tempEntry);
if (!dbFindRecord(&tempEntry, alias))
return S_dbLib_recExists;
status = dbFindRecord(&tempEntry, alias);
dbFinishEntry(&tempEntry);
if (!status)
return S_dbLib_recExists;
pnewnode = dbCalloc(1, sizeof(dbRecordNode));
pnewnode->recordname = epicsStrDup(alias);
@@ -1676,15 +1679,17 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
precnode->flags |= DBRN_FLAGS_HASALIAS;
ellInit(&pnewnode->infoList);
ellAdd(&precordType->recList, &pnewnode->node);
precordType->no_aliases++;
ppvd = dbPvdAdd(pdbentry->pdbbase, precordType, pnewnode);
if (!ppvd) {
errMessage(-1, "dbCreateAlias: Add to PVD failed");
free(pnewnode);
return -1;
}
ellAdd(&precordType->recList, &pnewnode->node);
pnewnode->order = pdbentry->pdbbase->no_records++;
precordType->no_aliases++;
return 0;
}
@@ -2041,13 +2046,17 @@ char *dbGetStringNum(DBENTRY *pdbentry)
{
dbFldDes *pflddes = pdbentry->pflddes;
void *pfield = pdbentry->pfield;
char *message;
char *message = getpMessage(pdbentry);
unsigned char cvttype;
if (!pfield) {
dbMsgCpy(pdbentry, "Field not found");
return message;
}
/* the following assumes that messagesize is large enough
* to hold the base 10 encoded value of a 32-bit integer.
*/
message = getpMessage(pdbentry);
cvttype = pflddes->base;
switch (pflddes->field_type) {
case DBF_CHAR:
@@ -2109,37 +2118,34 @@ char *dbGetStringNum(DBENTRY *pdbentry)
{
dbMenu *pdbMenu = (dbMenu *)pflddes->ftPvt;
epicsEnum16 choice_ind;
char *pchoice;
if (!pfield) {
dbMsgCpy(pdbentry, "Field not found");
return message;
}
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
if (!pdbMenu || choice_ind < 0 || choice_ind >= pdbMenu->nChoice)
if (!pdbMenu)
return NULL;
pchoice = pdbMenu->papChoiceValue[choice_ind];
dbMsgCpy(pdbentry, pchoice);
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
if (choice_ind >= pdbMenu->nChoice) {
dbMsgPrint(pdbentry, "%u", choice_ind);
}
else {
dbMsgCpy(pdbentry, pdbMenu->papChoiceValue[choice_ind]);
}
}
break;
case DBF_DEVICE:
{
dbDeviceMenu *pdbDeviceMenu;
dbDeviceMenu *pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
epicsEnum16 choice_ind;
char *pchoice;
if (!pfield) {
dbMsgCpy(pdbentry, "Field not found");
return message;
if (!pdbDeviceMenu) {
dbMsgCpy(pdbentry, "");
break;
}
pdbDeviceMenu = dbGetDeviceMenu(pdbentry);
if (!pdbDeviceMenu)
return NULL;
choice_ind = *((epicsEnum16 *) pdbentry->pfield);
if (choice_ind<0 || choice_ind>=pdbDeviceMenu->nChoice)
if (choice_ind>=pdbDeviceMenu->nChoice)
return NULL;
pchoice = pdbDeviceMenu->papChoice[choice_ind];
dbMsgCpy(pdbentry, pchoice);
dbMsgCpy(pdbentry, pdbDeviceMenu->papChoice[choice_ind]);
}
break;
default:
@@ -2200,11 +2206,11 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
*/
} else if(dbCanSetLink(plink, &link_info, devsup)!=0) {
errlogPrintf("Error: %s.%s: can't initialize link type %d with \"%s\" (type %d)\n",
errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %d with \"%s\" (type %d)\n",
prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
} else if(dbSetLink(plink, &link_info, devsup)) {
errlogPrintf("Error: %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n",
errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %d with \"%s\" (type %d)\n",
prec->name, pflddes->name, plink->type, plink->text, link_info.ltype);
}
free(plink->text);

View File

@@ -51,9 +51,9 @@ DBCORE_API void dbInitEntry(DBBASE *pdbbase,
DBENTRY *pdbentry);
DBCORE_API void dbFinishEntry(DBENTRY *pdbentry);
DBCORE_API DBENTRY * dbCopyEntry(DBENTRY *pdbentry);
DBCORE_API void dbCopyEntryContents(DBENTRY *pfrom,
DBENTRY *pto);
DBCORE_API DBENTRY * dbCopyEntry(const DBENTRY *pdbentry);
DBCORE_API void dbCopyEntryContents(const DBENTRY *pfrom,
DBENTRY *pto);
DBCORE_API extern int dbBptNotMonotonic;

View File

@@ -39,7 +39,9 @@ char *dbRecordName(DBENTRY *pdbentry);
char *dbGetStringNum(DBENTRY *pdbentry);
long dbPutStringNum(DBENTRY *pdbentry,const char *pstring);
void dbMsgPrint(DBENTRY *pdbentry, const char *fmt, ...) EPICS_PRINTF_STYLE(2,3);
void dbMsgPrint(
DBENTRY *pdbentry, EPICS_PRINTF_FMT(const char *fmt), ...
) EPICS_PRINTF_STYLE(2,3);
void dbPutStringSuggest(DBENTRY *pdbentry, const char *pstring);
@@ -115,6 +117,9 @@ PVDENTRY *dbPvdAdd(DBBASE *pdbbase,dbRecordType *precordType,dbRecordNode *precn
void dbPvdDelete(DBBASE *pdbbase,dbRecordNode *precnode);
void dbPvdFreeMem(DBBASE *pdbbase);
DBCORE_API
char** dbCompleteRecord(const char *word);
#ifdef __cplusplus
}
#endif

View File

@@ -143,7 +143,7 @@ long dbAllocRecord(DBENTRY *pdbentry,const char *precordName)
status = dbPutStringNum(pdbentry,pflddes->initial);
if(status)
epicsPrintf("Error initializing %s.%s initial %s\n",
epicsPrintf(ERL_ERROR " initializing %s.%s initial %s\n",
pdbRecordType->name,pflddes->name,pflddes->initial);
}
break;

View File

@@ -370,9 +370,9 @@ json_value: jsonNULL { $$ = dbmfStrdup("null"); }
static int yyerror(char *str)
{
if (str)
epicsPrintf("Error: %s\n", str);
epicsPrintf(ERL_ERROR ": %s\n", str);
else
epicsPrintf("Error");
epicsPrintf(ERL_ERROR "");
if (!yyFailed) { /* Only print this stuff once */
epicsPrintf(" at or before '%s'", yytext);
dbIncludePrint();

View File

@@ -43,11 +43,11 @@ extern "C" {
#define VXI_IO 15
#define LINK_NTYPES 16
typedef struct maplinkType {
char *strvalue;
const char *strvalue;
int value;
} maplinkType;
DBCORE_API extern maplinkType pamaplinkType[];
DBCORE_API extern const maplinkType pamaplinkType[LINK_NTYPES];
#define VXIDYNAMIC 0
#define VXISTATIC 1
@@ -193,7 +193,7 @@ struct lset;
struct link {
struct dbCommon *precord; /* Pointer to record owning link */
short type;
short flags;
unsigned short flags;
struct lset *lset;
char *text; /* Raw link text */
union value value;

View File

@@ -16,6 +16,7 @@
#include "osiUnistd.h"
#include "macLib.h"
#include "dbmf.h"
#include "errlog.h"
#include "epicsExport.h"
#include "dbAccess.h"
@@ -337,7 +338,7 @@ int dbLoadTemplate(const char *sub_file, const char *cmd_collect)
if (dbTemplateMaxVars < 1)
{
fprintf(stderr,"Error: dbTemplateMaxVars = %d, must be +ve\n",
fprintf(stderr,ERL_ERROR ": dbTemplateMaxVars = %d, must be +ve\n",
dbTemplateMaxVars);
return -1;
}

View File

@@ -13,7 +13,7 @@
/* dbLoadTemplate */
static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgString};
static const iocshArg dbLoadTemplateArg0 = {"filename", iocshArgStringPath};
static const iocshArg dbLoadTemplateArg1 = {"var1=value1,var2=value2", iocshArgString};
static const iocshArg * const dbLoadTemplateArgs[2] = {
&dbLoadTemplateArg0, &dbLoadTemplateArg1

View File

@@ -264,7 +264,7 @@ static void addMacroReplacements(MAC_HANDLE * const macPvt,
if (status) {
status = macInstallMacros(macPvt, pairs);
if (!status) {
fprintf(stderr, "Error from macInstallMacros\n");
fprintf(stderr, ERL_ERROR " from macInstallMacros\n");
usageExit(1);
}
free(pairs);
@@ -522,7 +522,7 @@ static void inputOpenFile(inputData *pinputData, const char * const filename)
}
if (!fp) {
fprintf(stderr, "msi: Can't open file '%s'\n", filename);
fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", filename);
inputErrPrint(pinputData);
abortExit(1);
}
@@ -672,7 +672,7 @@ static void substituteOpen(subInfo **ppvt, const std::string& substitutionName)
fp = fopen(substitutionName.c_str(), "r");
if (!fp) {
fprintf(stderr, "msi: Can't open file '%s'\n", substitutionName.c_str());
fprintf(stderr, ERL_ERROR " msi: Can't open file '%s'\n", substitutionName.c_str());
abortExit(1);
}

View File

@@ -17,7 +17,7 @@ IOCSH_STATIC_FUNC void dlload(const char* name)
}
}
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgString};
static const iocshArg dlloadArg0 = { "path/library.so", iocshArgStringPath};
static const iocshArg * const dlloadArgs[] = {&dlloadArg0};
static const iocshFuncDef dlloadFuncDef = {
"dlload",

View File

@@ -173,7 +173,7 @@ static int iocBuild_2(void)
scanInit();
if (asInit()) {
errlogPrintf("iocBuild: asInit Failed.\n");
errlogPrintf(ERL_ERROR " iocBuild: asInit Failed.\n");
return -1;
}
dbProcessNotifyInit();
@@ -716,13 +716,13 @@ int iocShutdown(void)
iterateRecords(doCloseLinks, NULL);
initHookAnnounce(initHookAfterCloseLinks);
if (iocBuildMode == buildIsolated) {
/* stop and "join" threads */
scanStop();
initHookAnnounce(initHookAfterStopScan);
callbackStop();
initHookAnnounce(initHookAfterStopCallback);
} else {
/* stop and "join" threads */
scanStop();
initHookAnnounce(initHookAfterStopScan);
callbackStop();
initHookAnnounce(initHookAfterStopCallback);
if (iocBuildMode != buildIsolated) {
dbStopServers();
}

View File

@@ -38,7 +38,7 @@ static void iocBuildCallFunc(const iocshArgBuf *args)
/* iocRun */
static const iocshFuncDef iocRunFuncDef = {"iocRun",0,NULL,
"Bring the IOC out of its initial quiescent state to the running state.\n"
"See more: iocBuild, iocPause"};
"See more: iocBuild, iocPause\n"};
static void iocRunCallFunc(const iocshArgBuf *args)
{
iocshSetError(iocRun());
@@ -47,7 +47,7 @@ static void iocRunCallFunc(const iocshArgBuf *args)
/* iocPause */
static const iocshFuncDef iocPauseFuncDef = {"iocPause",0,NULL,
"Brings a running IOC to a quiescent state with all record processing frozen.\n"
"See more: iocBuild, iocRub, iocInit"};
"See more: iocBuild, iocRub, iocInit\n"};
static void iocPauseCallFunc(const iocshArgBuf *args)
{
iocshSetError(iocPause());

View File

@@ -17,6 +17,7 @@
#include <epicsStdio.h>
#include <epicsFindSymbol.h>
#include <errlog.h>
#include <registryRecordType.h>
#include <registryDeviceSupport.h>
#include <registryDriverSupport.h>
@@ -248,7 +249,7 @@ registerAllRecordDeviceDrivers(DBBASE *pdbbase)
} catch(std::exception& e) {
dbFinishEntry(&entry);
fprintf(stderr, "Error: %s\n", e.what());
fprintf(stderr, ERL_ERROR ": %s\n", e.what());
return 2;
}
}

View File

@@ -120,6 +120,11 @@ static void req_server (void *pParm)
}
}
}
/* ATM never reached, just a placeholder */
cantProceed("Unreachable. Perpetual thread.");
taskwdRemove(0);
}
static
@@ -748,6 +753,7 @@ void rsrv_init (void)
if(!havesometcp)
cantProceed("CAS: No TCP server started\n");
}
free(socks);
/* servers list is considered read-only from this point */

View File

@@ -129,7 +129,11 @@ void rsrv_online_notify_task(void *pParm)
}
}
/* ATM never reached, just a placeholder */
cantProceed("Unreachable. Perpetual thread.");
free(lastError);
taskwdRemove(0);
}

View File

@@ -51,6 +51,9 @@ static long readLocked(struct link *pinp, void *dummy)
if (status) return status;
if (prec->mask)
prec->rval &= prec->mask;
if (dbLinkIsConstant(&prec->tsel) &&
prec->tse == epicsTimeEventDeviceTime)
dbGetTimeStamp(pinp, &prec->time);

View File

@@ -17,6 +17,7 @@
#include <stdio.h>
#include "freeList.h"
#include "caeventmask.h"
#include "db_field_log.h"
#include "chfPlugin.h"
#include "epicsExit.h"
@@ -60,7 +61,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
myStruct *my = (myStruct*) pvt;
epicsInt32 i = my->i;
if (pfl->ctx == dbfl_context_read)
if (pfl->ctx == dbfl_context_read || (pfl->mask & DBE_PROPERTY))
return pfl;
if (i++ == 0)

View File

@@ -1,59 +1,214 @@
=head1 Channel Filters
=title Field Modifiers and Channel Filters
Channel Filters can be applied to Channel Access channels by a client, using
a JSON Field Modifier to select the filter and any parameters.
The following filters are available in this release:
=head2 Contents
=over
=item * L<TimeStamp|/"TimeStamp Filter ts">
=item * L<Introduction
|/"Introduction">
=item * L<Deadband|/"Deadband Filter dbnd">
=item * L<Using Field Modifiers and Channel Filters
|/"Using Field Modifiers and Channel Filters">
=item * L<Array|/"Array Filter arr">
=item * L<Example Filters
|/"Example Filters">
=item * L<Synchronize|/"Synchronize Filter sync">
=item * L<Field Modifier Reference
|/"Field Modifier Reference">
=item * L<Decimation|/"Decimation Filter dec">
=over
=item * L<UTag|/"UTag Filter utag">
=item * L<Long String Modifier C<$>
|/"Long String Field Modifier $">
=item * L<Subarray Modifier C<<< [E<hellip>] >>>
|/"Subarray Field Modifier [start:increment:end]">
=back
=head2 Using Filters
=item * L<JSON5 Channel Filters
|/"JSON5 Channel Filters">
Channel filters can be added to any Channel Access channel name.
There can be more than one filter applied to the same channel, in which case the
order that they are specified will control the order in which they are applied
to the resulting data-stream.
The filter specification must appear after the field name, or if the default
(VAL) field is used after a dot C<.> appended to the record name.
With the exception of the array short-hand which is described below, all filters
must appear inside a pair of braces C< {} > after the dot expressed as a JSON
(L<JavaScript Object Notation|http://www.json.org/>) object, which allows filter
parameters to be included as needed.
=over
Each filter is given as a name/value pair. The filter name (given in parentheses
in the titles below) is a string, and must be enclosed inside double-quotes C<">
characters as per the JSON specification.
Parameters to that filter are provided as the value part of the name/value pair,
and will normally appear as a child JSON object consisting of name/value pairs
inside a nested pair of braces C< {} >.
=item * L<TimeStamp Filter C<<< {ts:{}} >>>
|/"TimeStamp Filter ts">
=head4 Example Filter
=item * L<Deadband Filter C<<< {dbnd:{E<hellip>}} >>>
|/"Deadband Filter dbnd">
Given a record called C<test:channel> the following would apply a filter C<f> to
the VAL field of that record, giving the filter two numeric parameters named
C<lo> and C<hi>:
=item * L<Array Filter C<<< {arr:{E<hellip>}} >>>
|/"Array Filter arr">
test:channel.{"f":{"lo":0,"hi":10}}
=item * L<Synchronize Filter C<<< {sync:{E<hellip>}} >>>
|/"Synchronize Filter sync">
Note that due to the required presence of the double-quote characters in the
JSON strings in the name string, it will usually be necessary to enclose a
filtered name within single-quotes C<< ' ... ' >> when typing it as an
argument to a Unix shell command.
=item * L<Decimation Filter C<<< {dec:{E<hellip>}} >>>
|/"Decimation Filter dec">
=head2 Filter Reference
=item * L<User Tag Filter C<<< {utag:{E<hellip>}} >>>
|/"User Tag Filter utag">
=back
=back
=head2 Introduction
A Field Modifier is a string that is appended to the field name part of a
Channel Access or PV Access channel name of an IOC-based server.
The IOC currently recognizes 3 different kinds of field modifier, which are
described below.
A Channel Filter is an IOC plugin that can be attached to an IOC process
variable channel using a field modifier, and can alter the data updates that are
served to the client that requested the filtering.
Clients that use a channel filter have no effect on other clients connected to
the same PV.
Filters can only modify or drop monitor events that the IOC posts; introducing
extra monitor events (in between the events posted by the IOC itself) is not
currently possible.
Most Channel Filters are configured by a field modifier that uses JSON5
syntax to select the type of filter and provide any parameters it accepts.
=head2 Using Field Modifiers and Channel Filters
Modifiers can be added to any Channel Access or PV Access channel name.
There can be more than one modifier or filter applied to a channel.
Modifiers must appear immediately after the field name if one is included in the
channel name.
If no field is named because the default (VAL) field is the target, the
modifiers must come immediately after a dot C<.> appended to the record name.
The order that modifiers and filters are specified controls the order in which
they are applied to the resulting data-stream.
If used, the Long String and Subarray field modifiers must appear first and in
that order, followed by any channel filters inside a pair of braces C< {} > that
form a JSON5 (L<JavaScript Object Notation|https://spec.json5.org/>) map, which
allows filters and their parameters to be specified as needed.
Each JSON5 filter is specified as a name:value pair.
The filter name is a map key that may be an unquoted identifier name (which all
of the filter names given below are), or a string enclosed inside either single-
or double-quote characters C<'> or C<"> as per the JSON5 specification.
Parameters to the filter are provided in the value part of the key:value pair
after the colon C<:> character, and will normally be an embedded JSON5 map
containing zero or more key/value pairs inside a nested pair of braces C< {} >.
Unless included inside a quoted string, white space characters are ignored and
skipped over by the JSON5 parser between other token characters.
This includes horizontal and vertical tabs, line feed/form feed/carriage return,
space and the non-breaking space character C< \xA0 >.
Within a quoted string, line breaks may escaped with a backslash C<\> to be
omitted from the parsed string.
An IOC Channel Access link can include filters in its channel name, but it is
important to not include any spaces at all in the filter specification.
If a filter name or parameter must contain a space it will be necessary to
express that space character as an escaped character C<\x20> or C<\u0020> inside
a quoted string, otherwise the space will mark the end of the channel name to
the link parsing code inside the IOC.
=head4 Example Filters
Given a record called C<test:channel> the following would all apply a channel
filter C<f> to the VAL field of that record, giving the filter two numeric
parameters named C<lo> and C<hi>:
test:channel.{f:{lo:0,hi:10}}
test:channel.{"f":{"lo":0, "hi":10}}
test:channel.{'f': {'lo':0, 'hi':10} }
When typing a filtered channel name as an argument to a Unix shell command, if
quote characters are used for keys or values in JSON strings within the channel
name string it may be necessary to enclose the name within quotes C<'> or C<">
or to use back-slash escapes before them.
Quotes may not be required when the Long String modifier C<$> is used at the end
of a field name with nothing following it, but will be necessary for a square
bracketted Subarray filter or if a dollar sign is followed by something else.
Hal$ caget test:channel.{f:{lo:0,hi:10}}
...
Hal$ caget 'test:channel.{"f":{"lo":0, "hi":10}}'
...
Hal$ caget -S calc:record.CALC$
...
Hal$ caget -S 'test:channel.NAME$[0:4]'
test:channel.NAME$[0:4] test
=head2 Field Modifier Reference
The two built-in field modifiers use a simplified syntax following the record
field name.
=head3 Long String Field Modifier C<$>
Appending a dollar sign C<$> to the name of a C<DBF_STRING> field causes the IOC
to change the representation of that field into an array of characters, which
allows strings longer than 40 character to be transported through Channel
Access.
Long strings are particularly useful for the CALC fields of a calculation or
calcout record, which can hold up to 80 characters, or the VAL fields of an lsi
(Long String Input) or lso (Long String Output) record which can be any length
as chosen by the database designer.
Hal$ cainfo test:channel.NAME
test:channel.NAME
State: connected
Host: 10.234.56.78:5064
Access: read, no write
Native data type: DBF_STRING
Request type: DBR_STRING
Element count: 1
Hal$ cainfo test:channel.NAME$
test:channel.NAME$
State: connected
Host: 10.234.56.78:5064
Access: read, no write
Native data type: DBF_CHAR
Request type: DBR_CHAR
Element count: 61
A CA client accessing a channel that uses the Long String field modifier will
have to be specifically configured to treat the data as a string instead of the
array of C<DBF_CHAR> that it looks like.
CA clients should not attempt to parse the channel name themselves to recognize
this field modifier in the name.
All long string values returned by the IOC should include a trailing zero byte
in their data as is standard for strings in the C language.
For the catools programs provided with Base, the flag C<-S> indicates that a
channel containing a character array should be treated as a long string.
Hal$ caget test:channel.NAME
test:channel.NAME test:channel
Hal$ caget test:channel.NAME$
test:channel.NAME$ 61 116 101 115 116 58 99 104 97 110 110 101 108 0 0 ...
Hal$ caget -S test:channel.NAME$
test:channel.NAME$ test:channel
=head3 Subarray Field Modifier C<[start:increment:end]>
This square-bracket field modifier syntax gets translated within the IOC into
calls to the L<Array Filter|/"Array Filter arr">, see that section below for
details of this shorthand.
The subarray field modifier syntax can immediately follow a Long String field
modifier, which permits fetching various kinds of substrings from the field.
This syntax cannot appear after a JSON filter specification though, the JSON
"arr" filter syntax must be used to apply an array filter after any other JSON
filter type.
Hal$ caget -S 'test:channel.NAME$[0:4]'
test:channel.NAME$[0:4] test
Hal$ caget -S 'test:channel.NAME$[5:-1]'
test:channel.NAME$[5:-1] channel
=head2 JSON5 Channel Filters
=cut
@@ -61,22 +216,96 @@ registrar(tsInitialize)
=head3 TimeStamp Filter C<"ts">
This filter is used to set the timestamp of the value fetched through
the channel to the time the value was fetched (or an update was sent),
rather than the time the record last
processed which could have been days or even weeks ago for some records, or set
to the EPICS epoch if the record has never processed.
This filter is used for two purposes:
=over
=item * to retrieve the timestamp of the record as a value in several different
formats;
=item * to retrieve the record value as normal, but replace the timestamp with
the time the value was fetched.
=back
=head4 Parameters
None, use an empty pair of braces.
=head4 No parameters (an empty pair of braces)
=head4 Example
Retrieve the record value as normal, but replace the timestamp with the time the
value was fetched (or an update was sent). This is useful for clients that can't
handle timestamps that are far in the past. Normally, the record's timestamp
indicates when the record last processed, which could have been days or even
weeks ago for some records, or set to the EPICS epoch if the record has never
processed.
Hal$ caget -a 'test:channel.{"ts":{}}'
test:channel.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID
Hal$ caget -a 'test:channel'
test:channel <undefined> 0 UDF INVALID
=head4 Numeric type C<"num">
The following values are accepted for this parameter:
=over
=item * C<"dbl"> requests the timestamp as C<epicsFloat64> representing the
non-integral number of seconds since epoch. This format is convenient,
but loses precision, depending on which epoch is used.
=item * C<"sec"> requests the number of seconds since epoch as C<epicsUInt32>.
=item * C<"nsec"> requests the number of nanoseconds since epoch as
C<epicsUInt32>.
=item * C<"ts"> requests the entire timestamp. It is provided as a two-element
array of C<epicsUInt32> representing seconds and nanoseconds.
=back
Note that C<epicsUInt32> cannot be transferred over Channel Access; in that
case, the value will be converted to C<epicsFloat64>.
=head4 String type C<"str">
The following values are accepted for this parameter:
=over
=item * C<"epics"> requests the timestamp as a string in the format used by
tools such as C<caget>.
=item * C<"iso"> requests the timestamp as a string in the ISO8601 format.
=back
=head4 Epoch adjustment C<"epoch">
The following values are accepted for this parameter:
=over
=item * C<"epics"> keeps the EPICS epoch (1990-01-01) and is the default if the
C<"epoch"> parameter is not specified.
=item * C<"unix"> converts the timestamp to the UNIX/POSIX epoch (1970-01-01).
=back
=head4 Examples
Hal$ caget -a 'test:invalid_ts.{"ts":{}}'
test:invalid_ts.{"ts":{}} 2012-08-28 22:10:31.192547 0 UDF INVALID
Hal$ caget -a 'test:invalid_ts'
test:invalid_ts <undefined> 0 UDF INVALID
Hal$ caget -a test:channel
test:channel 2021-03-11 18:23:48.265386 42
Hal$ caget 'test:channel.{"ts": {"str": "epics"}}'
test:channel.{"ts": {"str": "epics"}} 2021-03-11 18:23:48.265386
Hal$ caget 'test:channel.{"ts": {"str": "iso"}}'
test:channel.{"ts": {"str": "iso"}} 2021-03-11T18:23:48.265386+0100
Hal$ caget -f9 'test:channel.{"ts": {"num": "dbl"}}'
test:channel.{"ts": {"num": "dbl"}} 984331428.265386105
Hal$ caget -f1 'test:channel.{"ts": {"num": "ts"}}'
test:channel.{"ts": {"num": "ts"}} 2 984331428.0 265386163.0
Hal$ caget -f1 'test:channel.{"ts": {"num": "ts", "epoch": "unix"}}'
test:channel.{"ts": {"num": "ts", "epoch": "unix"}} 2 1615483428.0 265386163.0
=cut
@@ -125,7 +354,7 @@ The default mode is C<abs> if no mode parameter is included.
test:channel 2012-09-01 22:10:23.601023 5
test:channel 2012-09-01 22:10:24.601136 6 HIGH MINOR
^C
Hal$ camonitor 'test:channel.{"dbnd":{"abs":1.5}}'
Hal$ camonitor 'test:channel.{"dbnd":{"d":1.5}}'
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:49.613341 1 LOLO MAJOR
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:51.613615 3 LOW MINOR
test:channel.{"dbnd":{"d":1.5}} 2012-09-01 22:11:53.613804 5
@@ -144,16 +373,21 @@ subarrays).
=head4 Parameters
Note: Negative index numbers address from the end of the array, with C<-1> being the last element.
=over
=item Square bracket notation C<[start:increment:end]> (shorthand)
The common square bracket notation which can be used in place of JSON.
This much shorter square bracket notation can be used in place of JSON.
Any parameter may be omitted (keeping the colons) to use the default value.
If only one colon is included, this means C<[start:end]> with an increment of 1.
If only a single parameter is used C<[index]> the filter returns one element.
If only one colon is included, it means C<[start:end]> with an increment of 1.
If only a single parameter is given C<[index]> the filter returns one element.
Index numbers for the start and end parameters must be integers, with the first
array element being found at index C<0>.
The value of an index may be negative, in which case the indexing is counted
backwards from the end of the array, with C<-1> being the last element.
If the start index selects an element that comes after the end index element,
the subarray returned will always be empty.
=item Start index C<"s">
@@ -161,8 +395,8 @@ Index of the first original array element to retrieve.
=item Increment C<"i">
Index increment between retrieved elements of the original array; must be
a positive number.
The stride or increment to apply between elements of the original array to be
retrieved. This value must be a positive integer.
=item End index C<"e">
@@ -176,9 +410,9 @@ C<s=0> (first element), C<i=1> (fetch all elements), C<e=-1>
=head4 Example
Hal$ caget test:channel 'test:channel.{"arr":{"s":2,"i":2,"e":8}}' test:channel.[3:5] test:channel.[3:2:-3]
Hal$ caget test:channel 'test:channel.{"arr":{s:2,i:2,e:8}}' test:channel.[3:5] test:channel.[3:2:-3]
test:channel 10 0 1 2 3 4 5 6 7 8 9
test:channel.{"arr":{"s":2,"i":2,"e":8}} 4 2 4 6 8
test:channel.{"arr":{s:2,i:2,e:8}} 4 2 4 6 8
test:channel.[3:5] 3 3 4 5
test:channel.[3:2:-3] 3 3 5 7
@@ -202,13 +436,14 @@ C<dbStateSet()>.
=item Mode+State
Mode and state can be specified in one definition (shorthand).
The desired mode is given as parameter name (C<"before"> / C<"first"> /
C<"while"> / C<"last"> / C<"after"> / C<"unless">), with the state name
(enclosed in double quotes C<">) as value.
The desired mode is given as the parameter name (C<"before"> / C<"first"> /
C<"while"> / C<"last"> / C<"after"> / C<"unless"> which may be unquoted), with
the state name (enclosed in single or double quotes C<"> or C<'>) as the value.
=item Mode C<"m">
A single word from the list below, enclosed in double quotes C<">.
A single word from the list below, enclosed in single or double quotes C<'> or
C<">.
This controls how the state value should affect the monitor stream.
=over
@@ -235,7 +470,7 @@ as the state is false.
=item State C<"s">
The name of a state variable, enclosed in double quotes C<">.
The name of a state variable, enclosed in single or double quotes C<"> or C<'>.
=back
@@ -245,7 +480,7 @@ Assuming there is a system state called "blue", that is being controlled by
some other facility such as a timing system, updates could be restricted to
periods only when "blue" is true by using
Hal$ camonitor 'test:channel' 'test:channel.{"sync":{"while":"blue"}}'
Hal$ camonitor 'test:channel' 'test:channel.{sync:{while:"blue"}}'
...
=cut
@@ -283,17 +518,18 @@ client connects.
To sample a 60Hz channel at 1Hz, a 10Hz channel every 6 seconds or a 1Hz channel
once every minute:
Hal$ camonitor 'test:channel' 'test:channel.{"dec":{"n":60}}'
Hal$ camonitor 'test:channel' 'test:channel.{dec:{n:60}}'
...
=cut
registrar(utagInitialize)
=head3 UTag Filter C<"utag">
=head3 User Tag Filter C<"utag">
This filter applies a test UTAG&M==V to the value taken from the UTAG record field
and drops those updates which evaluate as false.
This filter applies a test C< (UTAG & M) == V > to the value of the record's
UTAG field at the time each monitor event is posted, and drops all updates for
which this expression is false.
=head4 Parameters
@@ -301,12 +537,19 @@ and drops those updates which evaluate as false.
=item Mask C<"M">
Bit mask.
An integer to be used as a bit mask.
=item Value C<"V">
Required value.
The integer value to be matched after applying the mask to the UTAG value.
=back
=head4 Example
To read a channel only when the UTAG value is even (bit 0 is 0):
Hal$ camonitor 'test:channel' 'test:channel.{utag:{m:1, v:0}}'
...
=cut

View File

@@ -14,6 +14,7 @@
#include <stdio.h>
#include "freeList.h"
#include "caeventmask.h"
#include "db_field_log.h"
#include "chfPlugin.h"
#include "dbState.h"
@@ -94,7 +95,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
myStruct *my = (myStruct*) pvt;
int actstate;
if (pfl->ctx == dbfl_context_read)
if (pfl->ctx == dbfl_context_read || (pfl->mask & DBE_PROPERTY))
return pfl;
actstate = dbStateGet(my->id);

View File

@@ -1,4 +1,5 @@
/*************************************************************************\
* Copyright (c) 2021 Cosylab d.d
* Copyright (c) 2010 Brookhaven National Laboratory.
* Copyright (c) 2010 Helmholtz-Zentrum Berlin
* fuer Materialien und Energie GmbH.
@@ -9,6 +10,7 @@
/*
* Author: Ralph Lange <Ralph.Lange@bessy.de>
* Author: Jure Varlec <jure.varlec@cosylab.com>
*/
#include <stdio.h>
@@ -16,20 +18,124 @@
#include <string.h>
#include "chfPlugin.h"
#include "db_field_log.h"
#include "dbExtractArray.h"
#include "dbLock.h"
#include "db_field_log.h"
#include "epicsExport.h"
#include "freeList.h"
#include "errlog.h"
/*
* The size of the data is different for each channel, and can even
* change at runtime, so a freeList doesn't make much sense here.
*/
/* Allocation size for freelists */
#define ALLOC_NUM_ELEMENTS 32
#define logicErrorMessage() \
errMessage(-1, "Logic error: invalid state encountered in ts filter")
/* Filter settings */
enum tsMode {
tsModeInvalid = 0,
tsModeGenerate = 1,
tsModeDouble = 2,
tsModeSec = 3,
tsModeNsec = 4,
tsModeArray = 5,
tsModeString = 6,
};
static const chfPluginEnumType ts_numeric_enum[] = {
{"dbl", 2}, {"sec", 3}, {"nsec", 4}, {"ts", 5}};
enum tsEpoch {
tsEpochEpics = 0,
tsEpochUnix = 1,
};
static const chfPluginEnumType ts_epoch_enum[] = {{"epics", 0}, {"unix", 1}};
enum tsString {
tsStringInvalid = 0,
tsStringEpics = 1,
tsStringIso = 2,
};
static const chfPluginEnumType ts_string_enum[] = {{"epics", 1}, {"iso", 2}};
typedef struct tsPrivate {
enum tsMode mode;
enum tsEpoch epoch;
enum tsString str;
} tsPrivate;
static const chfPluginArgDef ts_args[] = {
chfEnum(tsPrivate, mode, "num", 0, 0, ts_numeric_enum),
chfEnum(tsPrivate, epoch, "epoch", 0, 0, ts_epoch_enum),
chfEnum(tsPrivate, str, "str", 0, 0, ts_string_enum),
chfPluginArgEnd
};
static int parse_finished(void *pvt) {
tsPrivate *settings = (tsPrivate *)pvt;
if (settings->str != tsStringInvalid) {
settings->mode = tsModeString;
#if defined _MSC_VER && _MSC_VER <= 1700
// VS 2012 crashes in ISO mode, doesn't support timezones
if (settings->str == tsStringIso) {
return -1;
}
#endif
} else if (settings->mode == tsModeInvalid) {
settings->mode = tsModeGenerate;
}
return 0;
}
/* Allocation of filter settings */
static void *private_free_list;
static void * allocPvt() {
return freeListCalloc(private_free_list);
}
static void freePvt(void *pvt) {
freeListFree(private_free_list, pvt);
}
/* Allocation of two-element arrays for second+nanosecond pairs */
static void *ts_array_free_list;
static void *allocTsArray() {
return freeListCalloc(ts_array_free_list);
}
static void freeTsArray(db_field_log *pfl) {
freeListFree(ts_array_free_list, pfl->u.r.field);
}
/* Allocation of strings */
static void *string_free_list;
static void *allocString() {
return freeListCalloc(string_free_list);
}
static void freeString(db_field_log *pfl) {
freeListFree(string_free_list, pfl->u.r.field);
}
/* The dtor for waveform data for the case when we have to copy it. */
static void freeArray(db_field_log *pfl) {
/*
* The size of the data is different for each channel, and can even
* change at runtime, so a freeList doesn't make much sense here.
*/
free(pfl->u.r.field);
}
static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
static db_field_log* generate(void* pvt, dbChannel *chan, db_field_log *pfl) {
epicsTimeStamp now;
epicsTimeGetCurrent(&now);
@@ -44,7 +150,7 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
dbScanLock(dbChannelRecord(chan));
dbChannelGetArrayInfo(chan, &pSource, &nSource, &offset);
dbExtractArray(pSource, pTarget, pfl->field_size,
nSource, pfl->no_elements, offset, 1);
nSource, pfl->no_elements, offset, 1);
pfl->u.r.field = pTarget;
pfl->dtor = freeArray;
pfl->u.r.pvt = pvt;
@@ -56,34 +162,246 @@ static db_field_log* filter(void* pvt, dbChannel *chan, db_field_log *pfl) {
return pfl;
}
static void channelRegisterPre(dbChannel *chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out, db_field_log *probe)
{
static db_field_log *replace_fl_value(tsPrivate const *pvt,
db_field_log *pfl,
int (*func)(tsPrivate const *,
db_field_log *)) {
/* Get rid of the old value */
if (pfl->type == dbfl_type_ref && pfl->dtor) {
pfl->dtor(pfl);
pfl->dtor = NULL;
}
pfl->no_elements = 1;
pfl->type = dbfl_type_val;
if (func(pvt, pfl)) {
db_delete_field_log(pfl);
pfl = NULL;
}
return pfl;
}
static void ts_to_array(tsPrivate const *settings,
epicsTimeStamp const *ts,
epicsUInt32 arr[2]) {
arr[0] = ts->secPastEpoch;
arr[1] = ts->nsec;
if (settings->epoch == tsEpochUnix) {
/* Cannot use epicsTimeToWhatever because Whatever uses signed ints */
arr[0] += POSIX_TIME_AT_EPICS_EPOCH;
}
}
static int ts_seconds(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->u.v.field.dbf_ulong = arr[0];
return 0;
}
static int ts_nanos(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->u.v.field.dbf_ulong = arr[1];
return 0;
}
static int ts_double(tsPrivate const *settings, db_field_log *pfl) {
epicsUInt32 arr[2];
ts_to_array(settings, &pfl->time, arr);
pfl->field_type = DBF_DOUBLE;
pfl->field_size = sizeof(epicsFloat64);
pfl->u.v.field.dbf_double = arr[0] + arr[1] * 1e-9;
return 0;
}
static int ts_array(tsPrivate const *settings, db_field_log *pfl) {
pfl->field_type = DBF_ULONG;
pfl->field_size = sizeof(epicsUInt32);
pfl->type = dbfl_type_ref;
pfl->u.r.pvt = NULL;
pfl->u.r.field = allocTsArray();
if (pfl->u.r.field) {
pfl->no_elements = 2;
pfl->dtor = freeTsArray;
ts_to_array(settings, &pfl->time, (epicsUInt32*)pfl->u.r.field);
} else {
pfl->no_elements = 0;
pfl->dtor = NULL;
}
return 0;
}
static int ts_string(tsPrivate const *settings, db_field_log *pfl) {
char const *fmt;
char *field;
size_t n;
switch (settings->str) {
case tsStringEpics:
fmt = "%Y-%m-%d %H:%M:%S.%06f";
break;
case tsStringIso:
fmt = "%Y-%m-%dT%H:%M:%S.%06f%z";
break;
case tsStringInvalid:
default:
logicErrorMessage();
return 1;
}
pfl->field_type = DBF_STRING;
pfl->field_size = MAX_STRING_SIZE;
pfl->type = dbfl_type_ref;
pfl->u.r.pvt = NULL;
pfl->u.r.field = allocString();
if (!pfl->u.r.field) {
pfl->no_elements = 0;
pfl->dtor = NULL;
return 0;
}
pfl->dtor = freeString;
field = (char *)pfl->u.r.field;
n = epicsTimeToStrftime(field, MAX_STRING_SIZE, fmt, &pfl->time);
if (!n) {
field[0] = 0;
}
return 0;
}
static db_field_log *filter(void *pvt, dbChannel *chan, db_field_log *pfl) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
switch (settings->mode) {
case tsModeDouble:
return replace_fl_value(pvt, pfl, ts_double);
case tsModeSec:
return replace_fl_value(pvt, pfl, ts_seconds);
case tsModeNsec:
return replace_fl_value(pvt, pfl, ts_nanos);
case tsModeArray:
return replace_fl_value(pvt, pfl, ts_array);
case tsModeString:
return replace_fl_value(pvt, pfl, ts_string);
case tsModeGenerate:
case tsModeInvalid:
default:
logicErrorMessage();
db_delete_field_log(pfl);
pfl = NULL;
}
return pfl;
}
/* Only the "generate" mode is registered for the pre-queue chain as it creates
it's own timestamp which should be as close to the event as possible */
static void channelRegisterPre(dbChannel * chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out,
db_field_log *probe) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
(void)arg_out;
(void)probe;
*cb_out = settings->mode == tsModeGenerate ? generate : NULL;
}
/* For other modes, the post-chain is fine as they only manipulate existing
timestamps */
static void channelRegisterPost(dbChannel *chan, void *pvt,
chPostEventFunc **cb_out, void **arg_out,
db_field_log *probe) {
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
if (settings->mode == tsModeGenerate || settings->mode == tsModeInvalid) {
*cb_out = NULL;
return;
}
*cb_out = filter;
*arg_out = pvt;
/* Get rid of the value of the probe because we will be changing the
datatype */
if (probe->type == dbfl_type_ref && probe->dtor) {
probe->dtor(probe);
probe->dtor = NULL;
}
probe->no_elements = 1;
probe->type = dbfl_type_val;
switch (settings->mode) {
case tsModeArray:
probe->no_elements = 2;
/* fallthrough */
case tsModeSec:
case tsModeNsec:
probe->field_type = DBF_ULONG;
probe->field_size = sizeof(epicsUInt32);
break;
case tsModeDouble:
probe->field_type = DBF_DOUBLE;
probe->field_size = sizeof(epicsFloat64);
break;
case tsModeString:
probe->field_type = DBF_STRING;
probe->field_size = MAX_STRING_SIZE;
break;
case tsModeGenerate:
case tsModeInvalid:
// Already handled above, added here for completeness.
default:
logicErrorMessage();
*cb_out = NULL;
}
}
static void channel_report(dbChannel *chan, void *pvt, int level, const unsigned short indent)
{
printf("%*sTimestamp (ts)\n", indent, "");
tsPrivate *settings = (tsPrivate *)pvt;
(void)chan;
(void)level;
printf("%*sTimestamp (ts): mode: %d, epoch: %d, str: %d\n",
indent, "", settings->mode, settings->epoch, settings->str);
}
static chfPluginIf pif = {
NULL, /* allocPvt, */
NULL, /* freePvt, */
allocPvt,
freePvt,
NULL, /* parse_error, */
NULL, /* parse_ok, */
NULL, /* parse_error, */
parse_finished,
NULL, /* channel_open, */
channelRegisterPre,
NULL, /* channelRegisterPost, */
channelRegisterPost,
channel_report,
NULL /* channel_close */
};
static void tsInitialize(void)
{
chfPluginRegister("ts", &pif, NULL);
freeListInitPvt(&private_free_list, sizeof(tsPrivate),
ALLOC_NUM_ELEMENTS);
freeListInitPvt(&ts_array_free_list, 2 * sizeof(epicsUInt32),
ALLOC_NUM_ELEMENTS);
freeListInitPvt(&string_free_list, MAX_STRING_SIZE,
ALLOC_NUM_ELEMENTS);
chfPluginRegister("ts", &pif, ts_args);
}
epicsExportRegistrar(tsInitialize);

Some files were not shown because too many files have changed in this diff Show More