Compare commits
63 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
42a67524e0 | ||
|
|
d4ed70154b | ||
|
|
f776f6b422 | ||
|
|
cd746a339f | ||
|
|
6438750615 | ||
|
|
2ec5c80652 | ||
|
|
e1039bacfc | ||
|
|
54381b7bf9 | ||
|
|
69d3c94328 | ||
|
|
a02860728e | ||
|
|
440ccc3c4a | ||
|
|
3d19f19d81 | ||
|
|
214edd42d1 | ||
|
|
f8b0b2f5f0 | ||
|
|
8596dc41f2 | ||
|
|
88cb33a04d | ||
|
|
5af0663058 | ||
|
|
830704021d | ||
|
|
96ee2cd00c | ||
|
|
c2d2f671bb | ||
|
|
4b9958304d | ||
|
|
d0875f4f12 | ||
|
|
c74eb27474 | ||
|
|
5aa145920d | ||
|
|
bc3cbd990f | ||
|
|
53bf7cd994 | ||
|
|
a9c4d59537 | ||
|
|
d32332d545 | ||
|
|
88864e949b | ||
|
|
fe4f607e63 | ||
|
|
9e82a96700 | ||
|
|
ef7399159c | ||
|
|
d7b3293ba3 | ||
|
|
9014ca899d | ||
|
|
c5130468cd | ||
|
|
7dd067e887 | ||
|
|
c56091978c | ||
|
|
2f8e6bf17e | ||
|
|
144281e0a3 | ||
|
|
83c2414ad0 | ||
|
|
504665bf09 | ||
|
|
1411522a11 | ||
|
|
ae3d3904d9 | ||
|
|
002bafdf07 | ||
|
|
8bfa40d858 | ||
|
|
138e2f1ad5 | ||
|
|
fefe6fd1fc | ||
|
|
5eb49ebaf0 | ||
|
|
625172419e | ||
|
|
80343363a4 | ||
|
|
444b89f557 | ||
|
|
fa1ddeeb1b | ||
|
|
ca46bf70b7 | ||
|
|
c0e2a44365 | ||
|
|
84c3ec7c64 | ||
|
|
65e781d58a | ||
|
|
a613a96ad3 | ||
|
|
fbd2d4dd31 | ||
|
|
2c28d4b69e | ||
|
|
9946681f72 | ||
|
|
196baac7da | ||
|
|
ce93142fe1 | ||
|
|
5d61a512d1 |
@@ -29,7 +29,7 @@ EPICS_VERSION = 3
|
||||
EPICS_REVISION = 15
|
||||
|
||||
# EPICS_MODIFICATION must be a number >=0 and <256
|
||||
EPICS_MODIFICATION = 2
|
||||
EPICS_MODIFICATION = 3
|
||||
|
||||
# EPICS_PATCH_LEVEL must be a number (win32 resource file requirement)
|
||||
# Not included if zero
|
||||
|
||||
@@ -81,6 +81,8 @@ else
|
||||
host:
|
||||
endif
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
-include $(CONFIG)/RULES.Db
|
||||
|
||||
#---------------------------------------------------------------
|
||||
@@ -122,8 +124,6 @@ LIBTARGETS += $(LIBNAME) $(INSTALL_LIBS) $(TESTLIBNAME) \
|
||||
$(LOADABLE_SHRLIBNAME) $(INSTALL_LOADABLE_SHRLIBS)
|
||||
|
||||
|
||||
-include $(CONFIG)/RULES_FILE_TYPE
|
||||
|
||||
# Main targets
|
||||
|
||||
install: buildInstall
|
||||
|
||||
@@ -2,6 +2,9 @@
|
||||
|
||||
vpath %@ $(USR_VPATH) $(ALL_SRC_DIRS)
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Variable expansion
|
||||
|
||||
# Default settings
|
||||
EXPAND_TOOL ?= $(PERL) $(TOOLS)/expandVars.pl
|
||||
|
||||
@@ -24,3 +27,39 @@ expand_clean:
|
||||
@$(RM) $(EXPANDED)
|
||||
|
||||
.PHONY : expand_clean
|
||||
|
||||
#---------------------------------------------------------------
|
||||
# Assemblies (files assembled from snippets)
|
||||
|
||||
ASSEMBLE_TOOL ?= $(PERL) $(TOOLS)/assembleSnippets.pl
|
||||
|
||||
define COMMON_ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$(COMMON_DIR)/$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling common file $$@ from snippets"
|
||||
@$(RM) $1
|
||||
$(ASSEMBLE_TOOL) -o $1 $$^
|
||||
@$(MV) $1 $$@
|
||||
endef
|
||||
$(foreach asy, $(COMMON_ASSEMBLIES), \
|
||||
$(eval $(call COMMON_ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_template
|
||||
$1_SNIPPETS += $$(foreach dir, .. $$(SRC_DIRS), \
|
||||
$$(wildcard $$(dir)/$$($1_PATTERN)))
|
||||
$1: $$($1_SNIPPETS)
|
||||
$(ECHO) "Assembling file $$@ from snippets"
|
||||
@$(RM) $$@
|
||||
$(ASSEMBLE_TOOL) -o $$@ $$^
|
||||
endef
|
||||
$(foreach asy, $(ASSEMBLIES), \
|
||||
$(eval $(call ASSEMBLY_template,$(strip $(asy)))))
|
||||
|
||||
define ASSEMBLY_DEP_template
|
||||
$1$(DEP):
|
||||
@echo $1: > $$@
|
||||
endef
|
||||
$(foreach asy, $(sort $(COMMON_ASSEMBLIES) $(ASSEMBLIES)), \
|
||||
$(eval $(call ASSEMBLY_DEP_template,$(strip $(asy)))))
|
||||
|
||||
|
||||
@@ -4,17 +4,17 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
|
||||
<title>Known Problems in R3.15.2</title>
|
||||
<title>Known Problems in R3.15.3</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.2: Known Problems</h1>
|
||||
<h1 style="text-align: center">EPICS Base R3.15.3: Known Problems</h1>
|
||||
|
||||
<p>Any patch files linked below should be applied at the root of the
|
||||
base-3.15.2 tree. Download them, then use the GNU Patch program as
|
||||
base-3.15.3 tree. Download them, then use the GNU Patch program as
|
||||
follows:</p>
|
||||
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.2</b>
|
||||
<blockquote><pre>% <b>cd <i>/path/to/</i>base-3.15.3</b>
|
||||
% <b>patch -p0 < <i>/path/to/</i>file.patch</b></pre></blockquote>
|
||||
|
||||
<p>The following significant problems have been reported with this
|
||||
@@ -22,10 +22,18 @@ version of EPICS Base:</p>
|
||||
|
||||
<ul>
|
||||
|
||||
<li>See LaunchPad bug
|
||||
<a href="https://bugs.launchpad.net/epics-base/+bug/1466129">#1466129</a>:
|
||||
The feature that allows an IOC to bind to a single network interface is not
|
||||
working correctly. The bug mentioned above contains a patch that has been
|
||||
tested on Linux-64, a complete fix is expected for 3.15.4. Note that Windows
|
||||
systems are not affected by this bug.</li>
|
||||
|
||||
<li>Parallel builds ("make -j") on native Windows are not working properly.
|
||||
Builds tend to hang (make saturating one core) - interrupting and calling make
|
||||
again usually finishes the build. Sequential builds work and are recommended
|
||||
for automated build environments.</li>
|
||||
Builds tend to hang (saturating one core); interrupting and running make
|
||||
again usually finishes the build. Limiting the number of parallel jobs using
|
||||
something like "make -j8" also helps prevent this problem. Sequential builds
|
||||
always work and are recommended for automated build environments.</li>
|
||||
|
||||
<li>64-bit Windows builds of the CAS library may not work with some compilers.
|
||||
The code in <tt>src/legacy/gdd</tt> is currently incompatible with the LLP64
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
Installation Instructions
|
||||
|
||||
EPICS Base Release 3.15.2
|
||||
EPICS Base Release 3.15.3
|
||||
|
||||
--------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<BODY>
|
||||
<CENTER>
|
||||
<H1>Installation Instructions</H1>
|
||||
<H2>EPICS Base Release 3.15.2</H2><BR>
|
||||
<H2>EPICS Base Release 3.15.3</H2><BR>
|
||||
</CENTER>
|
||||
<HR>
|
||||
<H3> Table of Contents</H3>
|
||||
|
||||
@@ -3,14 +3,54 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv="content-type" content="text/html; charset=iso-8859-1">
|
||||
<title>EPICS Base R3.15.2 Release Notes</title>
|
||||
<title>EPICS Base R3.15.3 Release Notes</title>
|
||||
</head>
|
||||
|
||||
<body lang="en">
|
||||
<h1 align="center">EPICS Base Release 3.15.2</h1>
|
||||
<h1 align="center">EPICS Base Release 3.15.3</h1>
|
||||
|
||||
<h2 align="center">Changes between 3.15.2 and 3.15.3</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Make the NTP Time provider optional on VxWorks</h3>
|
||||
|
||||
<p>Recent versions of VxWorks (sometime after VxWorks 6) provide facilities for
|
||||
automatically synchronizing the OS clock time with an NTP server. The EPICS time
|
||||
system used to assume that it had to provide time synchronization on VxWorks,
|
||||
but now it tests for the existance of either of the two OS synchronization
|
||||
threads before starting the NTP time provider. It is still possible to force the
|
||||
NTP provider to be started even if the OS synchronization is running by defining
|
||||
the environment variable <tt>EPICS_TS_FORCE_NTPTIME</tt> in the startup script
|
||||
before loading the IOC's .munch file. Forcing may be necessary if the VxWorks
|
||||
image is not correctly configured with the IP address of a local NTP server.</p>
|
||||
|
||||
<h3>Assembling files from numbered snippets</h3>
|
||||
|
||||
<p>A tool has been added that assembles file snippets specified on the
|
||||
command line into a single output file, with sorting and replacing/adding of
|
||||
snippets done based on their file names. The build system integration requires
|
||||
the output file to be specified setting COMMON_ASSEMBLIES (arch independent)
|
||||
or ASSEMBLIES (created by arch), then defining the snippets for each assembly
|
||||
setting *_SNIPPETS (explicitly) or *_PATTERN (searched relative to all source
|
||||
directories).
|
||||
</p>
|
||||
|
||||
<h3>Clean up after GNU readline()</h3>
|
||||
|
||||
<p>If EPICS Base is built with readline support, any IOC that calls epicsExit()
|
||||
from a thread other than the main thread is likely to leave the user's terminal
|
||||
in a weird state, requiring the user to run something like 'stty sane' to clean
|
||||
it up. This release patches the readline support code to clean up automatically
|
||||
by registering an epicsAtExit() routine.</p>
|
||||
|
||||
<h3>Removed the last vestiges of RSET::get_value()</h3>
|
||||
|
||||
<p>The IOC has not called the get_value() routine in the RSET for a very long
|
||||
time, but there was still one implementation left in the event record support
|
||||
code, and a structure definition for one of the original arguments to that
|
||||
routine was defined in recGbl.h. Both of these have now been removed.</p>
|
||||
|
||||
<h2 align="center">Changes between 3.15.1 and 3.15.2</h2>
|
||||
<!-- Insert new items immediately below here ... -->
|
||||
|
||||
<h3>Raised limit on link field length in database files</h3>
|
||||
|
||||
|
||||
@@ -60,11 +60,8 @@ responsible for these tasks in the past and are expected to continue in the
|
||||
relevent roles unless the Release Manager designates otherwise:</p>
|
||||
|
||||
<dl>
|
||||
<dt><strong>Release Manager</strong> (Andrew Johnson)</dt>
|
||||
<dd>Responsible for the release</dd>
|
||||
<dt><strong>Configuration Manager</strong> (Janet Anderson)</dt>
|
||||
<dd>Responsible for committing version number updates and for
|
||||
creating tarfiles</dd>
|
||||
<dt><strong>Release Manager</strong> (Ralph Lange)</dt>
|
||||
<dd>Responsible for managing and tagging the release</dd>
|
||||
<dt><strong>Platform Developers</strong></dt>
|
||||
<dd>Responsible for individual operating system platforms</dd>
|
||||
<dt><strong>Application Developers</strong></dt>
|
||||
@@ -134,42 +131,42 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar, using these tag conventions:
|
||||
<ul>
|
||||
<li>
|
||||
<tt>R3.15.2-pre<i>n</i></tt>
|
||||
<tt>R3.15.3-pre<i>n</i></tt>
|
||||
— pre-release tag
|
||||
</li>
|
||||
<li>
|
||||
<tt>R3.15.2-rc<i>n</i></tt>
|
||||
<tt>R3.15.3-rc<i>n</i></tt>
|
||||
— release candidate tag, note the <tt>rc</tt> is now
|
||||
lower-case</li>
|
||||
</ul>
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2-rc<i>n</i>
|
||||
bzr tag R3.15.3-rc<i>n</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2-rc<i>n</i>
|
||||
-r tag:R3.15.2-rc<i>n</i>
|
||||
baseR3.15.2-rc<i>n</i>.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.15.3-rc<i>n</i>
|
||||
-r tag:R3.15.3-rc<i>n</i>
|
||||
base-3.15.3-rc<i>n</i>.tar.gz
|
||||
mirror-3.15
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -177,14 +174,15 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tarfile by extracting its contents and building it on at
|
||||
least one supported platform.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website.</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
@@ -223,13 +221,6 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
<td>Run the CA client side regression tests on all available host
|
||||
platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
<td>Build and run the
|
||||
<a href="https://launchpad.net/epics-base-tests">epics-base-tests</a>
|
||||
suite on all available platforms.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Platform Developers</td>
|
||||
@@ -279,32 +270,32 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Edit and commit changes to the EPICS version number file
|
||||
configure/CONFIG_BASE_VERSION.</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Tag the module in Bazaar:
|
||||
<blockquote><tt>
|
||||
cd ~/base/mirror-3.15<br />
|
||||
bzr tag R3.15.2</i>
|
||||
bzr tag R3.15.3</i>
|
||||
</tt></blockquote>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Export the tagged version into a tarfile. Note that this command
|
||||
generates a gzipped tarfile directly from the repository:
|
||||
<blockquote><tt>
|
||||
cd ~/base<br />
|
||||
bzr export --keywords=publish
|
||||
--root=base-3.15.2
|
||||
-r tag:R3.15.2
|
||||
baseR3.15.2.tar.gz
|
||||
mirror-3.15
|
||||
--root=base-3.15.3
|
||||
-r tag:R3.15.3
|
||||
base-3.15.3.tar.gz
|
||||
mirror-3.15
|
||||
</tt></blockquote>
|
||||
This requires that the Bazaar keywords plugin is installed and
|
||||
configured properly.
|
||||
@@ -312,7 +303,7 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Release Manager</td>
|
||||
<td>Test the tar file by extracting its contents and building it on at
|
||||
least one supported platform</td>
|
||||
</tr>
|
||||
@@ -321,8 +312,9 @@ relevent roles unless the Release Manager designates otherwise:</p>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
<td>Configuration Manager</td>
|
||||
<td>Copy the tar file to the Base download area of the website</td>
|
||||
<td>Website Manager</td>
|
||||
<td>If necessary recreate the tarfile following the instructions above.
|
||||
Copy the tar file to the Base download area of the website</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td> </td>
|
||||
|
||||
@@ -209,6 +209,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_pend_event">ca_poll</a></li>
|
||||
<li><a href="#ca_puser">ca_puser</a></li>
|
||||
<li><a href="#ca_put">ca_put</a></li>
|
||||
<li><a href="#ca_put">ca_put_callback</a></li>
|
||||
<li><a href="#ca_set_puser">ca_set_puser</a></li>
|
||||
<li><a href="#ca_signal">ca_signal</a></li>
|
||||
<li><a href="#ca_sg_block">ca_sg_block</a></li>
|
||||
@@ -223,7 +224,7 @@ $Date$</span></small></p>
|
||||
<li><a href="#ca_test_io">ca_test_io</a></li>
|
||||
<li><a href="#ca_write_access">ca_write_access</a></li>
|
||||
<li><a href="#ca_state">channel_state</a></li>
|
||||
<li><a href="#dbr_size[]">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size">dbr_size[]</a></li>
|
||||
<li><a href="#dbr_size_n">dbr_size_n</a></li>
|
||||
<li><a href="#dbr_value_size">dbr_value_size[]</a></li>
|
||||
<li><a href="#dbr_type_t">dbr_type_to_text</a></li>
|
||||
@@ -3831,7 +3832,7 @@ specified channel and boolean false otherwise.</p>
|
||||
channel and boolean false otherwise</dd>
|
||||
</dl>
|
||||
|
||||
<h3><code><a name="dbr_size[]">dbr_size[]</a></code></h3>
|
||||
<h3><code><a name="dbr_size">dbr_size[]</a></code></h3>
|
||||
<pre>#include <db_access.h>
|
||||
extern unsigned dbr_size[/* TYPE */];</pre>
|
||||
|
||||
@@ -3953,11 +3954,12 @@ int ca_sg_create ( CA_SYNC_GID *PGID );</pre>
|
||||
|
||||
<p>A synchronous group can be used to guarantee that a set of channel access
|
||||
requests have completed. Once a synchronous group has been created then channel
|
||||
access get and put requests may be issued within it using <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_put()</code> respectively. The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
access get and put requests may be issued within it using
|
||||
<code>ca_sg_array_get()</code> and <code>ca_sg_array_put()</code> respectively.
|
||||
The routines <code>ca_sg_block()</code> and <code>ca_sg_test()</code> can be
|
||||
used to block for and test for completion respectively. The routine
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which have timed out
|
||||
and in all likelihood will never be satisfied.</p>
|
||||
<code>ca_sg_reset()</code> is used to discard knowledge of old requests which
|
||||
have timed out and in all likelihood will never be satisfied.</p>
|
||||
|
||||
<p>Any number of asynchronous groups can have application requested operations
|
||||
outstanding within them at any given time.</p>
|
||||
@@ -4131,16 +4133,20 @@ status = ca_sg_reset(gid);</pre>
|
||||
|
||||
<h3><code><a name="ca_sg_put">ca_sg_array_put()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_put ( CA_SYNC_GID GID, chtype TYPE,
|
||||
unsigned long COUNT, chid CHID, void *PVALUE );</pre>
|
||||
|
||||
<p>Write a value, or array of values, to a channel and increment the
|
||||
outstanding request count of a synchronous group. The <code>ca_sg_array_put()</code>
|
||||
functionality is implemented using <code>ca_array_put_callback()</code>.</p>
|
||||
<p>Write a value, or array of values, to a channel and increment the outstanding
|
||||
request count of a synchronous group. The <code>ca_sg_put()</code> and
|
||||
<code>ca_sg_array_put()</code> functionality is implemented using
|
||||
<code>ca_array_put_callback()</code>.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding puts are not
|
||||
@@ -4193,6 +4199,8 @@ reissued.</p>
|
||||
|
||||
<h3><code><a name="ca_sg_get">ca_sg_array_get()</a></code></h3>
|
||||
<pre>#include <cadef.h>
|
||||
int ca_sg_get ( CA_SYNC_GID GID, chtype TYPE,
|
||||
chid CHID, void *PVALUE );
|
||||
int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
chtype TYPE, unsigned long COUNT,
|
||||
chid CHID, void *PVALUE );</pre>
|
||||
@@ -4200,16 +4208,19 @@ int ca_sg_array_get ( CA_SYNC_GID GID,
|
||||
<h4>Description</h4>
|
||||
|
||||
<p>Read a value from a channel and increment the outstanding request count of a
|
||||
synchronous group. The <code>ca_sg_array_get()</code> functionality is implemented using
|
||||
synchronous group. The <code>ca_sg_get()</code> and
|
||||
<code>ca_sg_array_get()</code> functionality is implemented using
|
||||
<code>ca_array_get_callback()</code>.</p>
|
||||
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code> should not be
|
||||
referenced by your program until ECA_NORMAL has been received from <code>ca_sg_block()</code>,
|
||||
or until <code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
<p>The values written into your program's variables by <code>ca_sg_get()</code>
|
||||
or <code>ca_sg_array_get()</code> should not be referenced by your program until
|
||||
ECA_NORMAL has been received from <code>ca_sg_block()</code>, or until
|
||||
<code>ca_sg_test()</code> returns ECA_IODONE.</p>
|
||||
|
||||
<p>All remote operation requests such as the above are accumulated (buffered)
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>, <code>ca_pend_io()</code>,
|
||||
<code>ca_pend_event()</code>, or <code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
and not forwarded to the server until one of <code>ca_flush_io()</code>,
|
||||
<code>ca_pend_io()</code>, <code>ca_pend_event()</code>, or
|
||||
<code>ca_sg_block()</code> are called. This allows several requests to be
|
||||
efficiently sent in one message.</p>
|
||||
|
||||
<p>If a connection is lost and then resumed outstanding gets are not
|
||||
|
||||
@@ -54,6 +54,7 @@ typedef struct cbQueueSet {
|
||||
epicsEventId semWakeUp;
|
||||
epicsRingPointerId queue;
|
||||
int queueOverflow;
|
||||
int shutdown;
|
||||
int threadsConfigured;
|
||||
int threadsRunning;
|
||||
} cbQueueSet;
|
||||
@@ -73,7 +74,8 @@ static epicsTimerQueueId timerQueue;
|
||||
enum ctl {ctlInit, ctlRun, ctlPause, ctlExit};
|
||||
static volatile enum ctl cbCtl;
|
||||
static epicsEventId startStopEvent;
|
||||
static void *exitCallback;
|
||||
|
||||
static int callbackIsInit;
|
||||
|
||||
/* Static data */
|
||||
static char *threadNamePrefix[NUM_CALLBACK_PRIORITIES] = {
|
||||
@@ -89,7 +91,7 @@ static int priorityValue[NUM_CALLBACK_PRIORITIES] = {0, 1, 2};
|
||||
|
||||
int callbackSetQueueSize(int size)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -99,7 +101,7 @@ int callbackSetQueueSize(int size)
|
||||
|
||||
int callbackParallelThreads(int count, const char *prio)
|
||||
{
|
||||
if (startStopEvent) {
|
||||
if (callbackIsInit) {
|
||||
errlogPrintf("Callback system already initialized\n");
|
||||
return -1;
|
||||
}
|
||||
@@ -149,12 +151,13 @@ int callbackParallelThreads(int count, const char *prio)
|
||||
|
||||
static void callbackTask(void *arg)
|
||||
{
|
||||
cbQueueSet *mySet = &callbackQueue[*(int*)arg];
|
||||
int prio = *(int*)arg;
|
||||
cbQueueSet *mySet = &callbackQueue[prio];
|
||||
|
||||
taskwdInsert(0, NULL, NULL);
|
||||
epicsEventSignal(startStopEvent);
|
||||
|
||||
while(TRUE) {
|
||||
while(!mySet->shutdown) {
|
||||
void *ptr;
|
||||
if (epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustWait(mySet->semWakeUp);
|
||||
@@ -163,42 +166,52 @@ static void callbackTask(void *arg)
|
||||
CALLBACK *pcallback = (CALLBACK *)ptr;
|
||||
if(!epicsRingPointerIsEmpty(mySet->queue))
|
||||
epicsEventMustTrigger(mySet->semWakeUp);
|
||||
if (ptr == &exitCallback) goto shutdown;
|
||||
mySet->queueOverflow = FALSE;
|
||||
(*pcallback->callback)(pcallback);
|
||||
}
|
||||
}
|
||||
|
||||
shutdown:
|
||||
mySet->threadsRunning--;
|
||||
if(!epicsAtomicDecrIntT(&mySet->threadsRunning))
|
||||
epicsEventSignal(startStopEvent);
|
||||
taskwdRemove(0);
|
||||
epicsEventSignal(startStopEvent);
|
||||
}
|
||||
|
||||
void callbackShutdown(void)
|
||||
void callbackStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
if (cbCtl == ctlExit) return;
|
||||
cbCtl = ctlExit;
|
||||
|
||||
/* sequential shutdown of workers */
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
while (callbackQueue[i].threadsRunning) {
|
||||
if(epicsRingPointerPush(callbackQueue[i].queue, &exitCallback)) {
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
epicsEventWait(startStopEvent);
|
||||
} else {
|
||||
epicsThreadSleep(0.05);
|
||||
}
|
||||
}
|
||||
assert(callbackQueue[i].threadsRunning==0);
|
||||
epicsEventDestroy(callbackQueue[i].semWakeUp);
|
||||
epicsRingPointerDelete(callbackQueue[i].queue);
|
||||
callbackQueue[i].shutdown = 1;
|
||||
epicsEventSignal(callbackQueue[i].semWakeUp);
|
||||
}
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
while (epicsAtomicGetIntT(&mySet->threadsRunning)) {
|
||||
epicsEventSignal(mySet->semWakeUp);
|
||||
epicsEventWaitWithTimeout(startStopEvent, 0.1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void callbackCleanup(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < NUM_CALLBACK_PRIORITIES; i++) {
|
||||
cbQueueSet *mySet = &callbackQueue[i];
|
||||
|
||||
assert(epicsAtomicGetIntT(&mySet->threadsRunning)==0);
|
||||
epicsEventDestroy(mySet->semWakeUp);
|
||||
epicsRingPointerDelete(mySet->queue);
|
||||
}
|
||||
|
||||
epicsTimerQueueRelease(timerQueue);
|
||||
epicsEventDestroy(startStopEvent);
|
||||
startStopEvent = NULL;
|
||||
callbackIsInit = 0;
|
||||
memset(callbackQueue, 0, sizeof(callbackQueue));
|
||||
}
|
||||
|
||||
@@ -208,10 +221,14 @@ void callbackInit(void)
|
||||
int j;
|
||||
char threadName[32];
|
||||
|
||||
if (startStopEvent)
|
||||
if (callbackIsInit) {
|
||||
errlogMessage("Warning: callbackInit called again before callbackCleanup\n");
|
||||
return;
|
||||
}
|
||||
callbackIsInit = 1;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
cbCtl = ctlRun;
|
||||
timerQueue = epicsTimerQueueAllocate(0, epicsThreadPriorityScanHigh);
|
||||
|
||||
@@ -239,7 +256,7 @@ void callbackInit(void)
|
||||
cantProceed("Failed to spawn callback thread %s\n", threadName);
|
||||
} else {
|
||||
epicsEventWait(startStopEvent);
|
||||
callbackQueue[i].threadsRunning++;
|
||||
epicsAtomicIncrIntT(&callbackQueue[i].threadsRunning);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,8 @@ typedef void (*CALLBACKFUNC)(struct callbackPvt*);
|
||||
( (USER) = (void *)((CALLBACK *)(PCALLBACK))->user )
|
||||
|
||||
epicsShareFunc void callbackInit(void);
|
||||
epicsShareFunc void callbackShutdown(void);
|
||||
epicsShareFunc void callbackStop(void);
|
||||
epicsShareFunc void callbackCleanup(void);
|
||||
epicsShareFunc int callbackRequest(CALLBACK *pCallback);
|
||||
epicsShareFunc void callbackSetProcess(
|
||||
CALLBACK *pcallback, int Priority, void *pRec);
|
||||
|
||||
@@ -1228,20 +1228,20 @@ long dbPut(DBADDR *paddr, short dbrType,
|
||||
status = dbPutConvertRoutine[dbrType][field_type](paddr, pbuffer,
|
||||
nRequest, no_elements, offset);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* update array info */
|
||||
if (paddr->pfldDes->special == SPC_DBADDR &&
|
||||
if (!status &&
|
||||
paddr->pfldDes->special == SPC_DBADDR &&
|
||||
prset && prset->put_array_info) {
|
||||
status = prset->put_array_info(paddr, nRequest);
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* check if special processing is required */
|
||||
/* Always do special processing if needed */
|
||||
if (special) {
|
||||
status = dbPutSpecial(paddr,1);
|
||||
if (status) goto done;
|
||||
long status2 = dbPutSpecial(paddr, 1);
|
||||
if (status2) goto done;
|
||||
}
|
||||
if (status) goto done;
|
||||
|
||||
/* Propagate monitor events for this field, */
|
||||
/* unless the field is VAL and PP is true. */
|
||||
|
||||
@@ -100,7 +100,7 @@ static int dbca_chan_count;
|
||||
* that has been deleted.
|
||||
*
|
||||
* Just a few words about handling dbCaRemoveLink because this is when
|
||||
* it is essential that nothing trys to use a caLink that has been freed.
|
||||
* it is essential that nothing tries to use a caLink that has been freed.
|
||||
*
|
||||
* dbCaRemoveLink is called when links are being modified. This is only
|
||||
* done with the dbScan mechanism guranteeing that nothing from
|
||||
@@ -119,19 +119,16 @@ static int dbca_chan_count;
|
||||
* dbCaTask issues a ca_clear_channel and then frees the caLink.
|
||||
*
|
||||
* If any channel access callback gets called before the ca_clear_channel
|
||||
* it finds pca->plink=0 and does nothing. Once ca_clear_channel
|
||||
* it finds pca->plink==0 and does nothing. Once ca_clear_channel
|
||||
* is called no other callback for this caLink will be called.
|
||||
*
|
||||
* dbCaPutLinkCallback causes an additional complication because
|
||||
* when dbCaRemoveLink is called the callback may not have occured.
|
||||
* What is done is the following:
|
||||
* If callback has not occured dbCaRemoveLink sets plinkPutCallback=plink
|
||||
* If putCallback is called before dbCaTask calls ca_clear_channel
|
||||
* it does NOT call the users callback.
|
||||
* dbCaTask calls the users callback passing plinkPutCallback AFTER
|
||||
* it has called ca_clear_channel
|
||||
* Thus the users callback will get called exactly once.
|
||||
*/
|
||||
* If putComplete sees plink==0 it will not call the user's code.
|
||||
* If pca->putCallback is non-zero, dbCaTask will call the
|
||||
* user's callback AFTER it has called ca_clear_channel.
|
||||
* Thus the user's callback will get called exactly once.
|
||||
*/
|
||||
|
||||
static void addAction(caLink *pca, short link_action)
|
||||
{
|
||||
@@ -168,7 +165,7 @@ static void addAction(caLink *pca, short link_action)
|
||||
static void dbCaLinkFree(caLink *pca)
|
||||
{
|
||||
dbCaCallback callback;
|
||||
struct link *plinkPutCallback = 0;
|
||||
void *userPvt = 0;
|
||||
|
||||
if (pca->chid) {
|
||||
ca_clear_channel(pca->chid);
|
||||
@@ -176,8 +173,7 @@ static void dbCaLinkFree(caLink *pca)
|
||||
}
|
||||
callback = pca->putCallback;
|
||||
if (callback) {
|
||||
plinkPutCallback = pca->plinkPutCallback;
|
||||
pca->plinkPutCallback = 0;
|
||||
userPvt = pca->putUserPvt;
|
||||
pca->putCallback = 0;
|
||||
pca->putType = 0;
|
||||
}
|
||||
@@ -188,12 +184,12 @@ static void dbCaLinkFree(caLink *pca)
|
||||
free(pca->pvname);
|
||||
epicsMutexDestroy(pca->lock);
|
||||
free(pca);
|
||||
if (callback) callback(plinkPutCallback);
|
||||
if (callback) callback(userPvt);
|
||||
}
|
||||
|
||||
void dbCaCallbackProcess(void *usrPvt)
|
||||
void dbCaCallbackProcess(void *userPvt)
|
||||
{
|
||||
struct link *plink = (struct link *)usrPvt;
|
||||
struct link *plink = (struct link *)userPvt;
|
||||
dbCommon *pdbCommon = plink->value.pv_link.precord;
|
||||
|
||||
dbScanLock(pdbCommon);
|
||||
@@ -223,11 +219,6 @@ void dbCaShutdown(void)
|
||||
}
|
||||
}
|
||||
|
||||
static void dbCaExit(void *arg)
|
||||
{
|
||||
dbCaShutdown();
|
||||
}
|
||||
|
||||
void dbCaLinkInitIsolated(void)
|
||||
{
|
||||
if (!workListLock)
|
||||
@@ -235,7 +226,6 @@ void dbCaLinkInitIsolated(void)
|
||||
if (!workListEvent)
|
||||
workListEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
dbCaCtl = ctlExit;
|
||||
epicsAtExit(dbCaExit, NULL);
|
||||
}
|
||||
|
||||
void dbCaLinkInit(void)
|
||||
@@ -297,8 +287,6 @@ void dbCaRemoveLink(struct link *plink)
|
||||
epicsMutexMustLock(pca->lock);
|
||||
pca->plink = 0;
|
||||
plink->value.pv_link.pvt = 0;
|
||||
if (pca->putCallback)
|
||||
pca->plinkPutCallback = plink;
|
||||
/* Unlock before addAction or dbCaTask might free first */
|
||||
epicsMutexUnlock(pca->lock);
|
||||
addAction(pca, CA_CLEAR_CHANNEL);
|
||||
@@ -800,7 +788,7 @@ static void exceptionCallback(struct exception_handler_args args)
|
||||
}
|
||||
}
|
||||
|
||||
static void putCallback(struct event_handler_args arg)
|
||||
static void putComplete(struct event_handler_args arg)
|
||||
{
|
||||
caLink *pca = (caLink *)arg.usr;
|
||||
struct link *plink;
|
||||
@@ -963,7 +951,7 @@ static void dbCaTask(void *arg)
|
||||
status = ca_array_put_callback(
|
||||
pca->dbrType, pca->nelements,
|
||||
pca->chid, pca->pputNative,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
@@ -986,7 +974,7 @@ static void dbCaTask(void *arg)
|
||||
status = ca_array_put_callback(
|
||||
DBR_STRING, 1,
|
||||
pca->chid, pca->pputString,
|
||||
putCallback, pca);
|
||||
putComplete, pca);
|
||||
} else {
|
||||
status = ECA_PUTFAIL;
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ typedef struct caLink
|
||||
short putType;
|
||||
dbCaCallback putCallback;
|
||||
void *putUserPvt;
|
||||
struct link *plinkPutCallback;
|
||||
/* The following are for access to additional attributes*/
|
||||
char gotAttributes;
|
||||
dbCaCallback getAttributes;
|
||||
|
||||
@@ -979,6 +979,7 @@ static void event_task (void *pParm)
|
||||
{
|
||||
struct event_user * const evUser = (struct event_user *) pParm;
|
||||
struct event_que * ev_que;
|
||||
unsigned char pendexit;
|
||||
|
||||
/* init hook */
|
||||
if (evUser->init_func) {
|
||||
@@ -1020,9 +1021,10 @@ static void event_task (void *pParm)
|
||||
event_read (ev_que);
|
||||
epicsMutexMustLock ( evUser->lock );
|
||||
}
|
||||
pendexit = evUser->pendexit;
|
||||
epicsMutexUnlock ( evUser->lock );
|
||||
|
||||
} while ( ! evUser->pendexit );
|
||||
} while( ! pendexit );
|
||||
|
||||
epicsMutexDestroy(evUser->firstque.writelock);
|
||||
|
||||
|
||||
@@ -233,6 +233,17 @@ static void dbtpnCallFunc(const iocshArgBuf *args)
|
||||
static const iocshFuncDef dbNotifyDumpFuncDef = {"dbNotifyDump",0,0};
|
||||
static void dbNotifyDumpCallFunc(const iocshArgBuf *args) { dbNotifyDump();}
|
||||
|
||||
/* dbPutAttribute */
|
||||
static const iocshArg dbPutAttrArg0 = { "record type",iocshArgString};
|
||||
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};
|
||||
static void dbPutAttrCallFunc(const iocshArgBuf *args)
|
||||
{ dbPutAttribute(args[0].sval,args[1].sval,args[2].sval);}
|
||||
|
||||
/* tpn */
|
||||
static const iocshArg tpnArg0 = { "record name",iocshArgString};
|
||||
static const iocshArg tpnArg1 = { "value",iocshArgString};
|
||||
@@ -405,6 +416,7 @@ void dbIocRegister(void)
|
||||
iocshRegister(&pftFuncDef,pftCallFunc);
|
||||
iocshRegister(&dbtpnFuncDef,dbtpnCallFunc);
|
||||
iocshRegister(&dbNotifyDumpFuncDef,dbNotifyDumpCallFunc);
|
||||
iocshRegister(&dbPutAttrFuncDef,dbPutAttrCallFunc);
|
||||
iocshRegister(&tpnFuncDef,tpnCallFunc);
|
||||
iocshRegister(&dblsrFuncDef,dblsrCallFunc);
|
||||
iocshRegister(&dbLockShowLockedFuncDef,dbLockShowLockedCallFunc);
|
||||
|
||||
@@ -151,7 +151,7 @@ static void buildScanLists(void);
|
||||
static void addToList(struct dbCommon *precord, scan_list *psl);
|
||||
static void deleteFromList(struct dbCommon *precord, scan_list *psl);
|
||||
|
||||
void scanShutdown(void)
|
||||
void scanStop(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
@@ -168,16 +168,16 @@ void scanShutdown(void)
|
||||
|
||||
scanOnce((dbCommon *)&exitOnce);
|
||||
epicsEventWait(startStopEvent);
|
||||
}
|
||||
|
||||
void scanCleanup(void)
|
||||
{
|
||||
|
||||
deletePeriodic();
|
||||
ioscanDestroy();
|
||||
|
||||
epicsRingPointerDelete(onceQ);
|
||||
|
||||
epicsEventDestroy(startStopEvent);
|
||||
epicsEventDestroy(onceSem);
|
||||
onceSem = startStopEvent = NULL;
|
||||
|
||||
free(periodicTaskId);
|
||||
papPeriodic = NULL;
|
||||
periodicTaskId = NULL;
|
||||
@@ -187,7 +187,8 @@ long scanInit(void)
|
||||
{
|
||||
int i;
|
||||
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!startStopEvent)
|
||||
startStopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
scanCtl = ctlPause;
|
||||
|
||||
initPeriodic();
|
||||
@@ -639,7 +640,8 @@ static void initOnce(void)
|
||||
if ((onceQ = epicsRingPointerCreate(onceQueueSize)) == NULL) {
|
||||
cantProceed("initOnce: Ring buffer create failed\n");
|
||||
}
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
if(!onceSem)
|
||||
onceSem = epicsEventMustCreate(epicsEventEmpty);
|
||||
onceTaskId = epicsThreadCreate("scanOnce",
|
||||
epicsThreadPriorityScanLow + nPeriodic,
|
||||
epicsThreadGetStackSize(epicsThreadStackBig), onceTask, 0);
|
||||
|
||||
@@ -46,7 +46,8 @@ struct dbCommon;
|
||||
epicsShareFunc long scanInit(void);
|
||||
epicsShareFunc void scanRun(void);
|
||||
epicsShareFunc void scanPause(void);
|
||||
epicsShareFunc void scanShutdown(void);
|
||||
epicsShareFunc void scanStop(void);
|
||||
epicsShareFunc void scanCleanup(void);
|
||||
|
||||
epicsShareFunc EVENTPVT eventNameToHandle(const char* event);
|
||||
epicsShareFunc void postEvent(EVENTPVT epvt);
|
||||
|
||||
@@ -334,7 +334,6 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
{
|
||||
DBADDR addr;
|
||||
long status;
|
||||
epicsUInt16 value;
|
||||
short dbrType;
|
||||
size_t n = 1;
|
||||
|
||||
@@ -346,16 +345,7 @@ long dbpf(const char *pname,const char *pvalue)
|
||||
if (nameToAddr(pname, &addr))
|
||||
return -1;
|
||||
|
||||
/* For enumerated types must allow for ENUM rather than string */
|
||||
/* If entire field is digits then use DBR_ENUM else DBR_STRING */
|
||||
if (addr.dbr_field_type == DBR_ENUM && *pvalue &&
|
||||
strspn(pvalue,"0123456789") == strlen(pvalue)) {
|
||||
|
||||
sscanf(pvalue, "%hu", &value);
|
||||
pvalue = (char *) &value;
|
||||
dbrType = DBR_ENUM;
|
||||
}
|
||||
else if (addr.no_elements > 1 &&
|
||||
if (addr.no_elements > 1 &&
|
||||
(addr.dbr_field_type == DBR_CHAR || addr.dbr_field_type == DBR_UCHAR)) {
|
||||
dbrType = addr.dbr_field_type;
|
||||
n = strlen(pvalue) + 1;
|
||||
|
||||
@@ -132,7 +132,7 @@ const char *initHookName(int state)
|
||||
"initHookAfterInterruptAccept",
|
||||
"initHookAtEnd"
|
||||
};
|
||||
if (state < 0 || state > NELEMENTS(stateName)) {
|
||||
if (state < 0 || state >= NELEMENTS(stateName)) {
|
||||
return "Not an initHookState";
|
||||
}
|
||||
return stateName[state];
|
||||
|
||||
@@ -309,11 +309,11 @@ static void getMaxRangeValues(short field_type, double *pupper_limit,
|
||||
{
|
||||
switch(field_type){
|
||||
case DBF_CHAR:
|
||||
*pupper_limit = -128.0;
|
||||
*plower_limit = 127.0;
|
||||
*pupper_limit = (double) CHAR_MAX;
|
||||
*plower_limit = (double) CHAR_MIN;
|
||||
break;
|
||||
case DBF_UCHAR:
|
||||
*pupper_limit = 255.0;
|
||||
*pupper_limit = (double) UCHAR_MAX;
|
||||
*plower_limit = 0.0;
|
||||
break;
|
||||
case DBF_SHORT:
|
||||
|
||||
@@ -184,7 +184,8 @@ MAIN(callbackParallelTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -181,7 +181,8 @@ MAIN(callbackTest)
|
||||
free(pcbt[i]);
|
||||
}
|
||||
|
||||
callbackShutdown();
|
||||
callbackStop();
|
||||
callbackCleanup();
|
||||
|
||||
return testDone();
|
||||
}
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
record(ai, "somename") {}
|
||||
@@ -73,15 +73,20 @@ static int yyreset(void)
|
||||
{whitespace} ;
|
||||
|
||||
{doublequote}({stringchar}|{escape})*{newline} { /* bad string */
|
||||
yyerror("Newline in string, closing quote missing");
|
||||
yyerrorAbort("Newline in string, closing quote missing");
|
||||
}
|
||||
|
||||
. {
|
||||
char message[40];
|
||||
YY_BUFFER_STATE *dummy=0;
|
||||
|
||||
sprintf(message,"Invalid character '%c'",yytext[0]);
|
||||
yyerror(message);
|
||||
if (isprint((int) yytext[0])) {
|
||||
sprintf(message, "Invalid character '%c'", yytext[0]);
|
||||
}
|
||||
else {
|
||||
sprintf(message, "Invalid character 0x%2.2x", yytext[0]);
|
||||
}
|
||||
yyerrorAbort(message);
|
||||
/*The following suppresses compiler warning messages*/
|
||||
if(FALSE) yyunput('c',(unsigned char *) message);
|
||||
if(FALSE) yy_switch_to_buffer(*dummy);
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
|
||||
/*The routines in this module are serially reusable NOT reentrant*/
|
||||
|
||||
#include <ctype.h>
|
||||
#include <epicsStdlib.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
|
||||
@@ -47,7 +47,7 @@
|
||||
|
||||
int dbStaticDebug = 0;
|
||||
static char *pNullString = "";
|
||||
#define messagesize 100
|
||||
#define messagesize 276
|
||||
#define RPCL_LEN INFIX_TO_POSTFIX_SIZE(80)
|
||||
|
||||
static char *ppstring[5]={"NPP","PP","CA","CP","CPP"};
|
||||
@@ -604,7 +604,10 @@ DBENTRY * dbAllocEntry(dbBase *pdbbase)
|
||||
|
||||
void dbFreeEntry(DBENTRY *pdbentry)
|
||||
{
|
||||
if(pdbentry->message) free((void *)pdbentry->message);
|
||||
if (!pdbentry)
|
||||
return;
|
||||
if (pdbentry->message)
|
||||
free((void *)pdbentry->message);
|
||||
dbmfFree(pdbentry);
|
||||
}
|
||||
|
||||
@@ -1198,7 +1201,7 @@ long dbPutRecordAttribute(
|
||||
|
||||
pnew = dbCalloc(1,sizeof(dbRecordAttribute));
|
||||
if(pattribute) {
|
||||
ellInsert(&precordType->attributeList,&pattribute->node,
|
||||
ellInsert(&precordType->attributeList,pattribute->node.previous,
|
||||
&pnew->node);
|
||||
} else {
|
||||
ellAdd(&precordType->attributeList,&pnew->node);
|
||||
@@ -1226,17 +1229,27 @@ long dbGetAttributePart(DBENTRY *pdbentry, const char **ppname)
|
||||
const char *pname = *ppname;
|
||||
dbRecordAttribute *pattribute;
|
||||
|
||||
if (!precordType) return S_dbLib_recordTypeNotFound;
|
||||
if (!precordType)
|
||||
return S_dbLib_recordTypeNotFound;
|
||||
|
||||
pattribute = (dbRecordAttribute *)ellFirst(&precordType->attributeList);
|
||||
while (pattribute) {
|
||||
size_t nameLen = strlen(pattribute->name);
|
||||
int compare = strncmp(pattribute->name, pname, nameLen);
|
||||
int ch = pname[nameLen];
|
||||
if (compare == 0 && !(ch == '_' || isalnum(ch))) {
|
||||
pdbentry->pflddes = pattribute->pdbFldDes;
|
||||
pdbentry->pfield = pattribute->value;
|
||||
*ppname = &pname[nameLen];
|
||||
return 0;
|
||||
|
||||
if (compare == 0) {
|
||||
int ch = pname[nameLen];
|
||||
|
||||
if (ch != '_' && !isalnum(ch)) {
|
||||
/* Any other character can't be in the attribute name */
|
||||
pdbentry->pflddes = pattribute->pdbFldDes;
|
||||
pdbentry->pfield = pattribute->value;
|
||||
*ppname = &pname[nameLen];
|
||||
return 0;
|
||||
}
|
||||
if (strlen(pname) > nameLen) {
|
||||
compare = -1;
|
||||
}
|
||||
}
|
||||
if (compare >= 0) break;
|
||||
pattribute = (dbRecordAttribute *)ellNext(&pattribute->node);
|
||||
|
||||
@@ -30,7 +30,7 @@ typedef struct rset { /* record support entry table */
|
||||
RECSUPFUN init_record; /*init record */
|
||||
RECSUPFUN process; /*process record */
|
||||
RECSUPFUN special; /*special processing */
|
||||
RECSUPFUN get_value; /*get value field */
|
||||
RECSUPFUN get_value; /*no longer used */
|
||||
RECSUPFUN cvt_dbaddr; /*cvt dbAddr */
|
||||
RECSUPFUN get_array_info;
|
||||
RECSUPFUN put_array_info;
|
||||
@@ -52,21 +52,12 @@ typedef struct rset { /* record support entry table */
|
||||
#define S_rec_outMem (M_recSup| 3) /*Out of Memory*/
|
||||
|
||||
|
||||
/* Definition os structure for routine get_value */
|
||||
|
||||
typedef struct valueDes {
|
||||
long field_type;
|
||||
long no_elements;
|
||||
void * pvalue;
|
||||
}valueDes;
|
||||
|
||||
/************************************************************************
|
||||
* report(FILE fp,void *precord);
|
||||
* init();
|
||||
* init_record(precord,pass);
|
||||
* process(void *precord);
|
||||
* special(struct dbAddr *paddr, after);
|
||||
* get_value(precord,struct valueDes *p);
|
||||
* cvt_dbaddr(struct dbAddr *paddr);
|
||||
* get_array_info(paddr,long *no_elements,long *offset);
|
||||
* put_array_info(paddr,nNew);
|
||||
|
||||
@@ -607,7 +607,8 @@ static void initialProcess(void)
|
||||
|
||||
|
||||
/*
|
||||
* Shutdown processing.
|
||||
* set DB_LINK and CA_LINK to PV_LINK
|
||||
* Delete record scans
|
||||
*/
|
||||
static void doCloseLinks(dbRecordType *pdbRecordType, dbCommon *precord,
|
||||
void *user)
|
||||
@@ -678,8 +679,15 @@ int iocShutdown(void)
|
||||
if (iocState == iocVirgin || iocState == iocStopped) return 0;
|
||||
iterateRecords(doCloseLinks, NULL);
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
scanShutdown();
|
||||
callbackShutdown();
|
||||
/* stop and "join" threads */
|
||||
scanStop();
|
||||
callbackStop();
|
||||
}
|
||||
dbCaShutdown();
|
||||
if (iocBuildMode==buildIsolated) {
|
||||
/* free resources */
|
||||
scanCleanup();
|
||||
callbackCleanup();
|
||||
iterateRecords(doFreeRecord, NULL);
|
||||
dbLockCleanupRecords(pdbbase);
|
||||
asShutdown();
|
||||
|
||||
@@ -52,9 +52,9 @@ void SingletonUntyped :: incrRefCount ( PBuild pBuild )
|
||||
|
||||
void SingletonUntyped :: decrRefCount ( PDestroy pDestroy )
|
||||
{
|
||||
assert ( _refCount > 0 );
|
||||
epicsGuard < epicsMutex >
|
||||
guard ( *pEPICSSigletonMutex );
|
||||
assert ( _refCount > 0 );
|
||||
_refCount--;
|
||||
if ( _refCount == 0 ) {
|
||||
( *pDestroy ) ( _pInstance );
|
||||
|
||||
@@ -120,10 +120,12 @@ epicsShareFunc int errlogPrintf(const char *pFormat, ...)
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
nchar = vfprintf(pvtData.console, pFormat, pvar);
|
||||
nchar = vfprintf(console, pFormat, pvar);
|
||||
va_end (pvar);
|
||||
fflush(pvtData.console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
if (pvtData.atExit)
|
||||
@@ -146,6 +148,7 @@ epicsShareFunc int errlogVprintf(
|
||||
int nchar;
|
||||
char *pbuffer;
|
||||
int isOkToBlock;
|
||||
FILE *console;
|
||||
|
||||
if (epicsInterruptIsInterruptContext()) {
|
||||
epicsInterruptContextMessage
|
||||
@@ -160,15 +163,17 @@ epicsShareFunc int errlogVprintf(
|
||||
|
||||
pbuffer = msgbufGetFree(isOkToBlock);
|
||||
if (!pbuffer) {
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
fflush(pvtData.console);
|
||||
console = pvtData.console ? pvtData.console : stderr;
|
||||
vfprintf(console, pFormat, pvar);
|
||||
fflush(console);
|
||||
return 0;
|
||||
}
|
||||
|
||||
nchar = tvsnPrint(pbuffer, pvtData.maxMsgSize, pFormat?pFormat:"", pvar);
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(pvtData.console, "%s", pbuffer);
|
||||
fflush(pvtData.console);
|
||||
console = pvtData.console ? pvtData.console : stderr;
|
||||
fprintf(console, "%s", pbuffer);
|
||||
fflush(console);
|
||||
}
|
||||
msgbufSetSize(nchar);
|
||||
return nchar;
|
||||
@@ -243,11 +248,13 @@ epicsShareFunc int errlogSevPrintf(
|
||||
|
||||
isOkToBlock = epicsThreadIsOkToBlock();
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
fprintf(pvtData.console, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "sevr=%s ", errlogGetSevEnumString(severity));
|
||||
va_start(pvar, pFormat);
|
||||
vfprintf(pvtData.console, pFormat, pvar);
|
||||
vfprintf(console, pFormat, pvar);
|
||||
va_end(pvar);
|
||||
fflush(pvtData.console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
va_start(pvar, pFormat);
|
||||
@@ -359,7 +366,9 @@ epicsShareFunc int epicsShareAPI errlogRemoveListeners(
|
||||
epicsMutexUnlock(pvtData.listenerLock);
|
||||
|
||||
if (count == 0) {
|
||||
fprintf(pvtData.console,
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console,
|
||||
"errlogRemoveListeners: No listeners found\n");
|
||||
}
|
||||
return count;
|
||||
@@ -376,7 +385,7 @@ epicsShareFunc int epicsShareAPI eltc(int yesno)
|
||||
epicsShareFunc int errlogSetConsole(FILE *stream)
|
||||
{
|
||||
errlogInit(0);
|
||||
pvtData.console = stream ? stream : stderr;
|
||||
pvtData.console = stream;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -405,17 +414,19 @@ epicsShareFunc void errPrintf(long status, const char *pFileName,
|
||||
}
|
||||
|
||||
if (pvtData.atExit || (isOkToBlock && pvtData.toConsole)) {
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
if (pFileName)
|
||||
fprintf(pvtData.console, "filename=\"%s\" line number=%d\n",
|
||||
fprintf(console, "filename=\"%s\" line number=%d\n",
|
||||
pFileName, lineno);
|
||||
if (status > 0)
|
||||
fprintf(pvtData.console, "%s ", name);
|
||||
fprintf(console, "%s ", name);
|
||||
|
||||
va_start(pvar, pformat);
|
||||
vfprintf(pvtData.console, pformat, pvar);
|
||||
vfprintf(console, pformat, pvar);
|
||||
va_end(pvar);
|
||||
fputc('\n', pvtData.console);
|
||||
fflush(pvtData.console);
|
||||
fputc('\n', console);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
if (pvtData.atExit)
|
||||
@@ -473,7 +484,7 @@ static void errlogInitPvt(void *arg)
|
||||
ellInit(&pvtData.listenerList);
|
||||
ellInit(&pvtData.msgQueue);
|
||||
pvtData.toConsole = TRUE;
|
||||
pvtData.console = stderr;
|
||||
pvtData.console = NULL;
|
||||
pvtData.waitForWork = epicsEventMustCreate(epicsEventEmpty);
|
||||
pvtData.listenerLock = epicsMutexMustCreate();
|
||||
pvtData.msgQueueLock = epicsMutexMustCreate();
|
||||
@@ -559,8 +570,10 @@ static void errlogThread(void)
|
||||
while ((pmessage = msgbufGetSend(&noConsoleMessage))) {
|
||||
epicsMutexMustLock(pvtData.listenerLock);
|
||||
if (pvtData.toConsole && !noConsoleMessage) {
|
||||
fprintf(pvtData.console,"%s",pmessage);
|
||||
fflush(pvtData.console);
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "%s", pmessage);
|
||||
fflush(console);
|
||||
}
|
||||
|
||||
plistenerNode = (listenerNode *)ellFirst(&pvtData.listenerList);
|
||||
@@ -680,7 +693,9 @@ static void msgbufFreeSend(void)
|
||||
epicsMutexMustLock(pvtData.msgQueueLock);
|
||||
pnextSend = (msgNode *)ellFirst(&pvtData.msgQueue);
|
||||
if (!pnextSend) {
|
||||
fprintf(pvtData.console, "errlog: msgbufFreeSend logic error\n");
|
||||
FILE *console = pvtData.console ? pvtData.console : stderr;
|
||||
|
||||
fprintf(console, "errlog: msgbufFreeSend logic error\n");
|
||||
epicsThreadSuspendSelf();
|
||||
}
|
||||
ellDelete(&pvtData.msgQueue, &pnextSend->node);
|
||||
|
||||
@@ -148,6 +148,10 @@ epicsShareFunc void epicsShareAPI freeListCleanup(void *pvt)
|
||||
epicsShareFunc size_t epicsShareAPI freeListItemsAvail(void *pvt)
|
||||
{
|
||||
FREELISTPVT *pfl = pvt;
|
||||
return pfl->nBlocksAvailable;
|
||||
size_t nBlocksAvailable;
|
||||
epicsMutexMustLock(pfl->lock);
|
||||
nBlocksAvailable = pfl->nBlocksAvailable;
|
||||
epicsMutexUnlock(pfl->lock);
|
||||
return nBlocksAvailable;
|
||||
}
|
||||
|
||||
|
||||
@@ -211,7 +211,7 @@ int testDone(void) {
|
||||
|
||||
/* Our test harness, for RTEMS and vxWorks */
|
||||
|
||||
static void harnessExit(void *dummy) {
|
||||
void testHarnessExit(void *dummy) {
|
||||
epicsTimeStamp ended;
|
||||
int Faulty;
|
||||
|
||||
@@ -248,7 +248,7 @@ static void harnessExit(void *dummy) {
|
||||
|
||||
void testHarness(void) {
|
||||
epicsThreadOnce(&onceFlag, testOnce, NULL);
|
||||
epicsAtExit(harnessExit, NULL);
|
||||
epicsAtExit(testHarnessExit, NULL);
|
||||
Harness = 1;
|
||||
Programs = 0;
|
||||
Tests = 0;
|
||||
|
||||
@@ -43,9 +43,11 @@ epicsShareFunc int testDone(void);
|
||||
|
||||
typedef int (*TESTFUNC)(void);
|
||||
epicsShareFunc void testHarness(void);
|
||||
epicsShareFunc void testHarnessExit(void *dummy);
|
||||
epicsShareFunc void runTestFunc(const char *name, TESTFUNC func);
|
||||
|
||||
#define runTest(func) runTestFunc(#func, func)
|
||||
#define testHarnessDone() testHarnessExit(0)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
||||
@@ -80,6 +80,10 @@ Com_SRCS += osiClockTime.c
|
||||
Com_SRCS_vxWorks += osiNTPTime.c
|
||||
Com_SRCS_RTEMS += osiNTPTime.c
|
||||
|
||||
ifeq ($(OS_CLASS),vxWorks)
|
||||
osdTime.o: USR_CXXFLAGS += -DBUILD_TIME=$(shell perl -e 'print time')
|
||||
endif
|
||||
|
||||
Com_SRCS += osdSock.c
|
||||
Com_SRCS += osdSockAddrReuse.cpp
|
||||
Com_SRCS += osiSock.c
|
||||
|
||||
@@ -3,9 +3,8 @@
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
/*
|
||||
@@ -14,15 +13,22 @@
|
||||
* Author: Jeff Hill
|
||||
*/
|
||||
|
||||
#ifndef osdTimeh
|
||||
#define osdTimeh
|
||||
#ifndef INC_osdTime_H
|
||||
#define INC_osdTime_H
|
||||
|
||||
#if ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED)
|
||||
# if _MSC_VER >= 1900
|
||||
# include <time.h>
|
||||
# else
|
||||
|
||||
#define _TIMESPEC_DEFINED 1
|
||||
struct timespec {
|
||||
time_t tv_sec; /* seconds since some epoch */
|
||||
long tv_nsec; /* nanoseconds within the second */
|
||||
time_t tv_sec; /* seconds since some epoch */
|
||||
long tv_nsec; /* nanoseconds within the second */
|
||||
};
|
||||
|
||||
# endif /* _MSC_VER */
|
||||
#endif /* ! defined(_MINGW) || ! defined(_TIMESPEC_DEFINED) */
|
||||
|
||||
#endif /* ifndef osdTimeh */
|
||||
#endif /* ifndef INC_osdTime_H */
|
||||
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
#define epicsExportSharedSymbols
|
||||
#include "envDefs.h"
|
||||
#include "epicsExit.h"
|
||||
#include "epicsReadline.h"
|
||||
|
||||
#define EPICS_COMMANDLINE_LIBRARY_EPICS 0
|
||||
@@ -78,12 +79,21 @@ epicsReadlineEnd(void *context)
|
||||
|
||||
#include <readline/readline.h>
|
||||
#include <readline/history.h>
|
||||
#include <unistd.h>
|
||||
|
||||
struct readlineContext {
|
||||
FILE *in;
|
||||
char *line;
|
||||
};
|
||||
|
||||
static enum {rlNone, rlIdle, rlBusy} rlState = rlNone;
|
||||
|
||||
static void rlExit(void *dummy) {
|
||||
if (rlState == rlBusy)
|
||||
rl_cleanup_after_signal();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Create a command-line context
|
||||
*/
|
||||
@@ -92,6 +102,11 @@ epicsReadlineBegin(FILE *in)
|
||||
{
|
||||
struct readlineContext *readlineContext;
|
||||
|
||||
if (rlState == rlNone) {
|
||||
epicsAtExit(rlExit, NULL);
|
||||
rlState = rlIdle;
|
||||
}
|
||||
|
||||
readlineContext = malloc(sizeof *readlineContext);
|
||||
if (readlineContext != NULL) {
|
||||
readlineContext->in = in;
|
||||
@@ -124,7 +139,17 @@ epicsReadline (const char *prompt, void *context)
|
||||
free (readlineContext->line);
|
||||
readlineContext->line = NULL;
|
||||
if (readlineContext->in == NULL) {
|
||||
if (!isatty(fileno(stdout))) {
|
||||
/* The libedit readline emulator on Darwin doesn't
|
||||
* print the prompt when the terminal isn't a tty.
|
||||
*/
|
||||
fputs (prompt, stdout);
|
||||
fflush (stdout);
|
||||
rl_already_prompted = 1;
|
||||
}
|
||||
rlState = rlBusy;
|
||||
line = readline (prompt);
|
||||
rlState = rlIdle;
|
||||
}
|
||||
else {
|
||||
line = (char *)malloc (linesize * sizeof *line);
|
||||
|
||||
@@ -27,7 +27,7 @@ epicsShareFunc void epicsShareAPI
|
||||
(char *) & yes, sizeof ( yes ) );
|
||||
if ( status < 0 ) {
|
||||
errlogPrintf (
|
||||
"epicsSocketEnablePortUseForDatagramFanout: "
|
||||
"epicsSocketEnableAddressReuseDuringTimeWaitState: "
|
||||
"unable to set SO_REUSEADDR?\n");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2008 UChicago Argonne LLC, as Operator of Argonne
|
||||
* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
@@ -7,11 +7,14 @@
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
|
||||
#define _VSB_CONFIG_FILE <../lib/h/config/vsbConfig.h>
|
||||
|
||||
#include <vxWorks.h>
|
||||
#include <sntpcLib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
#include "epicsTime.h"
|
||||
#include "osiNTPTime.h"
|
||||
@@ -21,6 +24,9 @@
|
||||
|
||||
#define NTP_REQUEST_TIMEOUT 4 /* seconds */
|
||||
|
||||
static char sntp_sync_task[] = "ipsntps";
|
||||
static char ntp_daemon[] = "ipntpd";
|
||||
|
||||
static const char *pserverAddr = NULL;
|
||||
extern char* sysBootLine;
|
||||
|
||||
@@ -30,18 +36,40 @@ static int timeRegister(void)
|
||||
if (getenv("TIMEZONE") == NULL) {
|
||||
const char *timezone = envGetConfigParamPtr(&EPICS_TIMEZONE);
|
||||
if (timezone == NULL) {
|
||||
printf("NTPTime_Init: No Time Zone Information\n");
|
||||
printf("timeRegister: No Time Zone Information\n");
|
||||
} else {
|
||||
epicsEnvSet("TIMEZONE", timezone);
|
||||
}
|
||||
}
|
||||
|
||||
NTPTime_Init(100); /* init NTP first so it can be used to sync SysTime */
|
||||
ClockTime_Init(CLOCKTIME_SYNC);
|
||||
// Define EPICS_TS_FORCE_NTPTIME to force use of NTPTime provider
|
||||
bool useNTP = getenv("EPICS_TS_FORCE_NTPTIME") != NULL;
|
||||
|
||||
if (!useNTP &&
|
||||
(taskNameToId(sntp_sync_task) != ERROR ||
|
||||
taskNameToId(ntp_daemon) != ERROR)) {
|
||||
// A VxWorks 6 SNTP/NTP sync task is running
|
||||
struct timespec clockNow;
|
||||
|
||||
useNTP = clock_gettime(CLOCK_REALTIME, &clockNow) != OK ||
|
||||
clockNow.tv_sec < BUILD_TIME;
|
||||
// Assumes VxWorks and the host OS have the same epoch
|
||||
}
|
||||
|
||||
if (useNTP) {
|
||||
// Start NTP first so it can be used to sync SysTime
|
||||
NTPTime_Init(100);
|
||||
ClockTime_Init(CLOCKTIME_SYNC);
|
||||
} else {
|
||||
ClockTime_Init(CLOCKTIME_NOSYNC);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
static int done = timeRegister();
|
||||
|
||||
|
||||
// Routines for NTPTime provider
|
||||
|
||||
int osdNTPGet(struct timespec *ts)
|
||||
{
|
||||
return sntpcTimeGet(const_cast<char *>(pserverAddr),
|
||||
@@ -54,6 +82,7 @@ void osdNTPInit(void)
|
||||
if (!pserverAddr) { /* use the boot host */
|
||||
BOOT_PARAMS bootParms;
|
||||
static char host_addr[BOOT_ADDR_LEN];
|
||||
|
||||
bootStringToStruct(sysBootLine, &bootParms);
|
||||
/* bootParms.had = host IP address */
|
||||
strncpy(host_addr, bootParms.had, BOOT_ADDR_LEN);
|
||||
@@ -63,10 +92,13 @@ void osdNTPInit(void)
|
||||
|
||||
void osdNTPReport(void)
|
||||
{
|
||||
printf("NTP Server = %s\n", pserverAddr);
|
||||
if (pserverAddr)
|
||||
printf("NTP Server = %s\n", pserverAddr);
|
||||
}
|
||||
|
||||
|
||||
// Other Time Routines
|
||||
|
||||
// vxWorks localtime_r returns different things in different versions.
|
||||
// It can't fail though, so we just ignore the return value.
|
||||
int epicsTime_localtime(const time_t *clock, struct tm *result)
|
||||
|
||||
@@ -70,37 +70,54 @@ static void ShutdownCallFunc(const iocshArgBuf *args)
|
||||
|
||||
/* Initialization */
|
||||
|
||||
static void ClockTime_InitOnce(void *psync)
|
||||
static void ClockTime_InitOnce(void *pfirst)
|
||||
{
|
||||
ClockTimePvt.synchronize = *(int *)psync;
|
||||
*(int *) pfirst = 1;
|
||||
|
||||
ClockTimePvt.loopEvent = epicsEventMustCreate(epicsEventEmpty);
|
||||
ClockTimePvt.lock = epicsMutexCreate();
|
||||
|
||||
if (ClockTimePvt.synchronize) {
|
||||
/* Start the sync thread */
|
||||
epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
ClockTimeSync, NULL);
|
||||
}
|
||||
else {
|
||||
ClockTimeGetCurrent(&ClockTimePvt.startTime);
|
||||
}
|
||||
|
||||
epicsAtExit(ClockTime_Shutdown, NULL);
|
||||
|
||||
/* Register the iocsh commands */
|
||||
iocshRegister(&ReportFuncDef, ReportCallFunc);
|
||||
if (ClockTimePvt.synchronize)
|
||||
iocshRegister(&ShutdownFuncDef, ShutdownCallFunc);
|
||||
iocshRegister(&ShutdownFuncDef, ShutdownCallFunc);
|
||||
|
||||
/* Finally register as a time provider */
|
||||
/* Register as a time provider */
|
||||
generalTimeRegisterCurrentProvider("OS Clock", LAST_RESORT_PRIORITY,
|
||||
ClockTimeGetCurrent);
|
||||
}
|
||||
|
||||
void ClockTime_Init(int synchronize)
|
||||
{
|
||||
epicsThreadOnce(&onceId, ClockTime_InitOnce, &synchronize);
|
||||
int firstTime = 0;
|
||||
|
||||
epicsThreadOnce(&onceId, ClockTime_InitOnce, &firstTime);
|
||||
|
||||
if (synchronize == CLOCKTIME_SYNC) {
|
||||
if (ClockTimePvt.synchronize == CLOCKTIME_NOSYNC) {
|
||||
/* Start synchronizing */
|
||||
ClockTimePvt.synchronize = synchronize;
|
||||
|
||||
epicsThreadCreate("ClockTimeSync", epicsThreadPriorityHigh,
|
||||
epicsThreadGetStackSize(epicsThreadStackSmall),
|
||||
ClockTimeSync, NULL);
|
||||
}
|
||||
else {
|
||||
/* No change, sync thread should already be running */
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) {
|
||||
/* Turn off synchronization thread */
|
||||
ClockTime_Shutdown(NULL);
|
||||
}
|
||||
else {
|
||||
/* No synchronization thread */
|
||||
if (firstTime)
|
||||
ClockTimeGetCurrent(&ClockTimePvt.startTime);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -108,7 +125,7 @@ void ClockTime_Init(int synchronize)
|
||||
|
||||
void ClockTime_Shutdown(void *dummy)
|
||||
{
|
||||
ClockTimePvt.synchronize = 0;
|
||||
ClockTimePvt.synchronize = CLOCKTIME_NOSYNC;
|
||||
epicsEventSignal(ClockTimePvt.loopEvent);
|
||||
}
|
||||
|
||||
@@ -126,7 +143,7 @@ static void ClockTimeSync(void *dummy)
|
||||
|
||||
for (epicsEventWaitWithTimeout(ClockTimePvt.loopEvent,
|
||||
ClockTimeSyncInterval);
|
||||
ClockTimePvt.synchronize;
|
||||
ClockTimePvt.synchronize == CLOCKTIME_SYNC;
|
||||
epicsEventWaitWithTimeout(ClockTimePvt.loopEvent,
|
||||
ClockTimeSyncInterval)) {
|
||||
epicsTimeStamp timeNow;
|
||||
@@ -144,8 +161,8 @@ static void ClockTimeSync(void *dummy)
|
||||
|
||||
epicsMutexMustLock(ClockTimePvt.lock);
|
||||
if (!ClockTimePvt.synchronized) {
|
||||
ClockTimePvt.startTime = timeNow;
|
||||
ClockTimePvt.synchronized = 1;
|
||||
ClockTimePvt.startTime = timeNow;
|
||||
ClockTimePvt.synchronized = 1;
|
||||
}
|
||||
ClockTimePvt.syncFromPriority = priority;
|
||||
ClockTimePvt.syncTime = timeNow;
|
||||
@@ -200,29 +217,39 @@ int ClockTime_Report(int level)
|
||||
#ifdef CLOCK_REALTIME
|
||||
"initialized"
|
||||
#else
|
||||
"included"
|
||||
"available"
|
||||
#endif /* CLOCK_REALTIME */
|
||||
);
|
||||
} else if (ClockTimePvt.synchronize) {
|
||||
}
|
||||
else if (ClockTimePvt.synchronize == CLOCKTIME_SYNC) {
|
||||
int synchronized, syncFromPriority;
|
||||
epicsTimeStamp startTime, syncTime;
|
||||
|
||||
epicsMutexMustLock(ClockTimePvt.lock);
|
||||
if (ClockTimePvt.synchronized) {
|
||||
printf("OS Clock driver has synchronized to a priority=%d provider\n",
|
||||
ClockTimePvt.syncFromPriority);
|
||||
synchronized = ClockTimePvt.synchronized;
|
||||
syncFromPriority = ClockTimePvt.syncFromPriority;
|
||||
startTime = ClockTimePvt.startTime;
|
||||
syncTime = ClockTimePvt.syncTime;
|
||||
epicsMutexUnlock(ClockTimePvt.lock);
|
||||
|
||||
if (synchronized) {
|
||||
printf("OS Clock driver is synchronized to a priority=%d provider\n",
|
||||
syncFromPriority);
|
||||
if (level) {
|
||||
epicsTimeToStrftime(timebuf, sizeof(timebuf),
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime);
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &startTime);
|
||||
printf("Initial sync was at %s\n", timebuf);
|
||||
epicsTimeToStrftime(timebuf, sizeof(timebuf),
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.syncTime);
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &syncTime);
|
||||
printf("Last successful sync was at %s\n", timebuf);
|
||||
}
|
||||
printf("Syncronization interval = %.0f seconds\n",
|
||||
ClockTimeSyncInterval);
|
||||
} else
|
||||
printf("OS Clock driver has *not* yet synchronized\n");
|
||||
|
||||
epicsMutexUnlock(ClockTimePvt.lock);
|
||||
} else {
|
||||
}
|
||||
else
|
||||
printf("OS Clock driver is *not* synchronized\n");
|
||||
}
|
||||
else {
|
||||
epicsTimeToStrftime(timebuf, sizeof(timebuf),
|
||||
"%Y-%m-%d %H:%M:%S.%06f", &ClockTimePvt.startTime);
|
||||
printf("Program started at %s\n", timebuf);
|
||||
|
||||
@@ -29,7 +29,9 @@ using namespace std;
|
||||
|
||||
#if defined ( _MSC_VER )
|
||||
// some interesting bugs found in the MS implementation of new
|
||||
# if _MSC_VER > 1310 /* this gets fixed some release after visual studio 7 we hope */
|
||||
# if _MSC_VER >= 1900
|
||||
static size_t unsuccessfulNewSize = numeric_limits < size_t > :: max ();
|
||||
# elif _MSC_VER > 1310 /* this gets fixed some release after visual studio 7 we hope */
|
||||
static const size_t unsuccessfulNewSize = numeric_limits < size_t > :: max ();
|
||||
# else
|
||||
static const size_t unsuccessfulNewSize = numeric_limits < size_t > :: max () - 100;
|
||||
|
||||
@@ -101,7 +101,12 @@ void epicsRunLibComTests(void)
|
||||
runTest(taskwdTest);
|
||||
|
||||
/*
|
||||
* Exit must come last as it never returns
|
||||
* Report now in case epicsExitTest dies
|
||||
*/
|
||||
testHarnessDone();
|
||||
|
||||
/*
|
||||
* epicsExitTest must come last as it never returns
|
||||
*/
|
||||
runTest(epicsExitTest);
|
||||
}
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Revision-Id$
|
||||
@@ -70,6 +69,7 @@ epicsTimerQueueActiveForC ::
|
||||
bool okToShare, unsigned priority ) :
|
||||
timerQueueActive ( refMgr, okToShare, priority )
|
||||
{
|
||||
timerQueueActive::start();
|
||||
}
|
||||
|
||||
epicsTimerQueueActiveForC::~epicsTimerQueueActiveForC ()
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Revision-Id$
|
||||
@@ -134,6 +133,7 @@ class timerQueueActive : public epicsTimerQueueActive,
|
||||
public:
|
||||
typedef epicsSingleton < timerQueueActiveMgr > :: reference RefMgr;
|
||||
timerQueueActive ( RefMgr &, bool okToShare, unsigned priority );
|
||||
void start ();
|
||||
epicsTimer & createTimer ();
|
||||
epicsTimerForC & createTimerForC ( epicsTimerCallback pCallback, void *pArg );
|
||||
void show ( unsigned int level ) const;
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
/*************************************************************************\
|
||||
* Copyright (c) 2002 The University of Chicago, as Operator of Argonne
|
||||
* Copyright (c) 2015 UChicago Argonne LLC, as Operator of Argonne
|
||||
* National Laboratory.
|
||||
* Copyright (c) 2002 The Regents of the University of California, as
|
||||
* Operator of Los Alamos National Laboratory.
|
||||
* EPICS BASE Versions 3.13.7
|
||||
* and higher are distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
* EPICS BASE is distributed subject to a Software License Agreement found
|
||||
* in file LICENSE that is included with this distribution.
|
||||
\*************************************************************************/
|
||||
/*
|
||||
* $Revision-Id$
|
||||
*
|
||||
* Author Jeffrey O. Hill
|
||||
* johill@lanl.gov
|
||||
@@ -49,6 +49,10 @@ timerQueueActive ::
|
||||
epicsThreadGetStackSize ( epicsThreadStackMedium ), priority ),
|
||||
sleepQuantum ( epicsThreadSleepQuantum() ), okToShare ( okToShareIn ),
|
||||
exitFlag ( false ), terminateFlag ( false )
|
||||
{
|
||||
}
|
||||
|
||||
void timerQueueActive::start ()
|
||||
{
|
||||
this->thread.start ();
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "dbChannel.h"
|
||||
#include "dbNotify.h"
|
||||
#include "epicsAssert.h"
|
||||
#include "epicsMath.h"
|
||||
#include "recGbl.h"
|
||||
#include "recSup.h"
|
||||
#include "devSup.h"
|
||||
@@ -190,7 +191,7 @@ static long read_ai(aiRecord *prec)
|
||||
}
|
||||
|
||||
/* Apply smoothing algorithm */
|
||||
if (prec->smoo != 0.0 && pdevPvt->smooth)
|
||||
if (prec->smoo != 0.0 && pdevPvt->smooth && finite(prec->val))
|
||||
prec->val = prec->val * prec->smoo +
|
||||
pdevPvt->buffer.value * (1.0 - prec->smoo);
|
||||
else
|
||||
|
||||
@@ -6,13 +6,13 @@ The following filters are available in this release:
|
||||
|
||||
=over
|
||||
|
||||
=item * L<TimeStamp|/"TimeStamp Filter "ts"">
|
||||
=item * L<TimeStamp|/"TimeStamp Filter ts">
|
||||
|
||||
=item * L<Deadband|/"Deadband Filter "dbnd"">
|
||||
=item * L<Deadband|/"Deadband Filter dbnd">
|
||||
|
||||
=item * L<Array|/"Array Filter "arr"">
|
||||
=item * L<Array|/"Array Filter arr">
|
||||
|
||||
=item * L<Synchronize|/"Synchronize Filter "sync"">
|
||||
=item * L<Synchronize|/"Synchronize Filter sync">
|
||||
|
||||
=back
|
||||
|
||||
|
||||
@@ -29,21 +29,21 @@ recordtype(bi) {
|
||||
}
|
||||
field(OSV,DBF_MENU) {
|
||||
prompt("One Error Severity")
|
||||
promptgroup(GUI_BITS1)
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(COSV,DBF_MENU) {
|
||||
prompt("Change of State Svr")
|
||||
promptgroup(GUI_BITS2)
|
||||
promptgroup(GUI_ALARMS)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
menu(menuAlarmSevr)
|
||||
}
|
||||
field(ZNAM,DBF_STRING) {
|
||||
prompt("Zero Name")
|
||||
promptgroup(GUI_CALC)
|
||||
promptgroup(GUI_DISPLAY)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
@@ -51,7 +51,7 @@ recordtype(bi) {
|
||||
}
|
||||
field(ONAM,DBF_STRING) {
|
||||
prompt("One Name")
|
||||
promptgroup(GUI_CLOCK)
|
||||
promptgroup(GUI_DISPLAY)
|
||||
pp(TRUE)
|
||||
interest(1)
|
||||
size(26)
|
||||
|
||||
@@ -46,7 +46,7 @@
|
||||
static long init_record(eventRecord *, int);
|
||||
static long process(eventRecord *);
|
||||
static long special(DBADDR *, int);
|
||||
static long get_value(eventRecord *, struct valueDes *);
|
||||
#define get_value NULL
|
||||
#define cvt_dbaddr NULL
|
||||
#define get_array_info NULL
|
||||
#define put_array_info NULL
|
||||
@@ -153,15 +153,6 @@ static long special(DBADDR *paddr, int after)
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static long get_value(eventRecord *prec, struct valueDes *pvdes)
|
||||
{
|
||||
pvdes->field_type = DBF_STRING;
|
||||
pvdes->no_elements=1;
|
||||
pvdes->pvalue = (void *)(&prec->val);
|
||||
return(0);
|
||||
}
|
||||
|
||||
|
||||
static void monitor(eventRecord *prec)
|
||||
{
|
||||
|
||||
@@ -32,6 +32,7 @@ PERL_MODULES += DBD/Recordtype.pm
|
||||
PERL_MODULES += DBD/Registrar.pm
|
||||
PERL_MODULES += DBD/Variable.pm
|
||||
|
||||
PERL_SCRIPTS += assembleSnippets.pl
|
||||
PERL_SCRIPTS += convertRelease.pl
|
||||
PERL_SCRIPTS += cvsclean.pl
|
||||
PERL_SCRIPTS += dos2unix.pl
|
||||
|
||||
151
src/tools/assembleSnippets.pl
Normal file
151
src/tools/assembleSnippets.pl
Normal file
@@ -0,0 +1,151 @@
|
||||
#!/usr/bin/env perl
|
||||
#*************************************************************************
|
||||
# Copyright (c) 2015 ITER Organization.
|
||||
# EPICS BASE is distributed subject to a Software License Agreement found
|
||||
# in file LICENSE that is included with this distribution.
|
||||
#*************************************************************************
|
||||
|
||||
# $Id$
|
||||
|
||||
use strict;
|
||||
use warnings;
|
||||
|
||||
use Getopt::Std;
|
||||
use Sys::Hostname;
|
||||
use File::Basename;
|
||||
use Data::Dumper;
|
||||
|
||||
our ($opt_o, $opt_d, $opt_m, $opt_i, $opt_M);
|
||||
|
||||
$Getopt::Std::OUTPUT_HELP_VERSION = 1;
|
||||
&HELP_MESSAGE if !getopts('M:i:m:o:d') || @ARGV == 0;
|
||||
|
||||
my $out;
|
||||
my $dep;
|
||||
my %snippets;
|
||||
my $ipattern;
|
||||
|
||||
my $datetime = localtime();
|
||||
my $user = $ENV{LOGNAME} || $ENV{USER} || $ENV{USERNAME};
|
||||
my $host = hostname;
|
||||
my %replacements = (
|
||||
_DATETIME_ => $datetime,
|
||||
_USERNAME_ => $user,
|
||||
_HOST_ => $host,
|
||||
);
|
||||
|
||||
if ($opt_o) {
|
||||
open $out, '>', $opt_o or
|
||||
die "Can't create $opt_o: $!\n";
|
||||
print STDERR "opened file $opt_o for output\n" if $opt_d;
|
||||
$replacements{_OUTPUTFILE_} = $opt_o;
|
||||
} else {
|
||||
open $out, '>&', STDOUT;
|
||||
print STDERR "using STDOUT for output\n" if $opt_d;
|
||||
$replacements{_OUTPUTFILE_} = 'STDERR';
|
||||
}
|
||||
|
||||
if ($opt_m) {
|
||||
foreach my $r (split /,/, $opt_m) {
|
||||
(my $k, my $v) = split /=/, $r;
|
||||
$replacements{$k} = $v;
|
||||
}
|
||||
}
|
||||
|
||||
if ($opt_M) {
|
||||
open $dep, '>', $opt_M or
|
||||
die "Can't create $opt_M: $!\n";
|
||||
print STDERR "opened dependency file $opt_M for output\n" if $opt_d;
|
||||
print $dep basename($opt_o), ":";
|
||||
}
|
||||
|
||||
if ($opt_i) {
|
||||
$ipattern = qr($opt_i);
|
||||
}
|
||||
|
||||
# %snippets is a hash {rank}
|
||||
# of hashes {name-after-rank}
|
||||
# of arrays[] [files...]
|
||||
# of arrays[2] [filename, command]
|
||||
print STDERR "reading input files\n" if $opt_d;
|
||||
foreach (@ARGV) {
|
||||
my $name = basename($_);
|
||||
if ($opt_i and not $name =~ /$ipattern/) {
|
||||
print STDERR " snippet $_ does not match input pattern $opt_i - ignoring\n" if $opt_d;
|
||||
next;
|
||||
}
|
||||
if ($name =~ /\A([ARD]?)([0-9]+)(.*[^~])\z/) {
|
||||
print STDERR " considering snippet $_\n" if $opt_d;
|
||||
if (exists $snippets{$2}) {
|
||||
my %rank = %{$snippets{$2}};
|
||||
my @files = @{ $rank{(keys %rank)[0]} };
|
||||
my $existcmd = $files[0]->[1];
|
||||
if ($1 eq "D" and $existcmd ne "D") {
|
||||
print STDERR " ignoring 'D' default for existing rank $2\n" if $opt_d;
|
||||
next;
|
||||
} elsif ($1 eq "R") {
|
||||
print STDERR " 'R' command - deleting existing rank $2 snippets\n" if $opt_d;
|
||||
$snippets{$2} = {};
|
||||
} elsif ($existcmd eq "D") {
|
||||
print STDERR " deleting existing rank $2 default snippet\n" if $opt_d;
|
||||
$snippets{$2} = {};
|
||||
}
|
||||
}
|
||||
if ($opt_d) {
|
||||
print STDERR " adding snippet ";
|
||||
print STDERR "marked as default " if $1 eq "D";
|
||||
print STDERR "to rank $2\n";
|
||||
}
|
||||
$snippets{$2}{$3} = () if (not exists $snippets{$2}{$3});
|
||||
push @{$snippets{$2}{$3}}, [ $_, $1 ];
|
||||
}
|
||||
}
|
||||
|
||||
if ($opt_d) {
|
||||
print STDERR "finished reading input files\n";
|
||||
print STDERR "dumping the final snippet structure\n";
|
||||
print STDERR Dumper(\%snippets);
|
||||
print STDERR "dumping the macro replacements\n";
|
||||
print STDERR Dumper(\%replacements);
|
||||
print STDERR "creating output\n";
|
||||
}
|
||||
|
||||
foreach my $r (sort {$a<=>$b} keys %snippets) {
|
||||
print STDERR " working on rank $r\n" if $opt_d;
|
||||
foreach my $n (sort keys %{$snippets{$r}}) {
|
||||
foreach my $s (@{$snippets{$r}{$n}}) {
|
||||
my $in;
|
||||
my $f = $s->[0];
|
||||
print STDERR " snippet $n from file $f\n" if $opt_d;
|
||||
open $in, '<', $f or die "Can't open $f: $!\n";
|
||||
$replacements{_SNIPPETFILE_} = $f;
|
||||
print $dep " \\\n $f" if $opt_M;
|
||||
while (<$in>) {
|
||||
chomp;
|
||||
foreach my $k (keys %replacements) {
|
||||
s/$k/$replacements{$k}/g;
|
||||
}
|
||||
print $out $_, "\n";
|
||||
}
|
||||
close $in;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
print STDERR "finished creating output, closing\n" if $opt_d;
|
||||
if ($opt_M) {
|
||||
print $dep "\n";
|
||||
close $dep;
|
||||
}
|
||||
close $out;
|
||||
|
||||
sub HELP_MESSAGE {
|
||||
print STDERR "Usage: assembleSnippets.pl [options] snippets ...\n";
|
||||
print STDERR "Options:\n";
|
||||
print STDERR " -o file output file [STDOUT]\n";
|
||||
print STDERR " -d debug mode [no]\n";
|
||||
print STDERR " -m macros list of macro replacements as \"key=val,key=val\"\n";
|
||||
print STDERR " -i pattern pattern for input files to match\n";
|
||||
print STDERR " -M file write file with dependency rule suitable for make\n";
|
||||
exit 2;
|
||||
}
|
||||
@@ -80,7 +80,7 @@ open my $out, '>', $opt_o or
|
||||
my $pod = join "\n", '=for html <div class="pod">', '',
|
||||
map {
|
||||
# Handle a 'recordtype' Pod directive
|
||||
if (m/^ =recordtype \s+ (.*)/x) {
|
||||
if (m/^ =recordtype \s+ (\w+) /x) {
|
||||
my $rn = $1;
|
||||
my $rtyp = $dbd->recordtype($rn);
|
||||
die "Unknown recordtype '$rn' in $infile POD directive\n"
|
||||
@@ -88,7 +88,7 @@ my $pod = join "\n", '=for html <div class="pod">', '',
|
||||
rtypeToPod($rtyp, $dbd);
|
||||
}
|
||||
# Handle a 'menu' Pod directive
|
||||
elsif (m/^ =menu \s+ (.*)/x) {
|
||||
elsif (m/^ =menu \s+ (\w+) /x) {
|
||||
my $mn = $1;
|
||||
my $menu = $dbd->menu($mn);
|
||||
die "Unknown menu '$mn' in $infile POD directive\n"
|
||||
@@ -152,7 +152,7 @@ sub rtypeToPod {
|
||||
my ($rtyp, $dbd) = @_;
|
||||
return map {
|
||||
# Handle a 'fields' Pod directive
|
||||
if (m/^ =fields \s+ (.*)/x) {
|
||||
if (m/^ =fields \s+ (\w+ (?:\s* , \s* \w+ )* )/x) {
|
||||
my @names = split /\s*,\s*/, $1;
|
||||
# Look up the named fields
|
||||
my @fields = map {
|
||||
@@ -170,7 +170,7 @@ sub rtypeToPod {
|
||||
'</table></blockquote>', '', '=end html';
|
||||
}
|
||||
# Handle a 'menu' Pod directive
|
||||
elsif (m/^ =menu \s+ (.*)/x) {
|
||||
elsif (m/^ =menu \s+ (\w+) /x) {
|
||||
my $mn = $1;
|
||||
my $menu = $dbd->menu($mn);
|
||||
die "Unknown menu '$mn' in $infile POD directive\n"
|
||||
@@ -218,7 +218,7 @@ sub fieldTableRow {
|
||||
# Native type presented to dbAccess users
|
||||
sub DBD::Recfield::public_type {
|
||||
my $fld = shift;
|
||||
m/^=type (.+)$/i && return $1 for $fld->comments;
|
||||
m/^ =type \s+ (.+) /x && return $1 for $fld->comments;
|
||||
my $type = $fld->dbf_type;
|
||||
$type =~ s/^DBF_//;
|
||||
return $type;
|
||||
@@ -227,7 +227,7 @@ sub DBD::Recfield::public_type {
|
||||
# Check if this field is readable
|
||||
sub DBD::Recfield::readable {
|
||||
my $fld = shift;
|
||||
m/^=read (Yes|No)$/i && return $1 for $fld->comments;
|
||||
m/^ =read \s+ (?i) (Yes|No) /x && return $1 for $fld->comments;
|
||||
return 'Probably'
|
||||
if $fld->attribute('special') eq "SPC_DBADDR";
|
||||
return $fld->dbf_type eq 'DBF_NOACCESS' ? 'No' : 'Yes';
|
||||
@@ -236,7 +236,7 @@ sub DBD::Recfield::readable {
|
||||
# Check if this field is writable
|
||||
sub DBD::Recfield::writable {
|
||||
my $fld = shift;
|
||||
m/^=write (Yes|No)$/i && return $1 for $fld->comments;
|
||||
m/^ =write \s+ (?i) (Yes|No) /x && return $1 for $fld->comments;
|
||||
my $special = $fld->attribute('special');
|
||||
return 'No'
|
||||
if $special eq "SPC_NOMOD";
|
||||
|
||||
@@ -18,6 +18,7 @@ TESTS += Menu
|
||||
TESTS += Recfield
|
||||
TESTS += Recordtype
|
||||
TESTS += Registrar
|
||||
TESTS += Snippets
|
||||
TESTS += Variable
|
||||
|
||||
TESTSCRIPTS_HOST += $(TESTS:%=%.t)
|
||||
|
||||
110
src/tools/test/Snippets.plt
Normal file
110
src/tools/test/Snippets.plt
Normal file
@@ -0,0 +1,110 @@
|
||||
#!/usr/bin/env perl
|
||||
|
||||
use File::Path;
|
||||
use Sys::Hostname;
|
||||
|
||||
use Test::More tests => 29;
|
||||
|
||||
my $user = $ENV{LOGNAME} || $ENV{USER} || $ENV{USERNAME};
|
||||
my $host = hostname;
|
||||
|
||||
mkdir "a$$";
|
||||
mkdir "b$$";
|
||||
|
||||
sub mksnip {
|
||||
my ($dir, $file, $line) = @_;
|
||||
open(my $fh, '>', "$dir$$/$file") or die "can't open $dir$$/$file : $!";
|
||||
print $fh $line;
|
||||
close $fh;
|
||||
}
|
||||
|
||||
sub assemble {
|
||||
my @cmd = ( 'perl', '../../assembleSnippets.pl', '-o', "out$$" );
|
||||
push @cmd, @_;
|
||||
system(@cmd);
|
||||
open(my $fh, '<', "out$$") or die "can't open out$$ : $!";
|
||||
chomp(my @result = <$fh>);
|
||||
close $fh;
|
||||
return join (' ', @result);
|
||||
}
|
||||
|
||||
# Adding two snippets of same rank, sorting alphabetically
|
||||
mksnip('a', '10_a', '10');
|
||||
mksnip('b', '10_c', '12');
|
||||
is assemble("a$$/10_a", "b$$/10_c"), '10 12', "adding same rank; ordered";
|
||||
is assemble("b$$/10_c", "a$$/10_a"), '10 12', "adding same rank; reverse order";
|
||||
|
||||
# Same, with 'A' cmd
|
||||
mksnip('a', 'A10_a', '10');
|
||||
mksnip('b', 'A10_c', '12');
|
||||
is assemble("a$$/10_a", "b$$/A10_c"), '10 12', "'A' add same rank; ordered";
|
||||
is assemble("b$$/10_c", "a$$/A10_a"), '10 12', "'A' add same rank; reverse order";
|
||||
|
||||
# Same name does not create issues
|
||||
mksnip('b', '10_a', '10x');
|
||||
is assemble("a$$/10_a", "b$$/10_a"), '10 10x', "adding same name twice; order a-b";
|
||||
is assemble("b$$/10_a", "a$$/10_a"), '10x 10', "adding same name twice; order b-a";
|
||||
|
||||
# Backup files (trailing ~) and hidden files (leading '.') get ignored
|
||||
mksnip('b', '10_c~', '12~');
|
||||
mksnip('b', '.10_c', '.12');
|
||||
is assemble("b$$/10_c", "b$$/10_c~"), '12', "backup file (trailing ~) gets ignored";
|
||||
is assemble("b$$/10_c", "b$$/.10_c"), '12', "hidden file (leading .) gets ignored";
|
||||
|
||||
# Non-numeric filenames get ignored
|
||||
mksnip('a', 'foo10_a', 'foo10');
|
||||
is assemble("b$$/10_c", "a$$/foo10_a"), '12', "file starting with [^ADR0-9] gets ignored";
|
||||
|
||||
# 'R' command replaces existing snippets of same rank
|
||||
mksnip('a', 'R10_b', '11');
|
||||
is assemble("a$$/10_a", "b$$/10_c", "a$$/R10_b"), '11', "'R' cmd; replace all";
|
||||
is assemble("a$$/10_a", "a$$/R10_b", "b$$/10_c"), '11 12', "'R' cmd; replace one (ordered)";
|
||||
is assemble("b$$/10_c", "a$$/R10_b", "a$$/10_a"), '10 11', "'R' cmd; replace one (reverse order)";
|
||||
|
||||
# 'D' command establishes default that gets overwritten or ignored
|
||||
mksnip('a', 'D10_a', 'D10');
|
||||
mksnip('b', 'D10_c', 'D12');
|
||||
is assemble("a$$/D10_a", "b$$/10_c"), '12', "'D' default; replaced by regular";
|
||||
is assemble("a$$/D10_a", "b$$/D10_c"), 'D12', "'D' default; replaced by new default (ordered)";
|
||||
is assemble("b$$/D10_c", "a$$/D10_a"), 'D10', "'D' default; replaced by new default (reverse order)";
|
||||
is assemble("a$$/D10_a", "a$$/R10_b"), '11', "'D' default; replaced by 'R' cmd";
|
||||
is assemble("b$$/10_c", "a$$/D10_a"), '12', "'D' default; ignored when regular exists";
|
||||
|
||||
# Ranks are sorted numerically
|
||||
mksnip('b', '09_a', '09');
|
||||
mksnip('a', '15_a', '15');
|
||||
mksnip('b', '2_a', '2');
|
||||
is assemble("a$$/10_a", "b$$/2_a", "a$$/15_a", "b$$/09_a"), '2 09 10 15', "ranks are sorted numerically";
|
||||
|
||||
# Builtin macros
|
||||
mksnip('a', '30_a', '_USERNAME_');
|
||||
mksnip('a', '30_b', '_OUTPUTFILE_');
|
||||
mksnip('a', '30_c', '_SNIPPETFILE_');
|
||||
mksnip('a', '30_d', '_HOST_');
|
||||
is assemble("a$$/30_a"), "$user", "builtin macro _USERNAME_";
|
||||
is assemble("a$$/30_b"), "out$$", "builtin macro _OUTPUTFILE_";
|
||||
is assemble("a$$/30_c"), "a$$/30_c", "builtin macro _SNIPPETFILE_";
|
||||
is assemble("a$$/30_d"), "$host", "builtin macro _HOST_";
|
||||
|
||||
# User macros
|
||||
mksnip('b', '35_a', 'Line _M1_');
|
||||
mksnip('b', '35_b', 'Line _M1_ with _M2_');
|
||||
mksnip('b', '35_c', 'Line _M2_ with _M2_');
|
||||
is assemble("-m", "_M1_=REP1", "b$$/35_a"), "Line REP1", "single user macro; single occurrence";
|
||||
is assemble("-m", "_M1_=REP1,_M2_=REP2", "b$$/35_b"), "Line REP1 with REP2", "multiple user macros";
|
||||
is assemble("-m", "_M2_=REP2", "b$$/35_c"), "Line REP2 with REP2", "single user macro; multiple occurrences";
|
||||
|
||||
# Input pattern
|
||||
mksnip('a', '10_a.cmd', '10cmd');
|
||||
is assemble("-i", "\.cmd", "a$$/10_a", "b$$/10_c", "a$$/R10_b", "a$$/10_a.cmd"), '10cmd', "input pattern";
|
||||
|
||||
# Dependency file generation
|
||||
assemble("-M", "./dep$$", "a$$/10_a", "b$$/10_c");
|
||||
open(my $fh, '<', "dep$$") or die "can't open dep$$ : $!";
|
||||
chomp(my @result = <$fh>);
|
||||
close $fh;
|
||||
is "$result[0]", "out$$: \\", "dependency file (line 1)";
|
||||
is "$result[1]", " a$$/10_a \\", "dependency file (line 2)";
|
||||
is "$result[2]", " b$$/10_c", "dependency file (line 3)";
|
||||
|
||||
rmtree([ "a$$", "b$$", "out$$", "dep$$" ]);
|
||||
Reference in New Issue
Block a user