Compare commits

...

115 Commits

Author SHA1 Message Date
Ralph Lange
ba1bf00ce7 Use split() for fetching last tag with darcs
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 / 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 + 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
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-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
(setting $/='' breaks reading multi-line into an array)
2023-12-01 16:50:24 +01:00
Érico Nogueira
68fea5c2ca 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-12-01 16:49:41 +01:00
Michael Davidsaver
3f03efe7fe Add ERROR to error messages 2023-12-01 16:49:30 +01:00
Michael Davidsaver
98dc97022a const-ify dbLink arrays
external code really should never be modifying pamaplinkType[]
2023-12-01 16:49:18 +01:00
Uchenna Ezeobi
5d01b637a7 Config: Fixed Hard coded LDFLAGS in MVME2500 2023-12-01 16:49:03 +01:00
Michael Davidsaver
69a184ee1d update submodules 2023-12-01 16:48:54 +01:00
Michael Davidsaver
aebacff3db release notes 2023-12-01 16:48:45 +01:00
Michael Davidsaver
27ad623d68 bi "Raw Soft Channel" use MASK
If set, apply MASK to value read into RVAL.
2023-12-01 16:48:06 +01:00
Michael Davidsaver
1b23701946 incorrect error check on GetStdHandle()
Likely inconsequential as GetConsoleMode() should
return 0 when given an invalid handle.
2023-12-01 16:47:45 +01:00
Michael Davidsaver
0502fe25e4 print ANSI escapes to stderr
unconditionally print ANSI some escapes (to colorize errors)
to the stderr stream.
2023-12-01 16:47:18 +01:00
Michael Davidsaver
77a37781af colorize more errlog messages 2023-12-01 16:38:54 +01:00
Jeremy Lorelli
ba21e655df 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-12-01 16:36:43 +01:00
Michael Davidsaver
2e727a0262 update ci-scripts 2023-12-01 16:36:35 +01:00
Michael Davidsaver
f4cb31d5d5 dbRecordField() add "did you mean..." hint for unknown field 2023-12-01 16:36:24 +01:00
Michael Davidsaver
ab4e687816 const-ify dbCopyEntry() and dbCopyEntryContents() 2023-12-01 16:36:12 +01:00
Michael Davidsaver
1d1926eaf3 avoid hang during concurrent db_cancel_event()
cf. fab8fd7102
2023-12-01 16:36:03 +01:00
Michael Davidsaver
d31ba6ade3 doc 2023-12-01 16:35:55 +01:00
609147dad0 gha: turn most warnings into errors in github build 2023-12-01 16:35:46 +01:00
Michael Davidsaver
5a5535aa5b iocsh: keep history file 2023-12-01 16:35:36 +01:00
JJL772
de61390049 Com: Make STATIC_ASSERT macro typedefs unique 2023-12-01 16:35:29 +01:00
Michael Davidsaver
304d8217d9 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-12-01 16:35:18 +01:00
Jack Harper
2c95dc91e9 iocsh: add comment to cvtArg explaining default iocsharg behaviour 2023-12-01 16:34:35 +01:00
Michael Davidsaver
8cc15c6e68 remove unused local 2023-12-01 16:34:24 +01:00
AlexWells
680e1734a4 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-12-01 16:34:04 +01:00
4aea7de032 update modules/pvData 2023-12-01 16:33:42 +01:00
Michael Davidsaver
bddbc38e0b update ci-scripts 2023-12-01 16:28:58 +01:00
Jeremy Lorelli
67e669c5e5 libCom: Fix buggy pointer dereference in postfix() 2023-12-01 16:02:34 +01:00
Michael Davidsaver
e291ca0a03 Fix compile w/ vs2012 2023-12-01 15:04:24 +01:00
Emilio Perez
5b84a86ccd 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-12-01 15:03:00 +01:00
Michael Davidsaver
cb6442da71 epicsThreadShow() zombies
Flag when the thread has returned, but the tracking
struct is still around.  eg. in need of joining.
2023-12-01 15:01:10 +01:00
Érico Nogueira
49b9573f3a 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-12-01 15:01:01 +01:00
Michael Davidsaver
42dfca2b54 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-12-01 15:00:51 +01:00
Michael Davidsaver
3fd79a21a2 privatize evSubscrip 2023-12-01 15:00:34 +01:00
Michael Davidsaver
12f311b780 dbEvent minor 2023-12-01 15:00:16 +01:00
Michael Davidsaver
8abc9f8d03 minor 2023-12-01 15:00:08 +01:00
Michael Davidsaver
ae6f01b8ee doc dbLock.h 2023-12-01 14:59:56 +01:00
1d3a2defa2 Merge 7.0 commit 'ca9c95' into PSI-7.0 2023-12-01 14:21:07 +01:00
43f6fd5060 install msi for all "Command" builds
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 / 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-10-03 16:50:39 +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
c8d596034a add missing include 2023-09-01 16:11:50 +02:00
Emilio Perez
1d3459982c Update release notes regarding adding error symbols 2023-09-01 16:11:50 +02:00
Emilio Perez
9934242ccf Add locking to error symbol table
This protects the cases of:
- simultaneously adding and requesting of an error symbol
- simultaneously adding many error symbols

x
2023-09-01 16:11:50 +02:00
Emilio Perez
e6dab90bf4 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
2023-09-01 16:11:50 +02:00
Emilio Perez
c680b9bebd error: add a constant for the minimum module number 2023-09-01 16:11:50 +02:00
Emilio Perez
9555ca05b8 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
2023-09-01 16:11:49 +02:00
Emilio Perez
0cd56fa40b 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
2023-09-01 16:11:49 +02:00
Michael Davidsaver
485ac85fa5 appveyor remove skip_commits: 2023-09-01 16:11:49 +02:00
1148931226 On Vxworks clone std* file handles because they are thread specific 2023-09-01 14:15:56 +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
73f80cced9 Merge branch '7.0' into PSI-7.0 2023-08-21 14:14:16 +02:00
152c72bab0 fix submodule pvData to use our fork 2023-08-14 15:42:10 +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
0f086126b7 fix escaped quotes in quoted strings
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 / Fedora-33 (push) Failing after 2s
Base / CentOS-7 (push) Failing after 5s
Base / Fedora-latest (push) Failing after 1s
Base / Win2019 mingw (push) Has been cancelled
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 xilinx_zynq_a9_qemu (push) Has been cancelled
Base / Ub-20 gcc-9 + RT-5.1 uC5282 (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 / 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-06-22 16:37:29 +02:00
5b058d7d27 Merge branch '7.0' into PSI-7.0 2023-06-22 16:36:32 +02: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
161 changed files with 3971 additions and 875 deletions

View File

@@ -25,22 +25,6 @@ init:
# Set clone depth (do not fetch complete history)
clone_depth: 5
# Skip commits affecting only specific files
skip_commits:
files:
# Removed - 'documentation/*'
# Unfortunately Appveyor only looks at the HEAD (latest) commit, so if
# the last commit pushed to a PR only updates the Release Notes the whole
# PR build gets skipped. GitHub Actions looks at the complete list
# of files changed since its last build, which is much more useful.
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.gitattributes'
- '.lgtm.yml'
- '**/*.html'
- '**/*.md'
#---------------------------------#
# build matrix configuration #
#---------------------------------#

View File

@@ -32,22 +32,6 @@ init:
# Set clone depth (do not fetch complete history)
clone_depth: 5
# Skip commits affecting only specific files
skip_commits:
files:
# Removed - 'documentation/*'
# Unfortunately Appveyor only looks at the HEAD (latest) commit, so if
# the last commit pushed to a PR only updates the Release Notes the whole
# PR build gets skipped. GitHub Actions looks at the complete list
# of files changed since its last build, which is much more useful.
- 'startup/*'
- '.github/*'
- '.tools/*'
- '.lgtm.yml'
- '.gitattributes'
- '**/*.html'
- '**/*.md'
#---------------------------------#
# build matrix configuration #
#---------------------------------#

2
.ci

Submodule .ci updated: 1e0e326f74...130e88b709

View File

@@ -51,6 +51,25 @@ 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
@@ -133,13 +152,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
@@ -151,6 +172,26 @@ 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@v3
with:

1
.gitignore vendored
View File

@@ -17,3 +17,4 @@ O.*/
*.log
.*.swp
.DS_Store
.iocsh_history

3
.gitmodules vendored
View File

@@ -1,6 +1,7 @@
[submodule "modules/pvData"]
path = modules/pvData
url = https://github.com/epics-base/pvDataCPP
# url = https://github.com/epics-base/pvDataCPP
url = git@git.psi.ch:epics_base/pvData.git
branch = master
[submodule "modules/pvAccess"]
path = modules/pvAccess

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.

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

@@ -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

@@ -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

@@ -9,12 +9,99 @@ 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)
**This version of EPICS has not been released yet.**
## Changes made on the 7.0 branch since 7.0.7
### 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.
@@ -26,7 +113,7 @@ 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
### COMMANDLINE\_LIBRARY fallback to GNU\_DIR
Fall back to the previous behavior when searching for `readline.h` with older compilers.
@@ -48,6 +135,27 @@ 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
@@ -60,7 +168,9 @@ This functionality was suggested in
be added to other output record types if the community finds it useful,
please send feedback about the feature to tech-talk.
### Tab completion for IOC shell
### 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
@@ -72,14 +182,25 @@ 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
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
`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
@@ -227,6 +348,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

@@ -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

@@ -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

@@ -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

@@ -21,7 +21,8 @@ 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"
};
static void asprulesCallFunc(const iocshArgBuf *args)
{
asprules(args[0].sval);
@@ -89,8 +101,14 @@ 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);
@@ -101,8 +119,10 @@ 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

@@ -344,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

@@ -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

@@ -18,6 +18,7 @@
* Ralph Lange <Ralph.Lange@bessy.de>
*/
#define EPICS_PRIVATE_API
#include <stddef.h>
#include <stdlib.h>
#include <stdio.h>
@@ -75,23 +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 */
@@ -101,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
@@ -122,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 ) {
@@ -139,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;
@@ -217,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);
}
@@ -226,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 ) {
@@ -318,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;
@@ -330,7 +321,6 @@ dbEventCtx db_init_events (void)
evUser->flowCtrlMode = FALSE;
evUser->extraLaborBusy = FALSE;
evUser->pSuicideEvent = NULL;
return (dbEventCtx) evUser;
fail:
if(evUser->lock)
@@ -339,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);
@@ -404,7 +392,6 @@ void db_close_events (dbEventCtx ctx)
epicsEventDestroy(evUser->pexitsem);
epicsEventDestroy(evUser->ppendsem);
epicsEventDestroy(evUser->pflush_sem);
epicsMutexDestroy(evUser->lock);
epicsMutexUnlock (stopSync);
@@ -461,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;
}
@@ -579,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 );
}
}
/*
@@ -934,10 +920,7 @@ void db_post_single_event (dbEventSubscription event)
*/
static int event_read ( struct event_que *ev_que )
{
db_field_log *pfl;
int notifiedRemaining = 0;
void ( *user_sub ) ( void *user_arg, struct dbChannel *chan,
int eventsRemaining, db_field_log *pfl );
/*
* evUser ring buffer must be locked for the multiple
@@ -958,19 +941,7 @@ 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];
int eventsRemaining;
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;
}
db_field_log *pfl = ev_que->valque[ev_que->getix];
/*
* Simple type values queued up for reliable interprocess
@@ -980,13 +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 );
eventsRemaining = ev_que->evque[ev_que->getix] != EVENTQEMPTY && !ev_que->nCanceled;
/*
* 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
@@ -998,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);
@@ -1016,27 +979,15 @@ static int event_read ( struct event_que *ev_que )
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);
}
@@ -1051,9 +1002,6 @@ static int event_read ( struct event_que *ev_que )
return DB_EVENT_OK;
}
/*
* EVENT_TASK()
*/
static void event_task (void *pParm)
{
struct event_user * const evUser = (struct event_user *) pParm;
@@ -1094,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

@@ -72,7 +72,9 @@ static void dbLoadRecordsCallFunc(const iocshArgBuf *args)
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 */
@@ -86,27 +88,36 @@ static void dbdCallFunc(const iocshArgBuf *args) { dbd(args[0].sval);}
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",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",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);}
@@ -114,14 +125,17 @@ static void dbpCallFunc(const iocshArgBuf *args)
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 */
@@ -131,9 +145,9 @@ 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);
@@ -144,7 +158,8 @@ 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);
@@ -156,7 +171,9 @@ 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);
@@ -166,7 +183,10 @@ static void dbelCallFunc(const iocshArgBuf *args)
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 */
@@ -176,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);
@@ -186,28 +211,35 @@ 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",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",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 */
@@ -215,7 +247,9 @@ 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 */
@@ -224,7 +258,8 @@ 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);}
@@ -232,9 +267,17 @@ static void dbpfCallFunc(const iocshArgBuf *args)
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);}
@@ -250,7 +293,9 @@ 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 */
@@ -258,7 +303,10 @@ 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);}
@@ -273,14 +321,21 @@ 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",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 */
@@ -288,7 +343,8 @@ 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);}
@@ -297,9 +353,11 @@ 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);}
@@ -314,9 +372,8 @@ 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);}
@@ -325,7 +382,8 @@ 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);}
@@ -334,16 +392,25 @@ 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);}
@@ -351,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);
@@ -364,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);
@@ -376,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);}
@@ -408,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);
@@ -421,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);
@@ -434,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);
@@ -447,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);
@@ -456,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);
@@ -468,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);
@@ -481,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);
@@ -494,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);

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

@@ -869,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

@@ -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

@@ -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

@@ -139,14 +139,16 @@ char** dbCompleteRecord(const char *cword)
if(!prefix.empty() || !suggestions.empty()) {
ret = (char**)malloc(sizeof(*ret)*(2u + suggestions.size()));
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();
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;
}
ret[n] = NULL;
}
return ret;

View File

@@ -1068,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;
}
@@ -1086,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;
@@ -1110,14 +1110,10 @@ static void dbRecordHead(char *recordType, char *name, int visible)
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;
@@ -1136,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;
}
@@ -1170,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;
}
@@ -1258,7 +1275,7 @@ static void dbAlias(char *name, char *alias)
DBENTRY dbEntry;
DBENTRY *pdbEntry = &dbEntry;
if(dbRecordNameValidate(alias))
if(dbRecordNameValidate(alias) || dbRecordNameValidate(name))
return;
dbInitEntry(savedPdbbase, pdbEntry);

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;
@@ -1652,6 +1652,7 @@ long dbCreateAlias(DBENTRY *pdbentry, const char *alias)
dbRecordNode *pnewnode;
DBENTRY tempEntry;
PVDENTRY *ppvd;
long status;
if (!precordType)
return S_dbLib_recordTypeNotFound;
@@ -1664,9 +1665,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 +1678,16 @@ 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);
precordType->no_aliases++;
return 0;
}
@@ -2201,12 +2204,12 @@ long dbInitRecordLinks(dbRecordType *rtyp, struct dbCommon *prec)
*/
} else if(dbCanSetLink(plink, &link_info, devsup)!=0) {
errlogPrintf("Error: %s.%s: can't initialize link type %s with \"%s\" (type %s)\n",
prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue);
errlogPrintf(ERL_ERROR ": %s.%s: can't initialize link type %d with \"%s\" (type %s)\n",
prec->name, pflddes->name, plink->type, plink->text, pamaplinkType[link_info.ltype].strvalue);
} else if(dbSetLink(plink, &link_info, devsup)) {
errlogPrintf("Error: %s.%s: failed to initialize link type %s with \"%s\" (type %s)\n",
prec->name, pflddes->name, pamaplinkType[plink->type].strvalue, plink->text, pamaplinkType[link_info.ltype].strvalue);
errlogPrintf(ERL_ERROR ": %s.%s: failed to initialize link type %d with \"%s\" (type %s)\n",
prec->name, pflddes->name, plink->type, plink->text, pamaplinkType[link_info.ltype].strvalue);
}
free(plink->text);
plink->text = NULL;

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);

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

View File

@@ -11,7 +11,7 @@
SRC_DIRS += $(IOCDIR)/dbtemplate
PROD_HOST += msi
PROD_CMD += msi
msi_SRCS = msi.cpp
msi_LIBS += Com

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

@@ -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

@@ -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();

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

@@ -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

@@ -216,22 +216,96 @@ registrar(tsInitialize)
=head3 TimeStamp Filter C<"ts">
This filter replaces the timestamp in the data fetched through the channel with
the time the value was fetched (or an update was sent). The record's timestamp
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
=head4 No parameters (an empty pair of braces)
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.
=head4 Parameters
=head4 Numeric type C<"num">
None, use an empty pair of braces.
The following values are accepted for this parameter:
=head4 Example
=over
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
=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

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);

View File

@@ -42,6 +42,8 @@ value is non-zero.
=back
This record type was included in base.dbd beginning with epics-base 3.14.10 .
=head2 Record-specific Menus
=head3 Menu aSubLFLG

View File

@@ -60,8 +60,10 @@ this chapter for information on soft device support.
If the record gets its values from hardware or uses the C<Raw Soft Channel>
device support, the device support routines place the value in the RVAL
field which is then converted using the process described in the next
section.
field.
(Since UNRELEASED) If the MASK field is non-zero, then this MASK is applied to RVAL.
The value of RVAL is then converted using the process described in the
next section.
=fields INP, DTYP, ZNAM, ONAM, RVAL, VAL

View File

@@ -36,7 +36,7 @@ The desired output needs to be in engineering units.
The first field that determines where the desired output originates is the
output mode select (OMSL) field, which can have two possible values:
C<losed_loop> or C<supervisory>. If C<supervisory> is specified, the value
C<closed_loop> or C<supervisory>. If C<supervisory> is specified, the value
in the VAL field can be set externally via dbPuts at run-time. If
C<closed_loop> is specified, the VAL field's value is obtained from the
address specified in the Desired Output Link (DOL) field which can be a

View File

@@ -460,12 +460,17 @@ conditions.
The HYST field defines an alarm deadband for each limit.
The AFTC field sets the time constant on a low-pass filter that delays the
reporting of limit alarms until the signal has been within the alarm range for
that number of seconds (the default AFTC value of zero retains the previous
behavior).
See L<Alarm Specification|https://docs.epics-controls.org/en/latest/guides/EPICS_Process_Database_Concepts.html#alarm-specification>
for a complete explanation of record alarms and of the standard fields.
L<Alarm Fields|dbCommonRecord/Alarm Fields> lists other fields related
to alarms that are common to all record types.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC
=head3 Monitor Parameters
@@ -496,7 +501,7 @@ manner for the VAL field.
=cut
include "dbCommon.dbd"
include "dbCommon.dbd"
field(VAL,DBF_DOUBLE) {
prompt("Result")
promptgroup("50 - Output")

View File

@@ -13,6 +13,8 @@ The histogram record is used to store frequency counts of a signal into an array
of arbitrary length. The user can configure the range of the signal value that
the array will store. Anything outside this range will be ignored.
This record type was included in base.dbd beginning with epics-base 3.15.0.1 .
=head2 Parameter Fields
The record-specific fields are described below.

View File

@@ -14,6 +14,8 @@ from a hardware input.
The record supports alarm limits, alarm filtering, graphics and control
limits.
This record type was included in base.dbd beginning with epics-base 3.16.1 .
=head2 Parameter Fields
The record-specific fields are described below.

View File

@@ -13,6 +13,8 @@ This record type is normally used to send an integer value of up to 64 bits
to an output device.
The record supports alarm, drive, graphics and control limits.
This record type was included in base.dbd beginning with epics-base 3.16.1 .
=head2 Parameter Fields
The record-specific fields are described below.

View File

@@ -70,6 +70,15 @@ The possible alarm conditions for long inputs are the SCAN, READ, and limit
alarms. The SCAN and READ alarms are called by the record or device support
routines.
The HYST field controls hysteresis to prevent alarm chattering from an input
signal that is close to one of the limits and suffers from significant readout
noise.
The AFTC field sets the time constant on a low-pass filter that delays the
reporting of limit alarms until the signal has been within the alarm range for
that number of seconds (the default AFTC value of zero retains the previous
behavior).
The limit alarms are configured by the user in the HIHI, LOLO, HIGH, and LOW
fields using numerical values. For each of these fields, there is a
corresponding severity field which can be either NO_ALARM, MINOR, or MAJOR. The
@@ -77,7 +86,7 @@ HYST field can be used to specify a deadband around each limit.
L<Alarm Fields|dbCommonRecord/Alarm Fields> lists the fields related to
alarms that are common to all record types.
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST
=fields HIHI, HIGH, LOW, LOLO, HHSV, HSV, LSV, LLSV, HYST, AFTC
=head3 Monitor Parameters

View File

@@ -11,6 +11,8 @@
The long string input record is used to retrieve an arbitrary ASCII string with
a maximum length of 65535 characters.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.

View File

@@ -11,6 +11,8 @@
The long string output record is used to write an arbitrary ASCII string with a
maximum length of 65535 characters.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.

View File

@@ -405,6 +405,11 @@ and state alarms. The state alarms are configured in the below severity fields.
These fields have the usual possible values for severity fields: NO_ALARM,
MINOR, and MAJOR.
The AFTC field sets the time constant on a low-pass filter that delays the
reporting of limit alarms until the signal has been within the alarm range for
that number of seconds (the default AFTC value of zero retains the previous
behavior).
The unknown state severity (UNSV) field, if set to MINOR or MAJOR, triggers an
alarm when the record support routine cannot find a matching value in the state
value fields for C<<< rval >>>.
@@ -420,7 +425,7 @@ for a complete explanation of record alarms and of the standard fields.
L<Alarm Fields|dbCommonRecord/Alarm Fields> lists other fields related
to alarms that are common to all record types.
=fields UNSV, COSV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV
=fields UNSV, COSV, ZRSV, ONSV, TWSV, THSV, FRSV, FVSV, SXSV, SVSV, EISV, NISV, TESV, ELSV, TVSV, TTSV, FTSV, FFSV, AFTC
=cut

View File

@@ -323,6 +323,7 @@ to alarms that are common to all record types.
field(B0,DBF_UCHAR) {
prompt("Bit 0")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -330,6 +331,7 @@ to alarms that are common to all record types.
field(B1,DBF_UCHAR) {
prompt("Bit 1")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -337,6 +339,7 @@ to alarms that are common to all record types.
field(B2,DBF_UCHAR) {
prompt("Bit 2")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -344,6 +347,7 @@ to alarms that are common to all record types.
field(B3,DBF_UCHAR) {
prompt("Bit 3")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -351,6 +355,7 @@ to alarms that are common to all record types.
field(B4,DBF_UCHAR) {
prompt("Bit 4")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -358,6 +363,7 @@ to alarms that are common to all record types.
field(B5,DBF_UCHAR) {
prompt("Bit 5")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -365,6 +371,7 @@ to alarms that are common to all record types.
field(B6,DBF_UCHAR) {
prompt("Bit 6")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -372,6 +379,7 @@ to alarms that are common to all record types.
field(B7,DBF_UCHAR) {
prompt("Bit 7")
promptgroup("51 - Output 0-7")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -379,6 +387,7 @@ to alarms that are common to all record types.
field(B8,DBF_UCHAR) {
prompt("Bit 8")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -386,6 +395,7 @@ to alarms that are common to all record types.
field(B9,DBF_UCHAR) {
prompt("Bit 9")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -393,6 +403,7 @@ to alarms that are common to all record types.
field(BA,DBF_UCHAR) {
prompt("Bit 10")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -400,6 +411,7 @@ to alarms that are common to all record types.
field(BB,DBF_UCHAR) {
prompt("Bit 11")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -407,6 +419,7 @@ to alarms that are common to all record types.
field(BC,DBF_UCHAR) {
prompt("Bit 12")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -414,6 +427,7 @@ to alarms that are common to all record types.
field(BD,DBF_UCHAR) {
prompt("Bit 13")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -421,6 +435,7 @@ to alarms that are common to all record types.
field(BE,DBF_UCHAR) {
prompt("Bit 14")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -428,6 +443,7 @@ to alarms that are common to all record types.
field(BF,DBF_UCHAR) {
prompt("Bit 15")
promptgroup("52 - Output 8-15")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -435,6 +451,7 @@ to alarms that are common to all record types.
field(B10,DBF_UCHAR) {
prompt("Bit 16")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -442,6 +459,7 @@ to alarms that are common to all record types.
field(B11,DBF_UCHAR) {
prompt("Bit 17")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -449,6 +467,7 @@ to alarms that are common to all record types.
field(B12,DBF_UCHAR) {
prompt("Bit 18")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -456,6 +475,7 @@ to alarms that are common to all record types.
field(B13,DBF_UCHAR) {
prompt("Bit 19")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -463,6 +483,7 @@ to alarms that are common to all record types.
field(B14,DBF_UCHAR) {
prompt("Bit 20")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -470,6 +491,7 @@ to alarms that are common to all record types.
field(B15,DBF_UCHAR) {
prompt("Bit 21")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -477,6 +499,7 @@ to alarms that are common to all record types.
field(B16,DBF_UCHAR) {
prompt("Bit 22")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -484,6 +507,7 @@ to alarms that are common to all record types.
field(B17,DBF_UCHAR) {
prompt("Bit 23")
promptgroup("53 - Output 16-23")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -491,6 +515,7 @@ to alarms that are common to all record types.
field(B18,DBF_UCHAR) {
prompt("Bit 24")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -498,6 +523,7 @@ to alarms that are common to all record types.
field(B19,DBF_UCHAR) {
prompt("Bit 25")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -505,6 +531,7 @@ to alarms that are common to all record types.
field(B1A,DBF_UCHAR) {
prompt("Bit 26")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -512,6 +539,7 @@ to alarms that are common to all record types.
field(B1B,DBF_UCHAR) {
prompt("Bit 27")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -519,6 +547,7 @@ to alarms that are common to all record types.
field(B1C,DBF_UCHAR) {
prompt("Bit 28")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -526,6 +555,7 @@ to alarms that are common to all record types.
field(B1D,DBF_UCHAR) {
prompt("Bit 29")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -533,6 +563,7 @@ to alarms that are common to all record types.
field(B1E,DBF_UCHAR) {
prompt("Bit 30")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)
@@ -540,6 +571,7 @@ to alarms that are common to all record types.
field(B1F,DBF_UCHAR) {
prompt("Bit 31")
promptgroup("54 - Output 24-31")
asl(ASL0)
special(SPC_MOD)
pp(TRUE)
interest(1)

View File

@@ -57,6 +57,14 @@
else \
flags |= F_BADLNK
#ifdef __GNUC__
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wformat-security"
/* Intentionally passing non-const format string to epicsSnprintf() below.
* Older GCC does not allow pragma GCC within function body.
*/
#endif
static void doPrintf(printfRecord *prec)
{
const char *pfmt = prec->fmt;
@@ -169,7 +177,7 @@ static void doPrintf(printfRecord *prec)
precision = 0;
if (ch == '%') {
added = epicsSnprintf(pval, vspace + 1, "%s", format);
added = epicsSnprintf(pval, vspace + 1, format);
}
else if (linkn++ >= PRINTF_NLINKS) {
/* No more LNKn fields */
@@ -314,6 +322,9 @@ static void doPrintf(printfRecord *prec)
prec->len = pval - prec->val;
}
#ifdef __GNUC__
# pragma GCC diagnostic pop
#endif
static long init_record(struct dbCommon *pcommon, int pass)
{

View File

@@ -11,6 +11,8 @@
The printf record is used to generate and write a string using a format
specification and parameters, analogous to the C C<printf()> function.
This record type was included in base.dbd beginning with epics-base 3.15.0.2 .
=head2 Parameter Fields
The record-specific fields are described below, grouped by functionality.

View File

@@ -3,7 +3,7 @@ To start the ioc from this directory execute the command
Alternatively make the st.cmd file directly executable with
chmod +x st.cmd
and check the executable name on the first line of the st.cmd file
and check the executable name on the first line of the st.cmd file
You may need to change the name of the .dbd file given in the
st.cmd's dbLoadDatabase() command before starting the ioc.

View File

@@ -181,6 +181,8 @@ dbStaticTest_SRCS += dbStaticTest.c
dbStaticTest_SRCS += dbTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += dbStaticTest.c
TESTFILES += ../dbStaticTest.db
TESTFILES += ../dbStaticTestAlias1.db
TESTFILES += ../dbStaticTestAlias2.db
TESTS += dbStaticTest
# This runs all the test programs in a known working order:

View File

@@ -4,6 +4,8 @@
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#define EPICS_PRIVATE_API
#include <string.h>
#include <errlog.h>

View File

@@ -291,6 +291,18 @@ static void testDbVerify(const char *record)
dbFinishEntry(&entry);
}
static void testWrongAliasRecord(const char *filename)
{
FILE *fp = NULL;
dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR "..");
dbOpenFile(pdbbase, filename, &fp);
if(!fp) {
testAbort("Unable to read %s", filename);
}
testOk(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL) != 0,
"Wrong alias record in %s is expected to fail", filename);
}
void dbTestIoc_registerRecordDeviceDriver(struct dbBase *);
MAIN(dbStaticTest)
@@ -298,13 +310,14 @@ MAIN(dbStaticTest)
const char *ldir;
FILE *fp = NULL;
testPlan(310);
testPlan(312);
testdbPrepare();
testdbReadDatabase("dbTestIoc.dbd", NULL, NULL);
dbTestIoc_registerRecordDeviceDriver(pdbbase);
dbPath(pdbbase,"." OSI_PATH_LIST_SEPARATOR "..");
if(!(ldir = dbOpenFile(pdbbase, "dbStaticTest.db", &fp))) {
ldir = dbOpenFile(pdbbase, "dbStaticTest.db", &fp);
if(!fp) {
testAbort("Unable to read dbStaticTest.db");
}
if(dbReadDatabaseFP(&pdbbase, fp, NULL, NULL)) {
@@ -312,6 +325,9 @@ MAIN(dbStaticTest)
ldir, OSI_PATH_LIST_SEPARATOR);
}
testWrongAliasRecord("dbStaticTestAlias1.db");
testWrongAliasRecord("dbStaticTestAlias2.db");
testEntry("testrec.VAL");
testEntry("testalias.VAL");
testEntry("testalias2.VAL");

View File

@@ -0,0 +1,4 @@
record(x, "testrec2alias") {
}
alias("testrec2alias.NAME", "testalias4")

View File

@@ -0,0 +1,4 @@
record(x, "testrec2alias2") {
}
alias("testrec2alias2", "testalias5.NAME")

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,9 +10,11 @@
/*
* Author: Ralph Lange <Ralph.Lange@bessy.de>
* Author: Jure Varlec <jure.varlec@cosylab.com>
*/
#include <string.h>
#include <math.h>
#include "dbStaticLib.h"
#include "dbAccessDefs.h"
@@ -25,11 +28,24 @@
#include "testMain.h"
#include "osiFileName.h"
/* A fill pattern for setting a field log to something "random". */
#define PATTERN 0x55
void filterTest_registerRecordDeviceDriver(struct dbBase *);
/* Use a "normal" timestamp for testing. What results from filling the field log
with the above pattern is a timestamp that causes problems with
epicsTimeToStrftime() on some platforms. */
static epicsTimeStamp const test_ts = { 616600420, 998425354 };
static db_field_log fl;
typedef int (*TypeCheck)(const db_field_log *pfl);
typedef int (*ValueCheck)(const db_field_log *pfl, const epicsTimeStamp *ts);
typedef struct {
char const *channel;
TypeCheck type_check;
ValueCheck value_check;
} TestSpec;
void filterTest_registerRecordDeviceDriver(struct dbBase *);
static int fl_equal(const db_field_log *pfl1, const db_field_log *pfl2) {
return !(memcmp(pfl1, pfl2, sizeof(db_field_log)));
@@ -42,21 +58,210 @@ static int fl_equal_ex_ts(const db_field_log *pfl1, const db_field_log *pfl2) {
return fl_equal(&fl1, pfl2);
}
MAIN(tsTest)
{
static void fl_reset(db_field_log *pfl) {
memset(pfl, PATTERN, sizeof(*pfl));
pfl->time = test_ts;
}
static void test_generate_filter(const chFilterPlugin *plug) {
dbChannel *pch;
chFilter *filter;
const chFilterPlugin *plug;
char ts[] = "ts";
ELLNODE *node;
chPostEventFunc *cb_out = NULL;
void *arg_out = NULL;
db_field_log fl;
db_field_log fl1;
db_field_log *pfl2;
epicsTimeStamp stamp, now;
dbEventCtx evtctx;
ELLNODE *node;
chPostEventFunc *cb_out = NULL;
void *arg_out = NULL;
char const *chan_name = "x.VAL{ts:{}}";
testPlan(12);
testOk(!!(pch = dbChannelCreate(chan_name)),
"dbChannel with plugin ts created");
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
fl_reset(&fl);
fl1 = fl;
node = ellFirst(&pch->filters);
filter = CONTAINER(node, chFilter, list_node);
plug->fif->channel_register_post(filter, &cb_out, &arg_out, &fl1);
plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
testOk(!!(cb_out) && !(arg_out),
"register_pre registers one filter w/o argument");
testOk(fl_equal(&fl1, &fl),
"register_pre does not change field_log data type");
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened");
node = ellFirst(&pch->pre_chain);
filter = CONTAINER(node, chFilter, pre_node);
testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL),
"ts has one filter w/o argument in pre chain");
testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain");
fl_reset(&fl);
fl1 = fl;
pfl2 = dbChannelRunPreChain(pch, &fl1);
testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log");
testOk(fl_equal_ex_ts(&fl1, pfl2),
"ts filter does not change field_log data");
testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel");
stamp = pfl2->time;
db_delete_field_log(pfl2);
pfl2 = dbChannelRunPreChain(pch, &fl1);
epicsTimeGetCurrent(&now);
testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) >= 0 &&
epicsTimeDiffInSeconds(&now, &pfl2->time) >= 0,
"ts filter sets time stamp to \"now\"");
dbChannelDelete(pch);
}
static void test_value_filter(const chFilterPlugin *plug, const char *chan_name,
TypeCheck tc_func, ValueCheck vc_func) {
dbChannel *pch;
chFilter *filter;
db_field_log fl;
db_field_log fl2;
db_field_log *pfl;
epicsTimeStamp ts;
ELLNODE *node;
chPostEventFunc *cb_out = NULL;
void *arg_out = NULL;
testDiag("Channel %s", chan_name);
testOk(!!(pch = dbChannelCreate(chan_name)),
"dbChannel with plugin ts created");
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
fl_reset(&fl);
fl.type = dbfl_type_val;
node = ellFirst(&pch->filters);
filter = CONTAINER(node, chFilter, list_node);
plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl);
plug->fif->channel_register_post(filter, &cb_out, &arg_out, &fl);
testOk(!!(cb_out) && arg_out,
"register_post registers one filter with argument");
testOk(tc_func(&fl), "register_post gives correct field type");
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened");
node = ellFirst(&pch->post_chain);
filter = CONTAINER(node, chFilter, post_node);
testOk((ellCount(&pch->post_chain) == 1 && filter->post_arg != NULL),
"ts has one filter with argument in post chain");
testOk((ellCount(&pch->pre_chain) == 0), "ts has no filter in pre chain");
fl_reset(&fl);
fl.type = dbfl_type_val;
ts = fl.time;
fl2 = fl;
pfl = dbChannelRunPostChain(pch, &fl);
testOk(pfl == &fl, "ts filter does not drop or replace field_log");
testOk(tc_func(pfl), "ts filter gives correct field type");
testOk((pfl->time.secPastEpoch == fl2.time.secPastEpoch &&
pfl->time.nsec == fl2.time.nsec && pfl->stat == fl2.stat &&
pfl->sevr == fl2.sevr),
"ts filter does not touch non-value fields of field_log");
testOk(vc_func(pfl, &ts), "ts filter gives correct field value");
dbChannelDelete(pch);
}
static int type_check_double(const db_field_log *pfl) {
return pfl->type == dbfl_type_val
&& pfl->field_type == DBR_DOUBLE
&& pfl->field_size == sizeof(epicsFloat64)
&& pfl->no_elements == 1;
}
static int value_check_double(const db_field_log *pfl, const epicsTimeStamp *ts) {
epicsFloat64 flt = pfl->u.v.field.dbf_double;
epicsFloat64 nsec = (flt - (epicsUInt32)(flt)) * 1e9;
return ts->secPastEpoch == (epicsUInt32)(flt)
&& fabs(ts->nsec - nsec) < 1000.; /* allow loss of precision */
}
static int type_check_sec_nsec(const db_field_log *pfl) {
return pfl->type == dbfl_type_val
&& pfl->field_type == DBR_ULONG
&& pfl->field_size == sizeof(epicsUInt32)
&& pfl->no_elements == 1;
}
static int value_check_sec(const db_field_log *pfl, const epicsTimeStamp *ts) {
return ts->secPastEpoch == pfl->u.v.field.dbf_ulong;
}
static int value_check_nsec(const db_field_log *pfl, const epicsTimeStamp *ts) {
return ts->nsec == pfl->u.v.field.dbf_ulong;
}
static int type_check_array(const db_field_log *pfl) {
return pfl->field_type == DBR_ULONG
&& pfl->field_size == sizeof(epicsUInt32)
&& pfl->no_elements == 2;
}
static int value_check_array(const db_field_log *pfl, const epicsTimeStamp *ts) {
epicsUInt32 *arr = (epicsUInt32*)pfl->u.r.field;
return pfl->type == dbfl_type_ref
&& pfl->u.r.field != NULL
&& pfl->dtor != NULL
&& pfl->u.r.pvt == NULL
&& ts->secPastEpoch == arr[0]
&& ts->nsec == arr[1];
}
static int value_check_unix(const db_field_log *pfl, const epicsTimeStamp *ts) {
epicsUInt32 *arr = (epicsUInt32 *)pfl->u.r.field;
return pfl->type == dbfl_type_ref
&& pfl->u.r.field != NULL
&& pfl->dtor != NULL
&& pfl->u.r.pvt == NULL
&& ts->secPastEpoch == arr[0] - POSIX_TIME_AT_EPICS_EPOCH
&& ts->nsec == arr[1];
}
static int type_check_string(const db_field_log *pfl) {
return pfl->field_type == DBR_STRING
&& pfl->field_size == MAX_STRING_SIZE
&& pfl->no_elements == 1;
}
static int value_check_string(const db_field_log *pfl, const epicsTimeStamp *ts) {
/* We can only verify the type, not the value, because using strptime()
might be problematic. */
(void)ts;
return pfl->type == dbfl_type_ref
&& pfl->u.r.field != NULL
&& pfl->dtor != NULL
&& pfl->u.r.pvt == NULL;
}
MAIN(tsTest) {
int i;
char ts[] = "ts";
dbEventCtx evtctx;
const chFilterPlugin *plug;
static TestSpec const tests[] = {
{"x.VAL{ts:{\"num\": \"dbl\"}}", type_check_double, value_check_double},
{"x.VAL{ts:{\"num\": \"sec\"}}", type_check_sec_nsec, value_check_sec},
{"x.VAL{ts:{\"num\": \"nsec\"}}", type_check_sec_nsec, value_check_nsec},
{"x.VAL{ts:{\"num\": \"ts\"}}", type_check_array, value_check_array},
{"x.VAL{ts:{\"num\": \"ts\", \"epoch\": \"epics\"}}", type_check_array, value_check_array},
{"x.VAL{ts:{\"num\": \"ts\", \"epoch\": \"unix\"}}", type_check_array, value_check_unix},
{"x.VAL{ts:{\"str\": \"epics\"}}", type_check_string, value_check_string},
#if !(defined _MSC_VER && _MSC_VER <= 1700)
{"x.VAL{ts:{\"str\": \"iso\"}}", type_check_string, value_check_string},
#endif
};
static int const num_value_tests = sizeof(tests) / sizeof(tests[0]);
testPlan(12 /* test_generate_filter() */
+ num_value_tests * 11 /* test_value_filter() */);
testdbPrepare();
@@ -77,41 +282,12 @@ MAIN(tsTest)
testAbort("plugin '%s' not registered", ts);
testPass("plugin '%s' registered correctly", ts);
testOk(!!(pch = dbChannelCreate("x.VAL{ts:{}}")), "dbChannel with plugin ts created");
testOk((ellCount(&pch->filters) == 1), "channel has one plugin");
test_generate_filter(plug);
memset(&fl, PATTERN, sizeof(fl));
fl1 = fl;
node = ellFirst(&pch->filters);
filter = CONTAINER(node, chFilter, list_node);
plug->fif->channel_register_pre(filter, &cb_out, &arg_out, &fl1);
testOk(!!(cb_out) && !(arg_out), "register_pre registers one filter w/o argument");
testOk(fl_equal(&fl1, &fl), "register_pre does not change field_log data type");
testOk(!(dbChannelOpen(pch)), "dbChannel with plugin ts opened");
node = ellFirst(&pch->pre_chain);
filter = CONTAINER(node, chFilter, pre_node);
testOk((ellCount(&pch->pre_chain) == 1 && filter->pre_arg == NULL),
"ts has one filter w/o argument in pre chain");
testOk((ellCount(&pch->post_chain) == 0), "ts has no filter in post chain");
memset(&fl, PATTERN, sizeof(fl));
fl1 = fl;
pfl2 = dbChannelRunPreChain(pch, &fl1);
testOk(pfl2 == &fl1, "ts filter does not drop or replace field_log");
testOk(fl_equal_ex_ts(&fl1, pfl2), "ts filter does not change field_log data");
testOk(!!(pfl2 = db_create_read_log(pch)), "create field log from channel");
stamp = pfl2->time;
db_delete_field_log(pfl2);
pfl2 = dbChannelRunPreChain(pch, &fl1);
epicsTimeGetCurrent(&now);
testOk(epicsTimeDiffInSeconds(&pfl2->time, &stamp) >= 0 &&
epicsTimeDiffInSeconds(&now, &pfl2->time) >= 0,
"ts filter sets time stamp to \"now\"");
dbChannelDelete(pch);
for (i = 0; i < num_value_tests; ++i) {
TestSpec const *t = &tests[i];
test_value_filter(plug, t->channel, t->type_check, t->value_check);
}
db_close_events(evtctx);

View File

@@ -23,6 +23,11 @@ PROD_LIBS = dbRecStdTest dbRecStd dbCore ca Com
TARGETS += $(COMMON_DIR)/recTestIoc.dbd
DBDDEPENDS_FILES += recTestIoc.dbd$(DEP)
recTestIoc_DBD = base.dbd
recTestIoc_DBD += bptTypeKdegC.dbd
recTestIoc_DBD += bptTypeKdegF.dbd
recTestIoc_DBD += bptTypeJdegF.dbd
recTestIoc_DBD += bptTypeJdegC.dbd
TESTFILES += $(COMMON_DIR)/recTestIoc.dbd
testHarness_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
@@ -93,6 +98,34 @@ testHarness_SRCS += longoutTest.c
TESTFILES += ../longoutTest.db
TESTS += longoutTest
TESTPROD_HOST += boTest
boTest_SRCS += boTest.c
boTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += boTest.c
TESTFILES += ../boTest.db
TESTS += boTest
TESTPROD_HOST += biTest
biTest_SRCS += biTest.c
biTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += biTest.c
TESTFILES += ../biTest.db
TESTS += biTest
TESTPROD_HOST += printfTest
printfTest_SRCS += printfTest.c
printfTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += printfTest.c
TESTFILES += ../printfTest.db
TESTS += printfTest
TESTPROD_HOST += aiTest
aiTest_SRCS += aiTest.c
aiTest_SRCS += recTestIoc_registerRecordDeviceDriver.cpp
testHarness_SRCS += aiTest.c
TESTFILES += ../aiTest.db
TESTS += aiTest
TARGETS += $(COMMON_DIR)/asTestIoc.dbd
DBDDEPENDS_FILES += asTestIoc.dbd$(DEP)
asTestIoc_DBD += base.dbd

View File

@@ -0,0 +1,445 @@
/*************************************************************************\
* Copyright (c) 2023 Karl Vestin
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include "dbUnitTest.h"
#include "testMain.h"
#include "errlog.h"
#include "dbAccess.h"
#include "menuAlarmSevr.h"
#include "menuConvert.h"
#include "epicsMath.h"
#include "menuScan.h"
#include "caeventmask.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void test_soft_input(void){
double value = 543.123;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set VAL to on linked record */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, value);
/* verify that this record VAL is updated but RVAL is not */
testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, value);
testdbGetFieldEqual("test_ai_rec.RVAL", DBF_DOUBLE, 0.0);
// number of tests = 6
}
static void test_raw_soft_input(void){
double value = -255;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set VAL to on linked record */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, value);
/* verify that this record RVAL and VAL are updated */
testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, value);
testdbGetFieldEqual("test_ai_rec.RVAL", DBF_DOUBLE, value);
// number of tests = 6
}
static void test_operator_display(void){
const short hopr = 199;
const short lopr = 50;
const short prec = 2;
const char egu[] = "mm";
const char desc[] = "hmm?";
/* set operator display parameters */
testdbPutFieldOk("test_ai_rec.EGU", DBF_STRING, egu);
testdbPutFieldOk("test_ai_rec.DESC", DBF_STRING, desc);
testdbPutFieldOk("test_ai_rec.HOPR", DBF_SHORT, hopr);
testdbPutFieldOk("test_ai_rec.LOPR", DBF_SHORT, lopr);
testdbPutFieldOk("test_ai_rec.PREC", DBF_SHORT, prec);
/* verify operator display parameters read back */
testdbGetFieldEqual("test_ai_rec.NAME", DBF_STRING, "test_ai_rec");
testdbGetFieldEqual("test_ai_rec.EGU", DBF_STRING, egu);
testdbGetFieldEqual("test_ai_rec.DESC", DBF_STRING, desc);
testdbGetFieldEqual("test_ai_rec.HOPR", DBF_SHORT, hopr);
testdbGetFieldEqual("test_ai_rec.LOPR", DBF_SHORT, lopr);
testdbGetFieldEqual("test_ai_rec.PREC", DBF_SHORT, prec);
// number of tests = 7
}
static void test_no_linr_unit_conversion(void){
const short roff = 10;
const short aslo = 2;
const short aoff = 4;
const short rval = 9;
const short val = (((rval + roff) * aslo) + aoff);
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set unit conversion parameters */
testdbPutFieldOk("test_ai_rec.ROFF", DBF_SHORT, roff);
testdbPutFieldOk("test_ai_rec.ASLO", DBF_SHORT, aslo);
testdbPutFieldOk("test_ai_rec.AOFF", DBF_SHORT, aoff);
testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertNO_CONVERSION);
/* verify conversion */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_SHORT, val);
testdbGetFieldEqual("test_ai_rec.RVAL", DBF_SHORT, rval);
// number of tests = 10
}
static void test_slope_linr_unit_conversion(void){
const short roff = 1;
const short aslo = 3;
const short aoff = 99;
const short eslo = 3;
const short eoff = 2;
const short rval = 7;
const short val = ((((rval + roff) * aslo) + aoff) * eslo) + eoff;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set unit conversion parameters */
testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff);
testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo);
testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff);
testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo);
testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff);
testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertSLOPE);
/* verify conversion */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_LONG, rval);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_LONG, val);
testdbGetFieldEqual("test_ai_rec.RVAL", DBF_LONG, rval);
// number of tests = 12
}
static void test_linear_linr_unit_conversion(void){
const long roff = 6;
const long aslo = 2;
const long aoff = 19;
const long eslo = 4;
const long eoff = 1;
const long egul = 9999;
const long eguf = -1000;
const long rval = 2;
/* Since our raw soft input does not support EGUL and EGUF conversion LINEAR should work like SLOPE */
const long val = ((((rval + roff) * aslo) + aoff) * eslo) + eoff;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set unit conversion parameters */
testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff);
testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo);
testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff);
testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo);
testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff);
/* Since our raw soft input does not support EGUL and EGUF conversion we set them to lagre values here just to check they dont break anything */
testdbPutFieldOk("test_ai_rec.EGUL", DBF_LONG, egul);
testdbPutFieldOk("test_ai_rec.EGUF", DBF_LONG, eguf);
testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertLINEAR);
/* verify conversion */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_LONG, rval);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_LONG, val);
testdbGetFieldEqual("test_ai_rec.RVAL", DBF_LONG, rval);
// number of tests = 14
}
static void test_bpt_conversion(void){
const float typeKdegFin = 699.550510;
const float typeKdegF = 3.427551e+02;
const float typeKdegCin = 2902.787322;
const float typeKdegC = 7.028131e+02;
const float typeJdegFin = 1218.865107;
const float typeJdegF = 3.897504e+02;
const float typeJdegCin = 4042.988372;
const float typeJdegC = 6.918437e+02;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set unit conversion parameters */
testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, 0);
testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, 1);
testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, 0);
/* Set type K deg F break point table and verify */
testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeKdegF);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeKdegFin);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeKdegF);
/* Set type K deg C break point table and verify */
testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeKdegC);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeKdegCin);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeKdegC);
/* Set type J deg F break point table and verify */
testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeJdegF);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeJdegFin);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeJdegF);
/* Set type J deg C break point table and verify */
testdbPutFieldOk("test_ai_rec.LINR", DBF_SHORT, menuConverttypeJdegC);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_FLOAT, typeJdegCin);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, typeJdegC);
// number of tests = 18
}
static void test_smoothing_filter(void){
const double smoo = 0.7;
const short roff = 1;
const short aslo = 3;
const short aoff = -2;
const short eslo = 2;
const short eoff = 6;
const short rval1 = 7;
const short val1 = ((((rval1 + roff) * aslo) + aoff) * eslo) + eoff;
const short rval2 = 9;
const short val2_pre_smoo = ((((rval2 + roff) * aslo) + aoff) * eslo) + eoff;
const float val2 = val1 * smoo + (1 - smoo) * val2_pre_smoo;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set unit conversion parameters */
testdbPutFieldOk("test_ai_rec.ROFF", DBF_LONG, roff);
testdbPutFieldOk("test_ai_rec.ASLO", DBF_LONG, aslo);
testdbPutFieldOk("test_ai_rec.AOFF", DBF_LONG, aoff);
testdbPutFieldOk("test_ai_rec.ESLO", DBF_LONG, eslo);
testdbPutFieldOk("test_ai_rec.EOFF", DBF_LONG, eoff);
testdbPutFieldOk("test_ai_rec.LINR", DBF_LONG, menuConvertSLOPE);
/* set well known starting point (without smmothing) */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval1);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_SHORT, val1);
/* set SMOO */
testdbPutFieldOk("test_ai_rec.SMOO", DBF_DOUBLE, smoo);
/* update value again and verify smoothing */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_SHORT, rval2);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_FLOAT, val2);
/* clear SMOO */
testdbPutFieldOk("test_ai_rec.SMOO", DBF_DOUBLE, 0.0);
// number of tests = 15
}
static void test_udf(void){
/* set soft channel */
testdbPutFieldOk("test_ai_rec2.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec2.INP", DBF_STRING, "test_ai_link_rec2.VAL");
testdbPutFieldOk("test_ai_link_rec2.FLNK", DBF_STRING, "test_ai_rec2");
/* verify UDF */
testdbGetFieldEqual("test_ai_rec2.UDF", DBF_CHAR, TRUE);
/* set VAL and verify UDF is cleared */
testdbPutFieldOk("test_ai_link_rec2.VAL", DBF_FLOAT, 7.0);
testdbGetFieldEqual("test_ai_rec2.UDF", DBF_CHAR, FALSE);
// number of tests = 6
}
static void test_alarm(void){
const double h = 5000;
const double hh = 7500;
const double l = 200;
const double ll = -20;
const double hyst = 5;
/* set soft channel */
testdbPutFieldOk("test_ai_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_ai_rec.INP", DBF_STRING, "test_ai_link_rec.VAL");
testdbPutFieldOk("test_ai_link_rec.FLNK", DBF_STRING, "test_ai_rec");
/* set alarm parameters */
testdbPutFieldOk("test_ai_rec.HIGH", DBF_DOUBLE, h);
testdbPutFieldOk("test_ai_rec.HIHI", DBF_DOUBLE, hh);
testdbPutFieldOk("test_ai_rec.LOW", DBF_DOUBLE, l);
testdbPutFieldOk("test_ai_rec.LOLO", DBF_DOUBLE, ll);
testdbPutFieldOk("test_ai_rec.HHSV", DBF_SHORT, menuAlarmSevrMAJOR);
testdbPutFieldOk("test_ai_rec.HSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_ai_rec.LSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_ai_rec.LLSV", DBF_SHORT, menuAlarmSevrMAJOR);
/* set non alarm VAL and verify */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 400.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM);
/* set LOW alarm VAL and verify */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 190.0);
testdbGetFieldEqual("test_ai_rec.VAL", DBF_DOUBLE, 190.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR);
/* set LOLO alarm VAL and verify */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, -9998.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
/* set HIGH alarm VAL and verify */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 6111.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR);
/* set HIHI alarm VAL and verify */
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, 19998.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
/* set HYST, verify and clear */
testdbPutFieldOk("test_ai_rec.HYST", DBF_DOUBLE, hyst);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, hh - 1.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
testdbPutFieldOk("test_ai_rec.HYST", DBF_DOUBLE, 0.0);
testdbPutFieldOk("test_ai_link_rec.VAL", DBF_DOUBLE, hh - 2.0);
testdbGetFieldEqual("test_ai_rec.SEVR", DBF_SHORT, menuAlarmSevrMINOR);
/* verify LALM */
testdbGetFieldEqual("test_ai_rec.LALM", DBF_DOUBLE, h);
// number of tests = 29
}
static void test_aftc(void){
const double h = 5000;
const double hh = 7500;
const double l = 200;
const double ll = -20;
const double aftc = 3;
testMonitor* test_mon = NULL;
epicsTimeStamp startTime;
epicsTimeStamp endTime;
double diffTime = 0;
/* set soft channel */
testdbPutFieldOk("test_ai_rec3.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_ai_rec3.INP", DBF_STRING, "test_ai_link_rec3.VAL");
testdbPutFieldOk("test_ai_link_rec3.FLNK", DBF_STRING, "test_ai_rec3");
/* set alarm parameters */
testdbPutFieldOk("test_ai_rec3.HIGH", DBF_DOUBLE, h);
testdbPutFieldOk("test_ai_rec3.HIHI", DBF_DOUBLE, hh);
testdbPutFieldOk("test_ai_rec3.LOW", DBF_DOUBLE, l);
testdbPutFieldOk("test_ai_rec3.LOLO", DBF_DOUBLE, ll);
testdbPutFieldOk("test_ai_rec3.HHSV", DBF_SHORT, menuAlarmSevrMAJOR);
testdbPutFieldOk("test_ai_rec3.HSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_ai_rec3.LSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_ai_rec3.LLSV", DBF_SHORT, menuAlarmSevrMAJOR);
/* test AFTC using a monitor and time stamps */
testdbPutFieldOk("test_ai_rec3.AFTC", DBF_DOUBLE, aftc);
testdbPutFieldOk("test_ai_rec3.SCAN", DBF_SHORT, menuScan_1_second);
/* set HIHI alarm VAL */
testdbPutFieldOk("test_ai_link_rec3.VAL", DBF_DOUBLE, 7550.0);
/* Create test monitor for alarm SEVR */
test_mon = testMonitorCreate("test_ai_rec3.VAL", DBE_ALARM, 0);
/* Get start time */
epicsTimeGetCurrent(&startTime);
/* wait for monitor to trigger on the new alarm status*/
testMonitorWait(test_mon);
epicsTimeGetCurrent(&endTime);
/* Verify that alarm status is now MAJOR */
testdbGetFieldEqual("test_ai_rec3.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
/* set HI alarm VAL */
testdbPutFieldOk("test_ai_link_rec3.VAL", DBF_DOUBLE, 5550.0);
/* Create test monitor for alarm SEVR */
test_mon = testMonitorCreate("test_ai_rec3.VAL", DBE_ALARM, 0);
/* Get start time */
epicsTimeGetCurrent(&startTime);
/* wait for monitor to trigger on the new alarm status*/
testMonitorWait(test_mon);
epicsTimeGetCurrent(&endTime);
/* Verify that alarm status is now MINOR */
testdbGetFieldEqual("test_ai_rec3.SEVR", DBF_SHORT, menuAlarmSevrMINOR);
/* Verify that time is at least equal to configured aftc */
diffTime = epicsTimeDiffInSeconds(&endTime, &startTime);
testOk(diffTime >= aftc, "ATFC time %lf", diffTime);
// number of tests = 18
}
MAIN(aiTest) {
#ifdef _WIN32
#if (defined(_MSC_VER) && _MSC_VER < 1900) || \
(defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT))
_set_output_format(_TWO_DIGIT_EXPONENT);
#endif
#endif
testPlan(6+6+11+10+12+14+18+15+6+29+18);
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("aiTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
test_soft_input();
test_raw_soft_input();
test_operator_display();
test_no_linr_unit_conversion();
test_slope_linr_unit_conversion();
test_linear_linr_unit_conversion();
test_bpt_conversion();
test_smoothing_filter();
test_udf();
test_alarm();
test_aftc();
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,20 @@
record(ai, "test_ai_rec") {
}
record(ao, "test_ai_link_rec") {
}
record(ai, "test_ai_rec2") {
}
record(ao, "test_ai_link_rec2") {
}
record(ai, "test_ai_rec3") {
}
record(ao, "test_ai_link_rec3") {
}

View File

@@ -0,0 +1,122 @@
/*************************************************************************\
* Copyright (c) 2023 Karl Vestin
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include "dbUnitTest.h"
#include "testMain.h"
#include "errlog.h"
#include "dbAccess.h"
#include "menuAlarmSevr.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void test_soft_input(void){
/* set soft channel */
testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL");
testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec");
/* set VAL to on linked record */
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE);
/* verify that this record VAL is updated but RVAL is not */
testdbGetFieldEqual("test_bi_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bi_rec.RVAL", DBF_SHORT, FALSE);
// number of tests = 6
}
static void test_raw_soft_input(void){
/* set soft channel */
testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Raw Soft Channel");
testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL");
testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec");
/* set VAL to on linked record */
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE);
/* verify that this record RVAL and VAL are updated */
testdbGetFieldEqual("test_bi_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bi_rec.RVAL", DBF_SHORT, TRUE);
// number of tests = 6
}
static void test_operator_display(void){
/* set operator display parameters */
testdbPutFieldOk("test_bi_rec.ZNAM", DBF_STRING, "ZNAM_TEST");
testdbPutFieldOk("test_bi_rec.ONAM", DBF_STRING, "ONAM_TEST");
testdbPutFieldOk("test_bi_rec.DESC", DBF_STRING, "DESC_TEST");
/* verify operator display parameters */
testdbGetFieldEqual("test_bi_rec.ZNAM", DBF_STRING, "ZNAM_TEST");
testdbGetFieldEqual("test_bi_rec.ONAM", DBF_STRING, "ONAM_TEST");
testdbGetFieldEqual("test_bi_rec.NAME", DBF_STRING, "test_bi_rec");
testdbGetFieldEqual("test_bi_rec.DESC", DBF_STRING, "DESC_TEST");
/* verify conversion */
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bi_rec.VAL", DBF_STRING, "ONAM_TEST");
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, FALSE);
testdbGetFieldEqual("test_bi_rec.VAL", DBF_STRING, "ZNAM_TEST");
// number of tests = 11
}
static void test_alarm(void){
/* set soft channel */
testdbPutFieldOk("test_bi_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_bi_rec.INP", DBF_STRING, "test_bi_link_rec.VAL");
testdbPutFieldOk("test_bi_link_rec.FLNK", DBF_STRING, "test_bi_rec");
/* set start VAL to FALSE*/
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, FALSE);
/* set alarm parameters */
testdbPutFieldOk("test_bi_rec.ZSV", DBF_SHORT, menuAlarmSevrNO_ALARM);
testdbPutFieldOk("test_bi_rec.OSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_bi_rec.COSV", DBF_SHORT, menuAlarmSevrINVALID);
/* verify alarm status is NO_ALARM*/
testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM);
/* set ZSV to MAJOR and verify that SEVR is now MAJOR */
testdbPutFieldOk("test_bi_rec.ZSV", DBF_SHORT, menuAlarmSevrMAJOR);
testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
/* set VAL to 1 on linked record and verify that COSV now sets the SEVR to INVALID */
testdbPutFieldOk("test_bi_link_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bi_rec.SEVR", DBF_SHORT, menuAlarmSevrINVALID);
/* verify LAML */
testdbGetFieldEqual("test_bi_rec.LALM", DBF_SHORT, TRUE);
// number of tests = 13
}
MAIN(biTest) {
testPlan(6+6+11+13);
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("biTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
test_soft_input();
test_raw_soft_input();
test_operator_display();
test_alarm();
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,6 @@
record(bi, "test_bi_rec") {
}
record(bo, "test_bi_link_rec") {
}

View File

@@ -0,0 +1,158 @@
/*************************************************************************\
* Copyright (c) 2023 Karl Vestin
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include "dbUnitTest.h"
#include "testMain.h"
#include "errlog.h"
#include "dbAccess.h"
#include "menuAlarmSevr.h"
#include "menuIvoa.h"
#include "boRecord.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void test_soft_output(void){
/* set soft channel */
testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec");
/* set VAL to process record */
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
/* verify that OUT record is updated */
testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, TRUE);
// number of tests = 4
}
static void test_high(void){
const int high_time = 2;
testMonitor* test_mon = NULL;
epicsTimeStamp startTime;
epicsTimeStamp endTime;
double diffTime = 0;
double diffTimeTolerance = 0.1;
/* set soft channel */
testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec");
/* set HIGH to 2 seconds */
testdbPutFieldOk("test_bo_rec.HIGH", DBF_SHORT, high_time);
/* Create test monitor */
test_mon = testMonitorCreate("test_bo_rec.VAL", DBR_SHORT, 0);
/* Get start time */
epicsTimeGetCurrent(&startTime);
/* set VAL to process record */
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
/* wait and verfiy time */
testMonitorWait(test_mon);
epicsTimeGetCurrent(&endTime);
/* EPICS timers have a tendency to trip slightly early, hence the test tolerance is added here to avoid false positives in testing */
diffTime = epicsTimeDiffInSeconds(&endTime, &startTime) + diffTimeTolerance;
testOk(diffTime >= high_time, "HIGH time %lf", diffTime);
/* verify that both records are set back to 0 */
testdbGetFieldEqual("test_bo_rec.VAL", DBF_SHORT, FALSE);
testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE);
testMonitorDestroy(test_mon);
// number of tests = 7
}
static void test_operator_display(void){
/* set operator display parameters */
testdbPutFieldOk("test_bo_rec.ZNAM", DBF_STRING, "ZNAM_TEST");
testdbPutFieldOk("test_bo_rec.ONAM", DBF_STRING, "ONAM_TEST");
testdbPutFieldOk("test_bo_rec.DESC", DBF_STRING, "DESC_TEST");
/* verify operator display parameters */
testdbGetFieldEqual("test_bo_rec.ZNAM", DBF_STRING, "ZNAM_TEST");
testdbGetFieldEqual("test_bo_rec.ONAM", DBF_STRING, "ONAM_TEST");
testdbGetFieldEqual("test_bo_rec.NAME", DBF_STRING, "test_bo_rec");
testdbGetFieldEqual("test_bo_rec.DESC", DBF_STRING, "DESC_TEST");
/* verify conversion */
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bo_rec.VAL", DBF_STRING, "ONAM_TEST");
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE);
testdbGetFieldEqual("test_bo_rec.VAL", DBF_STRING, "ZNAM_TEST");
// number of tests = 11
}
static void test_alarm(void){
/* set soft channel */
testdbPutFieldOk("test_bo_rec.DTYP", DBF_STRING, "Soft Channel");
testdbPutFieldOk("test_bo_rec.OUT", DBF_STRING, "test_bo_link_rec");
/* Set start VAL to FALSE*/
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE);
/* set alarm parameters */
testdbPutFieldOk("test_bo_rec.ZSV", DBF_SHORT, menuAlarmSevrNO_ALARM);
testdbPutFieldOk("test_bo_rec.OSV", DBF_SHORT, menuAlarmSevrMINOR);
testdbPutFieldOk("test_bo_rec.COSV", DBF_SHORT, menuAlarmSevrINVALID);
testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaSet_output_to_IVOV);
testdbPutFieldOk("test_bo_rec.IVOV", DBF_SHORT, FALSE);
/* Verify alarm status is NO_ALARM*/
testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrNO_ALARM);
/* Set ZSV to MAJOR and verify that SEVR is now MAJOR */
testdbPutFieldOk("test_bo_rec.ZSV", DBF_SHORT, menuAlarmSevrMAJOR);
testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrMAJOR);
/* Set VAL to 1 and verify that COSV now sets the SEVR to INVALID and in turn triggers the IVOV on output */
testdbPutFieldOk("test_bo_link_rec.VAL", DBF_SHORT, TRUE);
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bo_rec.SEVR", DBF_SHORT, menuAlarmSevrINVALID);
testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE);
/* verify IVOV continue normally action */
testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaContinue_normally);
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, TRUE);
/* verify IVOV dont drive outputs action */
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, FALSE);
testdbPutFieldOk("test_bo_rec.IVOA", DBF_SHORT, menuIvoaDon_t_drive_outputs);
testdbPutFieldOk("test_bo_rec.VAL", DBF_SHORT, TRUE);
testdbGetFieldEqual("test_bo_link_rec.VAL", DBF_SHORT, FALSE);
// number of tests = 22
}
MAIN(boTest) {
testPlan(4+7+11+22);
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("boTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
test_soft_output();
test_high();
test_operator_display();
test_alarm();
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,6 @@
record(bo, "test_bo_rec") {
}
record(bi, "test_bo_link_rec") {
}

View File

@@ -24,6 +24,10 @@ int asyncSoftTest(void);
int simmTest(void);
int mbbioDirectTest(void);
int scanEventTest(void);
int boTest(void);
int biTest(void);
int printfTest(void);
int aiTest(void);
void epicsRunRecordTests(void)
{
@@ -51,5 +55,13 @@ void epicsRunRecordTests(void)
runTest(scanEventTest);
runTest(boTest);
runTest(biTest);
runTest(printfTest);
runTest(aiTest);
epicsExit(0); /* Trigger test harness */
}

View File

@@ -0,0 +1,577 @@
/*************************************************************************\
* Copyright (c) 2023 Karl Vestin
* EPICS BASE is distributed subject to a Software License Agreement found
* in file LICENSE that is included with this distribution.
\*************************************************************************/
#include "dbUnitTest.h"
#include "testMain.h"
#include "errlog.h"
#include "dbAccess.h"
void recTestIoc_registerRecordDeviceDriver(struct dbBase *);
static void test_double_percentile(void){
const char format_string[] = "Format test string %%d";
const char result_string[] = "Format test string %d";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 1);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_d_format(void){
const char format_string[] = "Format test string %d";
const char result_string[] = "Format test string 7";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_c_format(void){
const char format_string[] = "Format test string %c";
const char result_string[] = "Format test string R";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0, 82 is ASCII for R */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 82);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_i_format(void){
const char format_string[] = "Format test string %i";
const char result_string[] = "Format test string -27";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, -27);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_o_format(void){
const char format_string[] = "Format test string %o";
const char result_string[] = "Format test string 6777";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 06777);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_u_format(void){
const char format_string[] = "Format test string %u";
const char result_string[] = "Format test string 8009";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 8009);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_x_format(void){
const char format_string[] = "Format test string %x";
const char result_string[] = "Format test string fafa";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xfafa);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_X_format(void){
const char format_string[] = "Format test string %X";
const char result_string[] = "Format test string BA";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 0x00ba);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_e_format(void){
const char format_string[] = "Format test string %e";
const char result_string[] = "Format test string -1.400000e+01";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, -14);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_E_format(void){
const char format_string[] = "Format test string %E";
const char result_string[] = "Format test string 1.992000E+03";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 1992);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_f_format(void){
const char format_string[] = "Format test string %f";
const char result_string[] = "Format test string -0.062000";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, -0.062);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_F_format(void){
const char format_string[] = "Format test string %F";
const char result_string[] = "Format test string 6729982.999000";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, 6729982.999);
/* verify that string is formatted as expected */
// visual studio less than 2015 does not support %F
// mingw/gcc also fails, suspect this may be gcc version
// related and checking __GNUC__ could resolve but
// initial attempts didn't work so excluding mingw entirely for now
#ifdef _WIN32
#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_MINGW)
testTodoBegin("Fails on windows with old visual studio versions and mingw");
#endif
#endif
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
#ifdef _WIN32
#if (defined(_MSC_VER) && _MSC_VER < 1900) || defined(_MINGW)
testTodoEnd();
#endif
#endif
// number of tests = 3
}
static void test_g_format(void){
const char format_string[] = "Format test string %g";
const char result_string[] = "Format test string -0.093";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, -93e-3);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_G_format(void){
const char format_string[] = "Format test string %G";
const char result_string[] = "Format test string 7.2884E+08";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 728839938);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_s_format(void){
const char format_string[] = "Format test string %d %s";
const char result_string[] = "Format test string 7 Molly III";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7);
/* set value on inp1 */
testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "Molly III");
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 4
}
static void test_plus_flag(void){
const char format_string[] = "Format test string %+d";
const char result_string[] = "Format test string +7";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 7);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_minus_flag(void){
const char format_string[] = "Format test string %-10d";
const char result_string[] = "Format test string 18 ";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 18);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_space_flag(void){
const char format_string[] = "Format test string % d";
const char result_string[] = "Format test string 12";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 12);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_hash_flag(void){
const char format_string[] = "Format test string %#o";
const char result_string[] = "Format test string 014";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 014);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_min_width_flag(void){
const char format_string[] = "Format test string %04i";
const char result_string[] = "Format test string 0003";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 3);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_prec_flag(void){
const char format_string[] = "Format test string %.4f";
const char result_string[] = "Format test string 71.2000";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_DOUBLE, 71.2);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_h_flag(void){
const char format_string[] = "Format test string %hx";
const char result_string[] = "Format test string baba";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xffbaba);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_hh_flag(void){
const char format_string[] = "Format test string %hhx";
const char result_string[] = "Format test string c1";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0xffc0c1);
/* verify that string is formatted as expected */
#ifdef __rtems__
testTodoBegin("Fails on UB-20 gcc-9 on RTEMS");
#endif
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
#ifdef __rtems__
testTodoEnd();
#endif
// number of tests = 3
}
static void test_l_flag(void){
const char format_string[] = "Format test string %lx";
const char result_string[] = "Format test string 70a1c0c1";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_LONG, 0x70a1c0c1);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 3
}
static void test_ll_flag(void){
const char format_string[] = "%d %s %llx";
const char result_string[] = "2 Reperbahn ba0110baa0a1c0c1";
const epicsInt64 value = 0xba0110baa0a1c0c1ull;
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 2);
/* set value on inp1 */
testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "Reperbahn");
/* set value on inp2 */
testdbPutFieldOk("test_printf_inp2_rec.VAL", DBR_INT64, value);
testdbGetFieldEqual("test_printf_inp2_rec.VAL", DBR_INT64, value);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 6
}
static void test_sizv(void){
const char format_string[] = "%d %s %llx";
const char result_string[] = "99 123456789012345678901234567890 6640baa0a1";
const epicsInt64 value = 0x06640baa0a1c0c1ull;
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 99);
/* set value on inp1 */
testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "123456789012345678901234567890");
/* set value on inp2 */
testdbPutFieldOk("test_printf_inp2_rec.VAL", DBR_INT64, value);
testdbGetFieldEqual("test_printf_inp2_rec.VAL", DBR_INT64, value);
/* verify that string is formatted as expected */
testdbGetArrFieldEqual("test_printf_rec.VAL$", DBF_CHAR, 45, 45, result_string);
// number of tests = 6
}
static void test_all_inputs(void){
const char format_string[] = "%d %s %i %i %i %i %i %i %i %i";
const char result_string[] = "0 One 2 3 4 5 6 7 8 9";
/* set format string */
testdbPutFieldOk("test_printf_rec.FMT", DBF_STRING, format_string);
/* set value on inp0 */
testdbPutFieldOk("test_printf_inp0_rec.VAL", DBF_SHORT, 0);
/* set value on inp1 */
testdbPutFieldOk("test_printf_inp1_rec.VAL", DBF_STRING, "One");
/* set value on inp2 */
testdbPutFieldOk("test_printf_inp2_rec.VAL", DBF_SHORT, 2);
/* set value on inp3 */
testdbPutFieldOk("test_printf_inp3_rec.VAL", DBF_SHORT, 3);
/* set value on inp4 */
testdbPutFieldOk("test_printf_inp4_rec.VAL", DBF_SHORT, 4);
/* set value on inp5 */
testdbPutFieldOk("test_printf_inp5_rec.VAL", DBF_SHORT, 5);
/* set value on inp6 */
testdbPutFieldOk("test_printf_inp6_rec.VAL", DBF_SHORT, 6);
/* set value on inp7 */
testdbPutFieldOk("test_printf_inp7_rec.VAL", DBF_SHORT, 7);
/* set value on inp8 */
testdbPutFieldOk("test_printf_inp8_rec.VAL", DBF_SHORT, 8);
/* set value on inp9 */
testdbPutFieldOk("test_printf_inp9_rec.VAL", DBF_SHORT, 9);
/* verify that string is formatted as expected */
testdbGetFieldEqual("test_printf_rec.VAL", DBF_STRING, result_string);
// number of tests = 12
}
MAIN(printfTest) {
#ifdef _WIN32
#if (defined(_MSC_VER) && _MSC_VER < 1900) || \
(defined(_MINGW) && defined(_TWO_DIGIT_EXPONENT))
_set_output_format(_TWO_DIGIT_EXPONENT);
#endif
#endif
testPlan(3+3+3+3+3+3+3+3+4+3+3+3+3+3+3+3+3+3+3+3+3+3+3+3+6+6+12);
testdbPrepare();
testdbReadDatabase("recTestIoc.dbd", NULL, NULL);
recTestIoc_registerRecordDeviceDriver(pdbbase);
testdbReadDatabase("printfTest.db", NULL, NULL);
eltc(0);
testIocInitOk();
eltc(1);
test_double_percentile();
test_c_format();
test_d_format();
test_i_format();
test_o_format();
test_u_format();
test_x_format();
test_X_format();
test_e_format();
test_E_format();
test_f_format();
test_F_format();
test_g_format();
test_G_format();
test_s_format();
test_plus_flag();
test_minus_flag();
test_space_flag();
test_hash_flag();
test_min_width_flag();
test_prec_flag();
test_h_flag();
test_hh_flag();
test_l_flag();
test_ll_flag();
test_sizv();
test_all_inputs();
testIocShutdownOk();
testdbCleanup();
return testDone();
}

View File

@@ -0,0 +1,53 @@
record(printf, "test_printf_rec") {
field(SIZV, 45)
field(INP0, "test_printf_inp0_rec")
field(INP1, "test_printf_inp1_rec")
field(INP2, "test_printf_inp2_rec")
field(INP3, "test_printf_inp3_rec")
field(INP4, "test_printf_inp4_rec")
field(INP5, "test_printf_inp5_rec")
field(INP6, "test_printf_inp6_rec")
field(INP7, "test_printf_inp7_rec")
field(INP8, "test_printf_inp8_rec")
field(INP9, "test_printf_inp9_rec")
}
record(ai, "test_printf_inp0_rec") {
field(FLNK, "test_printf_rec")
}
record(stringin, "test_printf_inp1_rec") {
field(FLNK, "test_printf_rec")
}
record(int64in, "test_printf_inp2_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp3_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp4_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp5_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp6_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp7_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp8_rec") {
field(FLNK, "test_printf_rec")
}
record(ai, "test_printf_inp9_rec") {
field(FLNK, "test_printf_rec")
}

View File

@@ -37,7 +37,7 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
sockfd = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP ); // Create a UDP socket.
if ( sockfd < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime creating socket" );
return -1;
}
@@ -51,24 +51,29 @@ int epicsNtpGetTime(char *ntpIp, struct timespec *now)
// Call up the server using its IP address and port number.
if ( connect( sockfd, ( struct sockaddr * ) &serv_addr, sizeof( serv_addr) ) < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime connecting socket" );
close( sockfd );
return -1;
}
// Send it the NTP packet it wants. If n == -1, it failed.
n = write( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime sending NTP request" );
close( sockfd );
return -1;
}
// Wait and receive the packet back from the server. If n == -1, it failed.
// Wait and receive the packet back from the server. If n == -1, it failed.
n = read( sockfd, ( char* ) &packet, sizeof( ntp_packet ) );
if ( n < 0 ) {
perror( "epicsNtpGetTime" );
perror( "epicsNtpGetTime reading NTP reply" );
close( sockfd );
return -1;
}
close( sockfd );
// These two fields contain the time-stamp seconds as the packet left the NTP server.
// The number of seconds correspond to the seconds passed since 1900.
// ntohl() converts the bit/byte order from the network's to host's "endianness".

View File

@@ -60,7 +60,19 @@ extern void *POSIX_Init(void *argument);
#define CONFIGURE_APPLICATION_NEEDS_STUB_DRIVER
#define CONFIGURE_APPLICATION_NEEDS_ZERO_DRIVER
/* Max FDs cannot exceed FD_SETSIZE from newlib (64) */
/* Note: The select() system call can only be used with the first FD_SETSIZE
* File Descriptors (newlib default is 64). Beginning RTEMS 5.1, FDs are
* allocated sequentially. So changing this CONFIGURE parameter such
* that CONFIGURE_MAXIMUM_FILE_DESCRIPTORS >= FD_SETSIZE will likely
* cause applications making select() calls to fault at some point.
*
* IOC core components (libca and RSRV) do not make select() calls.
*
* Applications and driver code using poll() or other socket
* multiplexers do not share this limitation.
*
* cf. https://github.com/epics-base/epics-base/issues/300
*/
#define CONFIGURE_MAXIMUM_FILE_DESCRIPTORS 64
#define CONFIGURE_IMFS_ENABLE_MKFIFO 2

View File

@@ -209,7 +209,7 @@ static int yyerror(char *str)
if (strlen(str))
errlogPrintf("%s at line %d\n", str, line_num);
else
errlogPrintf("Error at line %d\n", line_num);
errlogPrintf(ERL_ERROR " at line %d\n", line_num);
yyFailed = TRUE;
return 0;
}

View File

@@ -29,6 +29,8 @@
#include "postfix.h"
#include "asLib.h"
#undef ECHO /* from termios.h */
int asCheckClientIP;
static epicsMutexId asLock;
@@ -174,12 +176,12 @@ long epicsStdCall asInitFile(const char *filename,const char *substitutions)
fp = fopen(filename,"r");
if(!fp) {
errlogPrintf("asInitFile: Can't open file '%s'\n", filename);
fprintf(stderr, ERL_ERROR " asInitFile: Can't open file '%s'\n", filename);
return(S_asLib_badConfig);
}
status = asInitFP(fp,substitutions);
if(fclose(fp)==EOF) {
errMessage(0,"asInitFile: fclose failed!");
fprintf(stderr, ERL_ERROR " asInitFile: fclose failed!");
if(!status) status = S_asLib_badConfig;
}
return(status);
@@ -357,7 +359,6 @@ void epicsStdCall asPutMemberPvt(ASMEMBERPVT asMemberPvt,void *userPvt)
if(!asActive) return;
if(!pasgmember) return;
pasgmember->userPvt = userPvt;
return;
}
long epicsStdCall asAddClient(ASCLIENTPVT *pasClientPvt,ASMEMBERPVT asMemberPvt,

View File

@@ -336,6 +336,10 @@ LIBCOM_API long
break;
case SEPERATOR:
if (pstacktop == stack) {
*perror = CALC_ERR_BAD_SEPERATOR;
goto bad;
}
/* Move operators to the output until open paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {
@@ -354,6 +358,10 @@ LIBCOM_API long
break;
case CLOSE_PAREN:
if (pstacktop == stack) {
*perror = CALC_ERR_PAREN_NOT_OPEN;
goto bad;
}
/* Move operators to the output until matching paren */
while (pstacktop->name[0] != '(') {
if (pstacktop <= stack+1) {

View File

@@ -23,6 +23,9 @@
class SingletonUntyped {
public:
#if __cplusplus>=201103L
constexpr
#endif
SingletonUntyped () :_pInstance ( 0 ), _refCount ( 0 ) {}
# if 0
~SingletonUntyped () {
@@ -112,6 +115,9 @@ public:
epicsSingleton * _pSingleton;
};
friend class reference;
#if __cplusplus>=201103L
constexpr
#endif
epicsSingleton () {}
// mutex lock/unlock pair overhead incurred
// when either of these are called

View File

@@ -25,8 +25,8 @@
namespace {
static epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT );
static epicsMutex * pEPICSSigletonMutex = 0;
epicsThreadOnceId epicsSigletonOnceFlag ( EPICS_THREAD_ONCE_INIT );
epicsMutex * pEPICSSigletonMutex = 0;
extern "C" void SingletonMutexOnce ( void * /* pParm */ )
{

View File

@@ -34,8 +34,6 @@ void ellAdd (ELLLIST *pList, ELLNODE *pNode)
pList->node.previous = pNode;
pList->count++;
return;
}
/****************************************************************************
*
@@ -65,8 +63,6 @@ void ellConcat (ELLLIST *pDstList, ELLLIST *pAddList)
pAddList->count = 0;
pAddList->node.next = NULL;
pAddList->node.previous = NULL;
return;
}
/****************************************************************************
*
@@ -86,8 +82,6 @@ void ellDelete (ELLLIST *pList, ELLNODE *pNode)
pNode->previous->next = pNode->next;
pList->count--;
return;
}
/****************************************************************************
*
@@ -136,8 +130,6 @@ void ellExtract (ELLLIST *pSrcList, ELLNODE *pStartNode, ELLNODE *pEndNode, ELLL
}
pSrcList->count -= count;
pDstList->count += count;
return;
}
/****************************************************************************
*
@@ -194,8 +186,6 @@ void ellInsert (ELLLIST *plist, ELLNODE *pPrev, ELLNODE *pNode)
pNode->next->previous = pNode;
plist->count++;
return;
}
/****************************************************************************
*

View File

@@ -26,6 +26,7 @@ ERR_S_FILES += $(LIBCOM)/as/asLib.h
ERR_S_FILES += $(LIBCOM)/misc/epicsStdlib.h
ERR_S_FILES += $(LIBCOM)/pool/epicsThreadPool.h
ERR_S_FILES += $(LIBCOM)/error/errMdef.h
ERR_S_FILES += $(LIBCOM)/error/errSymTbl.h
ERR_S_FILES += $(TOP)/modules/database/src/ioc/db/dbAccessDefs.h
ERR_S_FILES += $(TOP)/modules/database/src/ioc/dbStatic/dbStaticLib.h

View File

@@ -19,6 +19,7 @@
#define RTN_SUCCESS(STATUS) ((STATUS)==0)
/* Module numbers start above 500 for compatibility with vxWorks errnoLib */
#define MIN_MODULE_NUM 501
/* FIXME: M_xxx values could be declared as integer variables and set
* at runtime from registration routines; the S_xxx definitions would
@@ -32,6 +33,7 @@
#define M_stdlib (504 << 16) /* EPICS Standard library */
#define M_pool (505 << 16) /* Thread pool */
#define M_time (506 << 16) /* epicsTime */
#define M_err (507 << 16) /* Error */
/* ioc */
#define M_dbAccess (511 << 16) /* Database Access Routines */

View File

@@ -23,6 +23,9 @@
#include "cantProceed.h"
#include "epicsAssert.h"
#include "epicsStdio.h"
#include "epicsTypes.h"
#include "epicsMutex.h"
#include "epicsThread.h"
#include "errMdef.h"
#include "errSymTbl.h"
#include "ellLib.h"
@@ -33,69 +36,49 @@
static epicsUInt16 errhash(long errNum);
typedef struct errnumnode {
ELLNODE node;
long errNum;
struct errnumnode *hashnode;
const char *message;
long pad;
} ERRNUMNODE;
static ELLLIST errnumlist = ELLLIST_INIT;
static ERRNUMNODE **hashtable;
static int initialized = 0;
typedef struct {
ERRNUMNODE *table[NHASH * sizeof(ERRNUMNODE *)];
epicsMutexId tableMutexId;
} errHashTable_t;
static errHashTable_t errHashTable;
extern ERRSYMTAB_ID errSymTbl;
/****************************************************************
* ERRSYMBLD
*
* Create the normal ell LIST of sorted error messages nodes
* Followed by linked hash lists - that link together those
* ell nodes that have a common hash number.
* Populate EPICS error symbols
*
***************************************************************/
int errSymBld(void)
{
ERRSYMBOL *errArray = errSymTbl->symbols;
ERRNUMNODE *perrNumNode = NULL;
ERRNUMNODE *pNextNode = NULL;
ERRNUMNODE **phashnode = NULL;
int i;
int modnum;
if (initialized)
return(0);
hashtable = (ERRNUMNODE**)callocMustSucceed
(NHASH, sizeof(ERRNUMNODE*),"errSymBld");
ERRSYMBOL *errArray = errSymTbl->symbols;
int i;
for (i = 0; i < errSymTbl->nsymbols; i++, errArray++) {
modnum = errArray->errNum >> 16;
if (modnum < 501) {
fprintf(stderr, "errSymBld: ERROR - Module number in errSymTbl < 501 was Module=%lx Name=%s\n",
errArray->errNum, errArray->name);
continue;
}
if ((errSymbolAdd(errArray->errNum, errArray->name)) < 0) {
if (errSymbolAdd(errArray->errNum, errArray->name)) {
fprintf(stderr, "errSymBld: ERROR - errSymbolAdd() failed \n");
continue;
}
}
perrNumNode = (ERRNUMNODE *) ellFirst(&errnumlist);
while (perrNumNode) {
/* hash each perrNumNode->errNum */
epicsUInt16 hashInd = errhash(perrNumNode->errNum);
phashnode = (ERRNUMNODE**)&hashtable[hashInd];
pNextNode = (ERRNUMNODE*) *phashnode;
/* search for last node (NULL) of hashnode linked list */
while (pNextNode) {
phashnode = &pNextNode->hashnode;
pNextNode = *phashnode;
}
*phashnode = perrNumNode;
perrNumNode = (ERRNUMNODE *) ellNext((ELLNODE *) perrNumNode);
}
initialized = 1;
return(0);
return 0;
}
static void _initErrorHashTable(void *_unused)
{
errHashTable.tableMutexId = epicsMutexMustCreate();
}
static void initErrorHashTable()
{
static epicsThreadOnceId initErrSymOnceFlag = EPICS_THREAD_ONCE_INIT;
epicsThreadOnce(&initErrSymOnceFlag, _initErrorHashTable, NULL);
}
/****************************************************************
@@ -109,21 +92,47 @@ static epicsUInt16 errhash(long errNum)
modnum = (unsigned short) (errNum >> 16);
errnum = (unsigned short) (errNum & 0xffff);
return (((modnum - 500) * 20) + errnum) % NHASH;
return ((modnum - (MIN_MODULE_NUM - 1)) * 20 + errnum) % NHASH;
}
/****************************************************************
* ERRSYMBOLADD
* adds symbols to the master errnumlist as compiled from errSymTbl.c
* adds symbols to the global error symbol hash table
***************************************************************/
int errSymbolAdd(long errNum, const char *name)
{
ERRNUMNODE *pNew = (ERRNUMNODE*) callocMustSucceed(1,
sizeof(ERRNUMNODE), "errSymbolAdd");
ERRNUMNODE *pNextNode = NULL;
ERRNUMNODE **phashnode = NULL;
ERRNUMNODE *pNew = NULL;
int modnum = (epicsUInt16) (errNum >> 16);
epicsUInt16 hashInd = errhash(errNum);
if (modnum < MIN_MODULE_NUM)
return S_err_invCode;
initErrorHashTable();
epicsMutexLock(errHashTable.tableMutexId);
phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
pNextNode = (ERRNUMNODE*) *phashnode;
/* search for last node (NULL) of hashnode linked list */
while (pNextNode) {
if (pNextNode->errNum == errNum) {
int notIdentical = strcmp(name, pNextNode->message);
epicsMutexUnlock(errHashTable.tableMutexId);
return notIdentical ? S_err_codeExists : 0;
}
phashnode = &pNextNode->hashnode;
pNextNode = *phashnode;
}
pNew = (ERRNUMNODE*) callocMustSucceed(
1, sizeof(ERRNUMNODE), "errSymbolAdd");
pNew->errNum = errNum;
pNew->message = name;
ellAdd(&errnumlist, (ELLNODE*)pNew);
*phashnode = pNew;
epicsMutexUnlock(errHashTable.tableMutexId);
return 0;
}
@@ -155,31 +164,31 @@ const char* errSymLookupInternal(long status)
if (!status)
return "Ok";
if (!initialized)
errSymBld();
initErrorHashTable();
modNum = (unsigned) status;
modNum >>= 16;
modNum &= 0xffff;
if (modNum <= 500) {
const char * pStr = strerror ((int) status);
if (pStr) {
return pStr;
}
if (modNum < MIN_MODULE_NUM) {
return strerror ((int) status);
}
else {
unsigned hashInd = errhash(status);
phashnode = (ERRNUMNODE**)&hashtable[hashInd];
const char *result = NULL;
epicsMutexLock(errHashTable.tableMutexId);
phashnode = (ERRNUMNODE**)&errHashTable.table[hashInd];
pNextNode = *phashnode;
while (pNextNode) {
if (pNextNode->errNum==status){
return pNextNode->message;
result = pNextNode->message;
break;
}
phashnode = &pNextNode->hashnode;
pNextNode = *phashnode;
}
epicsMutexUnlock(errHashTable.tableMutexId);
return result;
}
return NULL;
}
const char* errSymMsg(long status)
@@ -210,12 +219,12 @@ void errSymDump(void)
int i;
int msgcount = 0;
if (!initialized) errSymBld();
initErrorHashTable();
msgcount = 0;
printf("errSymDump: number of hash slots = %d\n", NHASH);
for (i = 0; i < NHASH; i++) {
ERRNUMNODE **phashnode = &hashtable[i];
ERRNUMNODE **phashnode = &errHashTable.table[i];
ERRNUMNODE *pNextNode = *phashnode;
int count = 0;
@@ -246,21 +255,21 @@ void errSymTestPrint(long errNum)
epicsUInt16 modnum;
epicsUInt16 errnum;
if (!initialized) errSymBld();
initErrorHashTable();
message[0] = '\0';
modnum = (epicsUInt16) (errNum >> 16);
errnum = (epicsUInt16) (errNum & 0xffff);
if (modnum < 501) {
if (modnum < MIN_MODULE_NUM) {
fprintf(stderr, "Usage: errSymTestPrint(long errNum) \n");
fprintf(stderr, "errSymTestPrint: module number < 501 \n");
fprintf(stderr,
"errSymTestPrint: module number < %d\n", MIN_MODULE_NUM);
return;
}
errSymLookup(errNum, message, sizeof(message));
if ( message[0] == '\0' ) return;
printf("module %hu number %hu message=\"%s\"\n",
modnum, errnum, message);
return;
}
/****************************************************************
@@ -272,10 +281,11 @@ void errSymTest(epicsUInt16 modnum, epicsUInt16 begErrNum,
long errNum;
epicsUInt16 errnum;
if (!initialized) errSymBld();
if (modnum < 501)
if (modnum < MIN_MODULE_NUM)
return;
initErrorHashTable();
/* print range of error messages */
for (errnum = begErrNum; errnum <= endErrNum; errnum++) {
errNum = modnum << 16;

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