Compare commits
30 Commits
stream_2_2
...
stream_2_3
Author | SHA1 | Date | |
---|---|---|---|
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 |
33
Makefile
33
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 += Exponential
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
@ -25,6 +26,16 @@ 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
|
||||
|
||||
ifeq (${EPICS_BASETYPE},3.13)
|
||||
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
|
||||
endif
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
|
||||
streamReferences:
|
||||
@ -37,8 +48,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); \
|
||||
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
|
||||
|
@ -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
|
||||
@ -934,6 +949,7 @@ void intrCallbackOctet(void* /*pvt*/, asynUser *pasynUser,
|
||||
// 3. eomReason=ASYN_EOM_CNT when message was too long for
|
||||
// internal buffer of asynDriver.
|
||||
|
||||
if (!interruptAccept) return; // too early to process records
|
||||
if (interface->ioAction == AsyncRead ||
|
||||
interface->ioAction == AsyncReadMore)
|
||||
{
|
||||
@ -1181,6 +1197,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 +1217,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 +1271,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 +1287,8 @@ void handleRequest(asynUser* pasynUser)
|
||||
{
|
||||
AsynDriverInterface* interface =
|
||||
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
|
||||
debug("AsynDriverInterface::handleRequest(%s)\n",
|
||||
interface->clientName());
|
||||
switch (interface->ioAction)
|
||||
{
|
||||
case None:
|
||||
@ -1294,6 +1323,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 +1343,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++);
|
||||
@ -72,25 +72,53 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
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 significan 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 significan bit first)
|
||||
if (!(format.flags & left_flag))
|
||||
{
|
||||
// pad left
|
||||
while (width > prec)
|
||||
{
|
||||
output.append(fill);
|
||||
width--;
|
||||
}
|
||||
}
|
||||
while (prec--)
|
||||
{
|
||||
output.append((value & (1 << 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;
|
||||
|
@ -40,6 +40,23 @@ FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += Exponential
|
||||
|
||||
# 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;
|
||||
}
|
||||
|
||||
|
99
src/ExponentialConverter.cc
Normal file
99
src/ExponentialConverter.cc
Normal file
@ -0,0 +1,99 @@
|
||||
/***************************************************************
|
||||
* StreamDevice Support *
|
||||
* *
|
||||
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
|
||||
* (C) 2007 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
|
||||
// Number of digits in exponent is at least 2
|
||||
// No leading '0' in mantissa (except for 0.0 of course)
|
||||
// Format flags +, -, and space are supported in the usual way
|
||||
// Flags #, 0 are not supported
|
||||
|
||||
class ExponentialConverter : 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 ExponentialConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
return double_format;
|
||||
}
|
||||
|
||||
int ExponentialConverter::
|
||||
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, exponent);
|
||||
return length;
|
||||
}
|
||||
|
||||
bool ExponentialConverter::
|
||||
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;
|
||||
|
||||
buf.printf("%.*e", fmt.prec-1, fabs(value));
|
||||
buf.remove(1,1);
|
||||
buf.remove(buf.find('e'),1);
|
||||
|
||||
spaces = fmt.width-buf.length();
|
||||
if (fmt.flags & (space_flag|sign_flag) || value < 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)
|
||||
output.append(' ');
|
||||
if (fmt.flags & sign_flag && value >= 0)
|
||||
output.append('+');
|
||||
if (value <= 0)
|
||||
output.append('-');
|
||||
output.append(buf);
|
||||
if (fmt.flags & left_flag)
|
||||
output.append(' ', spaces);
|
||||
return true;
|
||||
}
|
||||
|
||||
RegisterConverter (ExponentialConverter, "m");
|
||||
|
16
src/Makefile
16
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,6 +57,19 @@ SRCS += $(FORMATS:%=%Converter.cc)
|
||||
SRCS += $(RECORDS:%=dev%Stream.c)
|
||||
SRCS += $(STREAM_SRCS)
|
||||
|
||||
# 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 += Com dbIoc dbStaticIoc registryIoc iocsh
|
||||
|
||||
ifeq ($(USE_MEMGUARD),YES)
|
||||
@ -95,6 +108,7 @@ $(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
|
||||
done > $@
|
||||
@echo "driver(stream)" >> $@
|
||||
@echo "variable(streamDebug, int)" >> $@
|
||||
@echo "variable(showAsyncErrors, int)" >> $@
|
||||
@echo "registrar(streamRegistrar)" >> $@
|
||||
|
||||
endif
|
||||
|
@ -45,7 +45,7 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
int width = prec; // number of bytes in output
|
||||
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 +54,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 +107,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 +116,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;
|
||||
|
151
src/RawFloatConverter.cc
Normal file
151
src/RawFloatConverter.cc
Normal file
@ -0,0 +1,151 @@
|
||||
/***************************************************************
|
||||
* 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"
|
||||
|
||||
#ifdef vxWorks
|
||||
#include "vxWorks.h"
|
||||
#define __BYTE_ORDER _BYTE_ORDER
|
||||
#define __LITTLE_ENDIAN _LITTLE_ENDIAN
|
||||
#define __BIG_ENDIAN _BIG_ENDIAN
|
||||
#else
|
||||
// Let's hope all other architectures have endian.h
|
||||
#include "endian.h"
|
||||
#endif
|
||||
|
||||
#ifndef __BYTE_ORDER
|
||||
#error define __BYTE_ORDER as __LITTLE_ENDIAN or __BIG_ENDIAN
|
||||
#endif
|
||||
|
||||
#if (__BYTE_ORDER == __LITTLE_ENDIAN || __BYTE_ORDER == __BIG_ENDIAN)
|
||||
|
||||
// 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)
|
||||
{
|
||||
// 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 (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
bool swap = format.flags & alt_flag;
|
||||
#else
|
||||
bool swap = !(format.flags & alt_flag);
|
||||
#endif
|
||||
|
||||
if (swap)
|
||||
{
|
||||
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 (__BYTE_ORDER == __BIG_ENDIAN)
|
||||
bool swap = format.flags & alt_flag;
|
||||
#else
|
||||
bool swap = !(format.flags & alt_flag);
|
||||
#endif
|
||||
|
||||
if (swap)
|
||||
{
|
||||
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");
|
||||
|
||||
#endif /* known byte order */
|
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()(),
|
||||
len, 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
|
||||
@ -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);
|
||||
|
||||
|
@ -89,7 +89,7 @@ connectRequest (unsigned long)
|
||||
}
|
||||
|
||||
bool StreamBusInterface::
|
||||
disconnect ()
|
||||
disconnectRequest ()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@ -137,6 +137,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 /////////////////////////////////////////////
|
||||
@ -616,6 +616,7 @@ formatOutput()
|
||||
char command;
|
||||
const char* fieldName = NULL;
|
||||
const char* formatstring;
|
||||
int formatstringlen;
|
||||
while ((command = *commandIndex++) != StreamProtocolParser::eos)
|
||||
{
|
||||
switch (command)
|
||||
@ -625,7 +626,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 +636,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 +667,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();
|
||||
@ -921,7 +932,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;
|
||||
}
|
||||
@ -1323,7 +1338,7 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
|
||||
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 +1525,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);
|
||||
|
||||
|
@ -1157,7 +1157,7 @@ noMoreElements:
|
||||
}
|
||||
|
||||
#ifdef EPICS_3_14
|
||||
|
||||
// Pass command to iocsh
|
||||
void streamExecuteCommand(CALLBACK *pcallback)
|
||||
{
|
||||
Stream* pstream = static_cast<Stream*>(pcallback->user);
|
||||
@ -1172,7 +1172,8 @@ 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)
|
||||
{
|
||||
@ -1180,11 +1181,11 @@ void streamExecuteCommand(CALLBACK *pcallback)
|
||||
|
||||
if (execute(pstream->outputLine()) != OK)
|
||||
{
|
||||
pstream->execCallback(StreamBusInterface::ioFault);
|
||||
pstream->execCallback(StreamIoFault);
|
||||
}
|
||||
else
|
||||
{
|
||||
pstream->execCallback(StreamBusInterface::ioSuccess);
|
||||
pstream->execCallback(StreamIoSuccess);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
@ -384,7 +384,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++;
|
||||
|
@ -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;
|
||||
|
@ -22,7 +22,7 @@
|
||||
#define devStream_h
|
||||
|
||||
#define STREAM_MAJOR 2
|
||||
#define STREAM_MINOR 2
|
||||
#define STREAM_MINOR 3
|
||||
|
||||
#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,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aiRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -74,7 +74,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>
|
||||
|
||||
|
@ -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>
|
||||
|
||||
@ -48,7 +48,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>
|
||||
|
||||
@ -49,7 +49,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