Compare commits
55 Commits
stream_2_2
...
stream_2_4
Author | SHA1 | Date | |
---|---|---|---|
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:
|
40
Makefile
40
Makefile
@ -1,11 +1,9 @@
|
||||
include /ioc/tools/driver.makefile
|
||||
#EXCLUDE_VERSIONS = 3.13.2
|
||||
EXCLUDE_VERSIONS = 3.13
|
||||
PROJECT=stream
|
||||
EXCLUDE_VERSIONS = 3.13.2
|
||||
PROJECT=stream2
|
||||
BUILDCLASSES += Linux
|
||||
|
||||
DOCUDIR = doc
|
||||
SOURCES += $(wildcard src/*.c)
|
||||
SOURCES += $(wildcard src/*.cc)
|
||||
#DOCUDIR = doc
|
||||
|
||||
DBDS = stream.dbd
|
||||
|
||||
@ -13,8 +11,11 @@ BUSSES += AsynDriver
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += RawFloat
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += Regexp
|
||||
FORMATS += MantissaExponent
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
@ -23,7 +24,22 @@ RECORDTYPES += mbboDirect mbbiDirect
|
||||
RECORDTYPES += longout longin
|
||||
RECORDTYPES += stringout stringin
|
||||
RECORDTYPES += waveform
|
||||
RECORDTYPES += calcout
|
||||
|
||||
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
|
||||
|
||||
@ -37,8 +53,18 @@ streamReferences:
|
||||
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
|
||||
|
@ -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
|
||||
|
@ -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,12 @@ connectToAsynPort()
|
||||
void AsynDriverInterface::
|
||||
lockHandler()
|
||||
{
|
||||
int connected;
|
||||
debug("AsynDriverInterface::lockHandler(%s)\n",
|
||||
clientName());
|
||||
pasynManager->blockProcessCallback(pasynUser, false);
|
||||
lockCallback(StreamIoSuccess);
|
||||
connected = connectToAsynPort();
|
||||
lockCallback(connected ? StreamIoSuccess : StreamIoFault);
|
||||
}
|
||||
|
||||
// interface function: we don't need exclusive access any more
|
||||
@ -590,15 +605,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)
|
||||
{
|
||||
@ -750,7 +777,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 +785,7 @@ readHandler()
|
||||
pasynUser->timeout = replyTimeout;
|
||||
}
|
||||
bool waitForReply = true;
|
||||
int received;
|
||||
size_t received;
|
||||
int eomReason;
|
||||
asynStatus status;
|
||||
long readMore;
|
||||
@ -770,8 +797,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",
|
||||
@ -783,14 +810,14 @@ readHandler()
|
||||
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 +830,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 +851,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.
|
||||
@ -862,7 +889,7 @@ readHandler()
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"ioAction=%s, timeout after %d of %d bytes \"%s\"\n",
|
||||
clientName(), ioActionStr[ioAction],
|
||||
received, bytesToRead,
|
||||
(int)received, bytesToRead,
|
||||
StreamBuffer(buffer, received).expand()());
|
||||
#endif
|
||||
if (ioAction == AsyncRead || ioAction == AsyncReadMore)
|
||||
@ -929,25 +956,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 +1194,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 +1214,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 +1268,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 +1284,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 +1320,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 +1340,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;
|
||||
|
@ -38,8 +38,26 @@ SYNAPPS_RECORDS += scalcout
|
||||
FORMATS += Enum
|
||||
FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += RawFloat
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += MantissaExponent
|
||||
|
||||
# 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
|
||||
|
@ -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,12 +465,23 @@ 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
|
||||
@ -494,7 +510,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;
|
||||
}
|
||||
|
||||
|
@ -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++;
|
||||
|
17
src/Makefile
17
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,7 +57,20 @@ SRCS += $(FORMATS:%=%Converter.cc)
|
||||
SRCS += $(RECORDS:%=dev%Stream.c)
|
||||
SRCS += $(STREAM_SRCS)
|
||||
|
||||
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
|
||||
# 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)
|
||||
|
||||
ifeq ($(USE_MEMGUARD),YES)
|
||||
# memguard looks for memory leaks (gcc only)
|
||||
|
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& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
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 = 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, "/");
|
@ -60,7 +60,8 @@ 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 +84,7 @@ grow(long minsize)
|
||||
fprintf(stderr, "\n");
|
||||
abort();
|
||||
}
|
||||
#endif
|
||||
if (minsize < cap)
|
||||
{
|
||||
// just move contents to start of buffer and clear end
|
||||
@ -133,7 +135,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 +143,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 +194,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;
|
||||
|
@ -29,11 +29,11 @@
|
||||
|
||||
class StreamBuffer
|
||||
{
|
||||
char local[64];
|
||||
long len;
|
||||
long cap;
|
||||
long offs;
|
||||
char* buffer;
|
||||
char local[64];
|
||||
|
||||
void grow(long);
|
||||
void init(const void*, long);
|
||||
@ -103,11 +103,11 @@ 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; }
|
||||
{grow(size); char* p=buffer+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=1)
|
||||
{check(count); while(count-->0) buffer[offs+len++]=c; return *this;}
|
||||
|
||||
StreamBuffer& append(const void* s, long size);
|
||||
|
||||
@ -144,7 +144,7 @@ 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);}
|
||||
|
||||
@ -152,7 +152,7 @@ public:
|
||||
{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);}
|
||||
|
||||
|
@ -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 /////////////////////////////////////////////
|
||||
@ -360,7 +360,7 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
{
|
||||
buffer.append(exec_cmd);
|
||||
if (!protocol->compileString(buffer, args,
|
||||
NoFormat, this))
|
||||
PrintFormat, this))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -390,6 +390,22 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
|
||||
|
||||
// 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 +459,7 @@ finishProtocol(ProtocolResult status)
|
||||
flags & WaitPending ? "timerCallback()" : "");
|
||||
status = Fault;
|
||||
}
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
if (runningHandler)
|
||||
{
|
||||
// get original error status
|
||||
@ -518,6 +534,7 @@ finishProtocol(ProtocolResult status)
|
||||
flags &= ~BusOwner;
|
||||
}
|
||||
busFinish();
|
||||
flags &= ~(AcceptInput|AcceptEvent);
|
||||
protocolFinishHook(status);
|
||||
}
|
||||
|
||||
@ -539,16 +556,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();
|
||||
@ -616,6 +633,7 @@ formatOutput()
|
||||
char command;
|
||||
const char* fieldName = NULL;
|
||||
const char* formatstring;
|
||||
int formatstringlen;
|
||||
while ((command = *commandIndex++) != StreamProtocolParser::eos)
|
||||
{
|
||||
switch (command)
|
||||
@ -625,7 +643,7 @@ formatOutput()
|
||||
debug("StreamCore::formatOutput(%s): StreamProtocolParser::format_field\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 +653,23 @@ 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,12 +684,13 @@ formatOutput()
|
||||
flags &= ~Separator;
|
||||
if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL))
|
||||
{
|
||||
StreamBuffer formatstr(formatstring, formatstringlen);
|
||||
if (fieldName)
|
||||
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();
|
||||
@ -776,6 +804,8 @@ lockCallback(StreamIoStatus status)
|
||||
flags |= BusOwner;
|
||||
if (status != StreamIoSuccess)
|
||||
{
|
||||
error("%s: Lock timeout\n",
|
||||
name());
|
||||
finishProtocol(LockTimeout);
|
||||
return;
|
||||
}
|
||||
@ -886,11 +916,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;
|
||||
//// flags &= ~AcceptInput;
|
||||
unparsedInput = false;
|
||||
switch (status)
|
||||
{
|
||||
@ -921,7 +957,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;
|
||||
}
|
||||
@ -945,9 +985,15 @@ readCallback(StreamIoStatus status,
|
||||
{
|
||||
// look for terminator
|
||||
end = inputBuffer.find(inTerminator);
|
||||
if (end >= 0) termlen = inTerminator.length();
|
||||
debug("StreamCore::readCallback(%s) inTerminator %sfound\n",
|
||||
name(), end >= 0 ? "" : "not ");
|
||||
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 +1015,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 +1037,22 @@ 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());
|
||||
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",
|
||||
@ -1042,7 +1093,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;
|
||||
@ -1115,14 +1166,21 @@ matchInput()
|
||||
}
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if (fmt.flags & default_flag)
|
||||
{
|
||||
error("%s: Input \"%s%s\" does not match format %%%s\n",
|
||||
name(), inputLine.expand(consumedInput, 20)(),
|
||||
inputLine.length()-consumedInput > 20 ? "..." : "",
|
||||
formatstring);
|
||||
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;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
consumedInput += consumed;
|
||||
break;
|
||||
@ -1272,8 +1330,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;
|
||||
@ -1295,8 +1361,16 @@ scanValue(const StreamFormat& fmt, double& value)
|
||||
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;
|
||||
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 +1393,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;
|
||||
@ -1510,8 +1592,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"
|
||||
|
@ -200,6 +200,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();
|
||||
@ -280,12 +286,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 +303,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 +321,7 @@ report(int interest)
|
||||
printf(" %s\n", interface.name());
|
||||
++interface;
|
||||
}
|
||||
|
||||
|
||||
if (interest < 1) return OK;
|
||||
printf(" registered converters:\n");
|
||||
StreamFormatConverter* converter;
|
||||
@ -323,7 +334,7 @@ report(int interest)
|
||||
printf(" %%%c %s\n", c, converter->name());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Stream* pstream;
|
||||
printf(" connected records:\n");
|
||||
for (pstream = static_cast<Stream*>(first); pstream;
|
||||
@ -490,8 +501,8 @@ long streamScanSep(dbCommon* record)
|
||||
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 +536,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 +574,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 +803,12 @@ expire(CALLBACK *pcallback)
|
||||
|
||||
// StreamCore virtual methods ////////////////////////////////////////////
|
||||
|
||||
void Stream::
|
||||
protocolStartHook()
|
||||
{
|
||||
flags &= ~Aborted;
|
||||
}
|
||||
|
||||
void Stream::
|
||||
protocolFinishHook(ProtocolResult result)
|
||||
{
|
||||
@ -824,6 +843,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 +865,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());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1003,7 +1041,7 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
char* buffer;
|
||||
int status;
|
||||
const char* putfunc;
|
||||
|
||||
|
||||
if (fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to put value
|
||||
@ -1157,11 +1195,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 +1210,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
|
||||
|
@ -23,12 +23,13 @@
|
||||
#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
|
||||
} StreamFormatFlag;
|
||||
|
||||
typedef enum {
|
||||
|
@ -112,7 +112,7 @@ 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 != '?') info.append(*p);
|
||||
}
|
||||
|
||||
// Standard Long Converter for 'diouxX'
|
||||
@ -160,7 +160,9 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
|
||||
int length = -1;
|
||||
if (fmt.flags & skip_flag)
|
||||
{
|
||||
if (sscanf(input, fmt.info, &length) < 0) return -1;
|
||||
/* can't use return value on vxWorks: sscanf with %* format
|
||||
returns -1 at end of string whether value is found or not */
|
||||
sscanf(input, fmt.info, &length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -216,7 +218,9 @@ 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;
|
||||
/* can't use return value on vxWorks: sscanf with %* format
|
||||
returns -1 at end of string whether value is found or not */
|
||||
sscanf(input, fmt.info, &length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -274,12 +278,14 @@ scanString(const StreamFormat& fmt, const char* input,
|
||||
if (*input == '\0')
|
||||
{
|
||||
// match empty string
|
||||
value[0] = '\0';
|
||||
if (value) value[0] = '\0';
|
||||
return 0;
|
||||
}
|
||||
if (fmt.flags & skip_flag)
|
||||
{
|
||||
if (sscanf(input, fmt.info, &length) < 0) return -1;
|
||||
/* can't use return value on vxWorks: sscanf with %* format
|
||||
returns -1 at end of string whether value is found or not */
|
||||
sscanf(input, fmt.info, &length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -288,7 +294,7 @@ scanString(const StreamFormat& fmt, const char* input,
|
||||
if (maxlen <= fmt.width || fmt.width == 0)
|
||||
{
|
||||
// assure not to read too much
|
||||
sprintf(tmpformat, "%%%d%c%%n", maxlen-1, fmt.conv);
|
||||
sprintf(tmpformat, "%%%ld%c%%n", (long)maxlen-1, fmt.conv);
|
||||
f = tmpformat;
|
||||
}
|
||||
else
|
||||
@ -384,7 +390,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
fmt.prec, fmt.conv);
|
||||
return false;
|
||||
}
|
||||
info.printf("%%%d[", fmt.width);
|
||||
info.printf("%%%s%d[", fmt.flags & skip_flag ? "*" : "", fmt.width);
|
||||
while (*source && *source != ']')
|
||||
{
|
||||
if (*source == esc) source++;
|
||||
@ -406,7 +412,9 @@ scanString(const StreamFormat& fmt, const char* input,
|
||||
int length = -1;
|
||||
if (fmt.flags & skip_flag)
|
||||
{
|
||||
if (sscanf (input, fmt.info, &length) < 0) return -1;
|
||||
/* can't use return value on vxWorks: sscanf with %* format
|
||||
returns -1 at end of string whether value is found or not */
|
||||
sscanf(input, fmt.info, &length);
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -416,7 +424,7 @@ scanString(const StreamFormat& fmt, const char* input,
|
||||
{
|
||||
const char *p = strchr (fmt.info, '[');
|
||||
// assure not to read too much
|
||||
sprintf(tmpformat, "%%%d%s", maxlen-1, p);
|
||||
sprintf(tmpformat, "%%%ld%s", (long)maxlen-1, p);
|
||||
f = tmpformat;
|
||||
}
|
||||
else
|
||||
|
@ -1255,7 +1255,7 @@ compileString(StreamBuffer& buffer, const char*& source,
|
||||
// so that extra information is ready for format converter
|
||||
if (numFormats+1 == sizeof(formatPos))
|
||||
{
|
||||
errorMsg(line, "Too many formats in line");
|
||||
errorMsg(line, "Max 20 formats allowed in one protocol line");
|
||||
return false;
|
||||
}
|
||||
formatPos[numFormats++]=buffer.length();
|
||||
@ -1332,18 +1332,42 @@ 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},
|
||||
{"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;
|
||||
@ -1480,6 +1504,16 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
|
||||
}
|
||||
streamFormat.flags |= skip_flag;
|
||||
break;
|
||||
case '?':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
errorMsg(line,
|
||||
"Use of default modifier '?' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= default_flag;
|
||||
break;
|
||||
default:
|
||||
loop = false;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aaiRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -280,7 +280,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,7 +20,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aaoRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -280,7 +280,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,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <waveformRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
@ -271,7 +271,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
|
Reference in New Issue
Block a user