Compare commits
115 Commits
stream_2_2
...
stream_2_5
Author | SHA1 | Date | |
---|---|---|---|
5a6d293a5f | |||
d51d6648e0 | |||
f521da8606 | |||
b64d486925 | |||
7f1f1c1e65 | |||
2dc0918926 | |||
37d412108a | |||
f03af416eb | |||
82066af0e0 | |||
9c18afb178 | |||
04c6cb50d7 | |||
382784526c | |||
a91496884c | |||
7cdec03dcb | |||
3e5e4b8375 | |||
f8b944f325 | |||
c7e7878d9e | |||
5717dd6345 | |||
2fe3b5c68d | |||
db88081fd7 | |||
1230c01646 | |||
8142dd27eb | |||
19279b208a | |||
9619c359c1 | |||
6b948abc2d | |||
2c7391602a | |||
fd0fccda46 | |||
1164413bd2 | |||
7e1e038ecd | |||
2a8b0aed56 | |||
c856dec6dd | |||
53bb235ff1 | |||
29ac760f0e | |||
7836af6423 | |||
1af9c6ea55 | |||
8c619d3350 | |||
0155d5ab69 | |||
8252b629f5 | |||
494661c0e7 | |||
5b652abd25 | |||
a3e68d7f7d | |||
7cc19d2614 | |||
318d0e389b | |||
8dbe3abcdb | |||
649d775cd3 | |||
a941061a29 | |||
100b7d4543 | |||
1eb8a25432 | |||
9b6f8b3386 | |||
cc1bb004f2 | |||
ab6a6b730d | |||
3d928c49a0 | |||
8fe70f2e39 | |||
862686aeef | |||
2fa54cdfb1 | |||
d987a9abb9 | |||
d37c142d36 | |||
35768cbfbb | |||
5494aae1ba | |||
18ba904d6c | |||
2cd969d643 | |||
0c9d4dc4c0 | |||
ef3b2ec9fd | |||
1fb402e838 | |||
eae987e7f1 | |||
93906dd022 | |||
7e717c564d | |||
14b60c210a | |||
8dae0afa30 | |||
95bf2c78c2 | |||
8c10e5395c | |||
69de0b9f31 | |||
22ae510dca | |||
5fdf3912c8 | |||
b50aec424f | |||
4a1033b6dc | |||
68e178d41b | |||
e83ceda41c | |||
b307adb005 | |||
0a2a304a2b | |||
80477709aa | |||
706c59d069 | |||
6541c40818 | |||
04f64c0f67 | |||
af669103c2 | |||
40d9641150 | |||
9cf2770440 | |||
616bc9a410 | |||
53abbb750b | |||
ec3cbbf559 | |||
8a3cbc1c6a | |||
8a4cda6647 | |||
2d513b96d3 | |||
6a0d7b4492 | |||
755df7c3ee | |||
ddb602196a | |||
0a5f486a9b | |||
574052dcb8 | |||
eaba256c51 | |||
7eab96120f | |||
50f69a76f4 | |||
d40c9610c9 | |||
b97948a704 | |||
7ac080b8d7 | |||
7a83d91ec9 | |||
ad4b84fd60 | |||
1f82b0224a | |||
627035a2ea | |||
6bb963e852 | |||
e6c0b6af0e | |||
55c8977e7b | |||
ed8b3aded1 | |||
161f3c7d3c | |||
b1395f43f2 | |||
c3979f766f |
5
MODULE
Normal file
5
MODULE
Normal file
@ -0,0 +1,5 @@
|
||||
# Please change the following email with yours.
|
||||
Email: dirk.zimoch@psi.ch
|
||||
Module-Name: StreamDevice2
|
||||
Description: StreamDevice2
|
||||
Project-Name:
|
57
Makefile
57
Makefile
@ -1,44 +1,21 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
#EXCLUDE_VERSIONS = 3.13.2
|
||||
EXCLUDE_VERSIONS = 3.13
|
||||
PROJECT=stream
|
||||
TOP = ..
|
||||
|
||||
DOCUDIR = doc
|
||||
SOURCES += $(wildcard src/*.c)
|
||||
SOURCES += $(wildcard src/*.cc)
|
||||
DIRS = src
|
||||
|
||||
DBDS = stream.dbd
|
||||
# Look if we have EPICS R3.13 or R3.14
|
||||
ifeq ($(wildcard $(TOP)/configure),)
|
||||
# EPICS R3.13
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
CONFIG = $(TOP)/config
|
||||
else
|
||||
# EPICS R3.14
|
||||
include $(TOP)/configure/CONFIG
|
||||
ifneq ($(words $(CALC) $(SYNAPPS)), 0)
|
||||
# with synApps calc module (contains scalcout)
|
||||
DIRS += srcSynApps
|
||||
endif
|
||||
endif
|
||||
|
||||
BUSSES += AsynDriver
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
RECORDTYPES += mbbo mbbi
|
||||
RECORDTYPES += mbboDirect mbbiDirect
|
||||
RECORDTYPES += longout longin
|
||||
RECORDTYPES += stringout stringin
|
||||
RECORDTYPES += waveform
|
||||
RECORDTYPES += calcout
|
||||
DIRS += streamApp
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
|
||||
streamReferences:
|
||||
@for i in $(BUSSES); \
|
||||
do echo "extern void* ref_$${i}Interface;"; \
|
||||
echo "void* p$$i = ref_$${i}Interface;"; \
|
||||
done > $@
|
||||
@for i in $(FORMATS); \
|
||||
do echo "extern void* ref_$${i}Converter;"; \
|
||||
echo "void* p$$i = ref_$${i}Converter;"; \
|
||||
done >> $@
|
||||
|
||||
stream.dbd:
|
||||
@for r in $(RECORDTYPES); \
|
||||
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
|
||||
done > $@
|
||||
@echo "driver(stream)" >> $@
|
||||
include $(CONFIG)/RULES_DIRS
|
||||
|
@ -172,6 +172,9 @@ void <a href="#event">eventCallback</a>(StreamIoStatus status);
|
||||
<div class="indent"><code>
|
||||
void <a href="#connect">connectCallback</a>(StreamIoStatus status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void <a href="#connect">disconnectCallback</a>(StreamIoStatus status);
|
||||
</code></div>
|
||||
|
||||
<h3>Other provided methods, attibutes, and types</h3>
|
||||
|
||||
@ -280,6 +283,9 @@ bool disconnect();
|
||||
<div class="indent"><code>
|
||||
void connectCallback(IoStatus status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void disconnectCallback(IoStatus status);
|
||||
</code></div>
|
||||
<p>
|
||||
Connection should be handled automatically.
|
||||
If the device is disconnected, each attempt to access the
|
||||
@ -292,7 +298,8 @@ However, sometimes the client wants to connect or
|
||||
disconnect explicitely.
|
||||
To connect, the client calls <code>connectRequest()</code>.
|
||||
This function should return <code>true</code> immediately
|
||||
or <code>false</code> if the request cannot be accepted.
|
||||
or <code>false</code> if the request cannot be accepted or connection
|
||||
handling is not supported.
|
||||
The interface should call <code>connectCallback(StreamIoSuccess)</code>
|
||||
once the bus could be connected.
|
||||
If the bus cannot be connected within <code>connecttimeout_ms</code>
|
||||
@ -305,10 +312,14 @@ something wrong with the I/O hardware,
|
||||
<code>connectCallback(StreamIoFault)</code> may be called.
|
||||
</p>
|
||||
<p>
|
||||
To disconnect, the client calls <code>disconnect()</code>;
|
||||
To disconnect, the client calls <code>disconnectRequest()</code>;
|
||||
This function should return <code>true</code> immediately or
|
||||
<code>false</code> if disconnecting is impossible.
|
||||
There is no callback for <code>disconnect()</code>.
|
||||
<code>false</code> if the request cannot be accepted or connection
|
||||
handling is not supported.
|
||||
The interface should call <code>connectCallback(StreamIoSuccess)</code>
|
||||
once the bus is disconnected. There is no timeout for this operation.
|
||||
If disconnecting is impossible, the interface should call
|
||||
<code>connectCallback(StreamIoFault)</code>.
|
||||
</p>
|
||||
|
||||
<a name="lock"></a>
|
||||
|
128
doc/formats.html
128
doc/formats.html
@ -240,6 +240,8 @@ The <code>%b</code> format uses the characters <code>0</code> and
|
||||
<code>1</code>.
|
||||
With the <code>%B</code> format, you can choose two other characters
|
||||
to represent zero and one.
|
||||
With the <code>#</code> flag, the bit order is changed to <em>little
|
||||
endian</em>, i.e. least significant bit first.
|
||||
</p>
|
||||
<p>
|
||||
Examples: <code>%B.!</code> or <code>%B\x00\xff</code>.
|
||||
@ -264,23 +266,28 @@ one character.
|
||||
<h2>9. Raw LONG Converter (<code>%r</code>)</h2>
|
||||
<p>
|
||||
The raw converter does not really "convert".
|
||||
A signed integer value is written or read in the internal
|
||||
A signed or unsigned integer value is written or read in the internal
|
||||
(usually two's complement) representation of the computer.
|
||||
The normal byte order is <em>big endian</em>, i.e. most significant byte
|
||||
first.
|
||||
With the <code>#</code> flag, the byte order is changed to <em>little
|
||||
endian</em>, i.e. least significant byte first.
|
||||
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
||||
</p>
|
||||
<p>
|
||||
In output, the <em>width</em> least significant bytes of the value
|
||||
are written.
|
||||
If <em>width</em> is larger than the size of a <code>long</code>,
|
||||
the value is sign extended.
|
||||
the value is sign extended or zero extended, depending on the
|
||||
<code>0</code> flag.
|
||||
</p>
|
||||
<p>
|
||||
In input, <em>width</em> bytes are read and put into the value.
|
||||
If <em>width</em> is longer than the size of a <code>long</code>, only
|
||||
If <em>width</em> is larger than the size of a <code>long</code>, only
|
||||
the least significant bytes are used.
|
||||
If <em>width</em> is smaller than the size of a <code>long</code>,
|
||||
the value is sign extended or zero extended, depending on the
|
||||
<code>0</code> flag.
|
||||
</p>
|
||||
|
||||
<a name="bcd"></a>
|
||||
@ -352,56 +359,119 @@ In input, the next byte or bytes must match the checksum.
|
||||
|
||||
<h3>Implemented checksum functions</h3>
|
||||
<dl>
|
||||
<dt><code>%<SUM></code> or <code>%<SUM8></code></dt>
|
||||
<dt><code>%<sum></code> or <code>%<sum8></code></dt>
|
||||
<dd>One byte. The sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<SUM16></code></dt>
|
||||
<dt><code>%<sum16></code></dt>
|
||||
<dd>Two bytes. The sum of all characters modulo 2<sup>16</sup>.</dd>
|
||||
<dt><code>%<SUM32></code></dt>
|
||||
<dt><code>%<sum32></code></dt>
|
||||
<dd>Four bytes. The sum of all characters modulo 2<sup>32</sup>.</dd>
|
||||
<dt><code>%<NEGSUM></code> or <code>%<NSUM></code> or <code>%<-SUM></code></dt>
|
||||
<dt><code>%<negsum></code>, <code>%<nsum></code>, <code>%<-sum></code>, <code>%<negsum8></code>, <code>%<nsum8></code>, or <code>%<-sum8></code></dt>
|
||||
<dd>One byte. The negative of the sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<NOTSUM></code> or <code>%<~SUM></code></dt>
|
||||
<dt><code>%<negsum16></code>, <code>%<nsum16></code>, or <code>%<-sum16></code></dt>
|
||||
<dd>Two bytes. The negative of the sum of all characters modulo 2<sup>16</sup>.</dd>
|
||||
<dt><code>%<negsum32></code>, <code>%<nsum32></code>, or <code>%<-sum32></code></dt>
|
||||
<dd>Four bytes. The negative of the sum of all characters modulo 2<sup>32</sup>.</dd>
|
||||
<dt><code>%<notsum></code> or <code>%<~sum></code></dt>
|
||||
<dd>One byte. The bitwise inverse of the sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<XOR></code></dt>
|
||||
<dt><code>%<xor></code></dt>
|
||||
<dd>One byte. All characters xor'ed.</dd>
|
||||
<dt><code>%<CRC8></code></dt>
|
||||
<dd>One byte. An often used 8 bit CRC checksum
|
||||
<dt><code>%<xor7></code></dt>
|
||||
<dd>One byte. All characters xor'ed & 0x7F.</dd>
|
||||
<dt><code>%<crc8></code></dt>
|
||||
<dd>One byte. An often used 8 bit crc checksum
|
||||
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<CCITT8></code></dt>
|
||||
<dd>One byte. The CCITT standard 8 bit CRC checksum
|
||||
<dt><code>%<ccitt8></code></dt>
|
||||
<dd>One byte. The CCITT standard 8 bit crc checksum
|
||||
(poly=0x31, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<CRC16></code></dt>
|
||||
<dd>Two bytes. An often used 16 bit CRC checksum
|
||||
<dt><code>%<crc16></code></dt>
|
||||
<dd>Two bytes. An often used 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<CRC16R></code></dt>
|
||||
<dd>Two bytes. An often used reflected 16 bit CRC checksum
|
||||
<dt><code>%<crc16r></code></dt>
|
||||
<dd>Two bytes. An often used reflected 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<CCITT16></code></dt>
|
||||
<dt><code>%<ccitt16></code></dt>
|
||||
<dd>Two bytes. The usual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
|
||||
implementation of the CCITT standard 16 bit CRC checksum
|
||||
implementation of the CCITT standard 16 bit crc checksum
|
||||
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
|
||||
<dt><code>%<CCITT16A></code></dt>
|
||||
<dt><code>%<ccitt16a></code></dt>
|
||||
<dd>Two bytes. The unusual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
|
||||
implementation of the CCITT standard 16 bit CRC checksum with augment.
|
||||
implementation of the CCITT standard 16 bit crc checksum with augment.
|
||||
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
|
||||
<dt><code>%<CRC32></code></dt>
|
||||
<dd>Four bytes. The standard 32 bit CRC checksum.
|
||||
<dt><code>%<crc32></code></dt>
|
||||
<dd>Four bytes. The standard 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<CRC32R></code></dt>
|
||||
<dd>Four bytes. The standard reflected 32 bit CRC checksum.
|
||||
<dt><code>%<crc32r></code></dt>
|
||||
<dd>Four bytes. The standard reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<JAMCRC></code></dt>
|
||||
<dd>Four bytes. Another reflected 32 bit CRC checksum.
|
||||
<dt><code>%<jamcrc></code></dt>
|
||||
<dd>Four bytes. Another reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
|
||||
<dt><code>%<ADLER32></code></dt>
|
||||
<dt><code>%<adler32></code></dt>
|
||||
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
||||
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
||||
<dt><code>%<HEXSUM8></code></dt>
|
||||
<dt><code>%<hexsum8></code></dt>
|
||||
<dd>One byte. The sum of all hex digits. (Other characters are ignored.)</dd>
|
||||
</dl>
|
||||
|
||||
<a name="regex"></a>
|
||||
<h2>12. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
||||
<p>
|
||||
This input-only format matches <a target="ex"
|
||||
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
||||
It is only available if a PCRE library is installed.
|
||||
</p>
|
||||
<p>
|
||||
If PCRE is not available for your host or cross architecture, download
|
||||
the sourcecode from <a target="ex" href="http://www.pcre.org/">www.pcre.org</a>
|
||||
and try my EPICS compatible <a target="ex"
|
||||
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
||||
to compile it like a normal EPICS application.
|
||||
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
|
||||
In your RELEASE file define the variable <code>PCRE</code> so that
|
||||
it points to the install location of PCRE.
|
||||
</p>
|
||||
<p>
|
||||
If PCRE is already installed on your system, use the variables
|
||||
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
|
||||
the install directories of <code>pcre.h</code> and the library.
|
||||
</p>
|
||||
<p>
|
||||
If you have PCRE installed in different locations for different (cross)
|
||||
architectures, define the variables in RELEASE.Common.<architecture>
|
||||
instead of the global RELEASE file.
|
||||
</p>
|
||||
<p>
|
||||
If the regular expression is not anchored, i.e. does not start with
|
||||
<code>^</code>, leading non-matching input is skipped.
|
||||
A maximum of <em>width</em> bytes is matched, if specified.
|
||||
If <em>prec</em> is given, it specifies the sub-expression whose match
|
||||
is retuned.
|
||||
Otherwise the complete match is returned.
|
||||
In any case, the complete match is consumed from the input buffer.
|
||||
If the expression contains a <code>/</code> is must be escaped.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
||||
the title of an HTML page, skipps anything before the
|
||||
<code><title></code> tag and leaves anything after the
|
||||
<code></title></code> tag in the input buffer.
|
||||
</p>
|
||||
<a name="exp"></a>
|
||||
<h2>13. Exponential DOUBLE converter (<code>%m</code>)</h2>
|
||||
<p>
|
||||
This experimental input-only format matches numbers in the format
|
||||
<i>[sign] mantissa sign exponent<i>, e.g <code>+123-4</code> meaning
|
||||
123e-4 or 0.0123. Mantissa and exponent are integers. The sign of the
|
||||
mantissa is optional. Compared to the standard <code>%e</code> format,
|
||||
this format does not contain the characters <code>.</code> and <code>e</code>.
|
||||
</p>
|
||||
<p>
|
||||
Output formatting is ambigous (e.g. <code>123-4</code> versus
|
||||
<code>1230-5</code>). Until it has been specified how to handle this,
|
||||
output is not supported.
|
||||
</p>
|
||||
<hr>
|
||||
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
||||
<p><small>Dirk Zimoch, 2007</small></p>
|
||||
|
@ -120,6 +120,8 @@ h1 {font-size:120%;
|
||||
<li><a target="text" href="formats.html#raw">%r</a></li>
|
||||
<li><a target="text" href="formats.html#bcd">%D</a></li>
|
||||
<li><a target="text" href="formats.html#chksum">%<<em>checksum</em>></a></li>
|
||||
<li><a target="text" href="formats.html#regex">%/<em>regex</em>/</a></li>
|
||||
<li><a target="text" href="formats.html#exp">%m</a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="top">
|
||||
|
@ -36,6 +36,8 @@ href="http://www.aps.anl.gov/aod/bcda/synApps/index.php"
|
||||
Up to <em>calc</em> release R2-6 (<em>synApps</em> release R5_1),
|
||||
the <em>scalcout</em> record needs a fix.
|
||||
(See separate <a href="scalcout.html"><em>scalcout</em> page</a>.)
|
||||
Support for the scalcout is optional. <em>StreamDevice</em> works
|
||||
as well without scalcout or SynApps.
|
||||
</p>
|
||||
<p>
|
||||
Up to release R3.14.8.2, a fix in EPICS base is required to build
|
||||
@ -48,15 +50,40 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
|
||||
</pre>
|
||||
|
||||
<p>
|
||||
Make sure that the <em>asyn</em> library and the <em>calc</em> module of
|
||||
<em>synApps</em> can be found, e.g. by
|
||||
Make sure that the <em>asyn</em> library (and the <em>calc</em> module of
|
||||
<em>synApps</em>, if desired) can be found, e.g. by
|
||||
adding <code>ASYN</code>
|
||||
and (if installed) <code>CALC</code> or <code>SYNAPPS</code>
|
||||
to your <kbd><top>/configure/RELEASE</kbd> file:
|
||||
</p>
|
||||
<pre>
|
||||
ASYN=/home/epics/asyn/4-5
|
||||
CALC=/home/epics/synApps/calc/2-7
|
||||
</pre>
|
||||
<p>
|
||||
If you want to enable regular expression matching, you need the <em>PCRE</em> package.
|
||||
For most Linux systems, it is already installed. In that case add the locations
|
||||
of the <em>PCRE</em> header and library to your <kbd>RELEASE</kbd> file:
|
||||
</p>
|
||||
<pre>
|
||||
PCRE_INCLUDE=/usr/include/pcre
|
||||
PCRE_LIB=/usr/lib
|
||||
</pre>
|
||||
<p>
|
||||
If you want to build <em>StreamDevice</em> for platforms without <em>PCRE</em> support,
|
||||
it is the easiest to build <em>PCRE</em> as an EPICS application.
|
||||
Download the <em>PCRE</em> package from <a target=ex href="http://www.pcre.org">www.pcre.org</a>
|
||||
and compile it with my EPICS compatible
|
||||
<a target=ex href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>.
|
||||
Then define the location of the application in your RELEASE file.
|
||||
</p>
|
||||
<pre>
|
||||
PCRE=/home/epics/pcre
|
||||
</pre>
|
||||
<p>
|
||||
Regular expressions are optional. If you don't want them, you don't need this.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
For details on <kbd><top></kbd> directories and RELEASE files,
|
||||
please refer to the
|
||||
|
72
makefile
Normal file
72
makefile
Normal file
@ -0,0 +1,72 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
EXCLUDE_VERSIONS = 3.13.2
|
||||
PROJECT=stream2
|
||||
BUILDCLASSES += Linux
|
||||
|
||||
#DOCUDIR = doc
|
||||
|
||||
DBDS = stream.dbd
|
||||
|
||||
BUSSES += AsynDriver
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += RawFloat
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += Regexp
|
||||
FORMATS += MantissaExponent
|
||||
FORMATS += Timestamp
|
||||
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
RECORDTYPES += mbbo mbbi
|
||||
RECORDTYPES += mbboDirect mbbiDirect
|
||||
RECORDTYPES += longout longin
|
||||
RECORDTYPES += stringout stringin
|
||||
RECORDTYPES += waveform
|
||||
|
||||
SOURCES += $(RECORDTYPES:%=src/dev%Stream.c)
|
||||
SOURCES += $(FORMATS:%=src/%Converter.cc)
|
||||
SOURCES += $(BUSSES:%=src/%Interface.cc)
|
||||
SOURCES += $(wildcard src/Stream*.cc)
|
||||
SOURCES += src/StreamVersion.c
|
||||
SOURCES_3.14 += src/devcalcoutStream.c
|
||||
|
||||
HEADERS += StreamFormat.h
|
||||
HEADERS += StreamFormatConverter.h
|
||||
HEADERS += StreamBuffer.h
|
||||
HEADERS += StreamError.h
|
||||
|
||||
ifeq (${EPICS_BASETYPE},3.13)
|
||||
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
|
||||
endif
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
|
||||
streamReferences:
|
||||
@for i in $(BUSSES); \
|
||||
do echo "extern void* ref_$${i}Interface;"; \
|
||||
echo "void* p$$i = ref_$${i}Interface;"; \
|
||||
done > $@
|
||||
@for i in $(FORMATS); \
|
||||
do echo "extern void* ref_$${i}Converter;"; \
|
||||
echo "void* p$$i = ref_$${i}Converter;"; \
|
||||
done >> $@
|
||||
|
||||
ifeq (${EPICS_BASETYPE},3.13)
|
||||
stream.dbd:
|
||||
@for r in $(RECORDTYPES); \
|
||||
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
|
||||
done > $@
|
||||
@echo "driver(stream)" >> $@
|
||||
else
|
||||
stream.dbd:
|
||||
@for r in $(RECORDTYPES) calcout; \
|
||||
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
|
||||
done > $@
|
||||
@echo "driver(stream)" >> $@
|
||||
@echo "variable(streamDebug, int)" >> $@
|
||||
@echo "registrar(streamRegistrar)" >> $@
|
||||
endif
|
@ -59,12 +59,12 @@ writeRequest()
|
||||
pasynManager->queueRequest()
|
||||
when request is handled
|
||||
pasynOctet->flush()
|
||||
pasynOctet->writeRaw()
|
||||
if writeRaw() times out
|
||||
pasynOctet->write()
|
||||
if write() times out
|
||||
writeCallback(StreamIoTimeout)
|
||||
if writeRaw fails otherwise
|
||||
if write fails otherwise
|
||||
writeCallback(StreamIoFault)
|
||||
if writeRaw succeeds and all bytes have been written
|
||||
if write succeeds and all bytes have been written
|
||||
writeCallback(StreamIoSuccess)
|
||||
if not all bytes can be written
|
||||
pasynManager->queueRequest() to write next part
|
||||
@ -186,7 +186,7 @@ class AsynDriverInterface : StreamBusInterface
|
||||
bool supportsEvent();
|
||||
bool supportsAsyncRead();
|
||||
bool connectRequest(unsigned long connecttimeout_ms);
|
||||
bool disconnect();
|
||||
bool disconnectRequest();
|
||||
void finish();
|
||||
|
||||
#ifdef EPICS_3_14
|
||||
@ -458,17 +458,22 @@ connectToBus(const char* busname, int addr)
|
||||
bool AsynDriverInterface::
|
||||
lockRequest(unsigned long lockTimeout_ms)
|
||||
{
|
||||
int connected;
|
||||
asynStatus status;
|
||||
|
||||
debug("AsynDriverInterface::lockRequest(%s, %ld msec)\n",
|
||||
clientName(), lockTimeout_ms);
|
||||
asynStatus status;
|
||||
lockTimeout = lockTimeout_ms ? lockTimeout_ms*0.001 : -1.0;
|
||||
if (!lockTimeout_ms)
|
||||
{
|
||||
if (!connectToAsynPort()) return false;
|
||||
}
|
||||
|
||||
ioAction = Lock;
|
||||
status = pasynManager->queueRequest(pasynUser, priority(),
|
||||
status = pasynManager->isConnected(pasynUser, &connected);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
error("%s: pasynManager->isConnected() failed: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
return false;
|
||||
}
|
||||
status = pasynManager->queueRequest(pasynUser,
|
||||
connected ? priority() : asynQueuePriorityConnect,
|
||||
lockTimeout);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
@ -497,6 +502,8 @@ connectToAsynPort()
|
||||
clientName(), pasynUser->errorMessage);
|
||||
return false;
|
||||
}
|
||||
debug("AsynDriverInterface::connectToAsynPort(%s) is %s connected\n",
|
||||
clientName(), connected ? "already" : "not yet");
|
||||
if (!connected)
|
||||
{
|
||||
status = pasynCommon->connect(pvtCommon, pasynUser);
|
||||
@ -510,6 +517,12 @@ connectToAsynPort()
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// We probably should set REN=1 prior to sending but this
|
||||
// seems to hang up the device every other time.
|
||||
// if (pasynGpib)
|
||||
// {
|
||||
// pasynGpib->ren(pvtGpib, pasynUser, 1);
|
||||
// }
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -517,10 +530,21 @@ connectToAsynPort()
|
||||
void AsynDriverInterface::
|
||||
lockHandler()
|
||||
{
|
||||
int connected;
|
||||
debug("AsynDriverInterface::lockHandler(%s)\n",
|
||||
clientName());
|
||||
pasynManager->blockProcessCallback(pasynUser, false);
|
||||
lockCallback(StreamIoSuccess);
|
||||
|
||||
#ifndef ASYN_VERSION // asyn < 4.14
|
||||
asynStatus status;
|
||||
status = pasynManager->lockPort(pasynUser);
|
||||
if(status!=asynSuccess) {
|
||||
debug("Failed locking port");
|
||||
}
|
||||
#endif
|
||||
|
||||
connected = connectToAsynPort();
|
||||
lockCallback(connected ? StreamIoSuccess : StreamIoFault);
|
||||
}
|
||||
|
||||
// interface function: we don't need exclusive access any more
|
||||
@ -529,6 +553,15 @@ unlock()
|
||||
{
|
||||
debug("AsynDriverInterface::unlock(%s)\n",
|
||||
clientName());
|
||||
|
||||
#ifndef ASYN_VERSION // asyn < 4.14
|
||||
asynStatus status;
|
||||
status = pasynManager->unlockPort(pasynUser);
|
||||
if (status != asynSuccess) {
|
||||
debug("Failed unlocking port");
|
||||
}
|
||||
#endif
|
||||
|
||||
pasynManager->unblockProcessCallback(pasynUser, false);
|
||||
return true;
|
||||
}
|
||||
@ -590,15 +623,27 @@ writeHandler()
|
||||
|
||||
size_t streameoslen;
|
||||
const char* streameos = getOutTerminator(streameoslen);
|
||||
if (streameos) // stream has added eos
|
||||
int oldeoslen = -1;
|
||||
char oldeos[16];
|
||||
if (streameos) // stream has already added eos, don't do it again in asyn
|
||||
{
|
||||
status = pasynOctet->writeRaw(pvtOctet, pasynUser,
|
||||
outputBuffer, outputSize, &written);
|
||||
// clear terminator for asyn
|
||||
status = pasynOctet->getOutputEos(pvtOctet,
|
||||
pasynUser, oldeos, sizeof(oldeos)-1, &oldeoslen);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
oldeoslen = -1;
|
||||
// No EOS support?
|
||||
}
|
||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||
NULL, 0);
|
||||
}
|
||||
else // asyn should add eos
|
||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||
outputBuffer, outputSize, &written);
|
||||
if (oldeoslen >= 0) // restore asyn terminator
|
||||
{
|
||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||
outputBuffer, outputSize, &written);
|
||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||
oldeos, oldeoslen);
|
||||
}
|
||||
switch (status)
|
||||
{
|
||||
@ -636,6 +681,23 @@ writeHandler()
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
case asynDisconnected:
|
||||
error("%s: asynDisconnected in write: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
case asynDisabled:
|
||||
error("%s: asynDisconnected in write: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
error("%s: unknown asyn error in write: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
@ -750,7 +812,7 @@ readHandler()
|
||||
{
|
||||
// In AsyncRead mode just poll
|
||||
// and read as much as possible
|
||||
pasynUser->timeout = readTimeout;
|
||||
pasynUser->timeout = 0.0;
|
||||
bytesToRead = buffersize;
|
||||
}
|
||||
else
|
||||
@ -758,7 +820,7 @@ readHandler()
|
||||
pasynUser->timeout = replyTimeout;
|
||||
}
|
||||
bool waitForReply = true;
|
||||
int received;
|
||||
size_t received;
|
||||
int eomReason;
|
||||
asynStatus status;
|
||||
long readMore;
|
||||
@ -770,8 +832,8 @@ readHandler()
|
||||
eomReason = 0;
|
||||
|
||||
status = pasynOctet->read(pvtOctet, pasynUser,
|
||||
buffer, bytesToRead, (size_t*)&received, &eomReason);
|
||||
if (ioAction != AsyncRead || status != asynTimeout)
|
||||
buffer, bytesToRead, &received, &eomReason);
|
||||
if (ioAction == Read || status != asynTimeout)
|
||||
{
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"read(..., bytesToRead=%d, ...) [timeout=%f seconds] = %s\n",
|
||||
@ -779,18 +841,28 @@ readHandler()
|
||||
asynStatusStr[status]);
|
||||
}
|
||||
// pasynOctet->read() has already cut off terminator.
|
||||
|
||||
if (status == asynTimeout &&
|
||||
pasynUser->timeout == 0.0 &&
|
||||
received > 0)
|
||||
{
|
||||
// Jens Eden (PTB) pointed out that polling asynInterposeEos
|
||||
// with timeout = 0.0 returns asynTimeout even when bytes
|
||||
// have been received, but not yet the terminator.
|
||||
status = asynSuccess;
|
||||
}
|
||||
|
||||
switch (status)
|
||||
{
|
||||
case asynSuccess:
|
||||
if (ioAction == AsyncRead)
|
||||
if (ioAction != Read)
|
||||
{
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"AsyncRead poll: received %d of %d bytes \"%s\" "
|
||||
"eomReason=%s [data ignored]\n",
|
||||
clientName(), received, bytesToRead,
|
||||
StreamBuffer(buffer, received).expand()(),
|
||||
clientName(), (int)received, bytesToRead,
|
||||
StreamBuffer(buffer, (int)received).expand()(),
|
||||
eomReasonStr[eomReason&0x7]);
|
||||
#endif
|
||||
// ignore what we got from here.
|
||||
@ -803,8 +875,8 @@ readHandler()
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"received %d of %d bytes \"%s\" "
|
||||
"eomReason=%s\n",
|
||||
clientName(), received, bytesToRead,
|
||||
StreamBuffer(buffer, received).expand()(),
|
||||
clientName(), (int)received, bytesToRead,
|
||||
StreamBuffer(buffer, (int)received).expand()(),
|
||||
eomReasonStr[eomReason&0x7]);
|
||||
#endif
|
||||
// asynOctet->read() cuts off terminator, but:
|
||||
@ -824,7 +896,7 @@ readHandler()
|
||||
size_t i;
|
||||
for (i = 0; i < deveoslen; i++, received++)
|
||||
{
|
||||
if (received >= 0) buffer[received] = deveos[i];
|
||||
if ((int)received >= 0) buffer[received] = deveos[i];
|
||||
// It is safe to add to buffer here, because
|
||||
// the terminator was already there before
|
||||
// asynOctet->read() had cut it.
|
||||
@ -860,9 +932,9 @@ readHandler()
|
||||
// read timeout
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"ioAction=%s, timeout after %d of %d bytes \"%s\"\n",
|
||||
clientName(), ioActionStr[ioAction],
|
||||
received, bytesToRead,
|
||||
"ioAction=%s, timeout [%f seconds] after %d of %d bytes \"%s\"\n",
|
||||
clientName(), ioActionStr[ioAction], pasynUser->timeout,
|
||||
(int)received, bytesToRead,
|
||||
StreamBuffer(buffer, received).expand()());
|
||||
#endif
|
||||
if (ioAction == AsyncRead || ioAction == AsyncReadMore)
|
||||
@ -895,6 +967,23 @@ readHandler()
|
||||
clientName(), pasynUser->errorMessage);
|
||||
readCallback(StreamIoFault, buffer, received);
|
||||
break;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
case asynDisconnected:
|
||||
error("%s: asynDisconnected in read: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
case asynDisabled:
|
||||
error("%s: asynDisconnected in read: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
default:
|
||||
error("%s: unknown asyn error in read: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
}
|
||||
if (!readMore) break;
|
||||
if (readMore > 0)
|
||||
@ -929,25 +1018,11 @@ void intrCallbackOctet(void* /*pvt*/, asynUser *pasynUser,
|
||||
// Problems here:
|
||||
// 1. We get this message too when we are the poller.
|
||||
// Thus we have to ignore what we got from polling.
|
||||
// 2. We get this message multiple times when original reader
|
||||
// reads in chunks.
|
||||
// 3. eomReason=ASYN_EOM_CNT when message was too long for
|
||||
// 2. eomReason=ASYN_EOM_CNT when message was too long for
|
||||
// internal buffer of asynDriver.
|
||||
|
||||
if (interface->ioAction == AsyncRead ||
|
||||
interface->ioAction == AsyncReadMore)
|
||||
{
|
||||
interface->asynReadHandler(data, numchars, eomReason);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::intrCallbackOctet(%s, buffer=\"%s\", "
|
||||
"received=%d eomReason=%s) ioAction=%s\n",
|
||||
interface->clientName(), StreamBuffer(data, numchars).expand()(),
|
||||
numchars, eomReasonStr[eomReason&0x7], ioActionStr[interface->ioAction]);
|
||||
#endif
|
||||
}
|
||||
if (!interruptAccept) return; // too early to process records
|
||||
interface->asynReadHandler(data, numchars, eomReason);
|
||||
}
|
||||
|
||||
// get asynchronous input
|
||||
@ -1181,6 +1256,9 @@ connectRequest(unsigned long connecttimeout_ms)
|
||||
double queueTimeout = connecttimeout_ms*0.001;
|
||||
asynStatus status;
|
||||
ioAction = Connect;
|
||||
|
||||
debug("AsynDriverInterface::connectRequest %s\n",
|
||||
clientName());
|
||||
status = pasynManager->queueRequest(pasynUser,
|
||||
asynQueuePriorityConnect, queueTimeout);
|
||||
if (status != asynSuccess)
|
||||
@ -1198,49 +1276,51 @@ connectRequest(unsigned long connecttimeout_ms)
|
||||
void AsynDriverInterface::
|
||||
connectHandler()
|
||||
{
|
||||
asynStatus status;
|
||||
status = pasynCommon->connect(pvtCommon, pasynUser);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
error("%s connectRequest: pasynCommon->connect() failed: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
connectCallback(StreamIoFault);
|
||||
return;
|
||||
}
|
||||
connectCallback(StreamIoSuccess);
|
||||
connectCallback(connectToAsynPort() ? StreamIoSuccess : StreamIoFault);
|
||||
}
|
||||
|
||||
bool AsynDriverInterface::
|
||||
disconnect()
|
||||
disconnectRequest()
|
||||
{
|
||||
asynStatus status;
|
||||
ioAction = Disconnect;
|
||||
|
||||
debug("AsynDriverInterface::disconnectRequest %s\n",
|
||||
clientName());
|
||||
status = pasynManager->queueRequest(pasynUser,
|
||||
asynQueuePriorityConnect, 0.0);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
error("%s disconnect: pasynManager->queueRequest() failed: %s\n",
|
||||
error("%s disconnectRequest: pasynManager->queueRequest() failed: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
return false;
|
||||
}
|
||||
// continues with:
|
||||
// handleRequest() -> disconnectHandler()
|
||||
// or handleTimeout()
|
||||
// (does not expect callback)
|
||||
return true;
|
||||
}
|
||||
|
||||
void AsynDriverInterface::
|
||||
disconnectHandler()
|
||||
{
|
||||
int connected;
|
||||
asynStatus status;
|
||||
status = pasynCommon->disconnect(pvtCommon, pasynUser);
|
||||
if (status != asynSuccess)
|
||||
|
||||
pasynManager->isConnected(pasynUser, &connected);
|
||||
debug("AsynDriverInterface::disconnectHandler %s is %s disconnected\n",
|
||||
clientName(), !connected ? "already" : "not yet");
|
||||
if (connected)
|
||||
{
|
||||
error("%s connectRequest: pasynCommon->disconnect() failed: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
return;
|
||||
status = pasynCommon->disconnect(pvtCommon, pasynUser);
|
||||
if (status != asynSuccess)
|
||||
{
|
||||
error("%s connectRequest: pasynCommon->disconnect() failed: %s\n",
|
||||
clientName(), pasynUser->errorMessage);
|
||||
disconnectCallback(StreamIoFault);
|
||||
return;
|
||||
}
|
||||
}
|
||||
disconnectCallback(StreamIoSuccess);
|
||||
}
|
||||
|
||||
void AsynDriverInterface::
|
||||
@ -1250,6 +1330,12 @@ finish()
|
||||
clientName());
|
||||
cancelTimer();
|
||||
ioAction = None;
|
||||
// if (pasynGpib)
|
||||
// {
|
||||
// // Release GPIB device the the end of the protocol
|
||||
// // to re-enable local buttons.
|
||||
// pasynGpib->ren(pvtGpib, pasynUser, 0);
|
||||
// }
|
||||
debug("AsynDriverInterface::finish(%s) done\n",
|
||||
clientName());
|
||||
}
|
||||
@ -1260,6 +1346,8 @@ void handleRequest(asynUser* pasynUser)
|
||||
{
|
||||
AsynDriverInterface* interface =
|
||||
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
|
||||
debug("AsynDriverInterface::handleRequest(%s) %s\n",
|
||||
interface->clientName(), ioActionStr[interface->ioAction]);
|
||||
switch (interface->ioAction)
|
||||
{
|
||||
case None:
|
||||
@ -1294,6 +1382,8 @@ void handleTimeout(asynUser* pasynUser)
|
||||
{
|
||||
AsynDriverInterface* interface =
|
||||
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
|
||||
debug("AsynDriverInterface::handleTimeout(%s)\n",
|
||||
interface->clientName());
|
||||
switch (interface->ioAction)
|
||||
{
|
||||
case Lock:
|
||||
@ -1312,7 +1402,9 @@ void handleTimeout(asynUser* pasynUser)
|
||||
interface->connectCallback(StreamIoTimeout);
|
||||
break;
|
||||
case Disconnect:
|
||||
// not interested in callback
|
||||
error("AsynDriverInterface %s: disconnect timeout\n",
|
||||
interface->clientName());
|
||||
// should not happen because of infinite timeout
|
||||
break;
|
||||
// No AsyncRead here because we don't use timeout when polling
|
||||
default:
|
||||
|
@ -38,11 +38,11 @@ parse(const StreamFormat& format, StreamBuffer& info,
|
||||
if (format.conv == 'B')
|
||||
{
|
||||
// user defined characters for %B (next 2 in source)
|
||||
if (!*source )
|
||||
if (*source)
|
||||
{
|
||||
if (*source == esc) source++;
|
||||
info.append(*source++);
|
||||
if (!*source)
|
||||
if (*source)
|
||||
{
|
||||
if (*source == esc) source++;
|
||||
info.append(*source++);
|
||||
@ -65,32 +65,60 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
{
|
||||
// find number of significant bits
|
||||
prec = sizeof (value) * 8;
|
||||
while (prec && (value & (1 << (prec - 1))) == 0) prec--;
|
||||
while (prec && (value & (1L << (prec - 1))) == 0) prec--;
|
||||
}
|
||||
if (prec == 0) prec++; // print at least one bit
|
||||
int width = prec;
|
||||
if (format.width > width) width = format.width;
|
||||
char zero = format.info[0];
|
||||
char one = format.info[1];
|
||||
if (!(format.flags & left_flag))
|
||||
char fill = (format.flags & zero_flag) ? zero : ' ';
|
||||
if (format.flags & alt_flag)
|
||||
{
|
||||
// pad left
|
||||
char fill = (format.flags & zero_flag) ? zero : ' ';
|
||||
while (width > prec)
|
||||
// little endian (least significant bit first)
|
||||
if (!(format.flags & left_flag))
|
||||
{
|
||||
output.append(fill);
|
||||
// pad left
|
||||
while (width > prec)
|
||||
{
|
||||
output.append(' ');
|
||||
width--;
|
||||
}
|
||||
}
|
||||
while (prec--)
|
||||
{
|
||||
output.append((value & 1) ? one : zero);
|
||||
value >>= 1;
|
||||
width--;
|
||||
}
|
||||
while (width--)
|
||||
{
|
||||
// pad right
|
||||
output.append(fill);
|
||||
}
|
||||
}
|
||||
while (prec--)
|
||||
else
|
||||
{
|
||||
output.append((value & (1 << prec)) ? one : zero);
|
||||
width--;
|
||||
}
|
||||
while (width--)
|
||||
{
|
||||
// pad right
|
||||
output.append(' ');
|
||||
// big endian (most significant bit first)
|
||||
if (!(format.flags & left_flag))
|
||||
{
|
||||
// pad left
|
||||
while (width > prec)
|
||||
{
|
||||
output.append(fill);
|
||||
width--;
|
||||
}
|
||||
}
|
||||
while (prec--)
|
||||
{
|
||||
output.append((value & (1L << prec)) ? one : zero);
|
||||
width--;
|
||||
}
|
||||
while (width--)
|
||||
{
|
||||
// pad right
|
||||
output.append(' ');
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
@ -102,14 +130,28 @@ scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
int width = format.width;
|
||||
if (width == 0) width = -1;
|
||||
int length = 0;
|
||||
while (isspace(input[++length])); // skip whitespaces
|
||||
while (isspace(input[length])) length++; // skip whitespaces
|
||||
char zero = format.info[0];
|
||||
char one = format.info[1];
|
||||
if (input[length] != zero && input[length] != one) return -1;
|
||||
while (width-- && (input[length] == zero || input[length] == one))
|
||||
if (format.flags & alt_flag)
|
||||
{
|
||||
val <<= 1;
|
||||
if (input[length++] == one) val |= 1;
|
||||
// little endian (least significan bit first)
|
||||
long mask = 1;
|
||||
while (width-- && (input[length] == zero || input[length] == one))
|
||||
{
|
||||
if (input[length++] == one) val |= mask;
|
||||
mask <<= 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// big endian (most significan bit first)
|
||||
while (width-- && (input[length] == zero || input[length] == one))
|
||||
{
|
||||
val <<= 1;
|
||||
if (input[length++] == one) val |= 1;
|
||||
}
|
||||
}
|
||||
value = val;
|
||||
return length;
|
||||
|
@ -30,6 +30,8 @@ SYNAPPS_RECORDS += scalcout
|
||||
# asynDriver interface is added automatically
|
||||
# if ASYN is defined in your RELEASE file.
|
||||
# BUSSES += Debug
|
||||
BUSSES += Debug
|
||||
BUSSES += Dummy
|
||||
|
||||
# You may add more format converters
|
||||
# This requires the naming convention
|
||||
@ -38,8 +40,27 @@ SYNAPPS_RECORDS += scalcout
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += RawFloat
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += MantissaExponent
|
||||
FORMATS += Timestamp
|
||||
|
||||
# Want Perl regular expression matching?
|
||||
# If PCRE is installed at the same location for all
|
||||
# architectures, define the following variable in your
|
||||
# RELEASE file. If the PCRE installation is different
|
||||
# (or even missing) for some architectures, use
|
||||
# separate RELEASE.Common.<arch> files.
|
||||
# If PCRE is an EPICS-like project define
|
||||
# PCRE=<location of the PCRE project>
|
||||
# Else define
|
||||
# PCRE_INCLUDE=<location of the pcre.h file>
|
||||
# PCRE_LIB=<location of the PCRE library>
|
||||
|
||||
ifneq ($(words $(PCRE) $(PCRE_LIB) $(PCRE_INCLUDE)),0)
|
||||
FORMATS += Regexp
|
||||
endif
|
||||
|
||||
# Want a loadable module?
|
||||
# For Tornado 2.0.2, a fix is needed in the
|
||||
@ -48,10 +69,6 @@ FORMATS += Checksum
|
||||
# by "static IoccrfReg iocshReg;"
|
||||
# LOADABLE_MODULE = YES
|
||||
|
||||
# Want to add some memory tracing
|
||||
# to find memory leaks?
|
||||
# USE_MEMGUARD = YES
|
||||
|
||||
# Sources of Stream Kernel
|
||||
# Do not modify
|
||||
|
||||
|
@ -50,6 +50,11 @@ static ulong xor8(const uchar* data, ulong len, ulong sum)
|
||||
return sum;
|
||||
}
|
||||
|
||||
static ulong xor7(const uchar* data, ulong len, ulong sum)
|
||||
{
|
||||
return xor8(data, len, sum) & 0x7F;
|
||||
}
|
||||
|
||||
static ulong crc_0x07(const uchar* data, ulong len, ulong crc)
|
||||
{
|
||||
// x^8 + x^2 + x^1 + x^0 (0x07)
|
||||
@ -460,18 +465,32 @@ static checksum checksumMap[] =
|
||||
{"sum8", sum, 0x00, 0x00, 1}, // 0xDD
|
||||
{"sum16", sum, 0x0000, 0x0000, 2}, // 0x01DD
|
||||
{"sum32", sum, 0x00000000, 0x00000000, 4}, // 0x000001DD
|
||||
{"nsum", sum, 0xff, 0xff, 1}, // 0x23
|
||||
{"negsum", sum, 0xff, 0xff, 1}, // 0x23
|
||||
{"-sum", sum, 0xff, 0xff, 1}, // 0x23
|
||||
{"notsum", sum, 0x00, 0xff, 1}, // 0x22
|
||||
{"~sum", sum, 0x00, 0xff, 1}, // 0x22
|
||||
{"nsum", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"negsum", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"-sum", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"nsum8", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"negsum8", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"-sum8", sum, 0xFF, 0xFF, 1}, // 0x23
|
||||
{"nsum16", sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
|
||||
{"negsum16",sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
|
||||
{"-sum16", sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
|
||||
{"nsum32", sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
|
||||
{"negsum32",sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
|
||||
{"-sum32", sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
|
||||
{"notsum", sum, 0x00, 0xFF, 1}, // 0x22
|
||||
{"~sum", sum, 0x00, 0xFF, 1}, // 0x22
|
||||
{"xor", xor8, 0x00, 0x00, 1}, // 0x31
|
||||
{"xor8", xor8, 0x00, 0x00, 1}, // 0x31
|
||||
{"xor7", xor7, 0x00, 0x00, 1}, // 0x31
|
||||
{"crc8", crc_0x07, 0x00, 0x00, 1}, // 0xF4
|
||||
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
|
||||
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
|
||||
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
|
||||
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
|
||||
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
|
||||
{"ccitt16x",crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||
{"crc16c", crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||
{"xmodem", crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||
{"crc32", crc_0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFC891918
|
||||
{"crc32r", crc_0x04C11DB7_r, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xCBF43926
|
||||
{"jamcrc", crc_0x04C11DB7_r, 0xFFFFFFFF, 0x00000000, 4}, // 0x340BC6D9
|
||||
@ -494,7 +513,7 @@ parse(const StreamFormat&, StreamBuffer& info, const char*& source, bool)
|
||||
const char* p = strchr(source, '>');
|
||||
if (!p)
|
||||
{
|
||||
error ("Missing terminating '>' in checksum format.\n");
|
||||
error ("Missing closing '>' in checksum format.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
|
76
src/DummyInterface.cc
Normal file
76
src/DummyInterface.cc
Normal file
@ -0,0 +1,76 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 2011 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is the interface to a "dummy" bus driver for *
|
||||
* StreamDevice. It does not provide any I/O functionality. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "StreamBusInterface.h"
|
||||
#include "StreamError.h"
|
||||
#include "StreamBuffer.h"
|
||||
|
||||
class DummyInterface : StreamBusInterface
|
||||
{
|
||||
DummyInterface(Client* client);
|
||||
|
||||
// StreamBusInterface methods
|
||||
bool lockRequest(unsigned long lockTimeout_ms);
|
||||
bool unlock();
|
||||
|
||||
protected:
|
||||
~DummyInterface();
|
||||
|
||||
public:
|
||||
// static creator method
|
||||
static StreamBusInterface* getBusInterface(Client* client,
|
||||
const char* busname, int addr, const char* param);
|
||||
};
|
||||
|
||||
RegisterStreamBusInterface(DummyInterface);
|
||||
|
||||
DummyInterface::
|
||||
DummyInterface(Client* client) : StreamBusInterface(client)
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
DummyInterface::
|
||||
~DummyInterface()
|
||||
{
|
||||
// Nothing to do
|
||||
}
|
||||
|
||||
StreamBusInterface* DummyInterface::
|
||||
getBusInterface(Client* client,
|
||||
const char* busname, int addr, const char*)
|
||||
{
|
||||
if (strcmp(busname, "dummy") == 0)
|
||||
{
|
||||
DummyInterface* interface = new DummyInterface(client);
|
||||
return interface;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
bool DummyInterface::
|
||||
lockRequest(unsigned long lockTimeout_ms)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
bool DummyInterface::
|
||||
unlock()
|
||||
{
|
||||
return false;
|
||||
}
|
@ -21,6 +21,7 @@
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
#include "StreamProtocol.h"
|
||||
#include <stdlib.h>
|
||||
|
||||
// Enum %{string0|string1|...}
|
||||
|
||||
@ -31,44 +32,62 @@ class EnumConverter : public StreamFormatConverter
|
||||
int scanLong(const StreamFormat&, const char*, long&);
|
||||
};
|
||||
|
||||
// info format: <numEnums><index><string>0<index><string>0...
|
||||
|
||||
int EnumConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool)
|
||||
{
|
||||
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag|alt_flag))
|
||||
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag))
|
||||
{
|
||||
error("Use of modifiers '-', '+', ' ', '0', '#'"
|
||||
error("Use of modifiers '-', '+', ' ', '0' "
|
||||
"not allowed with %%{ conversion\n");
|
||||
return false;
|
||||
}
|
||||
int i = info.length(); // put maxValue here later
|
||||
info.append('\0');
|
||||
int maxValue = 0;
|
||||
long numEnums = 0;
|
||||
int n = info.length(); // put numEnums here later
|
||||
info.append(&numEnums, sizeof(numEnums));
|
||||
long index = 0;
|
||||
int i = 0;
|
||||
i = info.length(); // put index here later
|
||||
info.append(&index, sizeof(index));
|
||||
while (*source)
|
||||
{
|
||||
switch (*source)
|
||||
if (*source == '=' && (fmt.flags & alt_flag))
|
||||
{
|
||||
case '|':
|
||||
info.append('\0');
|
||||
if (++maxValue > 255)
|
||||
{
|
||||
error("Too many enums (max 256)\n");
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case '}':
|
||||
source++;
|
||||
info.append('\0');
|
||||
info[i] = maxValue;
|
||||
debug("EnumConverter::parse %d choices: %s\n",
|
||||
maxValue+1, info.expand(i+1)());
|
||||
return enum_format;
|
||||
case esc:
|
||||
info.append(*source++);
|
||||
default:
|
||||
info.append(*source);
|
||||
char* p;
|
||||
index = strtol(++source, &p, 0);
|
||||
if (p == source || (*p != '|' && *p != '}'))
|
||||
{
|
||||
error("Integer expected after '=' "
|
||||
"in %%{ format conversion\n");
|
||||
return false;
|
||||
}
|
||||
memcpy(info(i), &index, sizeof(index));
|
||||
source = p;
|
||||
}
|
||||
if (*source == '|' || *source == '}')
|
||||
{
|
||||
numEnums++;
|
||||
info.append('\0');
|
||||
|
||||
if (*source++ == '}')
|
||||
{
|
||||
memcpy(info(n), &numEnums, sizeof(numEnums));
|
||||
debug("EnumConverter::parse %ld choices: %s\n",
|
||||
numEnums, info.expand()());
|
||||
return enum_format;
|
||||
}
|
||||
index ++;
|
||||
i = info.length();
|
||||
info.append(&index, sizeof(index));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (*source == esc)
|
||||
info.append(*source++);
|
||||
info.append(*source++);
|
||||
}
|
||||
source++;
|
||||
}
|
||||
error("Missing '}' after %%{ format conversion\n");
|
||||
return false;
|
||||
@ -77,14 +96,10 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
bool EnumConverter::
|
||||
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
{
|
||||
long maxValue = fmt.info[0]; // number of enums
|
||||
const char* s = fmt.info+1; // first enum string
|
||||
if (value < 0 || value > maxValue)
|
||||
{
|
||||
error("Value %li out of range [0...%li]\n", value, maxValue);
|
||||
return false;
|
||||
}
|
||||
while (value--)
|
||||
const char* s = fmt.info;
|
||||
long numEnums = extract<long>(s);
|
||||
long index = extract<long>(s);
|
||||
while (numEnums-- && (value != index))
|
||||
{
|
||||
while(*s)
|
||||
{
|
||||
@ -92,6 +107,12 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
s++;
|
||||
}
|
||||
s++;
|
||||
index = extract<long>(s);
|
||||
}
|
||||
if (numEnums == -1)
|
||||
{
|
||||
error("Value %li not found in enum set\n", value);
|
||||
return false;
|
||||
}
|
||||
while(*s)
|
||||
{
|
||||
@ -106,14 +127,16 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
{
|
||||
debug("EnumConverter::scanLong(%%%c, \"%s\")\n",
|
||||
fmt.conv, input);
|
||||
long maxValue = fmt.info[0]; // number of enums
|
||||
const char* s = fmt.info+1; // first enum string
|
||||
const char* s = fmt.info;
|
||||
long numEnums = extract<long>(s);
|
||||
long index;
|
||||
int length;
|
||||
long val;
|
||||
|
||||
bool match;
|
||||
for (val = 0; val <= maxValue; val++)
|
||||
while (numEnums--)
|
||||
{
|
||||
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", val, s);
|
||||
index = extract<long>(s);
|
||||
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", index, s);
|
||||
length = 0;
|
||||
match = true;
|
||||
while(*s)
|
||||
@ -129,8 +152,8 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
}
|
||||
if (match)
|
||||
{
|
||||
debug("EnumConverter::scanLong: value %ld matches\n", val);
|
||||
value = val;
|
||||
debug("EnumConverter::scanLong: value %ld matches\n", index);
|
||||
value = index;
|
||||
return length;
|
||||
}
|
||||
s++;
|
||||
|
21
src/Makefile
21
src/Makefile
@ -2,7 +2,7 @@
|
||||
# StreamDevice Support #
|
||||
# #
|
||||
# (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) #
|
||||
# (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) #
|
||||
# (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) #
|
||||
# #
|
||||
# This is the EPICS 3.14 Makefile of StreamDevice. #
|
||||
# Normally it should not be necessary to modify this file. #
|
||||
@ -57,13 +57,20 @@ SRCS += $(FORMATS:%=%Converter.cc)
|
||||
SRCS += $(RECORDS:%=dev%Stream.c)
|
||||
SRCS += $(STREAM_SRCS)
|
||||
|
||||
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
|
||||
|
||||
ifeq ($(USE_MEMGUARD),YES)
|
||||
# memguard looks for memory leaks (gcc only)
|
||||
CPPFLAGS += -include ../memguard.h
|
||||
LIB_SRCS += memguard.cc
|
||||
# find system wide or local PCRE header and library
|
||||
ifdef PCRE_INCLUDE
|
||||
RegexpConverter_INCLUDES += -I$(PCRE_INCLUDE)
|
||||
endif
|
||||
ifdef PCRE
|
||||
LIB_LIBS += pcre
|
||||
else
|
||||
ifneq ($(words $(PCRE_LIB) $(PCRE_INCLUDE)),0)
|
||||
LIB_SYS_LIBS += pcre
|
||||
SHRLIB_DEPLIB_DIRS += $(PCRE_LIB)
|
||||
endif
|
||||
endif
|
||||
|
||||
LIB_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
INC += devStream.h
|
||||
|
||||
|
100
src/MantissaExponentConverter.cc
Normal file
100
src/MantissaExponentConverter.cc
Normal file
@ -0,0 +1,100 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 2008 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is a custom exponential format converter for *
|
||||
* StreamDevice. *
|
||||
* The number is represented as two signed integers, mantissa *
|
||||
* and exponent, like in +00011-01 *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
#include <math.h>
|
||||
|
||||
// Exponential Converter %m
|
||||
// Eric Berryman requested a double format that reads
|
||||
// +00011-01 as 11e-01
|
||||
// I.e integer mantissa and exponent without 'e' or '.'
|
||||
// But why not +11000-04 ?
|
||||
// For writing, I chose the following convention:
|
||||
// Format precision defines number of digits in mantissa
|
||||
// No leading '0' in mantissa (except for 0.0 of course)
|
||||
// Number of digits in exponent is at least 2
|
||||
// Format flags +, -, and space are supported in the usual way
|
||||
// Flags #, 0 are not supported
|
||||
|
||||
class MantissaExponentConverter : public StreamFormatConverter
|
||||
{
|
||||
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
virtual int scanDouble(const StreamFormat&, const char*, double&);
|
||||
virtual bool printDouble(const StreamFormat&, StreamBuffer&, double);
|
||||
};
|
||||
|
||||
int MantissaExponentConverter::
|
||||
parse(const StreamFormat&, StreamBuffer&,
|
||||
const char*&, bool)
|
||||
{
|
||||
return double_format;
|
||||
}
|
||||
|
||||
int MantissaExponentConverter::
|
||||
scanDouble(const StreamFormat& fmt, const char* input, double& value)
|
||||
{
|
||||
int mantissa;
|
||||
int exponent;
|
||||
int length = -1;
|
||||
|
||||
sscanf(input, "%d%d%n", &mantissa, &exponent, &length);
|
||||
if (fmt.flags & skip_flag) return length;
|
||||
if (length == -1) return -1;
|
||||
value = (double)(mantissa) * pow(10.0, exponent);
|
||||
return length;
|
||||
}
|
||||
|
||||
bool MantissaExponentConverter::
|
||||
printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
|
||||
{
|
||||
// Have to divide value into mantissa and exponent
|
||||
// precision field is number of characters in mantissa
|
||||
// number of characters in exponent is at least 2
|
||||
int spaces;
|
||||
StreamBuffer buf;
|
||||
int prec = fmt.prec;
|
||||
|
||||
if (prec < 1) prec = 6;
|
||||
buf.printf("%.*e", prec-1, fabs(value)/pow(10.0, prec-1));
|
||||
buf.remove(1,1);
|
||||
buf.remove(buf.find('e'),1);
|
||||
|
||||
spaces = fmt.width-buf.length();
|
||||
if (fmt.flags & (space_flag|sign_flag) || value < 0.0) spaces--;
|
||||
if (spaces < 0) spaces = 0;
|
||||
if (!(fmt.flags & left_flag))
|
||||
output.append(' ', spaces);
|
||||
if ((fmt.flags & (space_flag|sign_flag)) == space_flag && value >= 0.0)
|
||||
output.append(' ');
|
||||
if (fmt.flags & sign_flag && value >= 0.0)
|
||||
output.append('+');
|
||||
if (value < 0.0)
|
||||
output.append('-');
|
||||
output.append(buf);
|
||||
if (fmt.flags & left_flag)
|
||||
output.append(' ', spaces);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegisterConverter (MantissaExponentConverter, "m");
|
||||
|
@ -40,12 +40,14 @@ parse(const StreamFormat&, StreamBuffer&,
|
||||
bool RawConverter::
|
||||
printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
{
|
||||
int prec = format.prec; // number of bytes from value
|
||||
if (prec == -1) prec = 1; // default: 1 byte
|
||||
int width = prec; // number of bytes in output
|
||||
int prec = format.prec; // number of bytes from value
|
||||
if (prec == -1) prec = 1; // default: 1 byte
|
||||
int width = prec; // number of bytes in output
|
||||
if (prec > (int)sizeof(long)) prec=sizeof(long);
|
||||
if (format.width > width) width = format.width;
|
||||
|
||||
char byte = 0;
|
||||
if (format.flags & alt_flag) // lsb first (little endian)
|
||||
if (format.flags & alt_flag) // little endian (lsb first)
|
||||
{
|
||||
while (prec--)
|
||||
{
|
||||
@ -54,16 +56,34 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
value >>= 8;
|
||||
width--;
|
||||
}
|
||||
byte = (byte & 0x80) ? 0xFF : 0x00; // fill with sign
|
||||
if (format.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
byte = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill with sign
|
||||
byte = (byte & 0x80) ? 0xFF : 0x00;
|
||||
}
|
||||
while (width--)
|
||||
{
|
||||
output.append(byte);
|
||||
}
|
||||
}
|
||||
else // msb first (big endian)
|
||||
else // big endian (msb first)
|
||||
{
|
||||
byte = ((value >> (8 * (prec-1))) & 0x80) ? 0xFF : 0x00;
|
||||
while (width > prec) // fill with sign
|
||||
if (format.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
byte = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill with sign
|
||||
byte = ((value >> (8 * (prec-1))) & 0x80) ? 0xFF : 0x00;
|
||||
}
|
||||
while (width > prec)
|
||||
{
|
||||
output.append(byte);
|
||||
width--;
|
||||
@ -89,7 +109,7 @@ scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
}
|
||||
if (format.flags & alt_flag)
|
||||
{
|
||||
// little endian (sign extended)*/
|
||||
// little endian (lsb first)
|
||||
unsigned int shift = 0;
|
||||
while (--width && shift < sizeof(long)*8)
|
||||
{
|
||||
@ -98,14 +118,32 @@ scanLong(const StreamFormat& format, const char* input, long& value)
|
||||
}
|
||||
if (width == 0)
|
||||
{
|
||||
val |= ((signed char) input[length++]) << shift;
|
||||
if (format.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
val |= ((unsigned char) input[length++]) << shift;
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill with sign
|
||||
val |= ((signed char) input[length++]) << shift;
|
||||
}
|
||||
}
|
||||
length += width; // ignore upper bytes not fitting in long
|
||||
}
|
||||
else
|
||||
{
|
||||
// big endian (sign extended)*/
|
||||
val = (signed char) input[length++];
|
||||
// big endian (msb first)
|
||||
if (format.flags & zero_flag)
|
||||
{
|
||||
// fill with zero
|
||||
val = (unsigned char) input[length++];
|
||||
}
|
||||
else
|
||||
{
|
||||
// fill with sign
|
||||
val = (signed char) input[length++];
|
||||
}
|
||||
while (--width)
|
||||
{
|
||||
val <<= 8;
|
||||
|
136
src/RawFloatConverter.cc
Normal file
136
src/RawFloatConverter.cc
Normal file
@ -0,0 +1,136 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
|
||||
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is the raw format converter of StreamDevice. *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
|
||||
static int endian = 0;
|
||||
|
||||
// Raw Float Converter %R
|
||||
|
||||
class RawFloatConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
bool printDouble(const StreamFormat&, StreamBuffer&, double);
|
||||
int scanDouble(const StreamFormat&, const char*, double&);
|
||||
};
|
||||
|
||||
int RawFloatConverter::
|
||||
parse(const StreamFormat& format, StreamBuffer&,
|
||||
const char*&, bool)
|
||||
{
|
||||
// Find out byte order
|
||||
if (!endian) {
|
||||
union {long l; char c [sizeof(long)];} u;
|
||||
u.l=1;
|
||||
if (u.c[0]) { endian = 1234;} // little endian
|
||||
else if (u.c[sizeof(long)-1]) { endian = 4321;} // big endian
|
||||
else {
|
||||
error ("Cannot find out byte order for %%R format.\n");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Assume IEEE formats with 4 or 8 bytes (default: 4)
|
||||
if (format.width==0 || format.width==4 || format.width==8)
|
||||
return double_format;
|
||||
error ("Only width 4 or 8 allowed for %%R format.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RawFloatConverter::
|
||||
printDouble(const StreamFormat& format, StreamBuffer& output, double value)
|
||||
{
|
||||
int nbOfBytes;
|
||||
int n;
|
||||
union {
|
||||
double dval;
|
||||
float fval;
|
||||
char bytes[8];
|
||||
} buffer;
|
||||
|
||||
nbOfBytes = format.width;
|
||||
if (nbOfBytes == 0)
|
||||
nbOfBytes = 4;
|
||||
|
||||
if (nbOfBytes == 4)
|
||||
buffer.fval = (float)value;
|
||||
else
|
||||
buffer.dval = value;
|
||||
|
||||
if (!(format.flags & alt_flag) ^ (endian == 4321))
|
||||
{
|
||||
// swap if byte orders differ
|
||||
for (n = nbOfBytes-1; n >= 0; n--)
|
||||
{
|
||||
output.append(buffer.bytes[n]);
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < nbOfBytes; n++)
|
||||
{
|
||||
output.append(buffer.bytes[n]);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
int RawFloatConverter::
|
||||
scanDouble(const StreamFormat& format, const char* input, double& value)
|
||||
{
|
||||
int nbOfBytes;
|
||||
int i, n;
|
||||
|
||||
union {
|
||||
double dval;
|
||||
float fval;
|
||||
char bytes[8];
|
||||
} buffer;
|
||||
|
||||
nbOfBytes = format.width;
|
||||
if (nbOfBytes == 0)
|
||||
nbOfBytes = 4;
|
||||
|
||||
if (format.flags & skip_flag)
|
||||
{
|
||||
return(nbOfBytes); // just skip input
|
||||
}
|
||||
|
||||
if (!(format.flags & alt_flag) ^ (endian == 4321))
|
||||
{
|
||||
// swap if byte orders differ
|
||||
for (n = nbOfBytes-1, i = 0; n >= 0; n--, i++)
|
||||
{
|
||||
buffer.bytes[n] = input[i];
|
||||
}
|
||||
} else {
|
||||
for (n = 0; n < nbOfBytes; n++)
|
||||
{
|
||||
buffer.bytes[n] = input[n];
|
||||
}
|
||||
}
|
||||
|
||||
if (nbOfBytes == 4)
|
||||
value = buffer.fval;
|
||||
else
|
||||
value = buffer.dval;
|
||||
|
||||
return nbOfBytes;
|
||||
}
|
||||
|
||||
RegisterConverter (RawFloatConverter, "R");
|
125
src/RegexpConverter.cc
Normal file
125
src/RegexpConverter.cc
Normal file
@ -0,0 +1,125 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
|
||||
* (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is the regexp format converter of StreamDevice. *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
#include "string.h"
|
||||
#include "pcre.h"
|
||||
|
||||
// Perl regular expressions (PCRE) %/regexp/
|
||||
|
||||
/* Notes:
|
||||
- Memory for compiled regexp is allocated in parse but never freed.
|
||||
This should not be too much of a problem unless streamReload is
|
||||
called really often before the IOC is restarted. It is not a
|
||||
run-time leak.
|
||||
- A maximum of 9 subexpressions is supported. Only one of them can
|
||||
be the result of the match.
|
||||
- vxWorks and maybe other OS don't have a PCRE library. Provide one?
|
||||
*/
|
||||
|
||||
class RegexpConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
int scanString(const StreamFormat&, const char*, char*, size_t);
|
||||
};
|
||||
|
||||
int RegexpConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (!scanFormat)
|
||||
{
|
||||
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
if (fmt.prec > 9)
|
||||
{
|
||||
error("Subexpression index %d too big (>9)\n", fmt.prec);
|
||||
return false;
|
||||
}
|
||||
if (fmt.flags & (left_flag|space_flag|zero_flag|alt_flag))
|
||||
{
|
||||
error("Use of modifiers '-', ' ', '0', '#'"
|
||||
"not allowed with %%/regexp/ conversion\n");
|
||||
return false;
|
||||
}
|
||||
StreamBuffer pattern;
|
||||
while (*source != '/')
|
||||
{
|
||||
if (!*source) {
|
||||
error("Missing closing '/' after %%/ format conversion\n");
|
||||
return false;
|
||||
}
|
||||
if (*source == esc) {
|
||||
source++;
|
||||
pattern.printf("\\x%02x", *source++ & 0xFF);
|
||||
continue;
|
||||
}
|
||||
pattern.append(*source++);
|
||||
}
|
||||
source++;
|
||||
debug("regexp = \"%s\"\n", pattern());
|
||||
const char* errormsg;
|
||||
int eoffset;
|
||||
pcre* code = pcre_compile(pattern(), 0,
|
||||
&errormsg, &eoffset, NULL);
|
||||
if (!code)
|
||||
{
|
||||
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
|
||||
return false;
|
||||
}
|
||||
info.append(&code, sizeof(code));
|
||||
return string_format;
|
||||
}
|
||||
|
||||
int RegexpConverter::
|
||||
scanString(const StreamFormat& fmt, const char* input,
|
||||
char* value, size_t maxlen)
|
||||
{
|
||||
pcre* code;
|
||||
size_t len;
|
||||
int ovector[30];
|
||||
int rc;
|
||||
int subexpr = 0;
|
||||
|
||||
memcpy (&code, fmt.info, sizeof(code));
|
||||
|
||||
len = fmt.width > 0 ? fmt.width : strlen(input);
|
||||
subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
||||
rc = pcre_exec(code, NULL, input, len, 0, 0, ovector, 30);
|
||||
if (rc < 1) return -1;
|
||||
if (fmt.flags & skip_flag) return ovector[1];
|
||||
len = ovector[subexpr*2+1] - ovector[subexpr*2];
|
||||
if (len >= maxlen) {
|
||||
if (!(fmt.flags & sign_flag)) {
|
||||
debug("Matching string \"%s\" too long (%d>%d bytes)\n",
|
||||
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
|
||||
(int)len, (int)maxlen-1);
|
||||
return -1;
|
||||
}
|
||||
len = maxlen-1;
|
||||
}
|
||||
memcpy(value, input+ovector[subexpr*2], len);
|
||||
value[len]=0;
|
||||
return ovector[1];
|
||||
}
|
||||
|
||||
RegisterConverter (RegexpConverter, "/");
|
@ -46,7 +46,7 @@ init(const void* s, long minsize)
|
||||
else
|
||||
{
|
||||
// clear local buffer
|
||||
memset(buffer+minsize, 0, cap-minsize);
|
||||
memset(buffer, 0, cap);
|
||||
}
|
||||
if (s) {
|
||||
len = minsize;
|
||||
@ -54,13 +54,21 @@ init(const void* s, long minsize)
|
||||
}
|
||||
}
|
||||
|
||||
// How the buffer looks like:
|
||||
// |----free-----|####used####|-------free-------|
|
||||
///|<--- offs -->|<-- len --->|<- cap-offs-len ->|
|
||||
// 0 offs offs+len cap
|
||||
// |<-------------- minsize --------------->
|
||||
|
||||
|
||||
void StreamBuffer::
|
||||
grow(long minsize)
|
||||
{
|
||||
// make space for minsize + 1 (for termination) bytes
|
||||
char* newbuffer;
|
||||
long newcap;
|
||||
if (minsize > 10000)
|
||||
#ifdef EXPLODE
|
||||
if (minsize > 1000000)
|
||||
{
|
||||
// crude trap against infinite grow
|
||||
error ("StreamBuffer exploded growing from %ld to %ld chars. Exiting\n",
|
||||
@ -83,6 +91,7 @@ grow(long minsize)
|
||||
fprintf(stderr, "\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
if (minsize < cap)
|
||||
{
|
||||
// just move contents to start of buffer and clear end
|
||||
@ -133,7 +142,7 @@ find(const void* m, long size, long start) const
|
||||
start += len;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
if (start >= len-size+1) return -1; // find nothing after end
|
||||
if (start+size > len) return -1; // find nothing after end
|
||||
if (!m || size <= 0) return start; // find empty string at start
|
||||
const char* s = static_cast<const char*>(m);
|
||||
char* b = buffer+offs;
|
||||
@ -141,12 +150,12 @@ find(const void* m, long size, long start) const
|
||||
long i;
|
||||
while ((p = static_cast<char*>(memchr(p, s[0], b-p+len-size+1))))
|
||||
{
|
||||
i = 1;
|
||||
while (p[i] == s[i])
|
||||
for (i = 1; i < size; i++)
|
||||
{
|
||||
if (++i >= size) return p-b;
|
||||
if (p[i] != s[i]) goto next;
|
||||
}
|
||||
p++;
|
||||
return p-b;
|
||||
next: p++;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
@ -192,6 +201,7 @@ replace(long remstart, long remlen, const void* ins, long inslen)
|
||||
{
|
||||
// optimize remove of bufferstart
|
||||
offs += remlen;
|
||||
len -= remlen;
|
||||
return *this;
|
||||
}
|
||||
if (inslen < 0) inslen = 0;
|
||||
@ -263,9 +273,18 @@ StreamBuffer StreamBuffer::expand(long start, long length) const
|
||||
if (start < 0)
|
||||
{
|
||||
start += len;
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
end = length >= 0 ? start+length : len;
|
||||
if (length < 0)
|
||||
{
|
||||
start += length;
|
||||
length = -length;
|
||||
}
|
||||
if (start < 0)
|
||||
{
|
||||
length += start;
|
||||
start = 0;
|
||||
}
|
||||
end = start+length;
|
||||
if (end > len) end = len;
|
||||
StreamBuffer result((end-start)*2);
|
||||
start += offs;
|
||||
|
@ -29,32 +29,36 @@
|
||||
|
||||
class StreamBuffer
|
||||
{
|
||||
char local[64];
|
||||
long len;
|
||||
long cap;
|
||||
long offs;
|
||||
char* buffer;
|
||||
char local[64];
|
||||
|
||||
void grow(long);
|
||||
void init(const void*, long);
|
||||
void init(const void* s, long minsize);
|
||||
|
||||
void check(long size)
|
||||
{if (len+offs+size >= cap) grow(len+size);}
|
||||
|
||||
void grow(long minsize);
|
||||
|
||||
public:
|
||||
// Hints:
|
||||
// * Any index parameter (long) can be negative
|
||||
// meaning "count from end" (-1 is the last byte)
|
||||
// * Appending negative count deletes from end
|
||||
// * Any returned char* pointer becomes invalid when
|
||||
// the StreamBuffer is modified.
|
||||
// * End of StreamBuffer always contains 0x00 bytes
|
||||
// * Deleting from start and clearing is fast
|
||||
|
||||
StreamBuffer()
|
||||
{init(NULL, 0);}
|
||||
|
||||
StreamBuffer(const void*s, long size)
|
||||
StreamBuffer(const void* s, long size)
|
||||
{init(s, size);}
|
||||
|
||||
StreamBuffer(const char*s)
|
||||
StreamBuffer(const char* s)
|
||||
{init(s, s?strlen(s):0);}
|
||||
|
||||
StreamBuffer(const StreamBuffer& s)
|
||||
@ -68,10 +72,10 @@ public:
|
||||
|
||||
// operator (): get char* pointing to index
|
||||
const char* operator()(long index=0) const
|
||||
{buffer[offs+len]=0; return buffer+offs+(index<0?index+len:index);}
|
||||
{return buffer+offs+(index<0?index+len:index);}
|
||||
|
||||
char* operator()(long index=0)
|
||||
{buffer[offs+len]=0; return buffer+offs+(index<0?index+len:index);}
|
||||
{return buffer+offs+(index<0?index+len:index);}
|
||||
|
||||
// operator []: get byte at index
|
||||
char operator[](long index) const
|
||||
@ -103,12 +107,17 @@ public:
|
||||
// reserve: reserve size bytes of memory and return
|
||||
// pointer to that memory (for copying something to it)
|
||||
char* reserve(long size)
|
||||
{check(size); char* p=buffer+offs+len; len+=size; return p; }
|
||||
{check(size); char* p=buffer+offs+len; len+=size; return p;}
|
||||
|
||||
// append: append data at the end of the buffer
|
||||
StreamBuffer& append(char c)
|
||||
{check(1); buffer[offs+len++]=c; return *this;}
|
||||
|
||||
StreamBuffer& append(char c, long count)
|
||||
{if (count < 0) truncate(count);
|
||||
else {check(count); memset(buffer+offs+len, c, count); len+=count;}
|
||||
return *this;}
|
||||
|
||||
StreamBuffer& append(const void* s, long size);
|
||||
|
||||
StreamBuffer& append(const char* s)
|
||||
@ -116,6 +125,16 @@ public:
|
||||
|
||||
StreamBuffer& append(const StreamBuffer& s)
|
||||
{return append(s.buffer+s.offs, s.len);}
|
||||
|
||||
// operator += alias for set
|
||||
StreamBuffer& operator+=(char c)
|
||||
{return append(c);}
|
||||
|
||||
StreamBuffer& operator+=(const char* s)
|
||||
{return append(s);}
|
||||
|
||||
StreamBuffer& operator+=(const StreamBuffer& s)
|
||||
{return append(s);}
|
||||
|
||||
// set: clear buffer and fill with new data
|
||||
StreamBuffer& set(const void* s, long size)
|
||||
@ -127,7 +146,7 @@ public:
|
||||
StreamBuffer& set(const StreamBuffer& s)
|
||||
{clear(); return append(s.buffer+s.offs, s.len);}
|
||||
|
||||
// operator =: alias for set
|
||||
// operator = alias for set
|
||||
StreamBuffer& operator=(const char* s)
|
||||
{return set(s);}
|
||||
|
||||
@ -144,15 +163,16 @@ public:
|
||||
StreamBuffer& replace(long pos, long length, const StreamBuffer& s)
|
||||
{return replace(pos, length, s.buffer+s.offs, s.len);}
|
||||
|
||||
// replace: delete part of buffer
|
||||
// remove: delete from start/pos
|
||||
StreamBuffer& remove(long pos, long length)
|
||||
{return replace(pos, length, NULL, 0);}
|
||||
|
||||
// remove from start: no memset, no function call, fast!
|
||||
StreamBuffer& remove(long length)
|
||||
{if (length>len) length=len;
|
||||
offs+=length; len-=length; return *this;}
|
||||
|
||||
// replace: delete end of buffer
|
||||
// truncate: delete end of buffer
|
||||
StreamBuffer& truncate(long pos)
|
||||
{return replace(pos, len, NULL, 0);}
|
||||
|
||||
@ -188,17 +208,20 @@ public:
|
||||
long int find(const StreamBuffer& s, long start=0) const
|
||||
{return find(s.buffer+s.offs, s.len, start);}
|
||||
|
||||
// equals: returns true if first size bytes are equal
|
||||
bool equals(const void* s, long size) const
|
||||
// startswith: returns true if first size bytes are equal
|
||||
bool startswith(const void* s, long size) const
|
||||
{return len>=size ? memcmp(buffer+offs, s, size) == 0 : false;}
|
||||
|
||||
// equals: returns true if first string is equal (empty string match)
|
||||
bool equals(const char* s) const
|
||||
// startswith: returns true if first string is equal (empty string matches)
|
||||
bool startswith(const char* s) const
|
||||
{return len ? strcmp(buffer+offs, s) == 0 : !s || !*s;}
|
||||
|
||||
// expand: create copy of StreamBuffer where all nonprintable characters
|
||||
// are replaced by <xx> with xx being the hex code of the characters
|
||||
StreamBuffer expand(long start=0, long length=-1) const;
|
||||
StreamBuffer expand(long start, long length) const;
|
||||
|
||||
StreamBuffer expand(long start=0) const
|
||||
{return expand(start, len);}
|
||||
|
||||
// dump: debug function, like expand but also show the 'hidden' memory
|
||||
// before and after the real data. Uses colours.
|
||||
|
@ -20,7 +20,11 @@
|
||||
#include "StreamBusInterface.h"
|
||||
|
||||
const char* StreamIoStatusStr[] = {
|
||||
"StreamIoSuccess", "ioTimeout", "ioNoReply", "ioEnd", "ioFault"
|
||||
"StreamIoSuccess",
|
||||
"StreamIoTimeout",
|
||||
"StreamIoNoReply",
|
||||
"StreamIoEnd",
|
||||
"StreamIoFault"
|
||||
};
|
||||
|
||||
StreamBusInterfaceRegistrarBase* StreamBusInterfaceRegistrarBase::first;
|
||||
@ -89,7 +93,7 @@ connectRequest (unsigned long)
|
||||
}
|
||||
|
||||
bool StreamBusInterface::
|
||||
disconnect ()
|
||||
disconnectRequest ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -137,6 +141,11 @@ connectCallback(StreamIoStatus)
|
||||
{
|
||||
}
|
||||
|
||||
void StreamBusInterface::Client::
|
||||
disconnectCallback(StreamIoStatus)
|
||||
{
|
||||
}
|
||||
|
||||
long StreamBusInterface::Client::
|
||||
priority()
|
||||
{
|
||||
|
@ -42,6 +42,7 @@ public:
|
||||
const void* input, long size);
|
||||
virtual void eventCallback(StreamIoStatus status);
|
||||
virtual void connectCallback(StreamIoStatus status);
|
||||
virtual void disconnectCallback(StreamIoStatus status);
|
||||
virtual long priority();
|
||||
virtual const char* name() = 0;
|
||||
virtual const char* getInTerminator(size_t& length) = 0;
|
||||
@ -86,7 +87,7 @@ public:
|
||||
return businterface->connectRequest(timeout_ms);
|
||||
}
|
||||
bool busDisconnect() {
|
||||
return businterface->disconnect();
|
||||
return businterface->disconnectRequest();
|
||||
}
|
||||
};
|
||||
|
||||
@ -113,6 +114,8 @@ protected:
|
||||
{ client->eventCallback(status); }
|
||||
void connectCallback(StreamIoStatus status)
|
||||
{ client->connectCallback(status); }
|
||||
void disconnectCallback(StreamIoStatus status)
|
||||
{ client->disconnectCallback(status); }
|
||||
const char* getInTerminator(size_t& length)
|
||||
{ return client->getInTerminator(length); }
|
||||
const char* getOutTerminator(size_t& length)
|
||||
@ -132,7 +135,7 @@ protected:
|
||||
unsigned long replytimeout_ms); // supportsEvents() returns true
|
||||
virtual void release();
|
||||
virtual bool connectRequest(unsigned long connecttimeout_ms);
|
||||
virtual bool disconnect();
|
||||
virtual bool disconnectRequest();
|
||||
virtual void finish();
|
||||
|
||||
// pure virtual
|
||||
|
@ -30,7 +30,7 @@ const char* commandStr[] = { "end", "in", "out", "wait", "event", "exec",
|
||||
|
||||
inline const char* commandName(unsigned char i)
|
||||
{
|
||||
return i > exec_cmd ? "invalid" : commandStr[i];
|
||||
return i >= sizeof(commandStr)/sizeof(char*) ? "invalid" : commandStr[i];
|
||||
}
|
||||
|
||||
/// debug functions /////////////////////////////////////////////
|
||||
@ -39,7 +39,6 @@ static char* printCommands(StreamBuffer& buffer, const char* c)
|
||||
{
|
||||
unsigned long timeout;
|
||||
unsigned long eventnumber;
|
||||
unsigned short cmdlen;
|
||||
while (1)
|
||||
{
|
||||
switch(*c++)
|
||||
@ -67,7 +66,6 @@ static char* printCommands(StreamBuffer& buffer, const char* c)
|
||||
break;
|
||||
case exec_cmd:
|
||||
buffer.append(" exec \"");
|
||||
cmdlen = extract<unsigned short>(c);
|
||||
c = StreamProtocolParser::printString(buffer, c);
|
||||
buffer.append("\";\n");
|
||||
break;
|
||||
@ -325,7 +323,7 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
{
|
||||
if (!busSupportsEvent())
|
||||
{
|
||||
protocol->errorMsg(getLineNumber(command),
|
||||
error(getLineNumber(command), protocol->filename(),
|
||||
"Events not supported by businterface.\n");
|
||||
return false;
|
||||
}
|
||||
@ -339,7 +337,7 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
}
|
||||
if (*args != ')')
|
||||
{
|
||||
protocol->errorMsg(getLineNumber(command),
|
||||
error(getLineNumber(command), protocol->filename(),
|
||||
"Expect ')' instead of: '%s'\n", args);
|
||||
return false;
|
||||
}
|
||||
@ -360,7 +358,7 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
{
|
||||
buffer.append(exec_cmd);
|
||||
if (!protocol->compileString(buffer, args,
|
||||
NoFormat, this))
|
||||
PrintFormat, this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -383,13 +381,29 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
return true;
|
||||
}
|
||||
|
||||
protocol->errorMsg(getLineNumber(command),
|
||||
error(getLineNumber(command), protocol->filename(),
|
||||
"Unknown command name '%s'\n", command);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Run the protocol
|
||||
|
||||
// Input and events may come asynchonously from the bus driver
|
||||
// Especially in the sequence 'out "request"; in "reply";' the
|
||||
// reply can come before the 'in' command has actually started.
|
||||
// For asyncronous protocols, input can come at any time.
|
||||
// Thus, we must always accept input and event while the protocol
|
||||
// is running or when asyncronous mode is active.
|
||||
// Early input and event must be buffered until 'in' or 'event'
|
||||
// start. An 'out' command must discard any early input to avoid
|
||||
// problems with late input from aborted protocols.
|
||||
// Async mode ends on Abort or Error or if another command comes
|
||||
// after the asynchronous 'in' command.
|
||||
// Input can be discarded when it is not accepted any more, i.e.
|
||||
// at the end of syncronous protocols and when an asynchronous
|
||||
// mode ends (but not when 'in'-only protocol finishes normally).
|
||||
|
||||
|
||||
bool StreamCore::
|
||||
startProtocol(StartMode startMode)
|
||||
{
|
||||
@ -443,7 +457,7 @@ finishProtocol(ProtocolResult status)
|
||||
flags & WaitPending ? "timerCallback()" : "");
|
||||
status = Fault;
|
||||
}
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
if (runningHandler)
|
||||
{
|
||||
// get original error status
|
||||
@ -488,6 +502,7 @@ finishProtocol(ProtocolResult status)
|
||||
break;
|
||||
default:
|
||||
// get rid of all the rubbish whe might have collected
|
||||
unparsedInput = false;
|
||||
inputBuffer.clear();
|
||||
handler = NULL;
|
||||
}
|
||||
@ -518,6 +533,7 @@ finishProtocol(ProtocolResult status)
|
||||
flags &= ~BusOwner;
|
||||
}
|
||||
busFinish();
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
protocolFinishHook(status);
|
||||
}
|
||||
|
||||
@ -539,16 +555,16 @@ evalCommand()
|
||||
switch (*commandIndex++)
|
||||
{
|
||||
case out_cmd:
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
return evalOut();
|
||||
case in_cmd:
|
||||
flags &= ~AcceptEvent;
|
||||
//// flags &= ~AcceptEvent;
|
||||
return evalIn();
|
||||
case wait_cmd:
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
return evalWait();
|
||||
case event_cmd:
|
||||
flags &= ~AcceptInput;
|
||||
//// flags &= ~AcceptInput;
|
||||
return evalEvent();
|
||||
case exec_cmd:
|
||||
return evalExec();
|
||||
@ -573,9 +589,9 @@ evalCommand()
|
||||
bool StreamCore::
|
||||
evalOut()
|
||||
{
|
||||
inputBuffer.clear(); // flush all unread input
|
||||
// flush all unread input
|
||||
unparsedInput = false;
|
||||
outputLine.clear();
|
||||
inputBuffer.clear();
|
||||
if (!formatOutput())
|
||||
{
|
||||
finishProtocol(FormatError);
|
||||
@ -616,16 +632,19 @@ formatOutput()
|
||||
char command;
|
||||
const char* fieldName = NULL;
|
||||
const char* formatstring;
|
||||
int formatstringlen;
|
||||
|
||||
outputLine.clear();
|
||||
while ((command = *commandIndex++) != StreamProtocolParser::eos)
|
||||
{
|
||||
switch (command)
|
||||
{
|
||||
case StreamProtocolParser::format_field:
|
||||
{
|
||||
debug("StreamCore::formatOutput(%s): StreamProtocolParser::format_field\n",
|
||||
debug("StreamCore::formatOutput(%s): StreamProtocolParser::redirect_format\n",
|
||||
name());
|
||||
// code layout:
|
||||
// field <StreamProtocolParser::eos> addrlen AddressStructure formatstring <StreamProtocolParser::eos> StreamFormat [info]
|
||||
// field <eos> addrlen AddressStructure formatstring <eos> StreamFormat [info]
|
||||
fieldName = commandIndex;
|
||||
commandIndex += strlen(commandIndex)+1;
|
||||
unsigned short addrlen = extract<unsigned short>(commandIndex);
|
||||
@ -635,14 +654,25 @@ formatOutput()
|
||||
case StreamProtocolParser::format:
|
||||
{
|
||||
// code layout:
|
||||
// formatstring <StreamProtocolParser::eos> StreamFormat [info]
|
||||
// formatstring <eos> StreamFormat [info]
|
||||
formatstring = commandIndex;
|
||||
while (*commandIndex++); // jump after <StreamProtocolParser::eos>
|
||||
// jump after <eos>
|
||||
while (*commandIndex)
|
||||
{
|
||||
if (*commandIndex == esc) commandIndex++;
|
||||
commandIndex++;
|
||||
}
|
||||
formatstringlen = commandIndex-formatstring;
|
||||
commandIndex++;
|
||||
|
||||
StreamFormat fmt = extract<StreamFormat>(commandIndex);
|
||||
fmt.info = commandIndex; // point to info string
|
||||
commandIndex += fmt.infolen;
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::formatOutput(%s): format = %%%s\n",
|
||||
name(), formatstring);
|
||||
name(), StreamBuffer(formatstring, formatstringlen).expand()());
|
||||
#endif
|
||||
|
||||
if (fmt.type == pseudo_format)
|
||||
{
|
||||
if (!StreamFormatConverter::find(fmt.conv)->
|
||||
@ -657,18 +687,22 @@ formatOutput()
|
||||
flags &= ~Separator;
|
||||
if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL))
|
||||
{
|
||||
if (fieldName)
|
||||
StreamBuffer formatstr(formatstring, formatstringlen);
|
||||
if (fieldAddress)
|
||||
error("%s: Cannot format field '%s' with '%%%s'\n",
|
||||
name(), fieldName, formatstring);
|
||||
name(), fieldName, formatstr.expand()());
|
||||
else
|
||||
error("%s: Cannot format value with '%%%s'\n",
|
||||
name(), formatstring);
|
||||
name(), formatstr.expand()());
|
||||
return false;
|
||||
}
|
||||
fieldAddress.clear();
|
||||
fieldName = NULL;
|
||||
continue;
|
||||
}
|
||||
case StreamProtocolParser::whitespace:
|
||||
outputLine.append(' ');
|
||||
case StreamProtocolParser::skip:
|
||||
continue;
|
||||
case esc:
|
||||
// escaped literal byte
|
||||
command = *commandIndex++;
|
||||
@ -690,12 +724,21 @@ printSeparator()
|
||||
}
|
||||
if (!separator) return;
|
||||
long i = 0;
|
||||
if (separator[0] == ' ') i++; // ignore leading space
|
||||
for (; i < separator.length(); i++)
|
||||
{
|
||||
if (separator[i] == StreamProtocolParser::skip) continue; // wildcard
|
||||
if (separator[i] == esc) i++; // escaped literal byte
|
||||
outputLine.append(separator[i]);
|
||||
switch (separator[i])
|
||||
{
|
||||
case StreamProtocolParser::whitespace:
|
||||
outputLine.append(' '); // print single space
|
||||
case StreamProtocolParser::skip:
|
||||
continue;
|
||||
case esc:
|
||||
// escaped literal byte
|
||||
i++;
|
||||
default:
|
||||
// literal byte
|
||||
outputLine.append(separator[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -716,6 +759,8 @@ printValue(const StreamFormat& fmt, long value)
|
||||
name(), value);
|
||||
return false;
|
||||
}
|
||||
debug("StreamCore::printValue(%s, long): \"%s\"\n",
|
||||
name(), outputLine.expand()());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -736,6 +781,8 @@ printValue(const StreamFormat& fmt, double value)
|
||||
name(), value);
|
||||
return false;
|
||||
}
|
||||
debug("StreamCore::printValue(%s, double): \"%s\"\n",
|
||||
name(), outputLine.expand()());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -757,6 +804,8 @@ printValue(const StreamFormat& fmt, char* value)
|
||||
name(), buffer.expand()());
|
||||
return false;
|
||||
}
|
||||
debug("StreamCore::printValue(%s, char*): \"%s\"\n",
|
||||
name(), outputLine.expand()());
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -886,12 +935,17 @@ readCallback(StreamIoStatus status,
|
||||
|
||||
if (!(flags & AcceptInput))
|
||||
{
|
||||
error("StreamCore::readCallback(%s) called unexpectedly\n",
|
||||
name());
|
||||
#ifdef NO_TEMPORARY
|
||||
error("StreamCore::readCallback(%s, %s) called unexpectedly\n",
|
||||
name(), StreamIoStatusStr[status]);
|
||||
#else
|
||||
error("StreamCore::readCallback(%s, %s, \"%s\") called unexpectedly\n",
|
||||
name(), StreamIoStatusStr[status],
|
||||
StreamBuffer(input, size).expand()());
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
flags &= ~AcceptInput;
|
||||
unparsedInput = false;
|
||||
//// flags &= ~AcceptInput;
|
||||
switch (status)
|
||||
{
|
||||
case StreamIoTimeout:
|
||||
@ -921,7 +975,11 @@ readCallback(StreamIoStatus status,
|
||||
finishProtocol(ReplyTimeout);
|
||||
return 0;
|
||||
case StreamIoFault:
|
||||
error("%s: I/O error when reading from device\n", name());
|
||||
error("%s: I/O error after reading %ld byte%s: \"%s%s\"\n",
|
||||
name(),
|
||||
inputBuffer.length(), inputBuffer.length()==1 ? "" : "s",
|
||||
inputBuffer.length() > 20 ? "..." : "",
|
||||
inputBuffer.expand(-20,20)());
|
||||
finishProtocol(Fault);
|
||||
return 0;
|
||||
}
|
||||
@ -944,10 +1002,36 @@ readCallback(StreamIoStatus status,
|
||||
if (inTerminator)
|
||||
{
|
||||
// look for terminator
|
||||
end = inputBuffer.find(inTerminator);
|
||||
if (end >= 0) termlen = inTerminator.length();
|
||||
debug("StreamCore::readCallback(%s) inTerminator %sfound\n",
|
||||
name(), end >= 0 ? "" : "not ");
|
||||
// performance issue for long inputs that come in chunks:
|
||||
// do not parse old chunks again or performance decreases to O(n^2)
|
||||
// but make sure to get all terminators in multi-line input
|
||||
|
||||
long start;
|
||||
if (unparsedInput)
|
||||
{
|
||||
// multi-line input sets 'unparsedInput' and removes the line
|
||||
// remaining unparsed lines are left at start of inputBuffer
|
||||
start = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// long line does not set 'unparsedInput' but keeps
|
||||
// already parsed chunks in inputBuffer
|
||||
// start parsing at beginning of new data
|
||||
// but beware of split terminators
|
||||
start = inputBuffer.length() - size - inTerminator.length();
|
||||
if (start < 0) start = 0;
|
||||
}
|
||||
end = inputBuffer.find(inTerminator, start);
|
||||
if (end >= 0)
|
||||
{
|
||||
termlen = inTerminator.length();
|
||||
debug("StreamCore::readCallback(%s) inTerminator %s at position %ld\n",
|
||||
name(), inTerminator.expand()(), end);
|
||||
} else {
|
||||
debug("StreamCore::readCallback(%s) inTerminator %s not found\n",
|
||||
name(), inTerminator.expand()());
|
||||
}
|
||||
}
|
||||
if (status == StreamIoEnd && end < 0)
|
||||
{
|
||||
@ -969,6 +1053,12 @@ readCallback(StreamIoStatus status,
|
||||
end = maxInput;
|
||||
termlen = 0;
|
||||
}
|
||||
if (end >= 0)
|
||||
{
|
||||
// be forgiving with timeout because end is found
|
||||
if (status == StreamIoTimeout)
|
||||
status = StreamIoEnd;
|
||||
}
|
||||
if (end < 0)
|
||||
{
|
||||
// no end found
|
||||
@ -985,23 +1075,23 @@ readCallback(StreamIoStatus status,
|
||||
}
|
||||
// try to parse what we got
|
||||
end = inputBuffer.length();
|
||||
if (!(flags & AsyncMode))
|
||||
if (flags & AsyncMode)
|
||||
{
|
||||
debug("StreamCore::readCallback(%s) async timeout: just restart\n",
|
||||
name());
|
||||
unparsedInput = false;
|
||||
inputBuffer.clear();
|
||||
commandIndex = commandStart;
|
||||
evalIn();
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: Timeout after reading %ld byte%s \"%s%s\"\n",
|
||||
name(), end, end==1 ? "" : "s", end > 20 ? "..." : "",
|
||||
inputBuffer.expand(-20)());
|
||||
}
|
||||
}
|
||||
|
||||
if (status == StreamIoTimeout && (flags & AsyncMode))
|
||||
{
|
||||
debug("StreamCore::readCallback(%s) async timeout: just restart\n",
|
||||
name());
|
||||
inputBuffer.clear();
|
||||
commandIndex = commandStart;
|
||||
evalIn();
|
||||
return 0;
|
||||
}
|
||||
|
||||
inputLine.set(inputBuffer(), end);
|
||||
debug("StreamCore::readCallback(%s) input line: \"%s\"\n",
|
||||
@ -1014,6 +1104,10 @@ readCallback(StreamIoStatus status,
|
||||
name(), inputBuffer.expand()());
|
||||
unparsedInput = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
unparsedInput = false;
|
||||
}
|
||||
if (!matches)
|
||||
{
|
||||
if (status == StreamIoTimeout)
|
||||
@ -1042,7 +1136,7 @@ readCallback(StreamIoStatus status,
|
||||
return 0;
|
||||
}
|
||||
// end input mode and do next command
|
||||
flags &= ~(AsyncMode|AcceptInput);
|
||||
//// flags &= ~(AsyncMode|AcceptInput);
|
||||
// -- should we tell someone that input has finished? --
|
||||
evalCommand();
|
||||
return 0;
|
||||
@ -1056,7 +1150,9 @@ matchInput()
|
||||
is installed and starts with 'in' (then we reparse the input).
|
||||
*/
|
||||
char command;
|
||||
const char* fieldName = NULL;
|
||||
const char* formatstring;
|
||||
int formatstringlen;
|
||||
|
||||
consumedInput = 0;
|
||||
|
||||
@ -1068,6 +1164,7 @@ matchInput()
|
||||
{
|
||||
// code layout:
|
||||
// field <StreamProtocolParser::eos> addrlen AddressStructure formatstring <StreamProtocolParser::eos> StreamFormat [info]
|
||||
fieldName = commandIndex;
|
||||
commandIndex += strlen(commandIndex)+1;
|
||||
unsigned short addrlen = extract<unsigned short>(commandIndex);
|
||||
fieldAddress.set(commandIndex, addrlen);
|
||||
@ -1079,11 +1176,23 @@ matchInput()
|
||||
// code layout:
|
||||
// formatstring <eos> StreamFormat [info]
|
||||
formatstring = commandIndex;
|
||||
while (*commandIndex++ != StreamProtocolParser::eos); // jump after <eos>
|
||||
// jump after <eos>
|
||||
while (*commandIndex)
|
||||
{
|
||||
if (*commandIndex == esc) commandIndex++;
|
||||
commandIndex++;
|
||||
}
|
||||
formatstringlen = commandIndex-formatstring;
|
||||
commandIndex++;
|
||||
|
||||
StreamFormat fmt = extract<StreamFormat>(commandIndex);
|
||||
fmt.info = commandIndex;
|
||||
fmt.info = commandIndex; // point to info string
|
||||
commandIndex += fmt.infolen;
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::matchInput(%s): format = %%%s\n",
|
||||
name(), StreamBuffer(formatstring, formatstringlen).expand()());
|
||||
#endif
|
||||
|
||||
if (fmt.flags & skip_flag || fmt.type == pseudo_format)
|
||||
{
|
||||
long ldummy;
|
||||
@ -1114,17 +1223,74 @@ matchInput()
|
||||
return false;
|
||||
}
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (fmt.flags & default_flag)
|
||||
{
|
||||
consumed = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" does not match format %%%s\n",
|
||||
name(), inputLine.expand(consumedInput, 20)(),
|
||||
inputLine.length()-consumedInput > 20 ? "..." : "",
|
||||
formatstring);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
consumedInput += consumed;
|
||||
break;
|
||||
}
|
||||
if (fmt.flags & compare_flag)
|
||||
{
|
||||
outputLine.clear();
|
||||
flags &= ~Separator;
|
||||
if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL))
|
||||
{
|
||||
StreamBuffer formatstr(formatstring, formatstringlen);
|
||||
if (fieldAddress)
|
||||
error("%s: Cannot format field '%s' with '%%%s'\n",
|
||||
name(), fieldName, formatstr.expand()());
|
||||
else
|
||||
error("%s: Cannot format value with '%%%s'\n",
|
||||
name(), formatstr.expand()());
|
||||
return false;
|
||||
}
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::matchInput(%s): compare \"%s\" with \"%s\"\n",
|
||||
name(), inputLine.expand(consumedInput,
|
||||
outputLine.length())(), outputLine.expand()());
|
||||
#endif
|
||||
if (inputLine.length() - consumedInput < outputLine.length())
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" does not match format %%%s\n",
|
||||
name(), inputLine.expand(consumedInput, 20)(),
|
||||
inputLine.length()-consumedInput > 20 ? "..." : "",
|
||||
formatstring);
|
||||
error("%s: Input \"%s%s\" too short."
|
||||
" No match for format %%%s (\"%s\")\n",
|
||||
name(),
|
||||
inputLine.length() > 20 ? "..." : "",
|
||||
inputLine.expand(-20)(),
|
||||
formatstring,
|
||||
outputLine.expand()());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
consumedInput += consumed;
|
||||
if (!outputLine.startswith(inputLine(consumedInput),outputLine.length()))
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" does not match"
|
||||
" format %%%s (\"%s\")\n",
|
||||
name(), inputLine.expand(consumedInput, 20)(),
|
||||
inputLine.length()-consumedInput > 20 ? "..." : "",
|
||||
formatstring,
|
||||
outputLine.expand()());
|
||||
}
|
||||
return false;
|
||||
}
|
||||
consumedInput += outputLine.length();
|
||||
break;
|
||||
}
|
||||
flags &= ~Separator;
|
||||
@ -1138,19 +1304,23 @@ matchInput()
|
||||
inputLine.length()-consumedInput > 20 ? "..." : "",
|
||||
formatstring);
|
||||
else
|
||||
error("%s: Can't scan value with format %%%s\n",
|
||||
name(), formatstring);
|
||||
error("%s: Format %%%s has data type %s which does not match this variable.\n",
|
||||
name(), formatstring, StreamFormatTypeStr[fmt.type] );
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// matchValue() has already removed consumed bytes from inputBuffer
|
||||
fieldAddress = NULL;
|
||||
fieldAddress.clear();
|
||||
break;
|
||||
}
|
||||
case StreamProtocolParser::skip:
|
||||
// ignore next input byte
|
||||
consumedInput++;
|
||||
break;
|
||||
case StreamProtocolParser::whitespace:
|
||||
// any number of whitespace (including 0)
|
||||
while (isspace(inputLine[consumedInput])) consumedInput++;
|
||||
break;
|
||||
case esc:
|
||||
// escaped literal byte
|
||||
command = *commandIndex++;
|
||||
@ -1162,12 +1332,14 @@ matchInput()
|
||||
while (commandIndex[i] >= ' ') i++;
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" too short."
|
||||
" No match for \"%s\"\n",
|
||||
error("%s: Input \"%s%s\" too short.",
|
||||
name(),
|
||||
inputLine.length() > 20 ? "..." : "",
|
||||
inputLine.expand(-20)(),
|
||||
inputLine.expand(-20)());
|
||||
#ifndef NO_TEMPORARY
|
||||
error(" No match for \"%s\"\n",
|
||||
StreamBuffer(commandIndex-1,i+1).expand()());
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1185,10 +1357,12 @@ matchInput()
|
||||
consumedInput,
|
||||
consumedInput==1 ? "" : "s");
|
||||
|
||||
#ifndef NO_TEMPORARY
|
||||
error("%s: got \"%s\" where \"%s\" was expected\n",
|
||||
name(),
|
||||
inputLine.expand(consumedInput, 20)(),
|
||||
StreamBuffer(commandIndex-1,i+1).expand()());
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -1224,36 +1398,45 @@ matchInput()
|
||||
bool StreamCore::
|
||||
matchSeparator()
|
||||
{
|
||||
// called before value is read, first value has Separator flag cleared
|
||||
// for second and next value set Separator flag
|
||||
|
||||
if (!separator) {
|
||||
// empty separator matches
|
||||
return true;
|
||||
}
|
||||
if (!(flags & Separator))
|
||||
{
|
||||
// before first element, don't expect separator yet
|
||||
flags |= Separator;
|
||||
return true;
|
||||
}
|
||||
if (!separator) return true;
|
||||
long i = 0;
|
||||
if (separator[0] == ' ')
|
||||
long i;
|
||||
long j = consumedInput;
|
||||
for (i = 0; i < separator.length(); i++)
|
||||
{
|
||||
i++;
|
||||
// skip leading whitespaces
|
||||
while (isspace(inputLine[consumedInput++]));
|
||||
switch (separator[i])
|
||||
{
|
||||
case StreamProtocolParser::skip:
|
||||
j++;
|
||||
continue;
|
||||
case StreamProtocolParser::whitespace:
|
||||
while (isspace(inputLine[j])) j++;
|
||||
continue;
|
||||
case esc:
|
||||
i++;
|
||||
default:
|
||||
if (separator[i] != inputLine[j])
|
||||
{
|
||||
// no match
|
||||
// don't complain here, just return false
|
||||
return false;
|
||||
}
|
||||
j++;
|
||||
}
|
||||
}
|
||||
for (; i < separator.length(); i++,consumedInput++)
|
||||
{
|
||||
if (!inputLine[consumedInput]) return false;
|
||||
if (separator[i] == StreamProtocolParser::skip) continue; // wildcard
|
||||
if (separator[i] == esc) i++; // escaped literal byte
|
||||
if (separator[i] != inputLine[consumedInput]) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool StreamCore::
|
||||
scanSeparator()
|
||||
{
|
||||
// for compatibility only
|
||||
// read and remove separator
|
||||
if (!matchSeparator()) return false;
|
||||
flags &= ~Separator;
|
||||
// separator successfully read
|
||||
consumedInput = j;
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1272,8 +1455,16 @@ scanValue(const StreamFormat& fmt, long& value)
|
||||
scanLong(fmt, inputLine(consumedInput), value);
|
||||
debug("StreamCore::scanValue(%s, format=%%%c, long) input=\"%s\"\n",
|
||||
name(), fmt.conv, inputLine.expand(consumedInput)());
|
||||
if (consumed < 0 ||
|
||||
consumed > inputLine.length()-consumedInput) return -1;
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (fmt.flags & default_flag)
|
||||
{
|
||||
value = 0;
|
||||
consumed = 0;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %li\n",
|
||||
name(), value);
|
||||
flags |= GotValue;
|
||||
@ -1294,9 +1485,17 @@ scanValue(const StreamFormat& fmt, double& value)
|
||||
long consumed = StreamFormatConverter::find(fmt.conv)->
|
||||
scanDouble(fmt, inputLine(consumedInput), value);
|
||||
debug("StreamCore::scanValue(%s, format=%%%c, double) input=\"%s\"\n",
|
||||
name(), fmt.conv, inputLine.expand(consumedInput)());
|
||||
if (consumed < 0 ||
|
||||
consumed > inputLine.length()-consumedInput) return -1;
|
||||
name(), fmt.conv, inputLine.expand(consumedInput, 20)());
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (fmt.flags & default_flag)
|
||||
{
|
||||
value = 0.0;
|
||||
consumed = 0;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %#g\n",
|
||||
name(), value);
|
||||
flags |= GotValue;
|
||||
@ -1319,11 +1518,19 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
|
||||
scanString(fmt, inputLine(consumedInput), value, maxlen);
|
||||
debug("StreamCore::scanValue(%s, format=%%%c, char*, maxlen=%ld) input=\"%s\"\n",
|
||||
name(), fmt.conv, maxlen, inputLine.expand(consumedInput)());
|
||||
if (consumed < 0 ||
|
||||
consumed > inputLine.length()-consumedInput) return -1;
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (fmt.flags & default_flag)
|
||||
{
|
||||
value[0] = 0;
|
||||
consumed = 0;
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
|
||||
name(), StreamBuffer(value, consumed).expand()());
|
||||
name(), StreamBuffer(value, maxlen).expand()());
|
||||
#endif
|
||||
flags |= GotValue;
|
||||
return consumed;
|
||||
@ -1432,8 +1639,8 @@ timerCallback()
|
||||
bool StreamCore::
|
||||
evalExec()
|
||||
{
|
||||
outputLine.clear();
|
||||
formatOutput();
|
||||
debug ("StreamCore::evalExec: command = \"%s\"\n", outputLine.expand()());
|
||||
// release bus
|
||||
if (flags & BusOwner)
|
||||
{
|
||||
@ -1510,8 +1717,23 @@ bool StreamCore::evalDisconnect()
|
||||
finishProtocol(Fault);
|
||||
return false;
|
||||
}
|
||||
evalCommand();
|
||||
return true;
|
||||
}
|
||||
|
||||
void StreamCore::
|
||||
disconnectCallback(StreamIoStatus status)
|
||||
{
|
||||
switch (status)
|
||||
{
|
||||
case StreamIoSuccess:
|
||||
evalCommand();
|
||||
return;
|
||||
default:
|
||||
error("%s: Disconnect failed\n",
|
||||
name());
|
||||
finishProtocol(Fault);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#include "streamReferences"
|
||||
|
@ -139,7 +139,6 @@ protected:
|
||||
long scanValue(const StreamFormat& format, double& value);
|
||||
long scanValue(const StreamFormat& format, char* value, long maxlen);
|
||||
long scanValue(const StreamFormat& format);
|
||||
bool scanSeparator(); // disencouraged
|
||||
|
||||
StreamBuffer protocolname;
|
||||
unsigned long lockTimeout;
|
||||
@ -200,6 +199,7 @@ protected:
|
||||
void eventCallback(StreamIoStatus status);
|
||||
void execCallback(StreamIoStatus status);
|
||||
void connectCallback(StreamIoStatus status);
|
||||
void disconnectCallback(StreamIoStatus status);
|
||||
const char* getInTerminator(size_t& length);
|
||||
const char* getOutTerminator(size_t& length);
|
||||
|
||||
|
@ -41,6 +41,7 @@ extern "C" {
|
||||
|
||||
#include <semLib.h>
|
||||
#include <wdLib.h>
|
||||
#include <taskLib.h>
|
||||
|
||||
extern DBBASE *pdbbase;
|
||||
|
||||
@ -52,6 +53,7 @@ extern DBBASE *pdbbase;
|
||||
#include <epicsMutex.h>
|
||||
#include <epicsEvent.h>
|
||||
#include <epicsTime.h>
|
||||
#include <epicsThread.h>
|
||||
#include <registryFunction.h>
|
||||
#include <iocsh.h>
|
||||
|
||||
@ -76,10 +78,12 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
|
||||
enum MoreFlags {
|
||||
// 0x00FFFFFF used by StreamCore
|
||||
InDestructor = 0x0100000,
|
||||
ValueReceived = 0x0200000
|
||||
ValueReceived = 0x0200000,
|
||||
Aborted = 0x0400000
|
||||
};
|
||||
|
||||
extern "C" void streamExecuteCommand(CALLBACK *pcallback);
|
||||
extern "C" void streamRecordProcessCallback(CALLBACK *pcallback);
|
||||
extern "C" long streamReload(char* recordname);
|
||||
|
||||
class Stream : protected StreamCore
|
||||
@ -108,6 +112,7 @@ class Stream : protected StreamCore
|
||||
long currentValueLength;
|
||||
IOSCANPVT ioscanpvt;
|
||||
CALLBACK commandCallback;
|
||||
CALLBACK processCallback;
|
||||
|
||||
|
||||
#ifdef EPICS_3_14
|
||||
@ -118,7 +123,7 @@ class Stream : protected StreamCore
|
||||
#endif
|
||||
|
||||
// StreamCore methods
|
||||
// void protocolStartHook(); // Nothing to do here?
|
||||
void protocolStartHook();
|
||||
void protocolFinishHook(ProtocolResult);
|
||||
void startTimer(unsigned long timeout);
|
||||
bool getFieldAddress(const char* fieldname,
|
||||
@ -131,6 +136,7 @@ class Stream : protected StreamCore
|
||||
void releaseMutex();
|
||||
bool execute();
|
||||
friend void streamExecuteCommand(CALLBACK *pcallback);
|
||||
friend void streamRecordProcessCallback(CALLBACK *pcallback);
|
||||
|
||||
// Stream Epics methods
|
||||
long initRecord();
|
||||
@ -150,7 +156,6 @@ class Stream : protected StreamCore
|
||||
friend long streamPrintf(dbCommon *record, format_t *format, ...);
|
||||
friend long streamScanfN(dbCommon *record, format_t *format,
|
||||
void*, size_t maxStringSize);
|
||||
friend long streamScanSep(dbCommon *record);
|
||||
friend long streamReload(char* recordname);
|
||||
|
||||
public:
|
||||
@ -167,16 +172,6 @@ epicsExportAddress(int, streamDebug);
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef MEMGUARD
|
||||
static const iocshFuncDef memguardReportDef =
|
||||
{ "memguardReport", 0, NULL };
|
||||
|
||||
static void memguardReportFunc (const iocshArgBuf *args)
|
||||
{
|
||||
memguardReport();
|
||||
}
|
||||
#endif
|
||||
|
||||
// for subroutine record
|
||||
extern "C" long streamReloadSub()
|
||||
{
|
||||
@ -245,9 +240,6 @@ extern "C" void streamReloadFunc (const iocshArgBuf *args)
|
||||
|
||||
static void streamRegistrar ()
|
||||
{
|
||||
#ifdef MEMGUARD
|
||||
iocshRegister(&memguardReportDef, memguardReportFunc);
|
||||
#endif
|
||||
iocshRegister(&reloadDef, streamReloadFunc);
|
||||
// make streamReload available for subroutine records
|
||||
registryFunctionAdd("streamReload",
|
||||
@ -280,12 +272,15 @@ epicsExportAddress(drvet, stream);
|
||||
|
||||
void streamEpicsPrintTimestamp(char* buffer, int size)
|
||||
{
|
||||
int tlen;
|
||||
epicsTime tm = epicsTime::getCurrent();
|
||||
tm.strftime(buffer, size, "%Y/%m/%d %H:%M:%S.%03f");
|
||||
tlen = tm.strftime(buffer, size, "%Y/%m/%d %H:%M:%S.%03f");
|
||||
sprintf(buffer+tlen, " %.*s", size-tlen-2, epicsThreadGetNameSelf());
|
||||
}
|
||||
#else
|
||||
void streamEpicsPrintTimestamp(char* buffer, int size)
|
||||
{
|
||||
int tlen;
|
||||
char* c;
|
||||
TS_STAMP tm;
|
||||
tsLocalTime (&tm);
|
||||
@ -294,6 +289,8 @@ void streamEpicsPrintTimestamp(char* buffer, int size)
|
||||
if (c) {
|
||||
c[4] = 0;
|
||||
}
|
||||
tlen = strlen(buffer);
|
||||
sprintf(buffer+tlen, " %.*s", size-tlen-2, taskName(0));
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -310,7 +307,7 @@ report(int interest)
|
||||
printf(" %s\n", interface.name());
|
||||
++interface;
|
||||
}
|
||||
|
||||
|
||||
if (interest < 1) return OK;
|
||||
printf(" registered converters:\n");
|
||||
StreamFormatConverter* converter;
|
||||
@ -323,7 +320,7 @@ report(int interest)
|
||||
printf(" %%%c %s\n", c, converter->name());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Stream* pstream;
|
||||
printf(" connected records:\n");
|
||||
for (pstream = static_cast<Stream*>(first); pstream;
|
||||
@ -341,7 +338,6 @@ report(int interest)
|
||||
pstream->ioLink->value.instio.string);
|
||||
}
|
||||
}
|
||||
StreamPrintTimestampFunction = streamEpicsPrintTimestamp;
|
||||
return OK;
|
||||
}
|
||||
|
||||
@ -478,20 +474,11 @@ long streamPrintf(dbCommon *record, format_t *format, ...)
|
||||
return success ? OK : ERROR;
|
||||
}
|
||||
|
||||
long streamScanSep(dbCommon* record)
|
||||
{
|
||||
// depreciated
|
||||
debug("streamScanSep(%s)\n", record->name);
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream) return ERROR;
|
||||
return pstream->scanSeparator() ? OK : ERROR;
|
||||
}
|
||||
|
||||
long streamScanfN(dbCommon* record, format_t *format,
|
||||
void* value, size_t maxStringSize)
|
||||
{
|
||||
debug("streamScanfN(%s,format=%%%c,maxStringSize=%d)\n",
|
||||
record->name, format->priv->conv, maxStringSize);
|
||||
debug("streamScanfN(%s,format=%%%c,maxStringSize=%ld)\n",
|
||||
record->name, format->priv->conv, (long)maxStringSize);
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream) return ERROR;
|
||||
if (!pstream->scan(format, value, maxStringSize))
|
||||
@ -525,6 +512,8 @@ Stream(dbCommon* _record, struct link *ioLink,
|
||||
#endif
|
||||
callbackSetCallback(streamExecuteCommand, &commandCallback);
|
||||
callbackSetUser(this, &commandCallback);
|
||||
callbackSetCallback(streamRecordProcessCallback, &processCallback);
|
||||
callbackSetUser(this, &processCallback);
|
||||
status = ERROR;
|
||||
convert = DO_NOT_CONVERT;
|
||||
ioscanpvt = NULL;
|
||||
@ -561,14 +550,14 @@ initRecord()
|
||||
// scan link parameters: filename protocol busname addr busparam
|
||||
// It is safe to call this function again with different
|
||||
// link text or different protocol file.
|
||||
|
||||
|
||||
char filename[80];
|
||||
char protocol[80];
|
||||
char busname[80];
|
||||
int addr = -1;
|
||||
char busparam[80];
|
||||
int n;
|
||||
|
||||
|
||||
if (ioLink->type != INST_IO)
|
||||
{
|
||||
error("%s: Wrong link type %s\n", name(),
|
||||
@ -790,6 +779,12 @@ expire(CALLBACK *pcallback)
|
||||
|
||||
// StreamCore virtual methods ////////////////////////////////////////////
|
||||
|
||||
void Stream::
|
||||
protocolStartHook()
|
||||
{
|
||||
flags &= ~Aborted;
|
||||
}
|
||||
|
||||
void Stream::
|
||||
protocolFinishHook(ProtocolResult result)
|
||||
{
|
||||
@ -824,6 +819,7 @@ protocolFinishHook(ProtocolResult result)
|
||||
status = CALC_ALARM;
|
||||
break;
|
||||
case Abort:
|
||||
flags |= Aborted;
|
||||
case Fault:
|
||||
status = UDF_ALARM;
|
||||
if (record->pact || record->scan == SCAN_IO_EVENT)
|
||||
@ -845,28 +841,46 @@ protocolFinishHook(ProtocolResult result)
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
if (record->pact || record->scan == SCAN_IO_EVENT)
|
||||
{
|
||||
debug("Stream::protocolFinishHook(stream=%s,result=%d) "
|
||||
"processing record\n", name(), result);
|
||||
// process record
|
||||
// This will call streamReadWrite.
|
||||
dbScanLock(record);
|
||||
((DEVSUPFUN)record->rset->process)(record);
|
||||
dbScanUnlock(record);
|
||||
|
||||
debug("Stream::protocolFinishHook(stream=%s,result=%d) done\n",
|
||||
name(), result);
|
||||
}
|
||||
if (result != Abort && record->scan == SCAN_IO_EVENT)
|
||||
{
|
||||
// re-enable early input
|
||||
flags |= AcceptInput;
|
||||
}
|
||||
|
||||
if (record->pact || record->scan == SCAN_IO_EVENT)
|
||||
{
|
||||
// process record in callback thread to break possible recursion
|
||||
callbackSetPriority(priority(), &processCallback);
|
||||
callbackRequest(&processCallback);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void streamRecordProcessCallback(CALLBACK *pcallback)
|
||||
{
|
||||
Stream* pstream = static_cast<Stream*>(pcallback->user);
|
||||
dbCommon* record = pstream->record;
|
||||
|
||||
// process record
|
||||
// This will call streamReadWrite.
|
||||
debug("streamRecordProcessCallback(%s) processing record\n",
|
||||
pstream->name());
|
||||
dbScanLock(record);
|
||||
((DEVSUPFUN)record->rset->process)(record);
|
||||
dbScanUnlock(record);
|
||||
debug("streamRecordProcessCallback(%s) processing record done\n",
|
||||
pstream->name());
|
||||
|
||||
if (record->scan == SCAN_IO_EVENT && !(pstream->flags & Aborted))
|
||||
{
|
||||
// restart protocol for next turn
|
||||
debug("Stream::process(%s) restart async protocol\n",
|
||||
name());
|
||||
if (!startProtocol(StartAsync))
|
||||
debug("streamRecordProcessCallback(%s) restart async protocol\n",
|
||||
pstream->name());
|
||||
if (!pstream->startProtocol(Stream::StartAsync))
|
||||
{
|
||||
error("%s: Can't restart \"I/O Intr\" protocol\n",
|
||||
name());
|
||||
pstream->name());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -897,10 +911,15 @@ getFieldAddress(const char* fieldname, StreamBuffer& address)
|
||||
}
|
||||
else
|
||||
{
|
||||
// FIELD in this record
|
||||
// FIELD in this record or VAL in other record
|
||||
char fullname[PVNAME_SZ + 1];
|
||||
sprintf(fullname, "%s.%s", name(), fieldname);
|
||||
if (dbNameToAddr(fullname, &dbaddr) != OK) return false;
|
||||
if (dbNameToAddr(fullname, &dbaddr) != OK)
|
||||
{
|
||||
// VAL in other record
|
||||
sprintf(fullname, "%s.VAL", fieldname);
|
||||
if (dbNameToAddr(fullname, &dbaddr) != OK) return false;
|
||||
}
|
||||
}
|
||||
address.append(&dbaddr, sizeof(dbaddr));
|
||||
return true;
|
||||
@ -930,6 +949,26 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
long nelem = pdbaddr->no_elements;
|
||||
size_t size = nelem * typeSize[format.type];
|
||||
char* buffer = fieldBuffer.clear().reserve(size);
|
||||
|
||||
if (strcmp(((dbFldDes*)pdbaddr->pfldDes)->name, "TIME") == 0)
|
||||
{
|
||||
double time;
|
||||
|
||||
if (format.type != double_format)
|
||||
{
|
||||
error ("%s: can only read double values from TIME field\n", name());
|
||||
return false;
|
||||
}
|
||||
if (pdbaddr->precord == record)
|
||||
{
|
||||
/* if getting time from own record, update timestamp first */
|
||||
recGblGetTimeStamp(record);
|
||||
}
|
||||
time = pdbaddr->precord->time.secPastEpoch + 631152000u + pdbaddr->precord->time.nsec * 1e-9;
|
||||
debug("Stream::formatValue(%s): read %f from TIME field\n", name(), time);
|
||||
return printValue(format, time);
|
||||
}
|
||||
|
||||
if (dbGet(pdbaddr, dbfMapping[format.type], buffer,
|
||||
NULL, &nelem, NULL) != 0)
|
||||
{
|
||||
@ -996,14 +1035,13 @@ bool Stream::
|
||||
matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
{
|
||||
// this function must increase consumedInput
|
||||
// [I use goto and feel very ashamed for it.]
|
||||
long consumed;
|
||||
long lval;
|
||||
double dval;
|
||||
char* buffer;
|
||||
int status;
|
||||
const char* putfunc;
|
||||
|
||||
|
||||
if (fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to put value
|
||||
@ -1020,29 +1058,25 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
case long_format:
|
||||
{
|
||||
consumed = scanValue(format, lval);
|
||||
if (consumed < 0) goto noMoreElements;
|
||||
((epicsInt32*)buffer)[nord] = lval;
|
||||
if (consumed >= 0) ((epicsInt32*)buffer)[nord] = lval;
|
||||
break;
|
||||
}
|
||||
case enum_format:
|
||||
{
|
||||
consumed = scanValue(format, lval);
|
||||
if (consumed < 0) goto noMoreElements;
|
||||
((epicsUInt16*)buffer)[nord] = (epicsUInt16)lval;
|
||||
if (consumed >= 0) ((epicsUInt16*)buffer)[nord] = (epicsUInt16)lval;
|
||||
break;
|
||||
}
|
||||
case double_format:
|
||||
{
|
||||
consumed = scanValue(format, dval);
|
||||
if (consumed < 0) goto noMoreElements;
|
||||
((epicsFloat64*)buffer)[nord] = dval;
|
||||
if (consumed >= 0) ((epicsFloat64*)buffer)[nord] = dval;
|
||||
break;
|
||||
}
|
||||
case string_format:
|
||||
{
|
||||
consumed = scanValue(format,
|
||||
buffer+MAX_STRING_SIZE*nord, MAX_STRING_SIZE);
|
||||
if (consumed < 0) goto noMoreElements;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
@ -1050,15 +1084,15 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
"Illegal format type\n", name());
|
||||
return false;
|
||||
}
|
||||
if (consumed < 0) break;
|
||||
consumedInput += consumed;
|
||||
}
|
||||
noMoreElements:
|
||||
if (!nord)
|
||||
{
|
||||
// scan error: set other record to alarm status
|
||||
if (pdbaddr->precord != record)
|
||||
{
|
||||
recGblSetSevr(pdbaddr->precord, CALC_ALARM, INVALID_ALARM);
|
||||
(void) recGblSetSevr(pdbaddr->precord, CALC_ALARM, INVALID_ALARM);
|
||||
if (!INIT_RUN)
|
||||
{
|
||||
// process other record to send alarm monitor
|
||||
@ -1067,6 +1101,28 @@ noMoreElements:
|
||||
}
|
||||
return false;
|
||||
}
|
||||
if (strcmp(((dbFldDes*)pdbaddr->pfldDes)->name, "TIME") == 0)
|
||||
{
|
||||
#ifdef epicsTimeEventDeviceTime
|
||||
if (format.type != double_format)
|
||||
{
|
||||
error ("%s: can only write double values to TIME field\n", name());
|
||||
return false;
|
||||
}
|
||||
dval = dval-631152000u;
|
||||
pdbaddr->precord->time.secPastEpoch = (long)dval;
|
||||
/* rouding: we don't have 9 digits precision in a double of today's number of seconds */
|
||||
pdbaddr->precord->time.nsec = (long)((dval-(long)dval)*1e6)*1000;
|
||||
debug("Stream::matchValue(%s): writing %i.%i to TIME field\n", name(),
|
||||
pdbaddr->precord->time.secPastEpoch, pdbaddr->precord->time.nsec);
|
||||
pdbaddr->precord->tse = epicsTimeEventDeviceTime;
|
||||
return true;
|
||||
#else
|
||||
error ("%s: writing TIME field is not supported in this EPICS version\n", name());
|
||||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
if (pdbaddr->precord == record || INIT_RUN)
|
||||
{
|
||||
// write into own record, thus don't process it
|
||||
@ -1157,11 +1213,11 @@ noMoreElements:
|
||||
}
|
||||
|
||||
#ifdef EPICS_3_14
|
||||
|
||||
// Pass command to iocsh
|
||||
void streamExecuteCommand(CALLBACK *pcallback)
|
||||
{
|
||||
Stream* pstream = static_cast<Stream*>(pcallback->user);
|
||||
|
||||
|
||||
if (iocshCmd(pstream->outputLine()) != OK)
|
||||
{
|
||||
pstream->execCallback(StreamIoFault);
|
||||
@ -1172,19 +1228,20 @@ void streamExecuteCommand(CALLBACK *pcallback)
|
||||
}
|
||||
}
|
||||
#else
|
||||
extern "C" int execute (const char *cmd);
|
||||
// Pass command to vxWorks shell
|
||||
extern "C" int execute(const char *cmd);
|
||||
|
||||
void streamExecuteCommand(CALLBACK *pcallback)
|
||||
{
|
||||
Stream* pstream = static_cast<Stream*>(pcallback->user);
|
||||
|
||||
|
||||
if (execute(pstream->outputLine()) != OK)
|
||||
{
|
||||
pstream->execCallback(StreamBusInterface::ioFault);
|
||||
pstream->execCallback(StreamIoFault);
|
||||
}
|
||||
else
|
||||
{
|
||||
pstream->execCallback(StreamBusInterface::ioSuccess);
|
||||
pstream->execCallback(StreamIoSuccess);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -58,11 +58,19 @@ void StreamError(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
StreamVError(fmt, args);
|
||||
StreamVError(0, NULL, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void StreamVError(const char* fmt, va_list args)
|
||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
StreamVError(line, file, fmt, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
||||
{
|
||||
char timestamp[40];
|
||||
StreamPrintTimestampFunction(timestamp, 40);
|
||||
@ -79,6 +87,10 @@ void StreamVError(const char* fmt, va_list args)
|
||||
#endif
|
||||
fprintf(stderr, "\033[31;1m");
|
||||
fprintf(stderr, "%s ", timestamp);
|
||||
if (file)
|
||||
{
|
||||
fprintf(stderr, "%s line %d: ", file, line);
|
||||
}
|
||||
vfprintf(stderr, fmt, args);
|
||||
fprintf(stderr, "\033[0m");
|
||||
}
|
||||
|
@ -30,11 +30,19 @@
|
||||
extern int streamDebug;
|
||||
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
||||
|
||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||
__attribute__ ((format(printf,3,4)));
|
||||
|
||||
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
||||
__attribute__ ((format(printf,3,0)));
|
||||
|
||||
void StreamError(const char* fmt, ...)
|
||||
__attribute__ ((format(printf,1,2)));
|
||||
|
||||
void StreamVError(const char* fmt, va_list args)
|
||||
__attribute__ ((format(printf,1,0)));
|
||||
inline void StreamVError(const char* fmt, va_list args)
|
||||
{
|
||||
StreamVError(0, NULL, fmt, args);
|
||||
}
|
||||
|
||||
class StreamDebugClass
|
||||
{
|
||||
|
@ -23,12 +23,14 @@
|
||||
#define StreamFormat_h
|
||||
|
||||
typedef enum {
|
||||
left_flag = 0x01,
|
||||
sign_flag = 0x02,
|
||||
space_flag = 0x04,
|
||||
alt_flag = 0x08,
|
||||
zero_flag = 0x10,
|
||||
skip_flag = 0x20
|
||||
left_flag = 0x01,
|
||||
sign_flag = 0x02,
|
||||
space_flag = 0x04,
|
||||
alt_flag = 0x08,
|
||||
zero_flag = 0x10,
|
||||
skip_flag = 0x20,
|
||||
default_flag = 0x40,
|
||||
compare_flag = 0x80
|
||||
} StreamFormatFlag;
|
||||
|
||||
typedef enum {
|
||||
|
@ -19,8 +19,9 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <ctype.h>
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamFormat.h"
|
||||
#include "StreamError.h"
|
||||
|
||||
StreamFormatConverter* StreamFormatConverter::
|
||||
@ -31,6 +32,125 @@ StreamFormatConverter::
|
||||
{
|
||||
}
|
||||
|
||||
int StreamFormatConverter::
|
||||
parseFormat(const char*& source, FormatType formatType, StreamFormat& streamFormat, StreamBuffer& infoString)
|
||||
{
|
||||
/*
|
||||
source := [flags] [width] ['.' prec] conv [extra]
|
||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '='
|
||||
width := integer
|
||||
prec := integer
|
||||
conv := character
|
||||
extra := string
|
||||
*/
|
||||
|
||||
// look for flags
|
||||
streamFormat.flags = 0;
|
||||
bool loop = true;
|
||||
while (loop)
|
||||
{
|
||||
switch (*++source)
|
||||
{
|
||||
case '-':
|
||||
streamFormat.flags |= left_flag;
|
||||
break;
|
||||
case '+':
|
||||
streamFormat.flags |= sign_flag;
|
||||
break;
|
||||
case ' ':
|
||||
streamFormat.flags |= space_flag;
|
||||
break;
|
||||
case '#':
|
||||
streamFormat.flags |= alt_flag;
|
||||
break;
|
||||
case '0':
|
||||
streamFormat.flags |= zero_flag;
|
||||
break;
|
||||
case '*':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
error("Use of skip modifier '*' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= skip_flag;
|
||||
break;
|
||||
case '?':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
error("Use of default modifier '?' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= default_flag;
|
||||
break;
|
||||
case '=':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
error("Use of compare modifier '=' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= compare_flag;
|
||||
formatType = PrintFormat;
|
||||
break;
|
||||
default:
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
// look for width
|
||||
unsigned long val;
|
||||
char* p;
|
||||
val = strtoul (source, &p, 10);
|
||||
source = p;
|
||||
if (val > 0xFFFF)
|
||||
{
|
||||
error("Field width %ld out of range\n", val);
|
||||
return false;
|
||||
}
|
||||
streamFormat.width = (unsigned short)val;
|
||||
// look for prec
|
||||
streamFormat.prec = -1;
|
||||
if (*source == '.')
|
||||
{
|
||||
source++;
|
||||
val = strtoul(source, &p, 10);
|
||||
if (p == source)
|
||||
{
|
||||
debug("source = %s\n", source);
|
||||
error("Numeric precision field expected after '.'\n");
|
||||
return false;
|
||||
}
|
||||
source = p;
|
||||
if (val > 0x7FFF)
|
||||
{
|
||||
error("Precision %ld out of range\n", val);
|
||||
return false;
|
||||
}
|
||||
streamFormat.prec = (short)val;
|
||||
}
|
||||
// look for converter
|
||||
streamFormat.conv = *source++;
|
||||
if (!streamFormat.conv || strchr("'\" (.0+-*?=", streamFormat.conv))
|
||||
{
|
||||
error("Missing converter character\n");
|
||||
return false;
|
||||
}
|
||||
debug("StreamFormatConverter::parseFormat: converter='%c'\n",
|
||||
streamFormat.conv);
|
||||
StreamFormatConverter* converter;
|
||||
converter = StreamFormatConverter::find(streamFormat.conv);
|
||||
if (!converter)
|
||||
{
|
||||
error("No converter registered for format '%%%c'\n",
|
||||
streamFormat.conv);
|
||||
return false;
|
||||
}
|
||||
// parse format and get info string
|
||||
return converter->parse(streamFormat, infoString,
|
||||
source, formatType == ScanFormat);
|
||||
}
|
||||
|
||||
void StreamFormatConverter::
|
||||
provides(const char* name, const char* provided)
|
||||
{
|
||||
@ -112,11 +232,57 @@ static void copyFormatString(StreamBuffer& info, const char* source)
|
||||
const char* p = source - 1;
|
||||
while (*p != '%' && *p != ')') p--;
|
||||
info.append('%');
|
||||
while (++p != source-1) info.append(*p);
|
||||
while (++p != source-1) if (*p != '?' && *p != '=') info.append(*p);
|
||||
}
|
||||
|
||||
// A word on sscanf
|
||||
// GNU's sscanf implementation sucks. It calls strlen on the buffer.
|
||||
// That leads to a time consumption proportional to the buffer size.
|
||||
// When reading huge arrays element-wise by repeatedly calling sscanf,
|
||||
// the performance drops to O(n^2).
|
||||
// The vxWorks implementation sucks, too. When all parsed values are skipped
|
||||
// with %*, it returns -1 instead of 0 even though it was successful.
|
||||
|
||||
// Standard Long Converter for 'diouxX'
|
||||
|
||||
static int prepareval(const StreamFormat& fmt, const char*& input, bool& neg)
|
||||
{
|
||||
int length = 0;
|
||||
neg = false;
|
||||
while (isspace(*input)) { input++; length++; }
|
||||
if (fmt.width)
|
||||
{
|
||||
// take local copy because strto* don't have width parameter
|
||||
int width = fmt.width;
|
||||
if (fmt.flags & space_flag)
|
||||
{
|
||||
// normally whitespace does not count to width
|
||||
// but do so if space flag is present
|
||||
width -= length;
|
||||
}
|
||||
strncpy((char*)fmt.info, input, width);
|
||||
((char*)fmt.info)[width] = 0;
|
||||
input = fmt.info;
|
||||
}
|
||||
if (*input == '+')
|
||||
{
|
||||
goto skipsign;
|
||||
}
|
||||
if (*input == '-')
|
||||
{
|
||||
neg = true;
|
||||
skipsign:
|
||||
input++;
|
||||
length++;
|
||||
}
|
||||
if (isspace(*input))
|
||||
{
|
||||
// allow space after sign only if # flag is set
|
||||
if (!(fmt.flags & alt_flag)) return -1;
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
class StdLongConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse(const StreamFormat& fmt, StreamBuffer& output, const char*& value, bool scanFormat);
|
||||
@ -128,22 +294,22 @@ int StdLongConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (scanFormat && (fmt.flags & alt_flag))
|
||||
{
|
||||
error("Use of modifier '#' not allowed with %%%c input conversion\n",
|
||||
fmt.conv);
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
{
|
||||
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
|
||||
fmt.prec, fmt.conv);
|
||||
return false;
|
||||
}
|
||||
copyFormatString(info, source);
|
||||
info.append('l');
|
||||
info.append(fmt.conv);
|
||||
if (scanFormat) info.append("%n");
|
||||
if (scanFormat)
|
||||
{
|
||||
if (fmt.width) info.reserve(fmt.width+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyFormatString(info, source);
|
||||
info.append('l');
|
||||
info.append(fmt.conv);
|
||||
}
|
||||
return long_format;
|
||||
}
|
||||
|
||||
@ -157,15 +323,39 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
int StdLongConverter::
|
||||
scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
{
|
||||
int length = -1;
|
||||
if (fmt.flags & skip_flag)
|
||||
char* end;
|
||||
int length;
|
||||
bool neg;
|
||||
int base;
|
||||
|
||||
length = prepareval(fmt, input, neg);
|
||||
if (length < 0) return -1;
|
||||
switch (fmt.conv)
|
||||
{
|
||||
if (sscanf(input, fmt.info, &length) < 0) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sscanf(input, fmt.info, &value, &length) < 1) return -1;
|
||||
case 'd':
|
||||
base = 10;
|
||||
break;
|
||||
case 'o':
|
||||
base = 8;
|
||||
goto signcheck;
|
||||
case 'x':
|
||||
case 'X':
|
||||
base = 16;
|
||||
signcheck:
|
||||
// allow negative hex and oct numbers with - flag
|
||||
if (neg && !(fmt.flags & left_flag)) return -1;
|
||||
break;
|
||||
case 'u':
|
||||
if (neg) return -1;
|
||||
base = 10;
|
||||
break;
|
||||
default:
|
||||
base = 0;
|
||||
}
|
||||
value = strtoul(input, &end, base);
|
||||
if (neg) value = -value;
|
||||
if (end == input) return -1;
|
||||
length += end-input;
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -184,22 +374,21 @@ int StdDoubleConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (scanFormat && (fmt.flags & alt_flag))
|
||||
{
|
||||
error("Use of modifier '#' not allowed with %%%c input conversion\n",
|
||||
fmt.conv);
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
{
|
||||
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
|
||||
fmt.prec, fmt.conv);
|
||||
return false;
|
||||
}
|
||||
copyFormatString(info, source);
|
||||
if (scanFormat) info.append('l');
|
||||
info.append(fmt.conv);
|
||||
if (scanFormat) info.append("%n");
|
||||
if (scanFormat)
|
||||
{
|
||||
if (fmt.width) info.reserve(fmt.width+1);
|
||||
}
|
||||
else
|
||||
{
|
||||
copyFormatString(info, source);
|
||||
info.append(fmt.conv);
|
||||
}
|
||||
return double_format;
|
||||
}
|
||||
|
||||
@ -213,15 +402,16 @@ printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
|
||||
int StdDoubleConverter::
|
||||
scanDouble(const StreamFormat& fmt, const char* input, double& value)
|
||||
{
|
||||
int length = -1;
|
||||
if (fmt.flags & skip_flag)
|
||||
{
|
||||
if (sscanf(input, fmt.info, &length) < 0) return -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sscanf(input, fmt.info, &value, &length) < 1) return -1;
|
||||
}
|
||||
char* end;
|
||||
int length;
|
||||
bool neg;
|
||||
|
||||
length = prepareval(fmt, input, neg);
|
||||
if (length < 0) return -1;
|
||||
value = strtod(input, &end);
|
||||
if (neg) value = -value;
|
||||
if (end == input) return -1;
|
||||
length += end-input;
|
||||
return length;
|
||||
}
|
||||
|
||||
@ -240,11 +430,10 @@ int StdStringConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (fmt.flags & (sign_flag|space_flag|zero_flag|alt_flag))
|
||||
if (fmt.flags & (sign_flag|zero_flag))
|
||||
{
|
||||
error("Use of modifiers '+', ' ', '0', '#' "
|
||||
"not allowed with %%%c conversion\n",
|
||||
fmt.conv);
|
||||
error("Use of modifiers '+', '0'"
|
||||
"not allowed with %%s conversion\n");
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
@ -270,38 +459,52 @@ int StdStringConverter::
|
||||
scanString(const StreamFormat& fmt, const char* input,
|
||||
char* value, size_t maxlen)
|
||||
{
|
||||
int length = -1;
|
||||
if (*input == '\0')
|
||||
int length = 0;
|
||||
|
||||
int width = fmt.width;
|
||||
|
||||
if ((fmt.flags & skip_flag) || value == NULL) maxlen = 0;
|
||||
|
||||
// if user does not specify width assume "ininity" (-1)
|
||||
if (width == 0)
|
||||
{
|
||||
// match empty string
|
||||
value[0] = '\0';
|
||||
return 0;
|
||||
if (fmt.conv == 'c') width = 1;
|
||||
else width = -1;
|
||||
}
|
||||
if (fmt.flags & skip_flag)
|
||||
|
||||
while (isspace(*input) && width)
|
||||
{
|
||||
if (sscanf(input, fmt.info, &length) < 0) return -1;
|
||||
// normally leading whitespace does not count to width
|
||||
// but do so if space flag is present
|
||||
if (fmt.flags & space_flag)
|
||||
{
|
||||
if (maxlen > 1)
|
||||
{
|
||||
*value++ = *input;
|
||||
maxlen--;
|
||||
}
|
||||
width--;
|
||||
}
|
||||
length++;
|
||||
input++;
|
||||
}
|
||||
else
|
||||
while (*input && width)
|
||||
{
|
||||
char tmpformat[10];
|
||||
const char* f;
|
||||
if (maxlen <= fmt.width || fmt.width == 0)
|
||||
// normally whitespace ends string
|
||||
// but don't end if # flag is present
|
||||
if (!(fmt.flags & alt_flag) && isspace(*input)) break;
|
||||
if (maxlen > 1)
|
||||
{
|
||||
// assure not to read too much
|
||||
sprintf(tmpformat, "%%%d%c%%n", maxlen-1, fmt.conv);
|
||||
f = tmpformat;
|
||||
*value++ = *input;
|
||||
maxlen--;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = fmt.info;
|
||||
}
|
||||
if (sscanf(input, f, value, &length) < 1) return -1;
|
||||
if (length < 0) return -1;
|
||||
value[length] = '\0';
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StdStringConverter::scanString: length=%d, value=%s\n",
|
||||
length, StreamBuffer(value,length).expand()());
|
||||
#endif
|
||||
length++;
|
||||
width--;
|
||||
input++;
|
||||
}
|
||||
if (maxlen > 0)
|
||||
{
|
||||
*value = '\0';
|
||||
}
|
||||
return length;
|
||||
}
|
||||
@ -310,11 +513,11 @@ RegisterConverter (StdStringConverter, "s");
|
||||
|
||||
// Standard Characters Converter for 'c'
|
||||
|
||||
class StdCharsConverter : public StdStringConverter
|
||||
class StdCharsConverter : public StreamFormatConverter
|
||||
{
|
||||
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
virtual bool printLong(const StreamFormat&, StreamBuffer&, long);
|
||||
// scanString is inherited from %s format
|
||||
virtual int scanString(const StreamFormat&, const char*, char*, size_t);
|
||||
};
|
||||
|
||||
int StdCharsConverter::
|
||||
@ -324,8 +527,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
if (fmt.flags & (sign_flag|space_flag|zero_flag|alt_flag))
|
||||
{
|
||||
error("Use of modifiers '+', ' ', '0', '#' "
|
||||
"not allowed with %%%c conversion\n",
|
||||
fmt.conv);
|
||||
"not allowed with %%c conversion\n");
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
@ -351,6 +553,37 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
|
||||
return true;
|
||||
}
|
||||
|
||||
int StdCharsConverter::
|
||||
scanString(const StreamFormat& fmt, const char* input,
|
||||
char* value, size_t maxlen)
|
||||
{
|
||||
int length = 0;
|
||||
|
||||
int width = fmt.width;
|
||||
|
||||
if ((fmt.flags & skip_flag) || value == NULL) maxlen = 0;
|
||||
|
||||
// if user does not specify width assume 1
|
||||
if (width == 0) width = 1;
|
||||
|
||||
while (*input && width)
|
||||
{
|
||||
if (maxlen > 1)
|
||||
{
|
||||
*value++ = *input;
|
||||
maxlen--;
|
||||
}
|
||||
length++;
|
||||
width--;
|
||||
input++;
|
||||
}
|
||||
if (maxlen > 0)
|
||||
{
|
||||
*value = '\0';
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
||||
RegisterConverter (StdCharsConverter, "c");
|
||||
|
||||
// Standard Charset Converter for '['
|
||||
@ -362,6 +595,17 @@ class StdCharsetConverter : public StreamFormatConverter
|
||||
// no print method, %[ is readonly
|
||||
};
|
||||
|
||||
inline void markbit(StreamBuffer& info, bool notflag, char c)
|
||||
{
|
||||
char &infobyte = info[c>>3];
|
||||
char mask = 1<<(c&7);
|
||||
|
||||
if (notflag)
|
||||
infobyte |= mask;
|
||||
else
|
||||
infobyte &= ~mask;
|
||||
}
|
||||
|
||||
int StdCharsetConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
@ -374,8 +618,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag|alt_flag))
|
||||
{
|
||||
error("Use of modifiers '-', '+', ' ', '0', '#'"
|
||||
"not allowed with %%%c conversion\n",
|
||||
fmt.conv);
|
||||
"not allowed with %%[ conversion\n");
|
||||
return false;
|
||||
}
|
||||
if (scanFormat && fmt.prec >= 0)
|
||||
@ -384,18 +627,46 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
fmt.prec, fmt.conv);
|
||||
return false;
|
||||
}
|
||||
info.printf("%%%d[", fmt.width);
|
||||
while (*source && *source != ']')
|
||||
|
||||
bool notflag = false;
|
||||
char c = 0;
|
||||
|
||||
info.clear().reserve(32);
|
||||
if (*source == '^')
|
||||
{
|
||||
if (*source == esc) source++;
|
||||
info.append(*source++);
|
||||
notflag = true;
|
||||
source++;
|
||||
}
|
||||
else
|
||||
{
|
||||
memset(info(1), 255, 32);
|
||||
}
|
||||
if (*source == ']')
|
||||
{
|
||||
markbit(info, notflag, *source++);
|
||||
}
|
||||
for (; *source && *source != ']'; source++)
|
||||
{
|
||||
if (*source == esc)
|
||||
{
|
||||
markbit(info, notflag, *++source);
|
||||
continue;
|
||||
}
|
||||
if (*source == '-' && c && source[1] && source[1] != ']')
|
||||
{
|
||||
source++;
|
||||
while (c < *source) markbit(info, notflag, c++);
|
||||
while (c > *source) markbit(info, notflag, c--);
|
||||
}
|
||||
c = *source;
|
||||
markbit(info, notflag, c);
|
||||
}
|
||||
if (!*source) {
|
||||
error("Missing ']' after %%[ format conversion\n");
|
||||
return false;
|
||||
}
|
||||
source++; // consume ']'
|
||||
info.append("]%n");
|
||||
|
||||
return string_format;
|
||||
}
|
||||
|
||||
@ -403,31 +674,29 @@ int StdCharsetConverter::
|
||||
scanString(const StreamFormat& fmt, const char* input,
|
||||
char* value, size_t maxlen)
|
||||
{
|
||||
int length = -1;
|
||||
if (fmt.flags & skip_flag)
|
||||
int length = 0;
|
||||
int width = fmt.width;
|
||||
|
||||
if ((fmt.flags & skip_flag) || value == NULL) maxlen = 0;
|
||||
|
||||
// if user does not specify width assume "ininity" (-1)
|
||||
if (width == 0) width = -1;
|
||||
|
||||
while (*input && width)
|
||||
{
|
||||
if (sscanf (input, fmt.info, &length) < 0) return -1;
|
||||
if (fmt.info[*input>>3] & 1<<(*input&7)) break;
|
||||
if (maxlen > 1)
|
||||
{
|
||||
*value++ = *input;
|
||||
maxlen--;
|
||||
}
|
||||
length++;
|
||||
width--;
|
||||
input++;
|
||||
}
|
||||
else
|
||||
if (maxlen > 0)
|
||||
{
|
||||
char tmpformat[256];
|
||||
const char* f;
|
||||
if (maxlen <= fmt.width || fmt.width == 0)
|
||||
{
|
||||
const char *p = strchr (fmt.info, '[');
|
||||
// assure not to read too much
|
||||
sprintf(tmpformat, "%%%d%s", maxlen-1, p);
|
||||
f = tmpformat;
|
||||
}
|
||||
else
|
||||
{
|
||||
f = fmt.info;
|
||||
}
|
||||
if (sscanf(input, f, value, &length) < 1) return -1;
|
||||
if (length < 0) return -1;
|
||||
value[length] = '\0';
|
||||
debug("StdCharsetConverter::scanString: length=%d, value=%s\n",
|
||||
length, value);
|
||||
*value = '\0';
|
||||
}
|
||||
return length;
|
||||
}
|
||||
|
@ -22,6 +22,7 @@
|
||||
|
||||
#include "StreamFormat.h"
|
||||
#include "StreamBuffer.h"
|
||||
#include "StreamProtocol.h"
|
||||
|
||||
#define esc (0x1b)
|
||||
|
||||
@ -41,6 +42,7 @@ class StreamFormatConverter
|
||||
const char* _name;
|
||||
public:
|
||||
virtual ~StreamFormatConverter();
|
||||
static int parseFormat(const char*& source, FormatType, StreamFormat&, StreamBuffer& infoString);
|
||||
const char* name() { return _name; }
|
||||
void provides(const char* name, const char* provided);
|
||||
static StreamFormatConverter* find(unsigned char c);
|
||||
|
@ -81,18 +81,6 @@ StreamProtocolParser::
|
||||
delete next;
|
||||
}
|
||||
|
||||
void StreamProtocolParser::
|
||||
errorMsg(const char* fmt, ...)
|
||||
{
|
||||
char fmt2[200];
|
||||
sprintf (fmt2, "'%s' line %d: %s",
|
||||
filename(), line, fmt);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
StreamVError(fmt2, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void StreamProtocolParser::
|
||||
report()
|
||||
{
|
||||
@ -118,7 +106,7 @@ getProtocol(const char* filename, const StreamBuffer& protocolAndParams)
|
||||
// Have we already seen this file?
|
||||
for (parser = parsers; parser; parser = parser->next)
|
||||
{
|
||||
if (parser->filename.equals(filename))
|
||||
if (parser->filename.startswith(filename))
|
||||
{
|
||||
if (!parser->valid)
|
||||
{
|
||||
@ -234,7 +222,7 @@ getProtocol(const StreamBuffer& protocolAndParams)
|
||||
Protocol* protocol;
|
||||
for (protocol = protocols; protocol; protocol = protocol->next)
|
||||
{
|
||||
if (protocol->protocolname.equals(name()))
|
||||
if (protocol->protocolname.startswith(name()))
|
||||
// constructor also replaces parameters
|
||||
return new Protocol(*protocol, name, 0);
|
||||
}
|
||||
@ -292,18 +280,18 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
// end of protocol or handler definition
|
||||
return true;
|
||||
}
|
||||
errorMsg("Stray '}' in global context\n");
|
||||
error(line, filename(), "Stray '}' in global context\n");
|
||||
return false;
|
||||
}
|
||||
if (strchr("{=", token[0]))
|
||||
{
|
||||
errorMsg("Expect name before '%c'\n", token[0]);
|
||||
error(line, filename(), "Expect name before '%c'\n", token[0]);
|
||||
return false;
|
||||
}
|
||||
do op = readChar(); while (op == ' '); // what comes after token?
|
||||
if (op == EOF)
|
||||
{
|
||||
errorMsg("Unexpected end of file after: %s\n", token());
|
||||
error(line, filename(), "Unexpected end of file after: %s\n", token());
|
||||
return false;
|
||||
}
|
||||
if (op == '=')
|
||||
@ -311,20 +299,20 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
// variable assignment
|
||||
if (isHandlerContext(protocol, commands))
|
||||
{
|
||||
errorMsg("Variables are not allowed in handlers: %s\n",
|
||||
error(line, filename(), "Variables are not allowed in handlers: %s\n",
|
||||
token());
|
||||
return false;
|
||||
}
|
||||
if (token[0] == '@' || (token[0] >= '0' && token[0] <= '9'))
|
||||
{
|
||||
errorMsg("Variable name cannot start with '%c': %s\n",
|
||||
error(line, filename(), "Variable name cannot start with '%c': %s\n",
|
||||
token[0], token());
|
||||
return false;
|
||||
}
|
||||
if (!parseAssignment(token(), protocol))
|
||||
{
|
||||
line = startline;
|
||||
errorMsg("in variable assignment '%s = ...'\n", token());
|
||||
error(line, filename(), "in variable assignment '%s = ...'\n", token());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@ -336,7 +324,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
{
|
||||
if (isHandlerContext(protocol, commands))
|
||||
{
|
||||
errorMsg("Handlers are not allowed in handlers: %s\n",
|
||||
error(line, filename(), "Handlers are not allowed in handlers: %s\n",
|
||||
token());
|
||||
return false;
|
||||
}
|
||||
@ -345,7 +333,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
if (!parseProtocol(protocol, handler))
|
||||
{
|
||||
line = startline;
|
||||
errorMsg("in handler '%s'\n", token());
|
||||
error(line, filename(), "in handler '%s'\n", token());
|
||||
return false;
|
||||
}
|
||||
continue;
|
||||
@ -353,16 +341,16 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
// protocol definition
|
||||
if (!isGlobalContext(commands))
|
||||
{
|
||||
errorMsg("Definition of '%s' not in global context (missing '}' ?)\n",
|
||||
error(line, filename(), "Definition of '%s' not in global context (missing '}' ?)\n",
|
||||
token());
|
||||
return false;
|
||||
}
|
||||
Protocol** ppP;
|
||||
for (ppP = &protocols; *ppP; ppP = &(*ppP)->next)
|
||||
{
|
||||
if ((*ppP)->protocolname.equals(token()))
|
||||
if ((*ppP)->protocolname.startswith(token()))
|
||||
{
|
||||
errorMsg("Protocol '%s' redefined\n", token());
|
||||
error(line, filename(), "Protocol '%s' redefined\n", token());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -370,7 +358,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
if (!parseProtocol(*pP, pP->commands))
|
||||
{
|
||||
line = startline;
|
||||
errorMsg("in protocol '%s'\n", token());
|
||||
error(line, filename(), "in protocol '%s'\n", token());
|
||||
delete pP;
|
||||
return false;
|
||||
}
|
||||
@ -381,7 +369,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
// Must be a command or a protocol reference.
|
||||
if (isGlobalContext(commands))
|
||||
{
|
||||
errorMsg("Expect '=' or '{' instead of '%c' after '%s'\n",
|
||||
error(line, filename(), "Expect '=' or '{' instead of '%c' after '%s'\n",
|
||||
op, token());
|
||||
return false;
|
||||
}
|
||||
@ -392,7 +380,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
Protocol* p;
|
||||
for (p = protocols; p; p = p->next)
|
||||
{
|
||||
if (p->protocolname.equals(token()))
|
||||
if (p->protocolname.startswith(token()))
|
||||
{
|
||||
commands->append(*p->commands);
|
||||
break;
|
||||
@ -406,7 +394,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
|
||||
if (parseValue(*commands, true) == false)
|
||||
{
|
||||
line = startline;
|
||||
errorMsg("after command '%s'\n", token());
|
||||
error(line, filename(), "after command '%s'\n", token());
|
||||
return false;
|
||||
}
|
||||
debug("parseProtocol: command '%s'\n", (*commands).expand()());
|
||||
@ -486,7 +474,7 @@ Each time newline is read, line is incremented.
|
||||
c = getc(file);
|
||||
if (c != '}')
|
||||
{
|
||||
errorMsg("Expect '}' instead of '%c' after: %s\n",
|
||||
error(line, filename(), "Expect '}' instead of '%c' after: %s\n",
|
||||
c, buffer(token));
|
||||
return false;
|
||||
}
|
||||
@ -495,12 +483,12 @@ Each time newline is read, line is incremented.
|
||||
}
|
||||
if (c == EOF)
|
||||
{
|
||||
errorMsg("Unexpected end of file after '$'\n");
|
||||
error(line, filename(), "Unexpected end of file after '$'\n");
|
||||
return false;
|
||||
}
|
||||
if (strchr (specialchars, c))
|
||||
{
|
||||
errorMsg("Unexpected '%c' after '$'\n,", c);
|
||||
error(line, filename(), "Unexpected '%c' after '$'\n,", c);
|
||||
return false;
|
||||
}
|
||||
// variable like $xyz handled as word token
|
||||
@ -519,7 +507,7 @@ Each time newline is read, line is incremented.
|
||||
{
|
||||
if (c == EOF || c == '\n')
|
||||
{
|
||||
errorMsg("Unterminated quoted string: %s\n",
|
||||
error(line, filename(), "Unterminated quoted string: %s\n",
|
||||
buffer(token));
|
||||
return false;
|
||||
}
|
||||
@ -532,7 +520,7 @@ Each time newline is read, line is incremented.
|
||||
break;
|
||||
}
|
||||
buffer.append(c);
|
||||
if (c == quote && buffer[-1] != '\\')
|
||||
if (c == quote && buffer[-2] != '\\')
|
||||
{
|
||||
quote = false;
|
||||
break;
|
||||
@ -547,7 +535,7 @@ Each time newline is read, line is incremented.
|
||||
// end of file
|
||||
if (!eofAllowed)
|
||||
{
|
||||
errorMsg("Unexpected end of file\n");
|
||||
error(line, filename(), "Unexpected end of file\n");
|
||||
return false;
|
||||
}
|
||||
buffer.append('\0');
|
||||
@ -620,7 +608,7 @@ parseValue(StreamBuffer& buffer, bool lazy)
|
||||
}
|
||||
if (c == '{' || c == '=')
|
||||
{
|
||||
errorMsg("Unexpected '%c' (missing ';' or '\"' ?)\n", c);
|
||||
error(line, filename(), "Unexpected '%c' (missing ';' or '\"' ?)\n", c);
|
||||
return false;
|
||||
}
|
||||
if (strchr (";}", c))
|
||||
@ -657,6 +645,9 @@ printString(StreamBuffer& buffer, const char* s)
|
||||
case skip:
|
||||
buffer.append("\\?");
|
||||
break;
|
||||
case whitespace:
|
||||
buffer.append("\\_");
|
||||
break;
|
||||
case '"':
|
||||
buffer.append("\\\"");
|
||||
break;
|
||||
@ -768,18 +759,6 @@ StreamProtocolParser::Protocol::
|
||||
delete next;
|
||||
}
|
||||
|
||||
void StreamProtocolParser::Protocol::
|
||||
errorMsg(int l, const char* fmt, ...)
|
||||
{
|
||||
char fmt2[200];
|
||||
sprintf (fmt2, "'%s' line %d: %s",
|
||||
filename(), line, fmt);
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
StreamVError(fmt2, args);
|
||||
va_end(args);
|
||||
}
|
||||
|
||||
void StreamProtocolParser::Protocol::
|
||||
report()
|
||||
{
|
||||
@ -811,7 +790,7 @@ createVariable(const char* name, int linenr)
|
||||
Variable** ppV;
|
||||
for (ppV = &variables; *ppV; ppV = &(*ppV)->next)
|
||||
{
|
||||
if ((*ppV)->name.equals(name))
|
||||
if ((*ppV)->name.startswith(name))
|
||||
{
|
||||
(*ppV)->line = linenr;
|
||||
return &(*ppV)->value;
|
||||
@ -829,7 +808,7 @@ getVariable(const char* name)
|
||||
|
||||
for (pV = variables; pV; pV = pV->next)
|
||||
{
|
||||
if (pV->name.equals(name))
|
||||
if (pV->name.startswith(name))
|
||||
{
|
||||
pV->used = true;
|
||||
return pV;
|
||||
@ -847,12 +826,12 @@ getNumberVariable(const char* varname, unsigned long& value, unsigned long max)
|
||||
if (!compileNumber(value, source, max))
|
||||
{
|
||||
int linenr = getLineNumber(source);
|
||||
errorMsg(linenr, "in variable %s\n", varname);
|
||||
error(linenr, filename(), "in variable %s\n", varname);
|
||||
return false;
|
||||
}
|
||||
if (source != pvar->value.end())
|
||||
{
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"Garbage in variable '%s' after numeric value %ld: %s\n",
|
||||
varname, value, source);
|
||||
return false;
|
||||
@ -867,7 +846,7 @@ getEnumVariable(const char* varname, unsigned short& value, const char** enumstr
|
||||
if (!pvar) return true;
|
||||
for (value = 0; enumstrings[value]; value++)
|
||||
{
|
||||
if (pvar->value.equals(enumstrings[value]))
|
||||
if (pvar->value.startswith(enumstrings[value]))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -925,13 +904,14 @@ getCommands(const char* handlername,StreamBuffer& code, Client* client)
|
||||
{
|
||||
if (handlername)
|
||||
{
|
||||
errorMsg(pvar->line,
|
||||
error(pvar->line, filename(),
|
||||
"in handler '%s'\n", handlername);
|
||||
errorMsg(variables->line,
|
||||
error(variables->line, filename(),
|
||||
"used by protocol '%s'\n", protocolname());
|
||||
return false;
|
||||
}
|
||||
errorMsg(pvar->line, "in protocol '%s'\n", protocolname());
|
||||
error(pvar->line, filename(),
|
||||
"in protocol '%s'\n", protocolname());
|
||||
return false;
|
||||
}
|
||||
debug("commands %s: %s\n", handlername, pvar->value.expand()());
|
||||
@ -955,7 +935,7 @@ replaceVariable(StreamBuffer& buffer, const char* varname)
|
||||
const char* p = parameter[*varname-'0'];
|
||||
if (!p)
|
||||
{
|
||||
errorMsg(linenr,
|
||||
error(linenr, filename(),
|
||||
"Missing value for parameter $%c\n", *varname);
|
||||
return false;
|
||||
}
|
||||
@ -981,7 +961,7 @@ replaceVariable(StreamBuffer& buffer, const char* varname)
|
||||
const Variable* v = getVariable(varname);
|
||||
if (!v)
|
||||
{
|
||||
errorMsg(linenr,
|
||||
error(linenr, filename(),
|
||||
"Undefined variable '%s' referenced\n",
|
||||
varname);
|
||||
return false;
|
||||
@ -1045,7 +1025,7 @@ compileNumber(unsigned long& number, const char*& source, unsigned long max)
|
||||
{
|
||||
debug("StreamProtocolParser::Protocol::compileNumber: %s\n",
|
||||
buffer.expand()());
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"Unsigned numeric value expected: %s\n", buffer());
|
||||
return false;
|
||||
}
|
||||
@ -1053,7 +1033,7 @@ compileNumber(unsigned long& number, const char*& source, unsigned long max)
|
||||
{
|
||||
debug("StreamProtocolParser::Protocol::compileNumber: %s\n",
|
||||
buffer.expand()());
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"Garbage after numeric value: %s\n", buffer());
|
||||
return false;
|
||||
}
|
||||
@ -1061,7 +1041,7 @@ compileNumber(unsigned long& number, const char*& source, unsigned long max)
|
||||
{
|
||||
debug("StreamProtocolParser::Protocol::compileNumber: %s\n",
|
||||
buffer.expand()());
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"Value %s out of range [0...%ld]\n", buffer(), max);
|
||||
return false;
|
||||
}
|
||||
@ -1080,10 +1060,9 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
{
|
||||
bool escaped = false;
|
||||
int n;
|
||||
long formatPos[20];
|
||||
int numFormats = 0;
|
||||
int newline = 0;
|
||||
StreamBuffer formatbuffer;
|
||||
int formatpos = buffer.length();
|
||||
line = getLineNumber(source);
|
||||
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
@ -1094,41 +1073,58 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
// 1) read a line from protocol source and code quoted strings,
|
||||
// numerical bytes, tokens, etc, and replace variables and parameters
|
||||
// 2) replace the formats in the line
|
||||
// thus variables can be replaces inside the format info string
|
||||
// thus variables can be replaced inside the format info string
|
||||
|
||||
while (1)
|
||||
{
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
"buffer so far: %s\n", buffer.expand()());
|
||||
// this is step 2: replacing the formats
|
||||
if (!*source || (newline = getLineNumber(source)) != line)
|
||||
{
|
||||
// compile all formats in this line
|
||||
// We do this here after all variables in this line
|
||||
// have been replaced and after string has been coded.
|
||||
int offs = 0;
|
||||
for (n = 0; n < numFormats; n++)
|
||||
if (formatType != NoFormat)
|
||||
{
|
||||
int pos = formatPos[n] + offs;
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
"format=\"%s\"\n", buffer.expand(pos)());
|
||||
formatbuffer.clear();
|
||||
const char* p = buffer(pos);
|
||||
if (!compileFormat(formatbuffer, p, formatType, client))
|
||||
int nformats=0;
|
||||
char c;
|
||||
while ((c = buffer[formatpos]) != '\0')
|
||||
{
|
||||
p = buffer(pos);
|
||||
formatbuffer.clear();
|
||||
printString(formatbuffer, p);
|
||||
errorMsg(line, "in format string: \"%s\"\n",
|
||||
formatbuffer());
|
||||
return false;
|
||||
if (c == esc) {
|
||||
// ignore escaped %
|
||||
formatpos+=2;
|
||||
continue;
|
||||
}
|
||||
if (c == '%') {
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
"format=\"%s\"\n", buffer.expand(formatpos)());
|
||||
nformats++;
|
||||
formatbuffer.clear();
|
||||
const char* p = buffer(formatpos);
|
||||
if (!compileFormat(formatbuffer, p, formatType, client))
|
||||
{
|
||||
p = buffer(formatpos);
|
||||
formatbuffer.clear();
|
||||
printString(formatbuffer, p);
|
||||
error(line, filename(),
|
||||
"in format string: \"%s\"\n",
|
||||
formatbuffer());
|
||||
return false;
|
||||
}
|
||||
int formatlen = p - buffer(formatpos);
|
||||
buffer.replace(formatpos, formatlen, formatbuffer);
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
"replaced by: \"%s\"\n", buffer.expand(formatpos)());
|
||||
formatpos += formatbuffer.length();
|
||||
continue;
|
||||
}
|
||||
formatpos ++;
|
||||
}
|
||||
int formatlen = p - buffer(pos);
|
||||
buffer.replace(pos, formatlen, formatbuffer);
|
||||
debug("StreamProtocolParser::Protocol::compileString "
|
||||
"replaced by: \"%s\"\n", buffer.expand(pos)());
|
||||
offs += formatbuffer.length() - formatlen;
|
||||
"%d formats found in line %d\n", nformats, line);
|
||||
}
|
||||
if (!*source) break;
|
||||
numFormats = 0;
|
||||
line = newline;
|
||||
}
|
||||
// this is step 1: coding the string
|
||||
@ -1144,19 +1140,15 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
switch (*source)
|
||||
{
|
||||
case '$': // can't be: readToken would have made a token from this
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"INTERNAL ERROR: unconverted \\$ in quoted string\n");
|
||||
return false;
|
||||
case '?':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Quoted \\? only allowed in input: %s\n",
|
||||
source-1);
|
||||
return false;
|
||||
}
|
||||
buffer.append(skip);
|
||||
break;
|
||||
case '_':
|
||||
buffer.append(whitespace);
|
||||
break;
|
||||
case 'a':
|
||||
buffer.append(7);
|
||||
break;
|
||||
@ -1179,7 +1171,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
sscanf (source, "%3o%n", &temp, &n);
|
||||
if (temp > 0xFF)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Octal source %#o does not fit in byte: %s\n",
|
||||
temp, source-1);
|
||||
return false;
|
||||
@ -1195,7 +1187,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
case 'x': // hex numbers (max 2 digits after 0)
|
||||
if (sscanf (source+1, "%2x%n", &temp, &n) < 1)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Hex digit expected after \\x: %s\n",
|
||||
source-1);
|
||||
return false;
|
||||
@ -1220,7 +1212,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
sscanf (source, "%3u%n", &temp, &n);
|
||||
if (temp > 0xFF)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Decimal source %d does not fit in byte: %s\n",
|
||||
temp, source-1);
|
||||
return false;
|
||||
@ -1239,29 +1231,13 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
source++;
|
||||
continue;
|
||||
}
|
||||
if (quoted) // look for ending quotes, escapes, and formats
|
||||
if (quoted) // look for ending quotes and escapes
|
||||
{
|
||||
switch (*source)
|
||||
{
|
||||
case '\\': // escape next character
|
||||
escaped = true;
|
||||
break;
|
||||
case '%': // format specifier in string
|
||||
if (formatType != NoFormat)
|
||||
{
|
||||
// format is allowed here
|
||||
// just memorize position here and and do actual coding later
|
||||
// after all variables and parameters have been replaced
|
||||
// so that extra information is ready for format converter
|
||||
if (numFormats+1 == sizeof(formatPos))
|
||||
{
|
||||
errorMsg(line, "Too many formats in line");
|
||||
return false;
|
||||
}
|
||||
formatPos[numFormats++]=buffer.length();
|
||||
buffer.append(*source);
|
||||
break;
|
||||
}
|
||||
case '"':
|
||||
case '\'':
|
||||
if (*source == quoted) // ending quote
|
||||
@ -1310,13 +1286,13 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
{
|
||||
if (*p != 0)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Garbage after numeric source: %s", source);
|
||||
return false;
|
||||
}
|
||||
if (temp > 0xFF || temp < -0x80)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Value %s does not fit in byte\n", source);
|
||||
return false;
|
||||
}
|
||||
@ -1332,18 +1308,43 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
// try constant token
|
||||
struct {const char* name; char code;} codes [] =
|
||||
{
|
||||
{"skip", skip},
|
||||
{"?", skip},
|
||||
{"eot", 4 },
|
||||
{"ack", 6 },
|
||||
{"bel", 7 },
|
||||
{"bs", 8 },
|
||||
{"ht", 9 },
|
||||
{"tab", 9 },
|
||||
{"lf", '\n' },
|
||||
{"nl", '\n' },
|
||||
{"cr", '\r' },
|
||||
{"esc", esc },
|
||||
{"skip", skip},
|
||||
{"?", skip},
|
||||
{"nul", 0x00},
|
||||
{"soh", 0x01},
|
||||
{"stx", 0x02},
|
||||
{"etx", 0x03},
|
||||
{"eot", 0x04},
|
||||
{"enq", 0x05},
|
||||
{"ack", 0x06},
|
||||
{"bel", 0x07},
|
||||
{"bs", 0x08},
|
||||
{"ht", 0x09},
|
||||
{"tab", 0x09},
|
||||
{"lf", 0x0A},
|
||||
{"nl", 0x0A},
|
||||
{"vt", 0x0B},
|
||||
{"ff", 0x0C},
|
||||
{"np", 0x0C},
|
||||
{"cr", 0x0D},
|
||||
{"so", 0x0E},
|
||||
{"si", 0x0F},
|
||||
{"dle", 0x10},
|
||||
{"dc1", 0x11},
|
||||
{"dc2", 0x12},
|
||||
{"dc3", 0x13},
|
||||
{"dc4", 0x14},
|
||||
{"nak", 0x15},
|
||||
{"syn", 0x16},
|
||||
{"etb", 0x17},
|
||||
{"can", 0x18},
|
||||
{"em", 0x19},
|
||||
{"sub", 0x1A},
|
||||
{"esc", 0x1B},
|
||||
{"fs", 0x1C},
|
||||
{"gs", 0x1D},
|
||||
{"rs", 0x1E},
|
||||
{"us", 0x1F},
|
||||
{"del", 0x7f}
|
||||
};
|
||||
size_t i;
|
||||
@ -1355,7 +1356,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
c = codes[i].code;
|
||||
if (c == skip && formatType != ScanFormat)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Use of '%s' only allowed in input formats\n",
|
||||
source);
|
||||
return false;
|
||||
@ -1372,7 +1373,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
}
|
||||
if (c) continue;
|
||||
// source may contain a function name
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Unexpected word: %s\n", source);
|
||||
return false;
|
||||
}
|
||||
@ -1408,14 +1409,14 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
buffer.append(format_field);
|
||||
if (!client)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Using fieldname is not possible in this context\n");
|
||||
return false;
|
||||
}
|
||||
const char* fieldnameEnd = strchr(source+=2, ')');
|
||||
if (!fieldnameEnd)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Missing ')' after field name\n");
|
||||
return false;
|
||||
}
|
||||
@ -1427,7 +1428,7 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
StreamBuffer fieldAddress;
|
||||
if (!client->getFieldAddress(buffer(fieldname), fieldAddress))
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Field '%s' not found\n", buffer(fieldname));
|
||||
return false;
|
||||
}
|
||||
@ -1441,130 +1442,12 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
buffer.append(format);
|
||||
}
|
||||
const char* formatstart = source + 1;
|
||||
// look for flags
|
||||
streamFormat.flags = 0;
|
||||
bool loop = true;
|
||||
while (loop)
|
||||
{
|
||||
switch (*++source)
|
||||
{
|
||||
case '-':
|
||||
streamFormat.flags |= left_flag;
|
||||
break;
|
||||
case '+':
|
||||
streamFormat.flags |= sign_flag;
|
||||
break;
|
||||
case ' ':
|
||||
streamFormat.flags |= space_flag;
|
||||
break;
|
||||
case '#':
|
||||
streamFormat.flags |= alt_flag;
|
||||
break;
|
||||
case '0':
|
||||
streamFormat.flags |= zero_flag;
|
||||
break;
|
||||
case '*':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Use of skip modifier '*' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
if (fieldname)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Use of skip modifier '*' not allowed "
|
||||
"together with field name\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= skip_flag;
|
||||
break;
|
||||
default:
|
||||
loop = false;
|
||||
}
|
||||
}
|
||||
/*
|
||||
if (formatType != PrintFormat &&
|
||||
streamFormat.flags & (left_flag|sign_flag|space_flag|zero_flag))
|
||||
{
|
||||
errorMsg(line,
|
||||
"Use of format modifiers '-', '+', ' ', '0' "
|
||||
"only allowed in output formats\n");
|
||||
return false;
|
||||
}
|
||||
if (!(~streamFormat.flags & (left_flag|zero_flag)))
|
||||
{
|
||||
errorMsg(line,
|
||||
"Can't use modifiers '-' and '0' together\n");
|
||||
return false;
|
||||
}
|
||||
if (!(~streamFormat.flags & (space_flag|sign_flag)))
|
||||
{
|
||||
errorMsg(line,
|
||||
"Can't use modifiers ' ' and '+' together\n");
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
// look for width
|
||||
unsigned long val;
|
||||
char* p;
|
||||
val = strtoul (source, &p, 10);
|
||||
source = p;
|
||||
if (val > 0xFFFF)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Field width %ld out of range\n", val);
|
||||
return false;
|
||||
}
|
||||
streamFormat.width = (unsigned short)val;
|
||||
// look for prec
|
||||
streamFormat.prec = -1;
|
||||
if (*source == '.')
|
||||
{
|
||||
source++;
|
||||
val = strtoul(source, &p, 10);
|
||||
if (p == source)
|
||||
{
|
||||
debug("source = %s\n", source);
|
||||
errorMsg(line,
|
||||
"Numeric precision field expected after '.'\n");
|
||||
return false;
|
||||
}
|
||||
source = p;
|
||||
if (val > 0x7FFF)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Precision %ld out of range\n", val);
|
||||
return false;
|
||||
}
|
||||
streamFormat.prec = (short)val;
|
||||
}
|
||||
// look for converter
|
||||
streamFormat.conv = *source++;
|
||||
debug("StreamProtocolParser::Protocol::compileFormat: converter='%c'\n",
|
||||
streamFormat.conv);
|
||||
if (!streamFormat.conv || strchr("'\" (.0+-*", streamFormat.conv))
|
||||
{
|
||||
debug("StreamProtocolParser::Protocol::compileFormat: formatstr='%s'\n",
|
||||
formatstr);
|
||||
errorMsg(line,
|
||||
"Missing converter character\n");
|
||||
return false;
|
||||
}
|
||||
StreamFormatConverter* converter;
|
||||
converter = StreamFormatConverter::find(streamFormat.conv);
|
||||
if (!converter)
|
||||
{
|
||||
errorMsg(line,
|
||||
"No converter registered for format '%%%c'\n",
|
||||
streamFormat.conv);
|
||||
return false;
|
||||
}
|
||||
|
||||
// parse format and get info string
|
||||
StreamBuffer infoString;
|
||||
int type = converter->parse(streamFormat, infoString,
|
||||
source, formatType == ScanFormat);
|
||||
int type = StreamFormatConverter::parseFormat(source,
|
||||
formatType, streamFormat, infoString);
|
||||
|
||||
if (!type)
|
||||
{
|
||||
// parsing failed
|
||||
@ -1572,18 +1455,25 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
}
|
||||
if (type < long_format && type > pseudo_format)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Illegal format type %d returned from '%%%c' converter\n",
|
||||
type, streamFormat.conv);
|
||||
return false;
|
||||
}
|
||||
if (type == pseudo_format && fieldname)
|
||||
{
|
||||
errorMsg(line,
|
||||
error(line, filename(),
|
||||
"Fieldname not allowed with pseudo format: '%%(%s)%c'\n",
|
||||
buffer(fieldname), streamFormat.conv);
|
||||
return false;
|
||||
}
|
||||
if (fieldname && streamFormat.flags & skip_flag)
|
||||
{
|
||||
error(line, filename(),
|
||||
"Use of skip modifier '*' not allowed "
|
||||
"together with redirection\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.type = static_cast<StreamFormatType>(type);
|
||||
if (infoString && infoString[-1] != eos)
|
||||
{
|
||||
@ -1621,13 +1511,13 @@ compileCommands(StreamBuffer& buffer, const char*& source, Client* client)
|
||||
args = source + strlen(source)+1+sizeof(int);
|
||||
if (!client->compileCommand(this, buffer, command, args))
|
||||
{
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"in command '%s'\n", command);
|
||||
return false;
|
||||
}
|
||||
if (*args)
|
||||
{
|
||||
errorMsg(getLineNumber(source),
|
||||
error(getLineNumber(source), filename(),
|
||||
"Garbage after '%s' command: '%s'\n",
|
||||
command, args);
|
||||
return false;
|
||||
|
@ -32,7 +32,7 @@ public:
|
||||
|
||||
enum Codes
|
||||
{
|
||||
eos = 0, skip, format, format_field, last_function_code
|
||||
eos = 0, skip, whitespace, format, format_field, last_function_code
|
||||
};
|
||||
|
||||
class Client;
|
||||
@ -75,8 +75,6 @@ public:
|
||||
bool compileString(StreamBuffer& buffer, const char*& source,
|
||||
FormatType formatType = NoFormat, Client* = NULL, int quoted = false);
|
||||
bool checkUnused();
|
||||
void errorMsg(int line, const char* fmt, ...)
|
||||
__attribute__ ((format(printf,3,4)));
|
||||
~Protocol();
|
||||
void report();
|
||||
};
|
||||
@ -115,8 +113,6 @@ private:
|
||||
const char* specialchars = NULL, bool eofAllowed = false);
|
||||
bool parseAssignment(const char* variable, Protocol&);
|
||||
bool parseValue(StreamBuffer& buffer, bool lazy = false);
|
||||
void errorMsg(const char* fmt, ...)
|
||||
__attribute__ ((format(printf,2,3)));
|
||||
|
||||
protected:
|
||||
~StreamProtocolParser(); // get rid of cygnus-2.7.2 compiler warning
|
||||
|
539
src/TimestampConverter.cc
Normal file
539
src/TimestampConverter.cc
Normal file
@ -0,0 +1,539 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
|
||||
* (C) 2010 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is the time stamp converter of StreamDevice. *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include "StreamFormatConverter.h"
|
||||
#include "StreamError.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
#include <errno.h>
|
||||
|
||||
#ifdef _WIN32
|
||||
#define tzset() _tzset()
|
||||
#define timezone _timezone
|
||||
#define localtime_r(timet,tm) localtime_s(tm,timet) /* Windows sucks */
|
||||
#endif
|
||||
|
||||
#ifdef __rtems__
|
||||
#define timezone _timezone
|
||||
#endif
|
||||
|
||||
#ifdef vxWorks
|
||||
int timezone = 0;
|
||||
#define tzset() do {\
|
||||
(void) (sscanf(getenv("TIMEZONE"), "%*[^:]::%d", &timezone) || \
|
||||
sscanf(getenv("TIMEZONE"), "%*[^:]:%*[^:]:%d", &timezone) || \
|
||||
sscanf(getenv("EPICS_TS_MIN_WEST"), "%d", &timezone));\
|
||||
timezone*=60;\
|
||||
} while (0)
|
||||
#endif
|
||||
|
||||
class TimestampConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
bool printDouble(const StreamFormat&, StreamBuffer&, double);
|
||||
int scanDouble(const StreamFormat&, const char*, double&);
|
||||
};
|
||||
|
||||
int TimestampConverter::
|
||||
parse(const StreamFormat&, StreamBuffer& info,
|
||||
const char*& source, bool)
|
||||
{
|
||||
unsigned int n;
|
||||
char* c;
|
||||
|
||||
if (*source == '(')
|
||||
{
|
||||
while (*++source != ')')
|
||||
{
|
||||
switch (*source)
|
||||
{
|
||||
case 0:
|
||||
error ("missing ')' after %%T format\n");
|
||||
return false;
|
||||
case esc:
|
||||
info.append(*++source);
|
||||
if (*source == '%') info.append('%');
|
||||
break;
|
||||
case '%':
|
||||
source++;
|
||||
/* look for formatted fractions like %3f */
|
||||
if (isdigit(*source))
|
||||
{
|
||||
n = strtoul(source, &c, 10);
|
||||
if (*c == 'f')
|
||||
{
|
||||
source = c;
|
||||
info.printf("%%0%uf", n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* look for nanoseconds %N of %f */
|
||||
if (*source == 'N' || *source == 'f')
|
||||
{
|
||||
info.printf("%%09f");
|
||||
break;
|
||||
}
|
||||
/* look for seconds with fractions like %.3S */
|
||||
if (*source == '.')
|
||||
{
|
||||
c = (char*) source+1;
|
||||
n = isdigit(*c) ? strtoul(c, &c, 10) : 9;
|
||||
if (toupper(*c) == 'S')
|
||||
{
|
||||
source = c;
|
||||
info.printf("%%%c.%%0%uf", *c, n);
|
||||
break;
|
||||
}
|
||||
}
|
||||
/* else normal format */
|
||||
info.append('%');
|
||||
default:
|
||||
info.append(*source);
|
||||
}
|
||||
}
|
||||
source++;
|
||||
info.append('\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
info.append("%Y-%m-%d %H:%M:%S").append('\0');
|
||||
}
|
||||
return double_format;
|
||||
}
|
||||
|
||||
bool TimestampConverter::
|
||||
printDouble(const StreamFormat& format, StreamBuffer& output, double value)
|
||||
{
|
||||
struct tm brokenDownTime;
|
||||
char buffer [40];
|
||||
char fracbuffer [15];
|
||||
int length;
|
||||
time_t sec;
|
||||
double frac;
|
||||
int i, n;
|
||||
char* c;
|
||||
char* p;
|
||||
|
||||
sec = (time_t) value;
|
||||
frac = value - sec;
|
||||
localtime_r(&sec, &brokenDownTime);
|
||||
debug ("TimestampConverter::printDouble %f, '%s'\n", value, format.info);
|
||||
length = strftime(buffer, sizeof(buffer), format.info, &brokenDownTime);
|
||||
i = output.length();
|
||||
output.append(buffer, length);
|
||||
|
||||
/* look for fractional seconds */
|
||||
while ((i = output.find("%0",i)) != -1)
|
||||
{
|
||||
n = strtol(output(i+1), &c, 10);
|
||||
if (*c++ != 'f') return false;
|
||||
/* print fractional part */
|
||||
sprintf(fracbuffer, "%.*f", n, frac);
|
||||
p = strchr(fracbuffer, '.')+1;
|
||||
output.replace(i, c-output(i), p);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/* many OS don't have strptime or strptime does not fully support
|
||||
all fields, e.g. %z.
|
||||
*/
|
||||
|
||||
static int strmatch(const char*& input, const char** strings, int minlen)
|
||||
{
|
||||
int i;
|
||||
int c;
|
||||
|
||||
for (i=0; strings[i]; i++) {
|
||||
for (c=0; ; c++)
|
||||
{
|
||||
if (strings[i][c] == 0) {
|
||||
input += c;
|
||||
return i;
|
||||
}
|
||||
if (tolower(input[c]) != strings[i][c]) {
|
||||
if (c >= minlen) {
|
||||
input += c;
|
||||
return i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
static int nummatch(const char*& input, int min, int max)
|
||||
{
|
||||
int i;
|
||||
char *c;
|
||||
|
||||
i = strtol(input, &c, 10);
|
||||
if (c == input) return -10000;
|
||||
if (i < min || i > max) return -10000;
|
||||
input = c;
|
||||
return i;
|
||||
}
|
||||
|
||||
static const char* scantime(const char* input, const char* format, struct tm *tm, unsigned long *ns)
|
||||
{
|
||||
static const char* months[] = {
|
||||
"january", "february", "march", "april", "may", "june",
|
||||
"july", "august", "september", "november", "december", 0 };
|
||||
static const char* ampm[] = {
|
||||
"am", "pm", 0 };
|
||||
|
||||
int i, n;
|
||||
int pm = -1;
|
||||
int century = -1;
|
||||
int zone = 0;
|
||||
|
||||
tzset();
|
||||
zone = timezone/60;
|
||||
debug ("TimestampConverter::scantime: native time zone = %d\n", zone);
|
||||
|
||||
while (*format)
|
||||
{
|
||||
switch (*format)
|
||||
{
|
||||
case '%':
|
||||
debug ("TimestampConverter::scantime: input = '%s'\n", input);
|
||||
format++;
|
||||
startover:
|
||||
switch (*format++)
|
||||
{
|
||||
/* Modifiers (ignore) */
|
||||
case 'E':
|
||||
case 'O':
|
||||
goto startover;
|
||||
/* constants */
|
||||
case 0: /* stray % at end of format string */
|
||||
format--;
|
||||
case '%':
|
||||
if (*input++ != '%') return NULL;
|
||||
break;
|
||||
case 'n':
|
||||
if (*input++ != '\n') return NULL;
|
||||
break;
|
||||
case 't':
|
||||
if (*input++ != '\t') return NULL;
|
||||
break;
|
||||
/* ignored */
|
||||
case 'A': /* day of week name */
|
||||
case 'a':
|
||||
while (isalpha((int)*input)) input++;
|
||||
/* ignore */
|
||||
break;
|
||||
case 'u': /* day of week number (Monday = 1 to Sunday = 7) */
|
||||
case 'w': /* day of week number (Sunday = 0 to Saturday = 6) */
|
||||
i = nummatch(input, 0, 7);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing day of week: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
debug ("TimestampConverter::scantime: day of week = %d\n", i);
|
||||
/* ignore */
|
||||
break;
|
||||
case 'U': /* week number */
|
||||
case 'W':
|
||||
case 'V':
|
||||
i = nummatch(input, 0, 53);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing week number: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
debug ("TimestampConverter::scantime: week number = %d\n", i);
|
||||
/* ignore */
|
||||
break;
|
||||
case 'j': /* day of year */
|
||||
i = nummatch(input, 0, 366);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing day of year: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
debug ("TimestampConverter::scantime: day of year = %d\n", i);
|
||||
/* ignore */
|
||||
break;
|
||||
case 'Z': /* time zone name */
|
||||
while (isalpha((int)*input)) input++;
|
||||
/* ignore */
|
||||
break;
|
||||
/* date */
|
||||
case 'b': /* month */
|
||||
case 'h':
|
||||
case 'B':
|
||||
case 'm':
|
||||
i = strmatch(input, months, 3);
|
||||
if (i < 0)
|
||||
{
|
||||
i = nummatch(input, 1, 12);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing month: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
i--; /* Jan = 0 */
|
||||
}
|
||||
tm->tm_mon = i;
|
||||
debug ("TimestampConverter::scantime: month = %d (%s)\n", tm->tm_mon+1, months[tm->tm_mon]);
|
||||
break;
|
||||
case 'd': /* day of month */
|
||||
case 'e':
|
||||
i = nummatch(input, 1, 31);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing day of month: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
tm->tm_mday = i;
|
||||
debug ("TimestampConverter::scantime: day = %d\n", tm->tm_mday);
|
||||
break;
|
||||
case 'Y': /* year */
|
||||
case 'y':
|
||||
i = strtol(input, (char**)&input, 10);
|
||||
if (i < 100)
|
||||
{ /* 2 digit year */
|
||||
if (century == -1) century = (i >= 69);
|
||||
tm->tm_year = i + century * 100; /* 0 = 1900 */
|
||||
}
|
||||
else
|
||||
{ /* 4 digit year */
|
||||
tm->tm_year = i - 1900; /* 0 = 1900 */
|
||||
}
|
||||
debug ("TimestampConverter::scantime: year = %d\n", tm->tm_year + 1900);
|
||||
break;
|
||||
case 'C': /* century */
|
||||
i = nummatch(input, 0, 99);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing century: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
century = i - 19;
|
||||
tm->tm_year = tm->tm_year%100 + 100 * i; /* 0 = 1900 */
|
||||
debug ("TimestampConverter::scantime: year = %d\n", tm->tm_year + 1900);
|
||||
break;
|
||||
|
||||
/* time */
|
||||
case 'H': /* hour */
|
||||
case 'k':
|
||||
case 'I':
|
||||
case 'l':
|
||||
i = nummatch(input, 0, 23);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing hour: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
if ((pm == 0) && (i == 12)) i = 0;
|
||||
if ((pm == 1) && (i < 12)) i += 12;
|
||||
tm->tm_hour = i;
|
||||
debug ("TimestampConverter::scantime: hour = %d\n", tm->tm_hour);
|
||||
break;
|
||||
case 'P': /* AM / PM */
|
||||
case 'p':
|
||||
i = strmatch(input, ampm, 1);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing am/pm: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
pm = i;
|
||||
if ((pm == 0) && (tm->tm_hour == 12)) tm->tm_hour = 0;
|
||||
if ((pm == 1) && (tm->tm_hour < 12)) tm->tm_hour += 12;
|
||||
debug ("TimestampConverter::scantime: %s hour = %d\n", pm?"PM":"AM", tm->tm_hour);
|
||||
break;
|
||||
case 'M': /* minute */
|
||||
i = nummatch(input, 1, 59);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing minute: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
tm->tm_min = i;
|
||||
debug ("TimestampConverter::scantime: min = %d\n", tm->tm_min);
|
||||
break;
|
||||
case 'S': /* second */
|
||||
i = nummatch(input, 1, 60);
|
||||
if (i < 0)
|
||||
{
|
||||
error ("error parsing week second: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
tm->tm_sec = i;
|
||||
debug ("TimestampConverter::scantime: sec = %d\n", tm->tm_sec);
|
||||
break;
|
||||
case 's': /* second since 1970 */
|
||||
i = strtol(input, (char**)&input, 10);
|
||||
tm->tm_sec = i;
|
||||
tm->tm_mon = -1;
|
||||
tm->tm_isdst = 0;
|
||||
debug ("TimestampConverter::scantime: sec = %d\n", tm->tm_sec);
|
||||
break;
|
||||
case '0': /* fractions of seconds like %09f */
|
||||
n = strtol(format-1, (char**)&format, 10);
|
||||
if (*format++ != 'f') return NULL;
|
||||
debug ("max %d digits fraction in '%s'\n", n, input);
|
||||
i = 0;
|
||||
while (n-- && isdigit(*input))
|
||||
{
|
||||
i *= 10;
|
||||
i += *input++ - '0';
|
||||
}
|
||||
while (i < 100000000) i *= 10;
|
||||
*ns = i;
|
||||
debug ("TimestampConverter::scantime: nanosec = %d, rest '%s'\n", i, input);
|
||||
break;
|
||||
case 'z': /* time zone offset */
|
||||
i = nummatch(input, -2400, 2400);
|
||||
if (i < -2400)
|
||||
{
|
||||
error ("error parsing time zone: '%.20s'\n", input);
|
||||
return NULL;
|
||||
}
|
||||
zone = i / 100 * 60 + i % 100;
|
||||
tm->tm_isdst = 0;
|
||||
debug ("TimestampConverter::scantime: zone = %d\n", zone);
|
||||
break;
|
||||
case '+': /* set time zone in format string */
|
||||
case '-':
|
||||
format--;
|
||||
i = nummatch(format, -2400, 2400);
|
||||
zone = i / 100 * 60 + i % 100;
|
||||
tm->tm_isdst = 0;
|
||||
debug ("TimestampConverter::scantime: zone = %d\n", zone);
|
||||
break;
|
||||
/* shortcuts */
|
||||
case 'c':
|
||||
if ((input = scantime(input, "%a %b %d %H:%M:%S %Y", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'D':
|
||||
if ((input = scantime(input, "%m/%d/%y", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'F':
|
||||
if ((input = scantime(input, "%Y-%m-%d", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'R':
|
||||
if ((input = scantime(input, "%H:%M", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'T':
|
||||
if ((input = scantime(input, "%H:%M:%S", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'x':
|
||||
if ((input = scantime(input, "%m/%d/%y", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
case 'X':
|
||||
case 'r':
|
||||
if ((input = scantime(input, "%I:%M:%S %p", tm, ns)) == NULL)
|
||||
return NULL;
|
||||
break;
|
||||
default:
|
||||
error ("unknown time format %%%s\n", --format);
|
||||
return NULL;
|
||||
}
|
||||
break;
|
||||
case ' ':
|
||||
format++;
|
||||
while (isspace(*input)) input++;
|
||||
break;
|
||||
default:
|
||||
if (*format++ != *input++)
|
||||
{
|
||||
error("input '%.20s' does not match constant '%.20s'\n", --input, --format);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
}
|
||||
zone -= timezone/60;
|
||||
tm->tm_min += zone;
|
||||
tm->tm_hour += tm->tm_min / 60;
|
||||
tm->tm_min %= 60;
|
||||
if (tm->tm_min < 0)
|
||||
{
|
||||
tm->tm_min += 60;
|
||||
tm->tm_hour -= 1;
|
||||
}
|
||||
tm->tm_mday -= tm->tm_hour / 24;
|
||||
tm->tm_hour %= 24;
|
||||
if (tm->tm_hour < 0)
|
||||
{
|
||||
tm->tm_min += 24;
|
||||
tm->tm_mday -= 1;
|
||||
}
|
||||
return input;
|
||||
}
|
||||
|
||||
|
||||
|
||||
int TimestampConverter::
|
||||
scanDouble(const StreamFormat& format, const char* input, double& value)
|
||||
{
|
||||
struct tm brokenDownTime;
|
||||
time_t seconds;
|
||||
unsigned long nanoseconds;
|
||||
const char* end;
|
||||
|
||||
/* Init time stamp with "today" */
|
||||
time (&seconds);
|
||||
localtime_r(&seconds, &brokenDownTime);
|
||||
brokenDownTime.tm_sec = 0;
|
||||
brokenDownTime.tm_min = 0;
|
||||
brokenDownTime.tm_hour = 0;
|
||||
brokenDownTime.tm_yday = 0;
|
||||
brokenDownTime.tm_isdst = -1;
|
||||
nanoseconds = 0;
|
||||
|
||||
end = scantime(input, format.info, &brokenDownTime, &nanoseconds);
|
||||
if (end == NULL) {
|
||||
error ("error parsing time\n");
|
||||
return -1;
|
||||
}
|
||||
if (brokenDownTime.tm_mon == -1) {
|
||||
seconds = brokenDownTime.tm_sec;
|
||||
} else {
|
||||
seconds = mktime(&brokenDownTime);
|
||||
if (seconds == (time_t) -1 && brokenDownTime.tm_yday == 0)
|
||||
{
|
||||
error ("mktime failed for %02d/%02d/%04d %02d:%02d:%02d\n",
|
||||
brokenDownTime.tm_mon+1,
|
||||
brokenDownTime.tm_mday,
|
||||
brokenDownTime.tm_year+1900,
|
||||
brokenDownTime.tm_hour,
|
||||
brokenDownTime.tm_min,
|
||||
brokenDownTime.tm_sec);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
value = seconds + nanoseconds*1e-9;
|
||||
return end-input;
|
||||
}
|
||||
|
||||
RegisterConverter (TimestampConverter, "T");
|
@ -22,7 +22,7 @@
|
||||
#define devStream_h
|
||||
|
||||
#define STREAM_MAJOR 2
|
||||
#define STREAM_MINOR 2
|
||||
#define STREAM_MINOR 4
|
||||
|
||||
#if defined(__vxworks) || defined(vxWorks)
|
||||
#include <vxWorks.h>
|
||||
@ -85,7 +85,6 @@ long streamReport(int interest);
|
||||
long streamReadWrite(dbCommon *record);
|
||||
long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt);
|
||||
long streamPrintf(dbCommon *record, format_t *format, ...);
|
||||
long streamScanSep(dbCommon *record);
|
||||
long streamScanfN(dbCommon *record, format_t *format,
|
||||
void*, size_t maxStringSize);
|
||||
|
||||
@ -99,7 +98,7 @@ long streamScanfN(dbCommon *record, format_t *format,
|
||||
#define devStreamGetIointInfo streamGetIointInfo
|
||||
#define devStreamPrintf streamPrintf
|
||||
#define devStreamPrintSep(record) (0)
|
||||
#define devStreamScanSep streamScanSep
|
||||
#define devStreamScanSep (0)
|
||||
#define devStreamScanf(record, format, value) \
|
||||
streamScanfN(record, format, value, MAX_STRING_SIZE)
|
||||
#define streamScanf(record, format, value) \
|
||||
|
@ -20,8 +20,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aaiRecord.h>
|
||||
#include <errlog.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -280,7 +281,8 @@ static long initRecord (dbCommon *record)
|
||||
record->name);
|
||||
return ERROR;
|
||||
}
|
||||
return streamInitRecord (record, &aai->inp, readData, writeData);
|
||||
return streamInitRecord (record, &aai->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -20,8 +20,9 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aaoRecord.h>
|
||||
#include <errlog.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -280,7 +281,8 @@ static long initRecord (dbCommon *record)
|
||||
record->name);
|
||||
return ERROR;
|
||||
}
|
||||
return streamInitRecord (record, &aao->out, readData, writeData);
|
||||
return streamInitRecord (record, &aao->out, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,19 +18,19 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aiRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aiRecord *ai = (aiRecord *) record;
|
||||
double val;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
double val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
if (ai->aslo != 0.0) val *= ai->aslo;
|
||||
val += ai->aoff;
|
||||
@ -43,7 +43,10 @@ static long readData (dbCommon *record, format_t *format)
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamScanf (record, format, &ai->rval);
|
||||
long rval;
|
||||
if (streamScanf (record, format, &rval)) return ERROR;
|
||||
ai->rval = rval;
|
||||
return OK;
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
@ -64,7 +67,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamPrintf (record, format, ai->rval);
|
||||
return streamPrintf (record, format, (long) ai->rval);
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
@ -74,7 +77,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
aiRecord *ai = (aiRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &ai->inp, readData, writeData);
|
||||
return streamInitRecord (record, &ai->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aoRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -51,12 +51,12 @@ static long readData (dbCommon *record, format_t *format)
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
aoRecord *ao = (aoRecord *) record;
|
||||
double val;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
double val;
|
||||
if (INIT_RUN) val = ao->val;
|
||||
else val = ao->oval;
|
||||
val -= ao->aoff;
|
||||
@ -65,7 +65,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
}
|
||||
case DBF_LONG:
|
||||
{
|
||||
return streamPrintf (record, format, ao->rval);
|
||||
return streamPrintf (record, format, (long) ao->rval);
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <biRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
@ -90,7 +90,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
biRecord *bi = (biRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &bi->inp, readData, writeData);
|
||||
return streamInitRecord (record, &bi->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <boRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
|
@ -17,7 +17,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <postfix.h>
|
||||
#include <calcoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
@ -68,7 +68,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
calcoutRecord *co = (calcoutRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &co->out, readData, writeData);
|
||||
return streamInitRecord (record, &co->out, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <longinRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -28,7 +28,10 @@ static long readData (dbCommon *record, format_t *format)
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
{
|
||||
return streamScanf (record, format, &li->val);
|
||||
long val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
li->val = val;
|
||||
return OK;
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
@ -39,7 +42,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
{
|
||||
return streamPrintf (record, format, li->val);
|
||||
return streamPrintf (record, format, (long) li->val);
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
@ -48,7 +51,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
longinRecord *li = (longinRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &li->inp, readData, writeData);
|
||||
return streamInitRecord (record, &li->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <longoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -29,7 +29,10 @@ static long readData (dbCommon *record, format_t *format)
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
{
|
||||
return streamScanf (record, format, &lo->val);
|
||||
long val;
|
||||
if (streamScanf (record, format, &val)) return ERROR;
|
||||
lo->val = val;
|
||||
return OK;
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
@ -40,7 +43,7 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
|
||||
if (format->type == DBF_LONG || format->type == DBF_ENUM)
|
||||
{
|
||||
return streamPrintf (record, format, lo->val);
|
||||
return streamPrintf (record, format, (long) lo->val);
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
@ -49,7 +52,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
longoutRecord *lo = (longoutRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &lo->out, readData, writeData);
|
||||
return streamInitRecord (record, &lo->out, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbbiDirectRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -66,7 +66,8 @@ static long initRecord (dbCommon *record)
|
||||
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
|
||||
|
||||
mbbiD->mask <<= mbbiD->shft;
|
||||
return streamInitRecord (record, &mbbiD->inp, readData, writeData);
|
||||
return streamInitRecord (record, &mbbiD->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbbiRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
@ -114,7 +114,8 @@ static long initRecord (dbCommon *record)
|
||||
mbbiRecord *mbbi = (mbbiRecord *) record;
|
||||
|
||||
mbbi->mask <<= mbbi->shft;
|
||||
return streamInitRecord (record, &mbbi->inp, readData, writeData);
|
||||
return streamInitRecord (record, &mbbi->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbboDirectRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbboRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <stringinRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -48,7 +48,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
stringinRecord *si = (stringinRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &si->inp, readData, writeData);
|
||||
return streamInitRecord (record, &si->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <stringoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -48,7 +48,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
stringoutRecord *so = (stringoutRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &so->out, readData, writeData);
|
||||
return streamInitRecord (record, &so->out, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
@ -18,9 +18,10 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <waveformRecord.h>
|
||||
#include <string.h>
|
||||
#include <errlog.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
@ -271,7 +272,8 @@ static long initRecord (dbCommon *record)
|
||||
{
|
||||
waveformRecord *wf = (waveformRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &wf->inp, readData, writeData);
|
||||
return streamInitRecord (record, &wf->inp, readData, writeData) == ERROR ?
|
||||
ERROR : OK;
|
||||
}
|
||||
|
||||
struct {
|
||||
|
204
src/memguard.cc
204
src/memguard.cc
@ -1,204 +0,0 @@
|
||||
/***************************************************************
|
||||
* memguard *
|
||||
* *
|
||||
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* Include memguard.h and link memguard.o to get some memory *
|
||||
* guardiance. Allocated memory (using new) is tracked with *
|
||||
* source file and line of the new operator. Deleted memory is *
|
||||
* checked for duplicate deletion. *
|
||||
* Call memguardReport() to see all allocated memory. *
|
||||
* Calling memguardReport() shows currently allocated memory. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#undef new
|
||||
#undef delete
|
||||
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
struct memheader
|
||||
{
|
||||
static memheader* first;
|
||||
memheader* next;
|
||||
size_t size;
|
||||
long line;
|
||||
const char* file;
|
||||
long fill1;
|
||||
long fill2;
|
||||
unsigned long magic;
|
||||
};
|
||||
|
||||
extern "C" void memguardReport();
|
||||
|
||||
class memguard
|
||||
{
|
||||
public:
|
||||
memguard() {atexit(memguardReport);}
|
||||
void report();
|
||||
};
|
||||
|
||||
memheader* memheader::first = NULL;
|
||||
|
||||
static memguard guard;
|
||||
|
||||
extern "C" void memguardReport()
|
||||
{
|
||||
guard.report();
|
||||
}
|
||||
|
||||
void* operator new(size_t size, const char* file, long line) throw (std::bad_alloc)
|
||||
{
|
||||
memheader* header;
|
||||
memheader** prev;
|
||||
|
||||
header = (memheader*) malloc(size + sizeof(memheader));
|
||||
if (!header)
|
||||
{
|
||||
fprintf (stderr, "out of memory\n");
|
||||
if (file) fprintf (stderr, "in %s line %ld\n", file, line);
|
||||
guard.report();
|
||||
abort();
|
||||
}
|
||||
header->magic = 0xA110caed;
|
||||
for (prev = &memheader::first; *prev; prev = &(*prev)->next);
|
||||
*prev = header;
|
||||
header->next = NULL;
|
||||
header->size = size;
|
||||
header->line = line;
|
||||
header->file = file;
|
||||
// fprintf(stderr, "allocating %p: %d bytes for %s line %ld\n",
|
||||
// header+1, size, file, line);
|
||||
return header+1;
|
||||
}
|
||||
|
||||
|
||||
void* operator new[](size_t size, const char* file, long line) throw (std::bad_alloc)
|
||||
{
|
||||
return operator new(size, file, line);
|
||||
}
|
||||
|
||||
void* operator new(size_t size) throw (std::bad_alloc)
|
||||
{
|
||||
return operator new(size, NULL, 0);
|
||||
}
|
||||
|
||||
void* operator new[](size_t size) throw (std::bad_alloc)
|
||||
{
|
||||
return operator new(size, NULL, 0);
|
||||
}
|
||||
|
||||
static const char* deleteFile = NULL;
|
||||
static long deleteLine = 0;
|
||||
|
||||
int memguardLocation(const char* file, long line)
|
||||
{
|
||||
deleteFile = file;
|
||||
deleteLine = line;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void operator delete (void* memory) throw ()
|
||||
{
|
||||
memheader* header;
|
||||
memheader** prev;
|
||||
|
||||
if (!memory)
|
||||
{
|
||||
deleteFile = NULL;
|
||||
deleteLine = 0;
|
||||
return;
|
||||
}
|
||||
header = ((memheader*)memory)-1;
|
||||
if (header->magic == 0xDeadBeef)
|
||||
{
|
||||
fprintf (stderr, "memory at %p deleted twice\n", memory);
|
||||
if (header->file)
|
||||
fprintf (stderr, "allocated by %s line %ld\n",
|
||||
header->file, header->line);
|
||||
guard.report();
|
||||
abort();
|
||||
}
|
||||
for (prev = &memheader::first; *prev; prev = &(*prev)->next)
|
||||
{
|
||||
if (*prev == header)
|
||||
{
|
||||
if (header->magic != 0xA110caed)
|
||||
{
|
||||
fprintf (stderr, "deleted memory at %p corrupt\n", memory);
|
||||
if (header->file)
|
||||
fprintf (stderr, "allocated by %s line %ld\n",
|
||||
header->file, header->line);
|
||||
guard.report();
|
||||
abort();
|
||||
}
|
||||
*prev = header->next;
|
||||
header->magic = 0xDeadBeef;
|
||||
// fprintf(stderr, "deleting %p ", memory);
|
||||
// if (header->file) fprintf(stderr, "allocated in %s line %ld\n",
|
||||
// header->file, header->line);
|
||||
// if (deleteFile) fprintf(stderr, "probably deleted in %s line %ld\n",
|
||||
// deleteFile, deleteLine);
|
||||
deleteFile = NULL;
|
||||
deleteLine = 0;
|
||||
free(header);
|
||||
// fprintf(stderr, "done\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
if (deleteFile)
|
||||
{
|
||||
fprintf (stderr, "deleted memory at %p was never allocated\n", memory);
|
||||
fprintf (stderr, "delete was probably called in %s line %ld\n",
|
||||
deleteFile, deleteLine);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void operator delete[](void* memory) throw ()
|
||||
{
|
||||
operator delete(memory);
|
||||
}
|
||||
|
||||
void memguard::report()
|
||||
{
|
||||
memheader* header;
|
||||
unsigned long sum = 0;
|
||||
unsigned long chunks = 0;
|
||||
|
||||
if (memheader::first)
|
||||
{
|
||||
fprintf(stderr, "allocated memory:\n");
|
||||
for (header = memheader::first; header; header = header->next)
|
||||
{
|
||||
chunks++;
|
||||
sum += header->size;
|
||||
if (header->file)
|
||||
{
|
||||
fprintf (stderr, "%p: %d bytes by %s line %ld\n",
|
||||
header+1, header->size, header->file, header->line);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf (stderr, "%p: %d bytes by unknown\n",
|
||||
header+1, header->size);
|
||||
}
|
||||
if (header->magic != 0xA110caed)
|
||||
{
|
||||
fprintf (stderr, "memory list is corrupt\n");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
fprintf (stderr, "%lu bytes in %lu chunks\n", sum, chunks);
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "no memory allocated\n");
|
||||
}
|
||||
}
|
||||
|
@ -1,30 +0,0 @@
|
||||
/***************************************************************
|
||||
* memguard *
|
||||
* *
|
||||
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* Include memguard.h and link memguard.o to get some memory *
|
||||
* guardiance. Allocated memory (using new) is tracked with *
|
||||
* source file and line of the new operator. Deleted memory is *
|
||||
* checked for duplicate deletion. *
|
||||
* Call memguardReport() to see all allocated memory. *
|
||||
* Calling memguardReport() shows currently allocated memory. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#ifdef __cplusplus
|
||||
#ifndef memguard_h
|
||||
#define memguard_h
|
||||
#include <new.h>
|
||||
#define MEMGUARD
|
||||
void* operator new(size_t, const char*, long) throw (std::bad_alloc);
|
||||
void* operator new[](size_t, const char*, long) throw (std::bad_alloc);
|
||||
#define new new(__FILE__,__LINE__)
|
||||
int memguardLocation(const char*, long);
|
||||
#define delete if (memguardLocation(__FILE__,__LINE__)) delete
|
||||
extern "C" void memguardReport();
|
||||
#endif
|
||||
#endif
|
25
srcSynApps/Makefile
Normal file
25
srcSynApps/Makefile
Normal file
@ -0,0 +1,25 @@
|
||||
TOP=../..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
-include ../src/CONFIG_STREAM
|
||||
-include ../../src/CONFIG_STREAM
|
||||
|
||||
LIBRARY_DEFAULT = streamSynApps
|
||||
|
||||
DBD += $(LIBRARY_DEFAULT).dbd
|
||||
|
||||
ifeq ($(LOADABLE_MODULE),YES)
|
||||
SRCS += $(LIBRARY_DEFAULT)_registerRecordDeviceDriver.cpp
|
||||
endif
|
||||
SRCS += $(SYNAPPS_RECORDS:%=dev%Stream.c)
|
||||
|
||||
LIB_LIBS += stream $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
# create streamSynApps.dbd from all SYNAPPS_RECORDS
|
||||
$(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../../src/CONFIG_STREAM
|
||||
@for r in $(SYNAPPS_RECORDS); \
|
||||
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
|
||||
done > $@
|
108
srcSynApps/devscalcoutStream.c
Normal file
108
srcSynApps/devscalcoutStream.c
Normal file
@ -0,0 +1,108 @@
|
||||
/***************************************************************
|
||||
* Stream Device record interface for scalcout records *
|
||||
* *
|
||||
* (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is an EPICS record Interface for StreamDevice. *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include <sCalcoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
/* scalcout record has a bug: it never calls init_record
|
||||
of the device support.
|
||||
Fix: sCalcoutRecord.c, end of init_record() add
|
||||
|
||||
if(pscalcoutDSET->init_record ) {
|
||||
return (*pscalcoutDSET->init_record)(pcalc);
|
||||
}
|
||||
The bug has been fixed in version 2-6-1.
|
||||
*/
|
||||
|
||||
static long readData (dbCommon *record, format_t *format)
|
||||
{
|
||||
scalcoutRecord *sco = (scalcoutRecord *) record;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
return streamScanf (record, format, &sco->val);
|
||||
}
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
long lval;
|
||||
|
||||
if (streamScanf (record, format, &lval)) return ERROR;
|
||||
sco->val = lval;
|
||||
return OK;
|
||||
}
|
||||
case DBF_STRING:
|
||||
{
|
||||
return (streamScanfN (record, format,
|
||||
sco->sval, sizeof(sco->val)));
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
scalcoutRecord *sco = (scalcoutRecord *) record;
|
||||
|
||||
switch (format->type)
|
||||
{
|
||||
case DBF_DOUBLE:
|
||||
{
|
||||
return streamPrintf (record, format, sco->oval);
|
||||
}
|
||||
case DBF_LONG:
|
||||
case DBF_ENUM:
|
||||
{
|
||||
return streamPrintf (record, format, (long)sco->oval);
|
||||
}
|
||||
case DBF_STRING:
|
||||
{
|
||||
return streamPrintf (record, format, sco->osv);
|
||||
}
|
||||
}
|
||||
return ERROR;
|
||||
}
|
||||
|
||||
static long initRecord (dbCommon *record)
|
||||
{
|
||||
scalcoutRecord *sco = (scalcoutRecord *) record;
|
||||
|
||||
return streamInitRecord (record, &sco->out, readData, writeData);
|
||||
}
|
||||
|
||||
struct {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
DEVSUPFUN init;
|
||||
DEVSUPFUN init_record;
|
||||
DEVSUPFUN get_ioint_info;
|
||||
DEVSUPFUN write;
|
||||
} devscalcoutStream = {
|
||||
5,
|
||||
streamReport,
|
||||
streamInit,
|
||||
initRecord,
|
||||
streamGetIointInfo,
|
||||
streamWrite,
|
||||
};
|
||||
|
||||
epicsExportAddress(dset,devscalcoutStream);
|
4
streamApp/.cvsignore
Normal file
4
streamApp/.cvsignore
Normal file
@ -0,0 +1,4 @@
|
||||
StreamDebug.log
|
||||
streamApp
|
||||
streamApp.exe*
|
||||
TS.*
|
76
streamApp/Makefile
Normal file
76
streamApp/Makefile
Normal file
@ -0,0 +1,76 @@
|
||||
TOP=../..
|
||||
|
||||
# Look if we have EPICS R3.13 or R3.14
|
||||
ifeq ($(wildcard $(TOP)/configure),)
|
||||
# EPICS R3.13
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
# The real work is in Makefile.Vx
|
||||
include $(TOP)/config/RULES_ARCHS
|
||||
else
|
||||
|
||||
# EPICS R3.14
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
#HOST_OPT = NO
|
||||
|
||||
PROD = streamApp
|
||||
DBD = streamApp.dbd
|
||||
|
||||
streamApp_DBD += base.dbd
|
||||
#streamApp_DBD += aaiRecord.dbd aaoRecord.dbd
|
||||
|
||||
PROD_SRCS += streamApp_registerRecordDeviceDriver.cpp
|
||||
PROD_SRCS_DEFAULT = streamAppMain.cc
|
||||
PROD_SRCS_vxWorks = -nil-
|
||||
|
||||
PROD_LIBS = stream
|
||||
|
||||
ifdef PCRE
|
||||
# With local PCRE package
|
||||
PROD_LIBS += pcre
|
||||
else
|
||||
ifneq ($(words $(PCRE_LIB) $(PCRE_INCLUDE)),0)
|
||||
# With system wide PCRE installation
|
||||
PROD_SYS_LIBS += pcre
|
||||
PROD_DEPLIB_DIRS += $(PCRE_LIB)
|
||||
endif
|
||||
endif
|
||||
|
||||
ifdef ASYN
|
||||
# Which types of asyn busses do you have?
|
||||
ifneq ($(OS_CLASS), WIN32)
|
||||
# asynDriver up to version 4-8 does not support serial port for Windows!
|
||||
streamApp_DBD += drvAsynSerialPort.dbd
|
||||
endif
|
||||
streamApp_DBD += drvAsynIPPort.dbd
|
||||
#streamApp_DBD += drvGsIP488.dbd
|
||||
#streamApp_DBD += drvNi1014.dbd
|
||||
streamApp_DBD += drvVxi11.dbd
|
||||
PROD_LIBS += asyn
|
||||
endif
|
||||
|
||||
ifneq ($(words $(CALC) $(SYNAPPS)), 0)
|
||||
# With synApps scalcout record
|
||||
streamApp_DBD += calc.dbd
|
||||
streamApp_DBD += streamSynApps.dbd
|
||||
PROD_LIBS += calc sscan
|
||||
PROD_LIBS_DEFAULT += streamSynApps
|
||||
endif
|
||||
|
||||
streamApp_DBD += stream.dbd
|
||||
|
||||
PROD_LIBS += $(EPICS_BASE_IOC_LIBS)
|
||||
|
||||
# Write StreamDevice debug output to this file
|
||||
CPPFLAGS += -DDEBUGFILE=StreamDebug.log
|
||||
|
||||
include $(TOP)/configure/RULES
|
||||
|
||||
clean:: myclean
|
||||
|
||||
myclean:
|
||||
rm -f core* StreamDebug.log
|
||||
|
||||
endif
|
||||
|
||||
|
8
streamApp/Makefile.Host
Normal file
8
streamApp/Makefile.Host
Normal file
@ -0,0 +1,8 @@
|
||||
TOP = ../../..
|
||||
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
|
||||
DBDEXPAND = streamAppInclude-3-13.dbd
|
||||
DBDNAME = streamApp.dbd
|
||||
|
||||
include $(TOP)/config/RULES.Host
|
20
streamApp/Makefile.Vx
Normal file
20
streamApp/Makefile.Vx
Normal file
@ -0,0 +1,20 @@
|
||||
TOP = ../../..
|
||||
|
||||
include $(TOP)/config/CONFIG_APP
|
||||
|
||||
LIBNAME = streamApp
|
||||
|
||||
LDLIBS += $(COMPAT_BIN)/compatLib
|
||||
LDLIBS += $(ASYN_BIN)/asynLib
|
||||
LDLIBS += $(INSTALL_BIN)/streamLib
|
||||
|
||||
include ../base-3-13LIBOBJS
|
||||
|
||||
# Write StreamDevice debug output to this file
|
||||
CPPFLAGS += -DDEBUGFILE=StreamDebug.log
|
||||
|
||||
include $(TOP)/config/RULES.Vx
|
||||
include $(TOP)/config/RULES.munch
|
||||
|
||||
# Rebuild when LIBOBJS change
|
||||
$(LIBNAME): ../base-3-13LIBOBJS
|
242
streamApp/base-3-13.dbd
Normal file
242
streamApp/base-3-13.dbd
Normal file
@ -0,0 +1,242 @@
|
||||
include "menuGlobal.dbd"
|
||||
include "menuConvert.dbd"
|
||||
include "aiRecord.dbd"
|
||||
include "aaiRecord.dbd"
|
||||
include "aoRecord.dbd"
|
||||
include "aaoRecord.dbd"
|
||||
include "biRecord.dbd"
|
||||
include "boRecord.dbd"
|
||||
include "calcRecord.dbd"
|
||||
include "calcoutRecord.dbd"
|
||||
include "compressRecord.dbd"
|
||||
include "dfanoutRecord.dbd"
|
||||
#include "egRecord.dbd"
|
||||
#include "egeventRecord.dbd"
|
||||
#include "erRecord.dbd"
|
||||
#include "ereventRecord.dbd"
|
||||
include "eventRecord.dbd"
|
||||
include "fanoutRecord.dbd"
|
||||
#include "histogramRecord.dbd"
|
||||
include "longinRecord.dbd"
|
||||
include "longoutRecord.dbd"
|
||||
include "mbbiRecord.dbd"
|
||||
include "mbbiDirectRecord.dbd"
|
||||
include "mbboRecord.dbd"
|
||||
include "mbboDirectRecord.dbd"
|
||||
include "permissiveRecord.dbd"
|
||||
#include "pidRecord.dbd"
|
||||
#include "pulseCounterRecord.dbd"
|
||||
#include "pulseDelayRecord.dbd"
|
||||
#include "pulseTrainRecord.dbd"
|
||||
#include "scanRecord.dbd"
|
||||
include "selRecord.dbd"
|
||||
include "seqRecord.dbd"
|
||||
include "stateRecord.dbd"
|
||||
#include "steppermotorRecord.dbd"
|
||||
include "stringinRecord.dbd"
|
||||
include "stringoutRecord.dbd"
|
||||
include "subRecord.dbd"
|
||||
#include "gsubRecord.dbd"
|
||||
#include "palRecord.dbd"
|
||||
include "subArrayRecord.dbd"
|
||||
#include "timerRecord.dbd"
|
||||
#include "waitRecord.dbd"
|
||||
include "waveformRecord.dbd"
|
||||
device(ai,CONSTANT,devAiSoft,"Soft Channel")
|
||||
device(ai,CONSTANT,devAiSoftRaw,"Raw Soft Channel")
|
||||
#device(ai,VME_IO,devAiXy566Se,"XYCOM-566 SE Scanned")
|
||||
#device(ai,VME_IO,devAiXy566Di,"XYCOM-566 Dif Scanned")
|
||||
#device(ai,VME_IO,devAiXy566DiL,"XYCOM-566 Dif Latched")
|
||||
#device(ai,VME_IO,devAiDvx2502,"DVX-2502")
|
||||
#device(ai,CONSTANT,devAiTestAsyn,"Test Asyn")
|
||||
#device(ai,AB_IO,devAiAb1771Il,"AB-1771IL-Analog In")
|
||||
#device(ai,AB_IO,devAiAb1771Ife,"AB-1771IFE")
|
||||
#device(ai,AB_IO,devAiAb1771Ixe,"AB-1771IXE-Millivolt In")
|
||||
#device(ai,AB_IO,devAiAb1771IfeSe,"AB-1771IFE-SE")
|
||||
#device(ai,AB_IO,devAiAb1771IfeMa,"AB-1771IFE-4to20MA")
|
||||
#device(ai,AB_IO,devAiAb1771Ife0to5V,"AB-1771IFE-0to5Volt")
|
||||
#device(ai,AB_IO,devAiAb1771IrPlatinum,"AB-1771RTD-Platinum")
|
||||
#device(ai,AB_IO,devAiAb1771IrCopper,"AB-1771RTD-Copper")
|
||||
#device(ai,CAMAC_IO,devAiCamac,"Camac")
|
||||
#device(ai,VME_IO,devAiAt5Vxi,"VXI-AT5-AI")
|
||||
#device(ai,GPIB_IO,devAiK486Gpib,"Keithley-486")
|
||||
#device(ai,VME_IO,devAiKscV215,"KSC-V215")
|
||||
#device(ai,INST_IO,devAiVXStats,"VX stats")
|
||||
#device(aai,CAMAC_IO,devAaiCamac,"Camac")
|
||||
device(ao,CONSTANT,devAoSoft,"Soft Channel")
|
||||
device(ao,CONSTANT,devAoSoftRaw,"Raw Soft Channel")
|
||||
#device(ao,VME_IO,devAoVmiVme4100,"VMIVME-4100")
|
||||
#device(ao,CONSTANT,devAoTestAsyn,"Test Asyn")
|
||||
#device(ao,AB_IO,devAoAb1771Ofe,"AB-1771OFE")
|
||||
#device(ao,CAMAC_IO,devAoCamac,"Camac")
|
||||
#device(ao,VME_IO,devAoAt5Vxi,"VXI-AT5-AO")
|
||||
#device(ao,INST_IO,devAoVXStats,"VX stats")
|
||||
device(bi,CONSTANT,devBiSoft,"Soft Channel")
|
||||
device(bi,CONSTANT,devBiSoftRaw,"Raw Soft Channel")
|
||||
#device(bi,VME_IO,devBiMpv910,"MPV-910")
|
||||
#device(bi,VME_IO,devBiXVme210,"XVME-210")
|
||||
#device(bi,CONSTANT,devBiTestAsyn,"Test Asyn")
|
||||
#device(bi,AB_IO,devBiAb,"AB-Binary Input")
|
||||
#device(bi,AB_IO,devBiAb16,"AB-16 bit BI")
|
||||
#device(bi,AB_IO,devBiAb32,"AB-32 bit BI")
|
||||
#device(bi,CAMAC_IO,devBiCamac,"Camac")
|
||||
#device(bi,VME_IO,devBiAt5Vxi,"VXI-AT5-BI")
|
||||
#device(bi,VME_IO,devBiXy240,"XYCOM-240")
|
||||
#device(bi,VME_IO,devBiHpe1368a,"VXI-HPE1368-VS")
|
||||
#device(bi,VME_IO,devBiAt8Fp,"AT8-FP10S")
|
||||
#device(bi,VME_IO,devBiAvme9440,"AVME9440 I")
|
||||
device(bo,CONSTANT,devBoSoft,"Soft Channel")
|
||||
device(bo,CONSTANT,devBoSoftRaw,"Raw Soft Channel")
|
||||
#device(bo,VME_IO,devBoMpv902,"MPV-902")
|
||||
#device(bo,VME_IO,devBoXVme220,"XVME-220")
|
||||
#device(bo,CONSTANT,devBoTestAsyn,"Test Asyn")
|
||||
#device(bo,AB_IO,devBoAb,"AB-Binary Output")
|
||||
#device(bo,AB_IO,devBoAb16,"AB-16 bit BO")
|
||||
#device(bo,AB_IO,devBoAb32,"AB-32 bit BO")
|
||||
#device(bo,CAMAC_IO,devBoCamac,"Camac")
|
||||
#device(bo,VME_IO,devBoAt5Vxi,"VXI-AT5-BO")
|
||||
#device(bo,GPIB_IO,devBoK486Gpib,"Keithley-486")
|
||||
#device(bo,VME_IO,devBoXy240,"XYCOM-240")
|
||||
#device(bo,VME_IO,devBoHpe1368a,"VXI-HPE1368-VS")
|
||||
#device(bo,VME_IO,devBoAt8Fp,"AT8-FP10S")
|
||||
#device(bo,VME_IO,devBoAvme9440,"AVME9440 O")
|
||||
device(event,CONSTANT,devEventSoft,"Soft Channel")
|
||||
#device(event,VME_IO,devEventMz8310,"Mizar-8310")
|
||||
#device(event,CONSTANT,devEventTestIoEvent,"Test IoEvent")
|
||||
#device(event,VME_IO,devErEpicsEvent,"APS event receiver")
|
||||
#device(histogram,CONSTANT,devHistogramSoft,"Soft Channel")
|
||||
#device(histogram,CONSTANT,devHistogramTestAsyn,"Test Asyn")
|
||||
device(longin,CONSTANT,devLiSoft,"Soft Channel")
|
||||
#device(longin,CAMAC_IO,devLiCamac,"Camac")
|
||||
#device(longin,INST_IO,devLiVXStats,"VX stats")
|
||||
device(longout,CONSTANT,devLoSoft,"Soft Channel")
|
||||
#device(longout,CAMAC_IO,devLoCamac,"Camac")
|
||||
device(mbbi,CONSTANT,devMbbiSoft,"Soft Channel")
|
||||
device(mbbi,CONSTANT,devMbbiSoftRaw,"Raw Soft Channel")
|
||||
#device(mbbi,VME_IO,devMbbiMpv910,"MPV-910")
|
||||
#device(mbbi,VME_IO,devMbbiXVme210,"XVME-210")
|
||||
#device(mbbi,CONSTANT,devMbbiTestAsyn,"Test Asyn")
|
||||
#device(mbbi,AB_IO,devMbbiAb,"AB-Binary Input")
|
||||
#device(mbbi,AB_IO,devMbbiAb16,"AB-16 bit BI")
|
||||
#device(mbbi,AB_IO,devMbbiAb32,"AB-32 bit BI")
|
||||
#device(mbbi,AB_IO,devMbbiAbAdapterStat,"AB-Adapter Status")
|
||||
#device(mbbi,AB_IO,devMbbiAbCardStat,"AB-Card Status")
|
||||
#device(mbbi,CAMAC_IO,devMbbiCamac,"Camac")
|
||||
#device(mbbi,VME_IO,devMbbiAt5Vxi,"VXI-AT5-BI")
|
||||
#device(mbbi,VME_IO,devMbbiXy240,"XYCOM-240")
|
||||
#device(mbbi,VME_IO,devMbbiHpe1368a,"VXI-HPE1368-VS")
|
||||
#device(mbbi,VME_IO,devMbbiAt8Fp,"AT8-FP10S")
|
||||
#device(mbbi,VME_IO,devMbbiAvme9440,"AVME9440 I")
|
||||
device(mbbiDirect,CONSTANT,devMbbiDirectSoft,"Soft Channel")
|
||||
device(mbbiDirect,CONSTANT,devMbbiDirectSoftRaw,"Raw Soft Channel")
|
||||
#device(mbbiDirect,VME_IO,devMbbiDirectMpv910,"MPV-910")
|
||||
#device(mbbiDirect,VME_IO,devMbbiDirectXVme210,"XVME-210")
|
||||
#device(mbbiDirect,AB_IO,devMbbiDirectAb,"AB-Binary Input")
|
||||
#device(mbbiDirect,AB_IO,devMbbiDirectAb16,"AB-16 bit BI")
|
||||
#device(mbbiDirect,AB_IO,devMbbiDirectAb32,"AB-32 bit BI")
|
||||
#device(mbbiDirect,CAMAC_IO,devMbbiDirectCamac,"Camac")
|
||||
#device(mbbiDirect,VME_IO,devMbbiDirectAt5Vxi,"VXI-AT5-BI")
|
||||
device(mbbo,CONSTANT,devMbboSoft,"Soft Channel")
|
||||
device(mbbo,CONSTANT,devMbboSoftRaw,"Raw Soft Channel")
|
||||
#device(mbbo,VME_IO,devMbboMpv902,"MPV-902")
|
||||
#device(mbbo,VME_IO,devMbboXVme220,"XVME-220")
|
||||
#device(mbbo,CONSTANT,devMbboTestAsyn,"Test Asyn")
|
||||
#device(mbbo,AB_IO,devMbboAb,"AB-Binary Output")
|
||||
#device(mbbo,AB_IO,devMbboAb16,"AB-16 bit BO")
|
||||
#device(mbbo,AB_IO,devMbboAb32,"AB-32 bit BO")
|
||||
#device(mbbo,VME_IO,devMbboAt5Vxi,"VXI-AT5-BO")
|
||||
#device(mbbo,GPIB_IO,devMbboK486Gpib,"Keithley-486")
|
||||
#device(mbbo,VME_IO,devMbboXy240,"XYCOM-240")
|
||||
#device(mbbo,VME_IO,devMbboHpe1368a,"VXI-HPE1368-VS")
|
||||
#device(mbbo,VME_IO,devMbboAt8Fp,"AT8-FP10S")
|
||||
#device(mbbo,VME_IO,devMbboAvme9440,"AVME9440 O")
|
||||
device(mbboDirect,CONSTANT,devMbboDirectSoft,"Soft Channel")
|
||||
device(mbboDirect,CONSTANT,devMbboDirectSoftRaw,"Raw Soft Channel")
|
||||
#device(mbboDirect,VME_IO,devMbboDirectMpv902,"MPV-902")
|
||||
#device(mbboDirect,VME_IO,devMbboDirectXVme220,"XVME-220")
|
||||
#device(mbboDirect,AB_IO,devMbboDirectAb,"AB-Binary Output")
|
||||
#device(mbboDirect,AB_IO,devMbboDirectAb16,"AB-16 bit BO")
|
||||
#device(mbboDirect,AB_IO,devMbboDirectAb32,"AB-32 bit BO")
|
||||
#device(mbboDirect,CAMAC_IO,devMbboDirectCamac,"Camac")
|
||||
#device(mbboDirect,VME_IO,devMbboDirectAt5Vxi,"VXI-AT5-BO")
|
||||
#device(pulseCounter,VME_IO,devPcMz8310,"Mizar-8310")
|
||||
#device(pulseDelay,VME_IO,devPdMz8310,"Mizar-8310")
|
||||
#device(pulseTrain,CONSTANT,devPtSoft,"Soft Channel")
|
||||
#device(pulseTrain,VME_IO,devPtMz8310,"Mizar-8310")
|
||||
#device(steppermotor,VME_IO,devSmCompumotor1830,"Compumotor 1830")
|
||||
#device(steppermotor,VME_IO,devSmOms6Axis,"OMS 6-Axis")
|
||||
#device(steppermotor,AB_IO,devSmAB1746Hstp1,"Allen Bradley 1746-HTSP1")
|
||||
device(stringin,CONSTANT,devSiSoft,"Soft Channel")
|
||||
#device(stringin,CONSTANT,devSiTestAsyn,"Test Asyn")
|
||||
device(stringout,CONSTANT,devSoSoft,"Soft Channel")
|
||||
#device(stringout,CONSTANT,devSoTestAsyn,"Test Asyn")
|
||||
device(subArray,CONSTANT,devSASoft,"Soft Channel")
|
||||
#device(timer,VME_IO,devTmMizar8310,"Mizar-8310")
|
||||
#device(timer,VME_IO,devTmDg535,"DG 535")
|
||||
#device(timer,VME_IO,devTmAt5Vxi,"VXI-AT5-TIME")
|
||||
device(waveform,CONSTANT,devWfSoft,"Soft Channel")
|
||||
#device(waveform,VME_IO,devWfXy566Sc,"XYCOM-566 Single Channel")
|
||||
#device(waveform,VME_IO,devWfComet,"Comet Digitizer")
|
||||
#device(waveform,VME_IO,devWfJoergerVtr1,"Joerger Digitizer")
|
||||
#device(waveform,CONSTANT,devWfTestAsyn,"Test Asyn")
|
||||
#device(waveform,VME_IO,devWfDvx2502,"DVX-2502")
|
||||
#device(waveform,VME_IO,devWfPentek4261,"Pentek 4261")
|
||||
#device(waveform,CAMAC_IO,devWfCamac,"Camac")
|
||||
#device(waveform,VME_IO,devWfJoergerVtr1,"Joerger-VTR1")
|
||||
#device(waveform,VME_IO,devWfComet,"Omnibyte-COMET")
|
||||
#device(eg,VME_IO,devEg,"APS event generator G")
|
||||
#device(egevent,VME_IO,devEgEvent,"APS event generator E")
|
||||
#device(er,VME_IO,devEr,"APS event receiver")
|
||||
#device(erevent,VME_IO,devErevent,"APS event receiver")
|
||||
#device(wait,CONSTANT,devWaitIoEvent,"Soft Channel")
|
||||
#device(ai,INST_IO,devAiCan,"CANbus")
|
||||
#device(ao,INST_IO,devAoCan,"CANbus")
|
||||
#device(bi,INST_IO,devBiCan,"CANbus")
|
||||
#device(bo,INST_IO,devBoCan,"CANbus")
|
||||
#device(mbbi,INST_IO,devMbbiCan,"CANbus")
|
||||
#device(mbbo,INST_IO,devMbboCan,"CANbus")
|
||||
#device(mbbiDirect,INST_IO,devMbbiDirectCan,"CANbus")
|
||||
#device(mbboDirect,INST_IO,devMbboDirectCan,"CANbus")
|
||||
#device(bi,VME_IO,devBiVmic2534,"Vmic2534")
|
||||
#device(bo,VME_IO,devBoVmic2534,"Vmic2534")
|
||||
#device(mbbi,VME_IO,devMbbiVmic2534,"Vmic2534")
|
||||
#device(mbbo,VME_IO,devMbboVmic2534,"Vmic2534")
|
||||
|
||||
#include "symb.dbd"
|
||||
|
||||
#device(ai,AB_IO,devAiAbSlcDcm,"AB-SLC500DCM")
|
||||
#device(ai,AB_IO,devAiAbSlcDcmSigned,"AB-SLC500DCM-Signed")
|
||||
#device(ao,AB_IO,devAoAbSlcDcm,"AB-SLC500DCM")
|
||||
#device(longin,AB_IO,devLiAbSlcDcm,"AB-SLC500DCM")
|
||||
#device(longout,AB_IO,devLoAbSlcDcm,"AB-SLC500DCM")
|
||||
|
||||
#driver(drvXy010)
|
||||
#driver(drvVxi)
|
||||
#driver(drvGpib)
|
||||
#driver(drvBitBus)
|
||||
#driver(drvBb910)
|
||||
#driver(drvXy210)
|
||||
#driver(drvBb902)
|
||||
#driver(drvXy220)
|
||||
#driver(drvXy566)
|
||||
#driver(drvDvx)
|
||||
#driver(drvVmi4100)
|
||||
#driver(drvAb)
|
||||
#driver(drvAt5Vxi)
|
||||
#driver(drvCompuSm)
|
||||
#driver(drvOms)
|
||||
#driver(drvMz8310)
|
||||
#driver(drvHpe1368a)
|
||||
#driver(drvXy240)
|
||||
#driver(drvKscV215)
|
||||
#driver(drvComet)
|
||||
#driver(drvJgvtr1)
|
||||
#driver(drvFp)
|
||||
#driver(drvFpm)
|
||||
#driver(drvIpac)
|
||||
#driver(drvTip810)
|
||||
#driver(drvVmi2534)
|
||||
#include "bptTypeJdegC.dbd"
|
||||
#include "bptTypeJdegF.dbd"
|
||||
#include "bptTypeKdegC.dbd"
|
||||
#include "bptTypeKdegF.dbd"
|
216
streamApp/base-3-13LIBOBJS
Normal file
216
streamApp/base-3-13LIBOBJS
Normal file
@ -0,0 +1,216 @@
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/aaiRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/aaoRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/aiRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/aoRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/biRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/boRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/calcRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/calcoutRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/compressRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/dfanoutRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/egRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/egeventRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/erRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/ereventRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/eventRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/fanoutRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/gsubRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/histogramRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/longinRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/longoutRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/mbbiRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/mbbiDirectRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/mbboRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/mbboDirectRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/palRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/permissiveRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/pidRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/pulseCounterRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/pulseDelayRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/pulseTrainRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/scanRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/recWaitCa.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/recDynLink.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/recDynLinkTest.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/selRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/seqRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/stateRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/steppermotorRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/stringinRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/stringoutRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/subRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/subArrayRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/timerRecord.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/waitRecord.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/waveformRecord.o
|
||||
#
|
||||
# Device Support
|
||||
#
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAaiCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiDvx2502.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiKscV215.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAiSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAiSoftRaw.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devVXStats.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAiTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiXy566Di.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiXy566DiL.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiXy566Se.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAaoCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAoCamac.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAoSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAoSoftRaw.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devAoTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAoVmiVme4100.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devApsEg.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devApsEr.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAt5Vxi.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAt8Fp.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAvme9440.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBiCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBiMpv910.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBiSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBiSoftRaw.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBiTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBiXVme210.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBoCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBoMpv902.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBoSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBoSoftRaw.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devBoTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBoXVme220.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devCommonGpib.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devEventSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devEventTestIoEvent.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devHistogramSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devHistogramTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devHpe1368a.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devLiCamac.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devLiSoft.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devLoCamac.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devLoSoft.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectMpv910.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectSoftRaw.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectXVme210.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiMpv910.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbbiSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbbiSoftRaw.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbbiTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiXVme210.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectMpv902.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectSoftRaw.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectXVme220.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboMpv902.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbboSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbboSoftRaw.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devMbboTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboXVme220.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMz8310.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devPtSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devSASoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devSiSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devSiTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devSmCompumotor1830.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devSmOms6Axis.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devSoSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devSoTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devTimerMz8310.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfCamac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfComet.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfDvx2502.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfJoergerVtr1.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devWfSoft.o
|
||||
LIBOBJS += $(EPICS_BASE_BIN)/devWfTestAsyn.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfXy566Sc.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devWfPentek4261.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXy240.o
|
||||
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAB1771IFE.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAB1771IL.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAB1771IR.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAB1771IXE.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAB1771OFE.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devABSLCDCM.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devABBINARY.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devABStatus.o
|
||||
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAiCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAoCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBiCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBoCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbbiDirectCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devMbboDirectCan.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devVmic2534.o
|
||||
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/symb
|
||||
|
||||
#
|
||||
# Driver support ANSI
|
||||
#
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvAb.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvAt5Vxi.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvEpvxi.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvEpvxiMsg.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvHp1404a.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvHpe1368a.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvHpe1445a.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvKscV215.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvMz8310.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvStc.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvTime.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvCaenV265.o
|
||||
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvVipc310.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvVipc610.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvIpMv162.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvIpac.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvTip810.o
|
||||
#
|
||||
# Driver support NON ANSI
|
||||
#
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/module_types.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvBB232.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvBb902.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvBb910.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvBitBus.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvComet.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvCompuSm.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvDvx.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvFp.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvFpm.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvGpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvJgvtr1.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvMsg.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvOms.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvTranServ.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvVmi4100.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvXy010.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvXy210.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvXy220.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvXy240.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvXy566.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/drvVmic2534.o
|
||||
#
|
||||
#from src/devOpt
|
||||
#
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devAnalytekGpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devBBInteract.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devGpibInteract.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devK486Gpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devLibOpt.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxDc5009Gpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxDg535Gpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxK196Gpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxK263Gpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxSkeletonGpib.o
|
||||
#LIBOBJS += $(EPICS_BASE_BIN)/devXxSr620Gpib.o
|
||||
|
21
streamApp/example-3-13.cmd
Normal file
21
streamApp/example-3-13.cmd
Normal file
@ -0,0 +1,21 @@
|
||||
BIN="<library directory>"
|
||||
DBD="<dbd directory>"
|
||||
HOME="<ioc home direcory>"
|
||||
|
||||
#where can protocols be located?
|
||||
STREAM_PROTOCOL_PATH=".:protocols:../protocols/"
|
||||
|
||||
cd BIN
|
||||
ld < iocCore
|
||||
ld < streamApp.munch
|
||||
dbLoadDatabase "streamApp.dbd",DBD
|
||||
|
||||
drvAsynIPPortConfigure "terminal", "xxx.xxx.xxx.xxx:40000"
|
||||
|
||||
#load the records
|
||||
cd HOME
|
||||
dbLoadRecords "example.db","PREFIX=DZ"
|
||||
|
||||
#lots of debug output
|
||||
#streamDebug=1
|
||||
iocInit
|
38
streamApp/example.cmd
Normal file
38
streamApp/example.cmd
Normal file
@ -0,0 +1,38 @@
|
||||
dbLoadDatabase "O.Common/streamApp.dbd"
|
||||
streamApp_registerRecordDeviceDriver
|
||||
|
||||
#where can protocols be located?
|
||||
epicsEnvSet "STREAM_PROTOCOL_PATH", ".:protocols:../protocols/"
|
||||
|
||||
#setup the busses
|
||||
|
||||
#example serial port setup
|
||||
#drvAsynSerialPortConfigure "COM2", "/dev/ttyS1"
|
||||
#asynOctetSetInputEos "COM2",0,"\r\n"
|
||||
#asynOctetSetOutputEos "COM2",0,"\r\n"
|
||||
#asynSetOption ("COM2", 0, "baud", "9600")
|
||||
#asynSetOption ("COM2", 0, "bits", "8")
|
||||
#asynSetOption ("COM2", 0, "parity", "none")
|
||||
#asynSetOption ("COM2", 0, "stop", "1")
|
||||
#asynSetOption ("COM2", 0, "clocal", "Y")
|
||||
#asynSetOption ("COM2", 0, "crtscts", "N")
|
||||
|
||||
#example telnet style IP port setup
|
||||
drvAsynIPPortConfigure "terminal", "localhost:40000"
|
||||
asynOctetSetInputEos "terminal",0,"\r\n"
|
||||
asynOctetSetOutputEos "terminal",0,"\r\n"
|
||||
|
||||
#example VXI11 (GPIB via IP) port setup
|
||||
#vxi11Configure "GPIB","ins023",1,5.0,"hpib"
|
||||
|
||||
#load the records
|
||||
dbLoadRecords "example.db","PREFIX=DZ"
|
||||
#dbLoadRecords "scalcout.db","PREFIX=DZ"
|
||||
|
||||
#lots! of debug output
|
||||
#var streamDebug 1
|
||||
|
||||
iocInit
|
||||
|
||||
#enable debug output
|
||||
var streamDebug 1
|
170
streamApp/example.db
Normal file
170
streamApp/example.db
Normal file
@ -0,0 +1,170 @@
|
||||
# process this record to reload all stream protocols
|
||||
record (sub, "$(PREFIX):reload")
|
||||
{
|
||||
field (SNAM, "streamReload")
|
||||
}
|
||||
|
||||
# some scaling -10...10V <-> 0x0000...0xffff and back
|
||||
record (ao, "$(PREFIX):ao")
|
||||
{
|
||||
field (DESC, "An analog value")
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto set-ao terminal")
|
||||
field (PREC, "3")
|
||||
field (LINR, "LINEAR")
|
||||
field (ESLO, "0.000305180437934") # 20.0 / 0xffff
|
||||
field (EOFF, "-10")
|
||||
field (DRVL, "-10")
|
||||
field (DRVH, "10")
|
||||
field (EGU, "V")
|
||||
}
|
||||
|
||||
record (ai, "$(PREFIX):ai")
|
||||
{
|
||||
field (DESC, "An analog value")
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto ai terminal")
|
||||
field (PREC, "3")
|
||||
field (LINR, "LINEAR")
|
||||
field (ESLO, "0.000305180437934") # 20.0 / 0xffff
|
||||
field (EOFF, "-10")
|
||||
field (EGU, "V")
|
||||
}
|
||||
|
||||
# simple integer and binary i/o
|
||||
record (longout, "$(PREFIX):lo")
|
||||
{
|
||||
field (DESC, "An integer value")
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto lo terminal")
|
||||
}
|
||||
|
||||
record (longin, "$(PREFIX):li")
|
||||
{
|
||||
field (DESC, "An integer value")
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto li terminal")
|
||||
}
|
||||
|
||||
record (bo, "$(PREFIX):bo")
|
||||
{
|
||||
field (DESC, "A bit value")
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto bo terminal")
|
||||
field (MASK, "42")
|
||||
field (ZNAM, "OFF")
|
||||
field (ONAM, "ON")
|
||||
}
|
||||
|
||||
record (bi, "$(PREFIX):bi")
|
||||
{
|
||||
field (DESC, "A bit value")
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto bi terminal")
|
||||
field (ZNAM, "OFF")
|
||||
field (ONAM, "ON")
|
||||
}
|
||||
|
||||
# playing with enums
|
||||
record (mbbo, "$(PREFIX):mbbo")
|
||||
{
|
||||
field (DESC, "Print Enums")
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto enums terminal")
|
||||
field (ZRST, "A")
|
||||
field (ZRVL, "2")
|
||||
field (ONST, "B")
|
||||
field (ONVL, "3")
|
||||
field (TWST, "C")
|
||||
field (TWVL, "1")
|
||||
field (THST, "D")
|
||||
field (THVL, "0")
|
||||
}
|
||||
|
||||
record (stringout, "$(PREFIX):command")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto COMMAND terminal")
|
||||
}
|
||||
|
||||
record (stringin, "$(PREFIX):info")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto info terminal")
|
||||
}
|
||||
|
||||
# prints all available checksums
|
||||
record (stringout, "$(PREFIX):checksum")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto checksum terminal")
|
||||
}
|
||||
|
||||
# this record gets any (terminated) input line
|
||||
record (stringin, "$(PREFIX):spy")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto spy terminal")
|
||||
field (SCAN, "I/O Intr")
|
||||
field (FLNK, "$(PREFIX):log5")
|
||||
}
|
||||
|
||||
# these records log the last few input lines
|
||||
record (stringin, "$(PREFIX):log5")
|
||||
{
|
||||
field (INP, "$(PREFIX):log4")
|
||||
field (FLNK, "$(PREFIX):log4")
|
||||
}
|
||||
|
||||
record (stringin, "$(PREFIX):log4")
|
||||
{
|
||||
field (INP, "$(PREFIX):log3")
|
||||
field (FLNK, "$(PREFIX):log3")
|
||||
}
|
||||
|
||||
record (stringin, "$(PREFIX):log3")
|
||||
{
|
||||
field (INP, "$(PREFIX):log2")
|
||||
field (FLNK, "$(PREFIX):log2")
|
||||
}
|
||||
|
||||
record (stringin, "$(PREFIX):log2")
|
||||
{
|
||||
field (INP, "$(PREFIX):log1")
|
||||
field (FLNK, "$(PREFIX):log1")
|
||||
}
|
||||
|
||||
record (stringin, "$(PREFIX):log1")
|
||||
{
|
||||
field (INP, "$(PREFIX):spy")
|
||||
}
|
||||
|
||||
# caput to A,B,C or PROC to process this
|
||||
record (calcout, "$(PREFIX):calcout")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@example.proto calcout terminal")
|
||||
field (A, "3.1415")
|
||||
field (B, "15")
|
||||
field (C, "8")
|
||||
field (CALC, "0") # should be a valid expression
|
||||
field (DESC, "filename")
|
||||
}
|
||||
|
||||
# read a long string as a waveform of characteres
|
||||
record (waveform, "$(PREFIX):hugestring")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto char_wave_in terminal")
|
||||
field (FTVL, "CHAR")
|
||||
field (NELM, "1000")
|
||||
}
|
||||
|
||||
# write an array of strings
|
||||
record (waveform, "$(PREFIX):strings")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@example.proto string_wave_out terminal")
|
||||
field (FTVL, "STRING")
|
||||
field (NELM, "10")
|
||||
}
|
127
streamApp/example.proto
Normal file
127
streamApp/example.proto
Normal file
@ -0,0 +1,127 @@
|
||||
# example stream protocol file
|
||||
|
||||
Terminator = CR LF;
|
||||
ReplyTimeout = 10000; # 10 sec is very long, for keyboard input
|
||||
ReadTimeout = 1000; # also long for keyboard input
|
||||
|
||||
binary {
|
||||
out "%B\x00\xff";
|
||||
}
|
||||
|
||||
# note the field access
|
||||
ai {
|
||||
out "%(NAME)s [int] ? ";
|
||||
in "%i"; # read to RVAL and convert
|
||||
}
|
||||
|
||||
set-ao {
|
||||
out '%(NAME)s = %f %(EGU)s [0x%04x]';
|
||||
}
|
||||
|
||||
li {
|
||||
out "%(NAME)s [int] ? ";
|
||||
in "%i";
|
||||
}
|
||||
|
||||
lo {
|
||||
out '%(NAME)s = %i = %#x = %b(bin) = %D(BCD)';
|
||||
}
|
||||
|
||||
bi {
|
||||
out "%(NAME)s [0 or 1] ? ";
|
||||
in "%i";
|
||||
}
|
||||
|
||||
# using variables
|
||||
s0 = false;
|
||||
s1 = true;
|
||||
bo {
|
||||
out '%(NAME)s = %i = %{\${s0}|\${s1}} = "%s"';
|
||||
}
|
||||
|
||||
enums
|
||||
{
|
||||
out "%{choice0|choice1|choice2} %d %s";
|
||||
}
|
||||
|
||||
checksum {
|
||||
out "%s%<sum> sum";
|
||||
out "%s%<nsum> nsum";
|
||||
out "%s%<-sum> -sum";
|
||||
out "%s%<notsum> notsum";
|
||||
out "%s%<~sum> ~sum";
|
||||
out "%s%<xor> xor";
|
||||
out "%s%<crc8> crc8";
|
||||
out "%s%<ccitt8> ccitt8";
|
||||
out "%s%<sum16> sum16";
|
||||
out "%s%<crc16> crc16";
|
||||
out "%s%<crc16r> crc16r";
|
||||
out "%s%<ccitt16> ccitt16";
|
||||
out "%s%<ccitt16a> ccitt16 with augment";
|
||||
out "%s%<sum32> sum32";
|
||||
out "%s%<crc32> crc32";
|
||||
out "%s%<crc32r> crcr32";
|
||||
out "%s%<jamcrc> jamcrc";
|
||||
out "%s%<adler32> adler32";
|
||||
|
||||
out "%s%0<sum> sum (ASCII)";
|
||||
out "%s%0<nsum> nsum (ASCII)";
|
||||
out "%s%0<-sum> -sum (ASCII)";
|
||||
out "%s%0<notsum> notsum (ASCII)";
|
||||
out "%s%0<~sum> ~sum (ASCII)";
|
||||
out "%s%0<xor> xor (ASCII)";
|
||||
out "%s%0<crc8> crc8 (ASCII)";
|
||||
out "%s%0<ccitt8> ccitt8 (ASCII)";
|
||||
out "%s%0<sum16> sum16 (ASCII)";
|
||||
out "%s%0<crc16> crc16 (ASCII)";
|
||||
out "%s%0<crc16r> crc16r (ASCII)";
|
||||
out "%s%0<ccitt16> ccitt16 (ASCII)";
|
||||
out "%s%0<ccitt16a> ccitt16 with augment (ASCII)";
|
||||
out "%s%0<sum32> sum32 (ASCII)";
|
||||
out "%s%0<crc32> crc32 (ASCII)";
|
||||
out "%s%0<crc32r> crcr32 (ASCII)";
|
||||
out "%s%0<jamcrc> jamcrc (ASCII)";
|
||||
out "%s%0<adler32> adler32 (ASCII)";
|
||||
out "%s%0<hexsum8> hexsum8 (ASCII)";
|
||||
}
|
||||
|
||||
command {
|
||||
out "%s";
|
||||
}
|
||||
|
||||
info {
|
||||
out "%s";
|
||||
in "%39c";
|
||||
}
|
||||
|
||||
read {
|
||||
in "%39c";
|
||||
}
|
||||
|
||||
spy {
|
||||
extraInput=ignore;
|
||||
PollPeriod = 10;
|
||||
in "%39c";
|
||||
}
|
||||
|
||||
# accessing fields
|
||||
calcout {
|
||||
out "OVAL=%f A=%(A)f B=%(B)f C=%(C)f";
|
||||
}
|
||||
|
||||
scalcout {
|
||||
out "OVAL=%f SOV=%s A=%(A)f B=%(B)f AA=%(AA)s";
|
||||
}
|
||||
|
||||
# output an array of strings
|
||||
string_wave_out {
|
||||
separator = ", ";
|
||||
out "waveform %(NAME)s = (%s)";
|
||||
}
|
||||
|
||||
char_wave_in {
|
||||
outterminator = "";
|
||||
out CR LF "Enter String: ";
|
||||
in "%1000c";
|
||||
out 'Got: "%s"' CR LF;
|
||||
}
|
34
streamApp/regexp.README
Normal file
34
streamApp/regexp.README
Normal file
@ -0,0 +1,34 @@
|
||||
How to use regular expressions in StreamDevice.
|
||||
|
||||
First, you need the PCRE library.
|
||||
If it is already installed for your (Linux) system (try: rpm -ql pcre),
|
||||
set the following variables in your RELEASE file:
|
||||
PCRE_INCLUDE=<direcrory of pcre.h>
|
||||
PCRE_LIB=<direcrory of libpcre.so>
|
||||
|
||||
For vxWorks, Windows and others you're probably out of luck here.
|
||||
In that case, download the PCRE package from www.pcre.org and
|
||||
epics.web.psi.ch/software/streamdevice/pcre/Makefile and compile
|
||||
PCRE as an EPICS application. Use the variable PCRE in your RELEASE file
|
||||
to define the location of this application.
|
||||
|
||||
If either PCRE or PCRE_INCLUDE or PCRE_LIB are set in the RELEASE file,
|
||||
StreamDevice is automatically build with regular expression support.
|
||||
|
||||
The syntax is %/regexp/. It can only be used in input. It returns the
|
||||
next string that matches the regexp. Anything before this string is skipped.
|
||||
|
||||
To use sub-expressions use %.n/rexexp/ where n is a number from 1 to 9
|
||||
to match the n-th sub-expression.
|
||||
|
||||
It is possible to limit the input length to the match algorithm like
|
||||
%m/regexp/ where m is the maximal length.
|
||||
|
||||
Example:
|
||||
in "%100.1/<title>(.*)<\/title>/";
|
||||
This searches the next 100 bytes and returns the string bewteen
|
||||
<title> and </title>. Any input before <title> is skipped. Any input
|
||||
after </title> is left in the buffer (and can by matched by other formats).
|
||||
Note that the / in </title> must be escaped.
|
||||
|
||||
See regexp.cmd, regexp.proto, regexp.db for a working example.
|
10
streamApp/regexp.cmd
Normal file
10
streamApp/regexp.cmd
Normal file
@ -0,0 +1,10 @@
|
||||
dbLoadDatabase "O.Common/streamApp.dbd"
|
||||
streamApp_registerRecordDeviceDriver
|
||||
|
||||
# no autoconnect for web servers (see regexp.proto)
|
||||
drvAsynIPPortConfigure web epics.web.psi.ch:80 0 1
|
||||
|
||||
dbLoadRecords regexp.db
|
||||
|
||||
iocInit
|
||||
# var streamDebug 1
|
5
streamApp/regexp.db
Normal file
5
streamApp/regexp.db
Normal file
@ -0,0 +1,5 @@
|
||||
record (stringin, "DZ:regexp")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@regexp.proto readTitle web")
|
||||
}
|
18
streamApp/regexp.proto
Normal file
18
streamApp/regexp.proto
Normal file
@ -0,0 +1,18 @@
|
||||
# regular expression example
|
||||
# extract the title of from a web page
|
||||
|
||||
outterminator = NL;
|
||||
interminator = "</html>" NL; # terminators can have arbitrary length
|
||||
|
||||
# Web servers close the connection after sending a page.
|
||||
# Thus, we can't use autoconnect (see drvAsynIPPortConfigure)
|
||||
# Handle connection manually in protocol.
|
||||
|
||||
readTitle {
|
||||
extraInput=ignore;
|
||||
|
||||
connect 1000; # connect to server, 1 second timeout
|
||||
out "GET http://epics.web.psi.ch/"; # HTTP request
|
||||
in "%.1/<title>(.*)<\/title>/"; # get string in <title></title>
|
||||
disconnect; # servers closes, so do we.
|
||||
}
|
19
streamApp/simple.cmd
Normal file
19
streamApp/simple.cmd
Normal file
@ -0,0 +1,19 @@
|
||||
dbLoadDatabase "O.Common/streamApp.dbd"
|
||||
streamApp_registerRecordDeviceDriver
|
||||
|
||||
#where can protocols be located?
|
||||
epicsEnvSet "STREAM_PROTOCOL_PATH", ".:protocols:../protocols/"
|
||||
|
||||
#setup the busses
|
||||
|
||||
#drvAsynIPPortConfigure "L0", "localhost:40000"
|
||||
vxi11Configure "L0","gpib-dz-1",0,0.0,"gpib0",0,0
|
||||
|
||||
#load the records
|
||||
dbLoadRecords "simple.db","P=DZ,BUS=L0 28"
|
||||
|
||||
var streamDebug 1
|
||||
iocInit
|
||||
|
||||
#enable debug output
|
||||
var streamDebug 1
|
15
streamApp/simple.db
Normal file
15
streamApp/simple.db
Normal file
@ -0,0 +1,15 @@
|
||||
record (stringout, "$(P):cmd")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@simple.proto cmd $(BUS)")
|
||||
}
|
||||
record (stringout, "$(P):info")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@simple.proto info $(BUS)")
|
||||
}
|
||||
record (stringin, "$(P):read")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@simple.proto read $(BUS)")
|
||||
}
|
13
streamApp/simple.proto
Normal file
13
streamApp/simple.proto
Normal file
@ -0,0 +1,13 @@
|
||||
terminator = CR LF;
|
||||
|
||||
cmd {
|
||||
out "%s";
|
||||
}
|
||||
info {
|
||||
extrainput=ignore;
|
||||
out "%s";
|
||||
in "%39c";
|
||||
}
|
||||
read {
|
||||
in "%39c";
|
||||
}
|
3
streamApp/streamAppInclude-3-13.dbd
Normal file
3
streamApp/streamAppInclude-3-13.dbd
Normal file
@ -0,0 +1,3 @@
|
||||
include "base-3-13.dbd"
|
||||
include "asyn.dbd"
|
||||
include "stream.dbd"
|
37
streamApp/streamAppMain.cc
Normal file
37
streamApp/streamAppMain.cc
Normal file
@ -0,0 +1,37 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
|
||||
* *
|
||||
* This is an example application initializer for StreamDevice. *
|
||||
* Please refer to the HTML files in ../doc/ for a detailed *
|
||||
* documentation. *
|
||||
* *
|
||||
* If you do any changes in this file, you are not allowed to *
|
||||
* redistribute it any more. If there is a bug or a missing *
|
||||
* feature, send me an email and/or your patch. If I accept *
|
||||
* your changes, they will go to the next release. *
|
||||
* *
|
||||
* DISCLAIMER: If this software breaks something or harms *
|
||||
* someone, it's your problem. *
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <epicsThread.h>
|
||||
#include <iocsh.h>
|
||||
#include <devStream.h>
|
||||
|
||||
int main(int argc,char *argv[])
|
||||
{
|
||||
#ifdef DEBUGFILE
|
||||
#define STR2(x) #x
|
||||
#define STR(x) STR2(x)
|
||||
StreamDebugFile = fopen(STR(DEBUGFILE), "w");
|
||||
#endif
|
||||
if(argc>=2) {
|
||||
iocsh(argv[1]);
|
||||
epicsThreadSleep(.2);
|
||||
}
|
||||
iocsh(NULL);
|
||||
return(0);
|
||||
}
|
195
streamApp/terminal.tcl
Executable file
195
streamApp/terminal.tcl
Executable file
@ -0,0 +1,195 @@
|
||||
#!/usr/bin/env wish
|
||||
|
||||
wm iconify .
|
||||
|
||||
proc createTerm {sock} {
|
||||
global socket port
|
||||
toplevel .$sock
|
||||
text .$sock.t -yscrollcommand ".$sock.v set"
|
||||
scrollbar .$sock.v -command ".$sock.t yview"
|
||||
.$sock.t tag configure output -foreground red
|
||||
.$sock.t tag configure input -foreground darkgreen
|
||||
grid rowconfigure .$sock 0 -weight 1
|
||||
grid columnconfigure .$sock 0 -weight 1
|
||||
grid .$sock.t .$sock.v -sticky nsew
|
||||
bind .$sock.t <Destroy> "close $sock; unset socket(.$sock.t)"
|
||||
bind .$sock.t <F1> "%W delete 0.1 end"
|
||||
set socket(.$sock.t) $sock
|
||||
focus .$sock.t
|
||||
wm title .$sock "port $port <-> [fconfigure $sock -peername]"
|
||||
}
|
||||
|
||||
set port [lindex $argv 0]
|
||||
if {$port == ""} { set port 40000 }
|
||||
if [catch {
|
||||
socket -server connect $port
|
||||
} msg ] {
|
||||
return -code error "$msg (port $port)"
|
||||
}
|
||||
|
||||
proc connect {sock addr port} {
|
||||
fconfigure $sock -blocking 0 -buffering none
|
||||
createTerm $sock
|
||||
fileevent $sock readable "receiveHandler $sock"
|
||||
}
|
||||
|
||||
proc escape {string} {
|
||||
while {![string is print -failindex index $string]} {
|
||||
set char [string index $string $index]
|
||||
scan $char "%c" code
|
||||
switch $char {
|
||||
"\r" { set escaped "\\r" }
|
||||
"\n" { set escaped "\\n" }
|
||||
"\a" { set escaped "\\a" }
|
||||
"\t" { set escaped "\\t" }
|
||||
default { set escaped [format "<%02x>" $code] }
|
||||
}
|
||||
set string [string replace $string $index $index $escaped]
|
||||
}
|
||||
return $string
|
||||
}
|
||||
|
||||
proc sendReply {sock text} {
|
||||
.$sock.t mark set insert end
|
||||
.$sock.t insert end $text
|
||||
.$sock.t see end
|
||||
puts -nonewline $sock $text
|
||||
# puts "sending \"[escape $text]\"\n"
|
||||
}
|
||||
|
||||
proc checkNum {n} {
|
||||
if {[string is integer $n] && $n >= 0} {return $n}
|
||||
return -code error "argument $n must be a positive number"
|
||||
}
|
||||
|
||||
proc receiveHandler {sock} {
|
||||
set a [read $sock]
|
||||
if [eof $sock] {
|
||||
destroy .$sock
|
||||
return
|
||||
}
|
||||
.$sock.t mark set insert end
|
||||
.$sock.t insert end $a output
|
||||
.$sock.t see end
|
||||
set l [split $a]
|
||||
if [catch {
|
||||
switch -- [lindex $l 0] {
|
||||
"disconnect" {
|
||||
sendReply $sock [string range $a 11 end]
|
||||
destroy .$sock
|
||||
}
|
||||
"echo" {
|
||||
sendReply $sock [string range $a 5 end]
|
||||
}
|
||||
"longmsg" {
|
||||
set length [checkNum [lindex $l 1]]
|
||||
sendReply $sock "[string range x[string repeat 0123456789abcdefghijklmnopqrstuvwxyz [expr $length / 36 + 1]] 1 $length]\n"
|
||||
}
|
||||
"wait" {
|
||||
set wait [checkNum [lindex $l 1]]
|
||||
after $wait [list sendReply $sock "Done\n"]
|
||||
}
|
||||
"start" {
|
||||
set wait [checkNum [lindex $l 1]]
|
||||
set ::counter 0
|
||||
after $wait sendAsync $wait [list [lindex $l 2]]
|
||||
sendReply $sock "Started\n"
|
||||
}
|
||||
"stop" {
|
||||
set ::counter -1
|
||||
sendReply $sock "Stopped\n"
|
||||
}
|
||||
"set" {
|
||||
set ::values([lindex $a 1]) [lrange $l 2 end-1]
|
||||
sendReply $sock "Ok\n"
|
||||
}
|
||||
"get" {
|
||||
if [info exists ::values([lindex $l 1])] {
|
||||
sendReply $sock "[lindex $l 1] $::values([lindex $l 1])\n"
|
||||
} else {
|
||||
sendReply $sock "ERROR: [lindex $l 1] not found\n"
|
||||
}
|
||||
}
|
||||
"help" {
|
||||
sendReply $sock "help this text\n"
|
||||
sendReply $sock "echo string reply string\n"
|
||||
sendReply $sock "wait msec reply Done after some time\n"
|
||||
sendReply $sock "start msec start sending messages priodically\n"
|
||||
sendReply $sock "stop stop sending messages\n"
|
||||
sendReply $sock "set key value set a value\n"
|
||||
sendReply $sock "get key reply value\n"
|
||||
sendReply $sock "disconnect close connection\n"
|
||||
}
|
||||
}
|
||||
} msg] {
|
||||
sendReply $sock "ERROR: $msg\n"
|
||||
puts stderr $::errorInfo
|
||||
}
|
||||
}
|
||||
|
||||
proc sendAsync {wait message} {
|
||||
if {$::counter < 0} return
|
||||
foreach term [array names ::socket] {
|
||||
sendReply $::socket($term) "Message number [incr ::counter] $message\n";
|
||||
}
|
||||
after $wait sendAsync $wait [list $message]
|
||||
}
|
||||
|
||||
if {[info proc tkTextInsert] != ""} {
|
||||
set insert tkTextInsert
|
||||
set paste tkTextPaste
|
||||
set pastesel tkPasteSelection
|
||||
} else {
|
||||
set insert tk::TextInsert
|
||||
set paste tk_textPaste
|
||||
set pastesel ::tk::TextPasteSelection
|
||||
}
|
||||
|
||||
rename $insert tkTextInsert_org
|
||||
rename $paste tkTextPaste_org
|
||||
rename $pastesel tkTextPasteSel_org
|
||||
|
||||
proc $insert {w s} {
|
||||
puts [list insert $w $s]
|
||||
global socket
|
||||
if {[string equal $s ""] || [string equal [$w cget -state] "disabled"]} {
|
||||
return
|
||||
}
|
||||
sendReply $socket($w) $s
|
||||
}
|
||||
|
||||
proc $paste {w x y} {
|
||||
puts [list paste $w $s]
|
||||
global insert
|
||||
set s [selection get -displayof $w]
|
||||
$insert $w $s
|
||||
}
|
||||
|
||||
proc $pastesel {w x y} {
|
||||
global insert
|
||||
$w mark set insert [TextClosestGap $w $x $y]
|
||||
if {![catch {::tk::GetSelection $w PRIMARY} sel]} {
|
||||
set oldSeparator [$w cget -autoseparators]
|
||||
if {$oldSeparator} {
|
||||
$w configure -autoseparators 0
|
||||
$w edit separator
|
||||
}
|
||||
$insert $w $sel
|
||||
if {$oldSeparator} {
|
||||
$w edit separator
|
||||
$w configure -autoseparators 1
|
||||
}
|
||||
}
|
||||
if {[$w cget -state] eq "normal"} {focus $w}
|
||||
}
|
||||
|
||||
#remove bindings on Control-<letter>
|
||||
for {set ascii 0x61} {$ascii <= 0x7a} {incr ascii} {
|
||||
bind Text <Control-[format %c $ascii]> ""
|
||||
}
|
||||
#remove bindings on symbolic tags
|
||||
foreach tag {Clear Paste Copy Cut } {
|
||||
bind Text <<$tag>> ""
|
||||
}
|
||||
|
||||
bind Text <Control-Key> [list $insert %W %A]
|
900
streamApp/test.adl
Normal file
900
streamApp/test.adl
Normal file
@ -0,0 +1,900 @@
|
||||
|
||||
file {
|
||||
name="/afs/psi.ch/user/z/zimoch/top/StreamDevice-2/streamApp/test.adl"
|
||||
version=030100
|
||||
}
|
||||
display {
|
||||
object {
|
||||
x=261
|
||||
y=341
|
||||
width=552
|
||||
height=362
|
||||
}
|
||||
clr=14
|
||||
bclr=50
|
||||
cmap=""
|
||||
gridSpacing=5
|
||||
gridOn=1
|
||||
snapToGrid=1
|
||||
}
|
||||
"color map" {
|
||||
ncolors=65
|
||||
colors {
|
||||
ffffff,
|
||||
ececec,
|
||||
dadada,
|
||||
c8c8c8,
|
||||
bbbbbb,
|
||||
aeaeae,
|
||||
9e9e9e,
|
||||
919191,
|
||||
858585,
|
||||
787878,
|
||||
696969,
|
||||
5a5a5a,
|
||||
464646,
|
||||
2d2d2d,
|
||||
000000,
|
||||
00d800,
|
||||
1ebb00,
|
||||
339900,
|
||||
2d7f00,
|
||||
216c00,
|
||||
fd0000,
|
||||
de1309,
|
||||
be190b,
|
||||
a01207,
|
||||
820400,
|
||||
5893ff,
|
||||
597ee1,
|
||||
4b6ec7,
|
||||
3a5eab,
|
||||
27548d,
|
||||
fbf34a,
|
||||
f9da3c,
|
||||
eeb62b,
|
||||
e19015,
|
||||
cd6100,
|
||||
ffb0ff,
|
||||
d67fe2,
|
||||
ae4ebc,
|
||||
8b1a96,
|
||||
610a75,
|
||||
a4aaff,
|
||||
8793e2,
|
||||
6a73c1,
|
||||
4d52a4,
|
||||
343386,
|
||||
c7bb6d,
|
||||
b79d5c,
|
||||
a47e3c,
|
||||
7d5627,
|
||||
58340f,
|
||||
99ffff,
|
||||
73dfff,
|
||||
4ea5f9,
|
||||
2a63e4,
|
||||
0a00b8,
|
||||
ebf1b5,
|
||||
d4db9d,
|
||||
bbc187,
|
||||
a6a462,
|
||||
8b8239,
|
||||
73ff6b,
|
||||
52da3b,
|
||||
3cb420,
|
||||
289315,
|
||||
1a7309,
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=105
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log9"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=115
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log8"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=125
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log7"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=135
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log6"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=145
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log5"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=155
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log4"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=165
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log3"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=175
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log2"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=185
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log1"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=195
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log0"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text entry" {
|
||||
object {
|
||||
x=50
|
||||
y=265
|
||||
width=390
|
||||
height=20
|
||||
}
|
||||
control {
|
||||
chan="$(P):cmd"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=215
|
||||
width=390
|
||||
height=14
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):spy"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=105
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log9"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=115
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log8"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=125
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log7"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=135
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log6"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=145
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log5"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=155
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log4"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=165
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log3"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=175
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log2"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=185
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log1"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=195
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log0"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=215
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="spy"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=270
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="cmd"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=295
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="info"
|
||||
}
|
||||
"text entry" {
|
||||
object {
|
||||
x=50
|
||||
y=290
|
||||
width=390
|
||||
height=20
|
||||
}
|
||||
control {
|
||||
chan="$(P):info"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=235
|
||||
width=390
|
||||
height=14
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):spybin"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=235
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="spybin"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=320
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="request"
|
||||
}
|
||||
"text entry" {
|
||||
object {
|
||||
x=50
|
||||
y=315
|
||||
width=390
|
||||
height=20
|
||||
}
|
||||
control {
|
||||
chan="$(P):request"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=340
|
||||
width=390
|
||||
height=14
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):reply"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=340
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="reply"
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=340
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):reply.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=315
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):request.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=290
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):info.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=265
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):cmd.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=235
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):spybin.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=445
|
||||
y=215
|
||||
width=100
|
||||
height=15
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):spy.STAT"
|
||||
clr=14
|
||||
bclr=12
|
||||
}
|
||||
clrmod="alarm"
|
||||
format="string"
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=5
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log19"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=15
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log18"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=25
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log17"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=35
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log16"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=45
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log15"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=55
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log14"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=65
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log13"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=75
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log12"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=85
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log11"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
"text update" {
|
||||
object {
|
||||
x=50
|
||||
y=95
|
||||
width=390
|
||||
height=10
|
||||
}
|
||||
monitor {
|
||||
chan="$(P):log10"
|
||||
clr=14
|
||||
bclr=4
|
||||
}
|
||||
limits {
|
||||
}
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=5
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log19"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=15
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log18"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=25
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log17"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=35
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log16"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=45
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log15"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=55
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log14"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=65
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log13"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=75
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log12"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=85
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log11"
|
||||
}
|
||||
text {
|
||||
object {
|
||||
x=5
|
||||
y=95
|
||||
width=25
|
||||
height=10
|
||||
}
|
||||
"basic attribute" {
|
||||
clr=14
|
||||
}
|
||||
textix="log10"
|
||||
}
|
10
streamApp/test.cmd
Normal file
10
streamApp/test.cmd
Normal file
@ -0,0 +1,10 @@
|
||||
dbLoadDatabase "O.Common/streamApp.dbd"
|
||||
streamApp_registerRecordDeviceDriver
|
||||
|
||||
epicsEnvSet "STREAM_PROTOCOL_PATH", "."
|
||||
|
||||
drvAsynIPPortConfigure "terminal", "localhost:40000"
|
||||
|
||||
dbLoadRecords "test.db","P=TEST"
|
||||
iocInit
|
||||
var streamDebug 1
|
133
streamApp/test.db
Normal file
133
streamApp/test.db
Normal file
@ -0,0 +1,133 @@
|
||||
record (stringout, "$(P):cmd")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto command terminal")
|
||||
field (VAL, "")
|
||||
}
|
||||
record (stringout, "$(P):info")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto info terminal")
|
||||
}
|
||||
record (stringout, "$(P):request")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto request($(P):reply.VAL) terminal")
|
||||
}
|
||||
record (stringin, "$(P):reply")
|
||||
{
|
||||
}
|
||||
record (stringin, "$(P):spy")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto spy terminal")
|
||||
field (SCAN, "I/O Intr")
|
||||
field (FLNK, "$(P):log19")
|
||||
}
|
||||
record (stringin, "$(P):log0")
|
||||
{
|
||||
field (INP, "$(P):spy")
|
||||
}
|
||||
record (stringin, "$(P):log1")
|
||||
{
|
||||
field (INP, "$(P):log0")
|
||||
field (FLNK, "$(P):log0")
|
||||
}
|
||||
record (stringin, "$(P):log2")
|
||||
{
|
||||
field (INP, "$(P):log1")
|
||||
field (FLNK, "$(P):log1")
|
||||
}
|
||||
record (stringin, "$(P):log3")
|
||||
{
|
||||
field (INP, "$(P):log2")
|
||||
field (FLNK, "$(P):log2")
|
||||
}
|
||||
record (stringin, "$(P):log4")
|
||||
{
|
||||
field (INP, "$(P):log3")
|
||||
field (FLNK, "$(P):log3")
|
||||
}
|
||||
record (stringin, "$(P):log5")
|
||||
{
|
||||
field (INP, "$(P):log4")
|
||||
field (FLNK, "$(P):log4")
|
||||
}
|
||||
record (stringin, "$(P):log6")
|
||||
{
|
||||
field (INP, "$(P):log5")
|
||||
field (FLNK, "$(P):log5")
|
||||
}
|
||||
record (stringin, "$(P):log7")
|
||||
{
|
||||
field (INP, "$(P):log6")
|
||||
field (FLNK, "$(P):log6")
|
||||
}
|
||||
record (stringin, "$(P):log8")
|
||||
{
|
||||
field (INP, "$(P):log7")
|
||||
field (FLNK, "$(P):log7")
|
||||
}
|
||||
record (stringin, "$(P):log9")
|
||||
{
|
||||
field (INP, "$(P):log8")
|
||||
field (FLNK, "$(P):log8")
|
||||
}
|
||||
record (stringin, "$(P):log10")
|
||||
{
|
||||
field (INP, "$(P):log9")
|
||||
field (FLNK, "$(P):log9")
|
||||
}
|
||||
record (stringin, "$(P):log11")
|
||||
{
|
||||
field (INP, "$(P):log10")
|
||||
field (FLNK, "$(P):log10")
|
||||
}
|
||||
record (stringin, "$(P):log12")
|
||||
{
|
||||
field (INP, "$(P):log11")
|
||||
field (FLNK, "$(P):log11")
|
||||
}
|
||||
record (stringin, "$(P):log13")
|
||||
{
|
||||
field (INP, "$(P):log12")
|
||||
field (FLNK, "$(P):log12")
|
||||
}
|
||||
record (stringin, "$(P):log14")
|
||||
{
|
||||
field (INP, "$(P):log13")
|
||||
field (FLNK, "$(P):log13")
|
||||
}
|
||||
record (stringin, "$(P):log15")
|
||||
{
|
||||
field (INP, "$(P):log14")
|
||||
field (FLNK, "$(P):log14")
|
||||
}
|
||||
record (stringin, "$(P):log16")
|
||||
{
|
||||
field (INP, "$(P):log15")
|
||||
field (FLNK, "$(P):log15")
|
||||
}
|
||||
record (stringin, "$(P):log17")
|
||||
{
|
||||
field (INP, "$(P):log16")
|
||||
field (FLNK, "$(P):log16")
|
||||
}
|
||||
record (stringin, "$(P):log18")
|
||||
{
|
||||
field (INP, "$(P):log17")
|
||||
field (FLNK, "$(P):log17")
|
||||
}
|
||||
record (stringin, "$(P):log19")
|
||||
{
|
||||
field (INP, "$(P):log18")
|
||||
field (FLNK, "$(P):log18")
|
||||
}
|
||||
record (waveform, "$(P):spybin")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto spybin terminal")
|
||||
field (FTVL, "CHAR")
|
||||
field (NELM, "2000")
|
||||
field (SCAN, "I/O Intr")
|
||||
}
|
25
streamApp/test.proto
Normal file
25
streamApp/test.proto
Normal file
@ -0,0 +1,25 @@
|
||||
terminator = CR LF;
|
||||
readtimeout = 1000;
|
||||
pollperiod = 10;
|
||||
replytimeout = 1000;
|
||||
command {
|
||||
out "%s";
|
||||
}
|
||||
info {
|
||||
out "%s";
|
||||
in "%39c";
|
||||
}
|
||||
request {
|
||||
out "%s";
|
||||
in "%(\$1)39c";
|
||||
}
|
||||
spy {
|
||||
extraInput=ignore;
|
||||
in "%39c";
|
||||
}
|
||||
spybin {
|
||||
readtimeout = 1;
|
||||
terminator = "";
|
||||
extraInput=ignore;
|
||||
in "%r";
|
||||
}
|
21
streamApp/test.readme
Normal file
21
streamApp/test.readme
Normal file
@ -0,0 +1,21 @@
|
||||
Simple test environment:
|
||||
|
||||
1) Start device simulation:
|
||||
terminal.tcl &
|
||||
Or set up a real device.
|
||||
|
||||
2) Start ioc:
|
||||
xterm -e test.cmd &
|
||||
Or edit test.cmd first.
|
||||
|
||||
3) Start medm:
|
||||
medm -x -macro P=TEST test.adl &
|
||||
|
||||
Records:
|
||||
TEST:cmd - writes string to device
|
||||
TEST:info - writes string and reads reply (result in same record)
|
||||
TEST:spy - gets any input line from device
|
||||
TEST:spybin - gets any input (binary waveform)
|
||||
TEST:log* - history of TEST:spy
|
||||
|
||||
Try to write 'help' to TEST:cmd.
|
40
streamApp/tests/printdouble
Executable file
40
streamApp/tests/printdouble
Executable file
@ -0,0 +1,40 @@
|
||||
#!/bin/bash
|
||||
|
||||
filename=$$
|
||||
trap "rm -f $filename.c $filename " EXIT TERM KILL
|
||||
|
||||
cat > $filename.c << EOF
|
||||
#line 8
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
int main (int argc, char** args)
|
||||
{
|
||||
union {
|
||||
float f;
|
||||
double d;
|
||||
unsigned char c [8];
|
||||
} u;
|
||||
|
||||
int i,j;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
{
|
||||
u.f = atof (args[i]);
|
||||
for (j = 0; j < 4; j++)
|
||||
{
|
||||
printf ("\\\\x%02x", u.c[j]);
|
||||
}
|
||||
printf ("\n");
|
||||
u.d = atof (args[i]);
|
||||
for (j = 0; j < 8; j++)
|
||||
{
|
||||
printf ("\\\\x%02x", u.c[j]);
|
||||
}
|
||||
printf ("\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
EOF
|
||||
|
||||
cc -Wall -pedantic $filename.c -o $filename && $filename "$@"
|
201
streamApp/tests/streamtestlib.tcl
Normal file
201
streamApp/tests/streamtestlib.tcl
Normal file
@ -0,0 +1,201 @@
|
||||
#
|
||||
# Usage
|
||||
# 1) source this file
|
||||
# 2) define variables records, protocol, starup
|
||||
# 3) call startioc
|
||||
# 4) use ioccmd, assure, receive, send,...
|
||||
# 5) call finish
|
||||
|
||||
set testname [file tail $argv0]
|
||||
|
||||
proc bgerror msg {
|
||||
error $::errorInfo
|
||||
}
|
||||
|
||||
set debug 0
|
||||
proc debugmsg {string} {
|
||||
global debug
|
||||
if $debug {puts $string}
|
||||
}
|
||||
|
||||
proc deviceconnect {s addr port} {
|
||||
debugmsg "incoming connenction"
|
||||
global sock
|
||||
set sock $s
|
||||
fconfigure $sock -blocking no -buffering none -translation binary
|
||||
fileevent $sock readable "receiveHandler $sock"
|
||||
}
|
||||
|
||||
set inputbuffer {}
|
||||
proc receiveHandler {sock} {
|
||||
global inputbuffer inputlog
|
||||
set input [read $sock]
|
||||
puts -nonewline $inputlog $input
|
||||
append inputbuffer $input
|
||||
debugmsg "receiving \"[escape $inputbuffer]\""
|
||||
if [eof $sock] {
|
||||
close $sock
|
||||
debugmsg "connection closed by ioc"
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
proc startioc {} {
|
||||
global debug records protocol startup port sock ioc testname env streamversion
|
||||
set fd [open test.db w]
|
||||
puts $fd $records
|
||||
close $fd
|
||||
set fd [open test.proto w]
|
||||
puts $fd $protocol
|
||||
close $fd
|
||||
set fd [open test.cmd w 0777]
|
||||
|
||||
if [info exists streamversion] {
|
||||
puts $fd "#!/usr/local/bin/iocsh"
|
||||
puts $fd "require stream,$streamversion"
|
||||
} else {
|
||||
puts $fd "#!../O.$env(EPICS_HOST_ARCH)/streamApp"
|
||||
puts $fd "dbLoadDatabase ../O.Common/streamApp.dbd"
|
||||
puts $fd "streamApp_registerRecordDeviceDriver"
|
||||
}
|
||||
puts $fd "epicsEnvSet STREAM_PROTOCOL_PATH ."
|
||||
puts $fd "drvAsynIPPortConfigure device localhost:$port"
|
||||
puts $fd "dbLoadRecords test.db"
|
||||
puts $fd $startup
|
||||
puts $fd "iocInit"
|
||||
puts $fd "dbl"
|
||||
puts $fd "dbior stream 2"
|
||||
puts $fd "var streamDebug 1"
|
||||
close $fd
|
||||
if [info exists streamversion] {
|
||||
set ioc [open "|iocsh test.cmd >& $testname.ioclog 2>@stderr" w]
|
||||
} else {
|
||||
set ioc [open "|../O.$env(EPICS_HOST_ARCH)/streamApp test.cmd >& $testname.ioclog 2>@stderr" w]
|
||||
}
|
||||
fconfigure $ioc -blocking yes -buffering none
|
||||
debugmsg "waiting to connect"
|
||||
vwait sock
|
||||
}
|
||||
|
||||
set lastcommand ""
|
||||
set line 0
|
||||
proc ioccmd {command} {
|
||||
global ioc
|
||||
global lastcommand
|
||||
global line
|
||||
set lastcommand $command
|
||||
set line 0
|
||||
debugmsg "$command"
|
||||
puts $ioc $command
|
||||
}
|
||||
|
||||
proc send {string} {
|
||||
global sock lastsent
|
||||
set lastsent $string
|
||||
puts -nonewline $sock $string
|
||||
flush $sock
|
||||
}
|
||||
|
||||
set timeout 5000
|
||||
proc receive {} {
|
||||
global inputbuffer timeoutid timeout
|
||||
set timeoutid [after $timeout {
|
||||
set inputbuffer {}
|
||||
}]
|
||||
if {$inputbuffer == {}} { vwait inputbuffer }
|
||||
after cancel $timeoutid
|
||||
if {$inputbuffer == {}} {
|
||||
return -code error "Error in receive: timeout"
|
||||
}
|
||||
set index [string first "\n" $inputbuffer]
|
||||
if {$index > -1} {
|
||||
set input [string range $inputbuffer 0 $index]
|
||||
set inputbuffer [string range $inputbuffer [expr $index+1] end]
|
||||
} else {
|
||||
set input $inputbuffer
|
||||
set inputbuffer {}
|
||||
}
|
||||
return $input
|
||||
}
|
||||
|
||||
set faults 0
|
||||
proc assure {args} {
|
||||
global faults
|
||||
global lastcommand
|
||||
global lastsent
|
||||
global line
|
||||
|
||||
incr line
|
||||
set input {}
|
||||
for {set i 0} {$i < [llength $args]} {incr i} {
|
||||
if [catch {lappend input [receive]} msg] {
|
||||
puts stderr $msg
|
||||
break
|
||||
}
|
||||
}
|
||||
set notfound {}
|
||||
foreach expected $args {
|
||||
set index [lsearch -exact $input $expected]
|
||||
if {$index > -1} {
|
||||
set input [lreplace $input $index $index]
|
||||
} else {
|
||||
lappend notfound $expected
|
||||
}
|
||||
}
|
||||
if {[llength $notfound] || [llength $input]} {
|
||||
puts stderr "In command \"$lastcommand\""
|
||||
if [info exists lastsent] {
|
||||
puts stderr "last sent: \"[escape $lastsent]\""
|
||||
}
|
||||
}
|
||||
foreach string $notfound {
|
||||
puts stderr "Error in assure: line $line missing \"[escape $string]\""
|
||||
}
|
||||
foreach string $input {
|
||||
puts stderr "Error in assure: got unexpected \"[escape $string]\""
|
||||
}
|
||||
if {[llength $notfound] || [llength $input]} {incr faults}
|
||||
}
|
||||
|
||||
proc escape {string} {
|
||||
while {![string is print -failindex index $string]} {
|
||||
set char [string index $string $index]
|
||||
scan $char "%c" code
|
||||
switch $char {
|
||||
"\r" { set escaped "\\r" }
|
||||
"\n" { set escaped "\\n" }
|
||||
"\a" { set escaped "\\a" }
|
||||
"\t" { set escaped "\\t" }
|
||||
default { set escaped [format "<%02x>" $code] }
|
||||
}
|
||||
set string [string replace $string $index $index $escaped]
|
||||
}
|
||||
return $string
|
||||
}
|
||||
|
||||
proc finish {} {
|
||||
global ioc timeout testname faults
|
||||
set timeout 1000
|
||||
while {![catch {set string [receive]}]} {
|
||||
puts stderr "Error in finish: unexpected \"[escape $string]\""
|
||||
incr faults
|
||||
}
|
||||
after 100
|
||||
close $ioc
|
||||
if $faults {
|
||||
puts "\033\[31;7mTest failed.\033\[0m"
|
||||
exit 1
|
||||
}
|
||||
puts "\033\[32mTest passed.\033\[0m"
|
||||
eval file delete [glob -nocomplain test.*] StreamDebug.log $testname.ioclog
|
||||
}
|
||||
|
||||
set port 40123
|
||||
socket -server deviceconnect $port
|
||||
set inputlog [open "test.inputlog" w]
|
||||
|
||||
# SLS style driver modules (optionally with version)
|
||||
if {[lindex $argv 0] == "-sls"} {
|
||||
set streamversion [lindex $argv 1]
|
||||
set argv [lrange $argv 2 end]
|
||||
}
|
96
streamApp/tests/test64Bit
Executable file
96
streamApp/tests/test64Bit
Executable file
@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:ao")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto ao device")
|
||||
}
|
||||
record (longout, "DZ:longout")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto longout device")
|
||||
}
|
||||
record (bo, "DZ:bo")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto bo device")
|
||||
field (MASK, "-1")
|
||||
}
|
||||
record (mbbo, "DZ:mbbo")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto mbbo device")
|
||||
field (ZRVL, "0")
|
||||
field (ONVL, "-1")
|
||||
field (ZRST, "0")
|
||||
field (ONST, "-1")
|
||||
}
|
||||
record (mbboDirect, "DZ:mbboDirect")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto mbboDirect device")
|
||||
}
|
||||
record (ai, "DZ:ai")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto ai device")
|
||||
}
|
||||
record (longin, "DZ:longin")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto longin device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
# these records use signed values: %d
|
||||
ao {out "ao %.3f %d %(VAL).3f %(RVAL)d";}
|
||||
longout {out "longout %d %(VAL)d";}
|
||||
ai {out "ai?"; in "%d"; out "ai %d";}
|
||||
longin {out "longin?"; in "%d"; out "longin %d";}
|
||||
# these records use unsigned values: %u, %x
|
||||
bo {out "bo %u %x %b";}
|
||||
mbbo {out "mbbo %u %x %b";}
|
||||
mbboDirect {out "mbboDirect %u %x %b";}
|
||||
bi {out "bi?"; in "%d"; out "bi %d %x";}
|
||||
mbbi {out "mbbi?"; in "%d"; out "mbbi %d %x";}
|
||||
mbbiDirect {out "mbbiDirect?"; in "%d"; out "mbbiDirect %d %x";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
ioccmd {dbpf DZ:ao -1}
|
||||
assure "ao -1.000 -1 -1.000 -1\n"
|
||||
ioccmd {dbpf DZ:longout -1}
|
||||
assure "longout -1 -1\n"
|
||||
ioccmd {dbpf DZ:bo 1}
|
||||
assure "bo 4294967295 ffffffff 11111111111111111111111111111111\n"
|
||||
ioccmd {dbpf DZ:mbbo -1}
|
||||
assure "mbbo 4294967295 ffffffff 11111111111111111111111111111111\n"
|
||||
ioccmd {dbpf DZ:mbboDirect.B0 1}
|
||||
assure "mbboDirect 1 1 1\n"
|
||||
ioccmd {dbpf DZ:mbboDirect -1}
|
||||
assure "mbboDirect 65535 ffff 1111111111111111\n"
|
||||
ioccmd {dbpf DZ:ai.PROC 1}
|
||||
assure "ai?\n"
|
||||
send "-1\n"
|
||||
assure "ai -1\n"
|
||||
ioccmd {dbpf DZ:longin.PROC 1}
|
||||
assure "longin?\n"
|
||||
send "-1\n"
|
||||
assure "longin -1\n"
|
||||
|
||||
|
||||
finish
|
35
streamApp/tests/testBo
Executable file
35
streamApp/tests/testBo
Executable file
@ -0,0 +1,35 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (bo, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 device")
|
||||
field (ZNAM, "OFF")
|
||||
field (ONAM, "ON")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
test1 {out "%i %r %s";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1 0}
|
||||
assure "0 \x00 OFF"
|
||||
ioccmd {dbpf DZ:test1 1}
|
||||
assure "1 \x01 ON"
|
||||
|
||||
finish
|
108
streamApp/tests/testCharset
Executable file
108
streamApp/tests/testCharset
Executable file
@ -0,0 +1,108 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (stringin, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test1 device")
|
||||
}
|
||||
record (stringin, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test2 device")
|
||||
}
|
||||
record (stringin, "DZ:test3")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test3 device")
|
||||
}
|
||||
record (stringin, "DZ:test4")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test4 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
extraInput=ignore;
|
||||
@mismatch {out "mismatch";}
|
||||
test1 {in "%[a-zA-Z1-5]%(DESC) #s"; out "%s|%(DESC)s" }
|
||||
test2 {in "%[]A-Za-z ]%(DESC) #s"; out "%s|%(DESC)s" }
|
||||
test3 {in "%[^]A-Z]%(DESC) #s"; out "%s|%(DESC)s" }
|
||||
test4 {in "%[^]-A-Z]%(DESC) #s"; out "%s|%(DESC)s" }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "This is a test\n"
|
||||
assure "This| is a test\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "Test123]bla\n"
|
||||
assure "Test123|]bla\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "Test123456\n"
|
||||
assure "Test12345|6\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "Test43210\n"
|
||||
assure "Test4321|0\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "test-bla\n"
|
||||
assure "test|-bla\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " Space first\n"
|
||||
assure "| Space first\n"
|
||||
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "This is a test\n"
|
||||
assure "This is a test|\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "Test]xx123bla\n"
|
||||
assure "Test]xx|123bla\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "test-bla\n"
|
||||
assure "test|-bla\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " Space first\n"
|
||||
assure " Space first|\n"
|
||||
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "this is a test\n"
|
||||
assure "this is a test|\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "test]xx123bla\n"
|
||||
assure "test|]xx123bla\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "test-bla\n"
|
||||
assure "test-bla|\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " Space first\n"
|
||||
assure " |Space first\n"
|
||||
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "this is a test\n"
|
||||
assure "this is a test|\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "test]xx123bla\n"
|
||||
assure "test|]xx123bla\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "test-bla\n"
|
||||
assure "test|-bla\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " Space first\n"
|
||||
assure " |Space first\n"
|
||||
|
||||
finish
|
||||
|
30
streamApp/tests/testChecksum
Executable file
30
streamApp/tests/testChecksum
Executable file
@ -0,0 +1,30 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
test1 {out 0x55 0x40 0x04 0x00 0x00 'z;' "%<sum>";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
ioccmd {dbpf DZ:test1 "1"}
|
||||
assure "\x55\x40\x04\x00\x00z;\x4e"
|
||||
|
||||
finish
|
43
streamApp/tests/testCompare
Executable file
43
streamApp/tests/testCompare
Executable file
@ -0,0 +1,43 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
@mismatch { out "mismatch"; }
|
||||
test1 {out "set %.2f"; in "ack %=.2f X"; out "OK"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1 3.14}
|
||||
assure "set 3.14\n"
|
||||
send "ack 3.14 X\n"
|
||||
assure "OK\n"
|
||||
ioccmd {dbpf DZ:test1 0}
|
||||
assure "set 0.00\n"
|
||||
send "ack 0.00 X\n"
|
||||
assure "OK\n"
|
||||
ioccmd {dbpf DZ:test1 1}
|
||||
assure "set 1.00\n"
|
||||
send "ack 1.0 X\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
finish
|
56
streamApp/tests/testDefaultInput
Executable file
56
streamApp/tests/testDefaultInput
Executable file
@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (longin, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto integer device")
|
||||
}
|
||||
record (ai, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto double device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
integer {out "integer"; in "n: %?d xx"; out "n = %d"; }
|
||||
double {out "double"; in "n: %?g xx"; out "n = %.4f"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "integer\n";
|
||||
send "n: -12 xx\n";
|
||||
assure "n = -12\n";
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "integer\n";
|
||||
send "n: xx\n";
|
||||
assure "n = 0\n";
|
||||
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
assure "double\n";
|
||||
send "n: 3.1415 xx\n";
|
||||
assure "n = 3.1415\n";
|
||||
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
assure "double\n";
|
||||
send "n: xx\n";
|
||||
assure "n = 0.0000\n";
|
||||
|
||||
|
||||
finish
|
121
streamApp/tests/testDouble
Executable file
121
streamApp/tests/testDouble
Executable file
@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ai, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test1 device")
|
||||
}
|
||||
record (ai, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test2 device")
|
||||
}
|
||||
record (ai, "DZ:test3")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test3 device")
|
||||
}
|
||||
record (ai, "DZ:test4")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test4 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
@mismatch {out "mismatch";}
|
||||
test1 {in "%f"; out "|%f|%10f|%.2f|%- 10.5f|% f|%+f|%-5.1f|%#f|"; }
|
||||
test2 {in "%6f%(DESC)s"; out "|%g|%(DESC)s|"; }
|
||||
test3 {in "% 6f%(DESC)s"; out "|%g|%(DESC)s|"; }
|
||||
test4 {in "%#f"; out "%f"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "3.14159265359\n"
|
||||
assure "|3.141593| 3.141593|3.14| 3.14159 | 3.141593|+3.141593|3.1 |3.141593|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-3.14159265359\n"
|
||||
assure "|-3.141593| -3.141593|-3.14|-3.14159 |-3.141593|-3.141593|-3.1 |-3.141593|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "0\n"
|
||||
assure "|0.000000| 0.000000|0.00| 0.00000 | 0.000000|+0.000000|0.0 |0.000000|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "NAN\n"
|
||||
assure "|nan| nan|nan| nan | nan|+nan|nan |nan|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-Inf\n"
|
||||
assure "|-inf| -inf|-inf|-inf |-inf|-inf|-inf |-inf|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "1e6\n"
|
||||
assure "|1000000.000000|1000000.000000|1000000.00| 1000000.00000| 1000000.000000|+1000000.000000|1000000.0|1000000.000000|\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " - 3.14159265359\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "bla\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " bla\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-bla\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "3.14159265359\n"
|
||||
assure "|3.1415|9265359|\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 3.14159265359\n"
|
||||
assure "|3.1415|9265359|\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 10blabla\n"
|
||||
assure "|10|blabla|\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 1\n"
|
||||
assure "|1||\n"
|
||||
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "3.14159265359\n"
|
||||
assure "|3.1415|9265359|\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 3.14159265359\n"
|
||||
assure "|3|.14159265359|\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 10blabla\n"
|
||||
assure "|10|blabla|\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 1\n"
|
||||
assure "|1||\n"
|
||||
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "3.14159265359\n"
|
||||
assure "3.141593\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " -3.14159265359\n"
|
||||
assure "-3.141593\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " - 3.14159265359\n"
|
||||
assure "-3.141593\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " + 3.14159265359\n"
|
||||
assure "3.141593\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " +- 3.14159265359\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
finish
|
75
streamApp/tests/testDummyInterface
Executable file
75
streamApp/tests/testDummyInterface
Executable file
@ -0,0 +1,75 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "dummy" is not connected to any I/O
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 dummy")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
test1 {exec "dbpr %(NAME)s %i >testDummyInterface.out";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1 3}
|
||||
|
||||
after 1000
|
||||
|
||||
if [catch {
|
||||
set fd [open testDummyInterface.out]
|
||||
set output [read $fd]
|
||||
close $fd
|
||||
} msg] {
|
||||
puts stderr $msg
|
||||
incr faults
|
||||
}
|
||||
|
||||
set expected {ACKS: NO_ALARM ACKT: YES ADEL: 0 ALST: 0
|
||||
AOFF: 0 ASG: ASLO: 0 BKPT: 00
|
||||
DESC: DISA: 0 DISP: 0 DISS: NO_ALARM
|
||||
DISV: 1 DOL:CONSTANT DRVH: 0 DRVL: 0
|
||||
DTYP: stream EGU: EGUF: 0 EGUL: 0
|
||||
EOFF: 0 ESLO: 1 EVNT: 0 FLNK:CONSTANT 0
|
||||
HHSV: NO_ALARM HIGH: 0 HIHI: 0 HOPR: 0
|
||||
HSV: NO_ALARM HYST: 0 INIT: 1
|
||||
IVOA: Continue normally IVOV: 0 LALM: 3
|
||||
LBRK: 0 LCNT: 0 LINR: NO CONVERSION LLSV: NO_ALARM
|
||||
LOLO: 0 LOPR: 0 LOW: 0 LSV: NO_ALARM
|
||||
MDEL: 0 MLST: 0 NAME: DZ:test1 NSEV: NO_ALARM
|
||||
NSTA: NO_ALARM OIF: Full OMOD: 1 OMSL: supervisory
|
||||
ORAW: 0 ORBV: 0 OROC: 0
|
||||
OUT:INST_IO @test.proto test1 dummy OVAL: 3 PACT: 1
|
||||
PHAS: 0 PINI: NO PREC: 0 PRIO: LOW
|
||||
PROC: 0 PUTF: 1 PVAL: 3 RBV: 0
|
||||
ROFF: 0 RPRO: 0 RVAL: 3 SCAN: Passive
|
||||
SDIS:CONSTANT SEVR: INVALID SIML:CONSTANT SIMM: NO
|
||||
SIMS: NO_ALARM SIOL:CONSTANT STAT: UDF TIME: <undefined>
|
||||
TPRO: 0 TSE: 0 TSEL:CONSTANT UDF: 0
|
||||
VAL: 3
|
||||
}
|
||||
|
||||
if {"$output" != "$expected"} {
|
||||
puts stderr "Wrong output:"
|
||||
puts stderr "$output"
|
||||
puts stderr "instead of:"
|
||||
puts stderr "$expected"
|
||||
incr faults
|
||||
} else {
|
||||
file delete testDummyInterface.out
|
||||
}
|
||||
|
||||
finish
|
124
streamApp/tests/testEnum
Executable file
124
streamApp/tests/testEnum
Executable file
@ -0,0 +1,124 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (longout, "DZ:testout")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto out device")
|
||||
}
|
||||
record (longin, "DZ:testin")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto in device")
|
||||
}
|
||||
record (longout, "DZ:testout1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto out1 device")
|
||||
}
|
||||
record (longin, "DZ:testin1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto in1 device")
|
||||
}
|
||||
record (longout, "DZ:testout2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto out2 device")
|
||||
}
|
||||
record (longin, "DZ:testin2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto in2 device")
|
||||
}
|
||||
record (longin, "DZ:testin3")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto in3 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
out {out "%{zero|one|two}bla";}
|
||||
in {in "%{zero|one|two}bla"; out "%d";}
|
||||
out1 {out "%#{zero|one|two}bla";}
|
||||
in1 {in "%#{zero|one|two}bla"; out "%d";}
|
||||
out2 {out "%#{zero=-1|one|two=5}bla";}
|
||||
in2 {in "%#{zero=-1|one|two=5}bla"; out "%d";}
|
||||
in3 {in "%{\x00|\r|}bla"; out "%d";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
var streamDebug 1
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:testout 0}
|
||||
assure "zerobla\n"
|
||||
ioccmd {dbpf DZ:testout 1}
|
||||
assure "onebla\n"
|
||||
ioccmd {dbpf DZ:testout 2}
|
||||
assure "twobla\n"
|
||||
ioccmd {dbpf DZ:testout1 0}
|
||||
assure "zerobla\n"
|
||||
ioccmd {dbpf DZ:testout1 1}
|
||||
assure "onebla\n"
|
||||
ioccmd {dbpf DZ:testout1 2}
|
||||
assure "twobla\n"
|
||||
ioccmd {dbpf DZ:testout2 -1}
|
||||
assure "zerobla\n"
|
||||
ioccmd {dbpf DZ:testout2 0}
|
||||
assure "onebla\n"
|
||||
ioccmd {dbpf DZ:testout2 5}
|
||||
assure "twobla\n"
|
||||
|
||||
ioccmd {dbpf DZ:testin.PROC 1}
|
||||
send "zerobla\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:testin.PROC 1}
|
||||
send "onebla\n"
|
||||
assure "1\n"
|
||||
ioccmd {dbpf DZ:testin.PROC 1}
|
||||
send "twobla\n"
|
||||
assure "2\n"
|
||||
ioccmd {dbpf DZ:testin1.PROC 1}
|
||||
send "zerobla\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:testin1.PROC 1}
|
||||
send "onebla\n"
|
||||
assure "1\n"
|
||||
ioccmd {dbpf DZ:testin1.PROC 1}
|
||||
send "twobla\n"
|
||||
assure "2\n"
|
||||
ioccmd {dbpf DZ:testin2.PROC 1}
|
||||
send "zerobla\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:testin2.PROC 1}
|
||||
send "onebla\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:testin2.PROC 1}
|
||||
send "twobla\n"
|
||||
assure "5\n"
|
||||
|
||||
ioccmd {dbpf DZ:testin3.PROC 1}
|
||||
send "\000bla\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:testin3.PROC 1}
|
||||
send "\rbla\n"
|
||||
assure "1\n"
|
||||
ioccmd {dbpf DZ:testin3.PROC 1}
|
||||
send "bla\n"
|
||||
assure "2\n"
|
||||
|
||||
|
||||
finish
|
67
streamApp/tests/testFormatm
Executable file
67
streamApp/tests/testFormatm
Executable file
@ -0,0 +1,67 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:ao")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto ao device")
|
||||
}
|
||||
record (ai, "DZ:ai")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto ai device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
ao {out "'%m' '%.4m' '%+.4m' '% .4m' '% +.4m' '%10.4m' '%-10.4m'";}
|
||||
ai {out "?"; in "%m"; out "%.4e";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:ao 0}
|
||||
assure "'000000+00' '0000+00' '+0000+00' ' 0000+00' '+0000+00' ' 0000+00' '0000+00 '\n"
|
||||
ioccmd {dbpf DZ:ao 1}
|
||||
assure "'100000-05' '1000-03' '+1000-03' ' 1000-03' '+1000-03' ' 1000-03' '1000-03 '\n"
|
||||
ioccmd {dbpf DZ:ao 1000}
|
||||
assure "'100000-02' '1000+00' '+1000+00' ' 1000+00' '+1000+00' ' 1000+00' '1000+00 '\n"
|
||||
ioccmd {dbpf DZ:ao 1000000}
|
||||
assure "'100000+01' '1000+03' '+1000+03' ' 1000+03' '+1000+03' ' 1000+03' '1000+03 '\n"
|
||||
ioccmd {dbpf DZ:ao -1}
|
||||
assure "'-100000-05' '-1000-03' '-1000-03' '-1000-03' '-1000-03' ' -1000-03' '-1000-03 '\n"
|
||||
ioccmd {dbpf DZ:ao 12345}
|
||||
assure "'123450-01' '1235+01' '+1235+01' ' 1235+01' '+1235+01' ' 1235+01' '1235+01 '\n"
|
||||
ioccmd {dbpf DZ:ao -1.2345e-15}
|
||||
assure "'-123450-20' '-1235-18' '-1235-18' '-1235-18' '-1235-18' ' -1235-18' '-1235-18 '\n"
|
||||
ioccmd {dbpf DZ:ao 1e-100}
|
||||
assure "'100000-105' '1000-103' '+1000-103' ' 1000-103' '+1000-103' ' 1000-103' '1000-103 '\n"
|
||||
ioccmd {dbpf DZ:ai.PROC 1}
|
||||
assure "?\n"
|
||||
send "+1234+56\n"
|
||||
assure "1.2340e+59\n"
|
||||
ioccmd {dbpf DZ:ai.PROC 1}
|
||||
assure "?\n"
|
||||
send "-1234-56\n"
|
||||
assure "-1.2340e-53\n"
|
||||
ioccmd {dbpf DZ:ai.PROC 1}
|
||||
assure "?\n"
|
||||
send "-12340000-60\n"
|
||||
assure "-1.2340e-53\n"
|
||||
ioccmd {dbpf DZ:ai.PROC 1}
|
||||
assure "?\n"
|
||||
send "+00000000+0\n"
|
||||
assure "0.0000e+00\n"
|
||||
finish
|
102
streamApp/tests/testFormats
Executable file
102
streamApp/tests/testFormats
Executable file
@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ao, "DZ:ao")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto ao device")
|
||||
field (EOFF, "-10")
|
||||
field (ESLO, "0.000305180437934")
|
||||
field (LINR, "LINEAR")
|
||||
}
|
||||
record (longout, "DZ:lo")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto lo device")
|
||||
}
|
||||
record (longout, "DZ:bcd")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto bcd device")
|
||||
}
|
||||
record (stringout, "DZ:chksum")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto chksum device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
ao {out "%.2f %.2e %.2E %.2g %.2G %i %d %u %o %04x %#.2f %#.2e %#.2E %#.2g %#.2G %#i %#d %#u %#o %#06x";}
|
||||
lo {out "%d %(VAL)d %06d %x %06X %b %06b %.6b %B.! %06B.!";}
|
||||
bcd {out "%D %6D %.2D %.4D %.6D %.8D %#D %#6D %#.2D %#.4D %#.6D";}
|
||||
chksum {out "%s%<xor>";
|
||||
out "%s%0<sum>";
|
||||
out "%s%0<crc8>";
|
||||
out "%s%0<crc16>";
|
||||
out "%s%0<crc32>";
|
||||
out "%s%0<adler32>";
|
||||
}
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
# Some formats give different results on 32 bit and 64 bit machines.
|
||||
# This occurs when printing negative numbers with unsigned formats.
|
||||
# This is normal. E.g. -1 HAS a different number of 1 bits.
|
||||
# Specify the width field in the format if this is a problem.
|
||||
|
||||
ioccmd {dbpf DZ:ao 0}
|
||||
assure "0.00 0.00e+00 0.00E+00 0 0 32767 32767 32767 77777 7fff 0.00 0.00e+00 0.00E+00 0.0 0.0 32767 32767 32767 077777 0x7fff\n"
|
||||
ioccmd {dbpf DZ:ao 10}
|
||||
assure "10.00 1.00e+01 1.00E+01 10 10 65535 65535 65535 177777 ffff 10.00 1.00e+01 1.00E+01 10. 10. 65535 65535 65535 0177777 0xffff\n"
|
||||
ioccmd {dbpf DZ:ao -10}
|
||||
assure "-10.00 -1.00e+01 -1.00E+01 -10 -10 0 0 0 0 0000 -10.00 -1.00e+01 -1.00E+01 -10. -10. 0 0 0 0 000000\n"
|
||||
ioccmd {dbpf DZ:ao 1e-6}
|
||||
assure "0.00 1.00e-06 1.00E-06 1e-06 1E-06 32768 32768 32768 100000 8000 0.00 1.00e-06 1.00E-06 1.0e-06 1.0E-06 32768 32768 32768 0100000 0x8000\n"
|
||||
ioccmd {dbpf DZ:lo 0}
|
||||
assure "0 0 000000 0 000000 0 000000 000000 . ......\n"
|
||||
ioccmd {dbpf DZ:lo 12345}
|
||||
assure "12345 12345 012345 3039 003039 11000000111001 11000000111001 111001 !!......!!!..! !!......!!!..!\n"
|
||||
ioccmd {dbpf DZ:lo -1}
|
||||
if {$tcl_platform(machine) == "x86_64"} {
|
||||
assure "-1 -1 -00001 ffffffffffffffff FFFFFFFFFFFFFFFF 1111111111111111111111111111111111111111111111111111111111111111 1111111111111111111111111111111111111111111111111111111111111111 111111 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
} else {
|
||||
assure "-1 -1 -00001 ffffffff FFFFFFFF 11111111111111111111111111111111 11111111111111111111111111111111 111111 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n"
|
||||
}
|
||||
ioccmd {dbpf DZ:lo -1234}
|
||||
if {$tcl_platform(machine) == "x86_64"} {
|
||||
assure "-1234 -1234 -01234 fffffffffffffb2e FFFFFFFFFFFFFB2E 1111111111111111111111111111111111111111111111111111101100101110 1111111111111111111111111111111111111111111111111111101100101110 101110 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.!!..!.!!!. !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!.!!..!.!!!.\n"
|
||||
} else {
|
||||
assure "-1234 -1234 -01234 fffffb2e FFFFFB2E 11111111111111111111101100101110 11111111111111111111101100101110 101110 !!!!!!!!!!!!!!!!!!!!!.!!..!.!!!. !!!!!!!!!!!!!!!!!!!!!.!!..!.!!!.\n"
|
||||
}
|
||||
ioccmd {dbpf DZ:lo 255}
|
||||
assure "255 255 000255 ff 0000FF 11111111 11111111 111111 !!!!!!!! !!!!!!!!\n"
|
||||
ioccmd {dbpf DZ:lo 65535}
|
||||
assure "65535 65535 065535 ffff 00FFFF 1111111111111111 1111111111111111 111111 !!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!\n"
|
||||
ioccmd {dbpf DZ:bcd 1020304}
|
||||
if {$tcl_platform(machine) == "x86_64"} {
|
||||
assure "\0\0\0\0\1\2\3\4 \0\0\0\0\1\2\3\4 \4 \3\4 \2\3\4 \1\2\3\4 \4\3\2\1\0\0\0\0 \4\3\2\1\0\0\0\0 \4 \4\3 \4\3\2\n"
|
||||
} else {
|
||||
assure "\1\2\3\4 \0\0\1\2\3\4 \4 \3\4 \2\3\4 \1\2\3\4 \4\3\2\1 \4\3\2\1\0\0 \4 \4\3 \4\3\2\n"
|
||||
}
|
||||
ioccmd {dbpf DZ:chksum "123456789"}
|
||||
assure "1234567891\n"
|
||||
assure "123456789DD\n"
|
||||
assure "123456789F4\n"
|
||||
assure "123456789FEE8\n"
|
||||
assure "123456789FC891918\n"
|
||||
assure "123456789091E01DE\n"
|
||||
finish
|
44
streamApp/tests/testHalfInputTerminator
Executable file
44
streamApp/tests/testHalfInputTerminator
Executable file
@ -0,0 +1,44 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (stringin, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test1 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = CR LF;
|
||||
test1 {out "Give input"; in "%s"; out "%s"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\r\n"
|
||||
send "abc\r\n"
|
||||
assure "abc\r\n"
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\r\n"
|
||||
send "x\r\n"
|
||||
assure "x\r\n"
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\r\n"
|
||||
send "\r\n"
|
||||
assure "\r\n"
|
||||
|
||||
finish
|
455
streamApp/tests/testInteger
Executable file
455
streamApp/tests/testInteger
Executable file
@ -0,0 +1,455 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (ai, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test1 device")
|
||||
}
|
||||
record (ai, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test2 device")
|
||||
}
|
||||
record (ai, "DZ:test3")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test3 device")
|
||||
}
|
||||
record (ai, "DZ:test4")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test4 device")
|
||||
}
|
||||
record (ai, "DZ:test5")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test5 device")
|
||||
}
|
||||
record (ai, "DZ:test6")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test6 device")
|
||||
}
|
||||
record (ai, "DZ:test7")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test7 device")
|
||||
}
|
||||
record (ai, "DZ:test8")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test8 device")
|
||||
}
|
||||
record (ai, "DZ:test9")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test9 device")
|
||||
}
|
||||
record (ai, "DZ:test10")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test10 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
extraInput=ignore;
|
||||
@mismatch {out "mismatch";}
|
||||
test1 {in "%i"; out "%i"; }
|
||||
test2 {in "%d"; out "%d"; }
|
||||
test3 {in "%u"; out "%u"; }
|
||||
test4 {in "%x"; out "%x"; }
|
||||
test5 {in "%o"; out "%o"; }
|
||||
test6 {in "%4i"; out "%i"; }
|
||||
test7 {in "% 4i"; out "%i"; }
|
||||
test8 {in "%#i"; out "%i"; }
|
||||
test9 {in "%-x"; out "%i"; }
|
||||
test10 {in "%-o"; out "%i"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "5349\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "-74565\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "1\n"
|
||||
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test2.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "0\n"
|
||||
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test3.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "ffffffff\n"
|
||||
ioccmd {dbpf DZ:test4.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test5.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "mismatch\n"
|
||||
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "12345\n"
|
||||
assure "1234\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-123\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "1234\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-123\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "18\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "83\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "255\n"
|
||||
ioccmd {dbpf DZ:test6.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "-15\n"
|
||||
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "12345\n"
|
||||
assure "1234\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-123\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "1\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "1\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "10\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "255\n"
|
||||
ioccmd {dbpf DZ:test7.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "-15\n"
|
||||
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "12345\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "-12345\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "5349\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "-74565\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:test8.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "1\n"
|
||||
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "12345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "-74565\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "-1\n"
|
||||
ioccmd {dbpf DZ:test9.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "1\n"
|
||||
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "12345\n"
|
||||
assure "5349\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "-12345\n"
|
||||
assure "-5349\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send " 12345\n"
|
||||
assure "5349\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send " -12345\n"
|
||||
assure "-5349\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "- 12345\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "\n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send " \n"
|
||||
assure "mismatch\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send " 0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send " 012345\n"
|
||||
assure "5349\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "-0x12345\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "0xffffffff\n"
|
||||
assure "0\n"
|
||||
ioccmd {dbpf DZ:test10.PROC 1}
|
||||
send "-0xffffffff\n"
|
||||
assure "0\n"
|
||||
|
||||
finish
|
81
streamApp/tests/testIoIntrPollTimeout
Executable file
81
streamApp/tests/testIoIntrPollTimeout
Executable file
@ -0,0 +1,81 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (bo, "DZ:ready")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto ready device")
|
||||
field (PINI, "YES")
|
||||
}
|
||||
record (longin, "DZ:read")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto readintr device")
|
||||
field (SCAN, "I/O Intr")
|
||||
field (FLNK, "DZ:count")
|
||||
}
|
||||
record (calc, "DZ:count")
|
||||
{
|
||||
field (INPA, "DZ:count")
|
||||
field (CALC, "A+1")
|
||||
field (FLNK, "DZ:errorcount")
|
||||
}
|
||||
record (calc, "DZ:errorcount")
|
||||
{
|
||||
field (INPA, "DZ:errorcount")
|
||||
field (INPB, "DZ:read")
|
||||
field (INPC, "DZ:count")
|
||||
field (CALC, "A+(B#C)")
|
||||
}
|
||||
record (longout, "DZ:printresult")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (DOL, "DZ:read")
|
||||
field (OMSL, "closed_loop")
|
||||
field (OUT, "@test.proto printresult device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
PollPeriod=10;
|
||||
ReadTimeout=50;
|
||||
ready {out "ready"; }
|
||||
readintr {in "This is a line in chunks with number %d"; }
|
||||
printresult {out "Last line received: %d";
|
||||
out "Line count: %(DZ:count)d";
|
||||
out "Error count: %(DZ:errorcount)d"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
set rep 10
|
||||
|
||||
assure "ready\n"
|
||||
for {set i 1} {$i <= $rep} {incr i} {
|
||||
send "This "
|
||||
after 10
|
||||
send "is a "
|
||||
after 10
|
||||
send "line in chunks "
|
||||
after 10
|
||||
send "with number $i\n"
|
||||
after 100
|
||||
}
|
||||
ioccmd {dbpf "DZ:printresult.PROC" 1}
|
||||
assure "Last line received: $rep\n"
|
||||
assure "Line count: $rep\n"
|
||||
assure "Error count: 0\n"
|
||||
finish
|
||||
|
45
streamApp/tests/testLongInputTerminator
Executable file
45
streamApp/tests/testLongInputTerminator
Executable file
@ -0,0 +1,45 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (stringin, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto test1 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
InTerminator = CR CR CR;
|
||||
OutTerminator = LF;
|
||||
test1 {out "Give input"; in "%39c"; out "%s"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\n"
|
||||
send "abc\r\r\r"
|
||||
assure "abc\n"
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\n"
|
||||
send "klm\rxyz\r\r\r"
|
||||
assure "klm\rxyz\n"
|
||||
|
||||
ioccmd {dbpf DZ:test1.PROC 1}
|
||||
assure "Give input\n"
|
||||
send "123\r\rxyz\r\r\r"
|
||||
assure "123\r\rxyz\n"
|
||||
|
||||
finish
|
73
streamApp/tests/testLongUnsolicitedInput
Executable file
73
streamApp/tests/testLongUnsolicitedInput
Executable file
@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (bo, "DZ:ready")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto ready device")
|
||||
field (PINI, "YES")
|
||||
}
|
||||
record (stringin, "DZ:read")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (INP, "@test.proto readintr device")
|
||||
field (SCAN, "I/O Intr")
|
||||
field (FLNK, "DZ:count")
|
||||
}
|
||||
record (calc, "DZ:count")
|
||||
{
|
||||
field (INPA, "DZ:count")
|
||||
field (CALC, "A+1")
|
||||
}
|
||||
record (longout, "DZ:countout")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (DOL, "DZ:count")
|
||||
field (OMSL, "closed_loop")
|
||||
field (OUT, "@test.proto printnum device")
|
||||
}
|
||||
record (stringout, "DZ:stringout")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (DOL, "DZ:read")
|
||||
field (OMSL, "closed_loop")
|
||||
field (OUT, "@test.proto printstr device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
ready {out "ready"; }
|
||||
readintr {extraInput=ignore; in "%39c"; }
|
||||
printnum {out "%d";}
|
||||
printstr {out "%s";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
var streamDebug 1
|
||||
}
|
||||
|
||||
set debug 0
|
||||
set rep 500
|
||||
|
||||
startioc
|
||||
|
||||
assure "ready\n"
|
||||
after 1000
|
||||
for {set i 1} {$i <= $rep} {incr i} {
|
||||
append output "This is line $i.\n"
|
||||
}
|
||||
send $output
|
||||
after 2000
|
||||
ioccmd {dbpf "DZ:stringout.PROC" 1}
|
||||
assure "This is line $rep.\n"
|
||||
ioccmd {dbpf "DZ:countout.PROC" 1}
|
||||
assure "$rep\n"
|
||||
|
||||
finish
|
454
streamApp/tests/testMultiLineFormatString
Executable file
454
streamApp/tests/testMultiLineFormatString
Executable file
@ -0,0 +1,454 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
#
|
||||
# Simple database for Dawn RuSH system monitor
|
||||
#
|
||||
|
||||
record(bo, "DZ:SetPower")
|
||||
{
|
||||
field(DESC, "Control power supplies")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@test.proto ps() device 0")
|
||||
field(ZNAM, "OFF")
|
||||
field(ONAM, "ON")
|
||||
field(FLNK, "DZ:VP3")
|
||||
}
|
||||
record(bo, "DZ:SetPower1")
|
||||
{
|
||||
field(DESC, "Control power supply 1")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@test.proto ps(1) device 0")
|
||||
field(ZNAM, "OFF")
|
||||
field(ONAM, "ON")
|
||||
field(FLNK, "DZ:VP3")
|
||||
}
|
||||
record(bo, "DZ:SetPower2")
|
||||
{
|
||||
field(DESC, "Control power supply 2")
|
||||
field(DTYP, "stream")
|
||||
field(OUT, "@test.proto ps(2) device 0")
|
||||
field(ZNAM, "OFF")
|
||||
field(ONAM, "ON")
|
||||
field(FLNK, "DZ:VP3")
|
||||
}
|
||||
|
||||
record(ai, "DZ:VP3")
|
||||
{
|
||||
field(DESC, "+3.3V supply")
|
||||
field(DTYP, "stream")
|
||||
field(INP, "@test.proto query(DZ:) device 0")
|
||||
field(EGU, "V")
|
||||
field(PREC, "3")
|
||||
field(LINR, "SLOPE")
|
||||
field(ESLO, "1e-3")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "5")
|
||||
field(LOLO, "2.97")
|
||||
field(LOW, "3.135")
|
||||
field(HIGH, "3.465")
|
||||
field(HIHI, "3.63")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "0.033")
|
||||
field(FLNK, "DZ:VP5")
|
||||
}
|
||||
record(ai, "DZ:VP5")
|
||||
{
|
||||
field(DESC, "+5V supply")
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(EGU, "V")
|
||||
field(PREC, "3")
|
||||
field(LINR, "SLOPE")
|
||||
field(ESLO, "1e-3")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "5")
|
||||
field(LOLO, "4.5")
|
||||
field(LOW, "4.75")
|
||||
field(HIGH, "5.25")
|
||||
field(HIHI, "5.5")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "0.05")
|
||||
field(FLNK, "DZ:VP12")
|
||||
}
|
||||
record(ai, "DZ:VP12")
|
||||
{
|
||||
field(DESC, "+12V supply")
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(EGU, "V")
|
||||
field(PREC, "3")
|
||||
field(LINR, "SLOPE")
|
||||
field(ESLO, "1e-3")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "12")
|
||||
field(LOLO, "10.8")
|
||||
field(LOW, "11.4")
|
||||
field(HIGH, "12.6")
|
||||
field(HIHI, "13.2")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "0.12")
|
||||
field(FLNK, "DZ:VM12")
|
||||
}
|
||||
record(ai, "DZ:VM12")
|
||||
{
|
||||
field(DESC, "-12V supply")
|
||||
field(DTYP, "Raw Soft Channel")
|
||||
field(EGU, "V")
|
||||
field(PREC, "3")
|
||||
field(LINR, "SLOPE")
|
||||
field(ESLO, "-1e-3")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "12")
|
||||
field(LOLO, "-13.2")
|
||||
field(LOW, "-12.6")
|
||||
field(HIGH, "-11.4")
|
||||
field(HIHI, "-10.8")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "-0.12")
|
||||
field(FLNK, "DZ:Temp1")
|
||||
}
|
||||
record(longin, "DZ:Temp1")
|
||||
{
|
||||
field(DESC, "Sensor 1 temperature")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Degrees C")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "100")
|
||||
field(HIGH, "45")
|
||||
field(HIHI, "55")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "2")
|
||||
field(FLNK, "DZ:Temp2")
|
||||
}
|
||||
record(longin, "DZ:Temp2")
|
||||
{
|
||||
field(DESC, "Sensor 2 temperature")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Degrees C")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "100")
|
||||
field(HIGH, "45")
|
||||
field(HIHI, "55")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "2")
|
||||
field(FLNK, "DZ:Temp3")
|
||||
}
|
||||
record(longin, "DZ:Temp3")
|
||||
{
|
||||
field(DESC, "Sensor 3 temperature")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Degrees C")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "100")
|
||||
field(HIGH, "45")
|
||||
field(HIHI, "55")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "2")
|
||||
field(FLNK, "DZ:Temp4")
|
||||
}
|
||||
record(longin, "DZ:Temp4")
|
||||
{
|
||||
field(DESC, "Sensor 4 temperature")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Degrees C")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "100")
|
||||
field(HIGH, "45")
|
||||
field(HIHI, "55")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "2")
|
||||
field(FLNK, "DZ:Fan1")
|
||||
}
|
||||
record(longin, "DZ:Fan1")
|
||||
{
|
||||
field(DESC, "Fan 1 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan2")
|
||||
}
|
||||
record(longin, "DZ:Fan2")
|
||||
{
|
||||
field(DESC, "Fan 2 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan3")
|
||||
}
|
||||
record(longin, "DZ:Fan3")
|
||||
{
|
||||
field(DESC, "Fan 3 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan4")
|
||||
}
|
||||
record(longin, "DZ:Fan4")
|
||||
{
|
||||
field(DESC, "Fan 4 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan5")
|
||||
}
|
||||
record(longin, "DZ:Fan5")
|
||||
{
|
||||
field(DESC, "Fan 5 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan6")
|
||||
}
|
||||
record(longin, "DZ:Fan6")
|
||||
{
|
||||
field(DESC, "Fan 6 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan7")
|
||||
}
|
||||
record(longin, "DZ:Fan7")
|
||||
{
|
||||
field(DESC, "Fan 7 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan8")
|
||||
}
|
||||
record(longin, "DZ:Fan8")
|
||||
{
|
||||
field(DESC, "Fan 8 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:Fan9")
|
||||
}
|
||||
record(longin, "DZ:Fan9")
|
||||
{
|
||||
field(DESC, "Fan 9 speed")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "RPM")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "2500")
|
||||
field(LOLO, "900")
|
||||
field(LOW, "1050")
|
||||
field(HIGH, "2500")
|
||||
field(HIHI, "2600")
|
||||
field(LLSV, "MAJOR")
|
||||
field(LSV, "MINOR")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(HYST, "10")
|
||||
field(FLNK, "DZ:PS1status")
|
||||
}
|
||||
record(bi, "DZ:PS1status")
|
||||
{
|
||||
field(DESC, "Power supply 1 status")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(ZNAM, "Off")
|
||||
field(ONAM, "On")
|
||||
field(FLNK, "DZ:PS2status")
|
||||
}
|
||||
record(bi, "DZ:PS2status")
|
||||
{
|
||||
field(DESC, "Power supply 2 status")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(ZNAM, "Off")
|
||||
field(ONAM, "On")
|
||||
field(FLNK, "DZ:HoursOn")
|
||||
}
|
||||
record(ai, "DZ:HoursOn")
|
||||
{
|
||||
field(DESC, "Power on time")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Hours")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "50000")
|
||||
field(PREC, "1")
|
||||
field(FLNK, "DZ:MaxTemp")
|
||||
}
|
||||
record(longin, "DZ:MaxTemp")
|
||||
{
|
||||
field(DESC, "Maximum recorded temperature")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(EGU, "Degrees C")
|
||||
field(LOPR, "0")
|
||||
field(HOPR, "100")
|
||||
field(HIGH, "45")
|
||||
field(HIHI, "55")
|
||||
field(HSV, "MINOR")
|
||||
field(HHSV, "MAJOR")
|
||||
field(FLNK, "DZ:Serial")
|
||||
}
|
||||
record(longin, "DZ:Serial")
|
||||
{
|
||||
field(DESC, "Serial number")
|
||||
field(DTYP, "Soft Channel")
|
||||
field(FLNK, "DZ:Name")
|
||||
}
|
||||
record(stringin, "DZ:Name")
|
||||
{
|
||||
field(DESC, "Unit name")
|
||||
field(DTYP, "Soft Channel")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
InTerminator = "\r\n";
|
||||
OutTerminator = "";
|
||||
|
||||
ps {
|
||||
out "PS\$1%{OFF|ON}";
|
||||
}
|
||||
|
||||
query {
|
||||
out "Q";
|
||||
in "Q:"
|
||||
"V3=%d,"
|
||||
"V5=%(\$1VP5.RVAL)d,"
|
||||
"V+12=%(\$1VP12.RVAL)d,"
|
||||
"V12=%(\$1VM12.RVAL)d,"
|
||||
"T1=%(\$1Temp1.VAL)d,"
|
||||
"T2=%(\$1Temp2.VAL)d,"
|
||||
"T3=%(\$1Temp3.VAL)d,"
|
||||
"T4=%(\$1Temp4.VAL)d,"
|
||||
"F1=%(\$1Fan1.VAL)d,"
|
||||
"F2=%(\$1Fan2.VAL)d,"
|
||||
"F3=%(\$1Fan3.VAL)d,"
|
||||
"F4=%(\$1Fan4.VAL)d,"
|
||||
"F5=%(\$1Fan5.VAL)d,"
|
||||
"F6=%(\$1Fan6.VAL)d,"
|
||||
"F7=%(\$1Fan7.VAL)d,"
|
||||
"F8=%(\$1Fan8.VAL)d,"
|
||||
"F9=%(\$1Fan9.VAL)d,"
|
||||
"PS1=%(\$1PS1status.VAL)d,"
|
||||
"PS2=%(\$1PS2status.VAL)d,"
|
||||
"POH=%(\$1HoursOn.VAL)g,"
|
||||
"MAXTMP=%(\$1MaxTemp.VAL)g,"
|
||||
"PS1=%*d,"
|
||||
"PS2=%*d,"
|
||||
"MSG=%*d,"
|
||||
"SW=%*d,"
|
||||
"SN=%(\$1Serial.VAL)d,"
|
||||
"UN=%(\$1Name.VAL)39[^,],";
|
||||
# in ";";
|
||||
out "%d\n";
|
||||
@mismatch {out "mismatch\n"; }
|
||||
ExtraInput = Ignore;
|
||||
}
|
||||
}
|
||||
|
||||
set startup {
|
||||
var streamDebug 1
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
|
||||
ioccmd {dbpf DZ:VP3.PROC 1}
|
||||
assure "Q"
|
||||
send "Q:V3=0,V5=0,V+12=0,V12=0,T1=38,T2=40,T3=43,T4=45,F1=1350,F2=1361,F3=1373,F4=1384,F5=1396,F6=1407,F7=1419,F8=1430,F9=1442,PS1=0,PS2=0,POH=23.5,MAXTMP=45,PS1=0,PS2=0,MSG=0,SW=1,SN=8765,UN=APEX1,IP=128.12\r\n"
|
||||
assure "0\n"
|
||||
finish
|
40
streamApp/tests/testOutTerminators
Executable file
40
streamApp/tests/testOutTerminators
Executable file
@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
set records {
|
||||
record (stringout, "DZ:test1")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test1 device")
|
||||
}
|
||||
record (stringout, "DZ:test2")
|
||||
{
|
||||
field (DTYP, "stream")
|
||||
field (OUT, "@test.proto test2 device")
|
||||
}
|
||||
}
|
||||
|
||||
set protocol {
|
||||
test1 {out "%s";}
|
||||
Terminator = "S" CR LF;
|
||||
test2 { out "%s";}
|
||||
}
|
||||
|
||||
set startup {
|
||||
asynOctetSetOutputEos device -1 "A\n"
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
ioccmd {dbpf DZ:test1 "string 1"}
|
||||
assure "string 1A\n"
|
||||
ioccmd {dbpf DZ:test2 "string 2"}
|
||||
assure "string 2S\r\n"
|
||||
|
||||
finish
|
57
streamApp/tests/testPINI
Executable file
57
streamApp/tests/testPINI
Executable file
@ -0,0 +1,57 @@
|
||||
#!/usr/bin/env tclsh
|
||||
source streamtestlib.tcl
|
||||
|
||||
# Define records, protocol and startup (text goes to files)
|
||||
# The asynPort "device" is connected to a network TCP socket
|
||||
# Talk to the socket with send/receive/assure
|
||||
# Send commands to the ioc shell with ioccmd
|
||||
|
||||
foreach rtype {ao bo mbbo mbboDirect longout stringout calcout} {
|
||||
append records "
|
||||
record ($rtype, \"DZ:$rtype\")
|
||||
{
|
||||
field (DTYP, \"stream\")
|
||||
field (OUT, \"@test.proto init($rtype) device\")
|
||||
field (PINI, \"YES\")
|
||||
field (VAL, \"0\")
|
||||
}
|
||||
"
|
||||
}
|
||||
foreach rtype {ai bi mbbi mbbiDirect longin stringin waveform} {
|
||||
append records "
|
||||
record ($rtype, \"DZ:$rtype\")
|
||||
{
|
||||
field (DTYP, \"stream\")
|
||||
field (INP, \"@test.proto init($rtype) device\")
|
||||
field (PINI, \"YES\")
|
||||
}
|
||||
"
|
||||
}
|
||||
|
||||
set protocol {
|
||||
Terminator = LF;
|
||||
init {out "init \$1"; }
|
||||
}
|
||||
|
||||
set startup {
|
||||
}
|
||||
|
||||
set debug 0
|
||||
|
||||
startioc
|
||||
assure "init ao\n" \
|
||||
"init bo\n" \
|
||||
"init mbbo\n" \
|
||||
"init mbboDirect\n" \
|
||||
"init longout\n" \
|
||||
"init stringout\n" \
|
||||
"init ai\n" \
|
||||
"init bi\n" \
|
||||
"init mbbi\n" \
|
||||
"init mbbiDirect\n" \
|
||||
"init longin\n" \
|
||||
"init stringin\n" \
|
||||
"init calcout\n" \
|
||||
"init waveform\n" \
|
||||
|
||||
finish
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user