Compare commits
14 Commits
stream_2_2
...
stream_2_3
Author | SHA1 | Date | |
---|---|---|---|
d40c9610c9 | |||
b97948a704 | |||
7ac080b8d7 | |||
7a83d91ec9 | |||
ad4b84fd60 | |||
1f82b0224a | |||
627035a2ea | |||
6bb963e852 | |||
e6c0b6af0e | |||
55c8977e7b | |||
ed8b3aded1 | |||
161f3c7d3c | |||
b1395f43f2 | |||
c3979f766f |
20
Makefile
20
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)
|
||||
|
||||
DBDS = stream.dbd
|
||||
|
||||
@ -15,6 +13,8 @@ FORMATS += BCD
|
||||
FORMATS += Raw
|
||||
FORMATS += Binary
|
||||
FORMATS += Checksum
|
||||
FORMATS += Regexp
|
||||
FORMATS += Exponential
|
||||
RECORDTYPES += aai aao
|
||||
RECORDTYPES += ao ai
|
||||
RECORDTYPES += bo bi
|
||||
@ -25,6 +25,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:
|
||||
|
@ -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>
|
||||
|
101
doc/formats.html
101
doc/formats.html
@ -352,56 +352,105 @@ 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>
|
||||
<hr>
|
||||
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
||||
<p><small>Dirk Zimoch, 2007</small></p>
|
||||
|
@ -120,6 +120,7 @@ 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>
|
||||
</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
|
||||
@ -462,11 +462,6 @@ lockRequest(unsigned long lockTimeout_ms)
|
||||
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(),
|
||||
lockTimeout);
|
||||
@ -497,6 +492,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 +507,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 +520,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 +939,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 +1187,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 +1207,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 +1261,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());
|
||||
}
|
||||
@ -1312,7 +1329,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++);
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
63
src/ExponentialConverter.cc
Normal file
63
src/ExponentialConverter.cc
Normal file
@ -0,0 +1,63 @@
|
||||
/***************************************************************
|
||||
* 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: format +00351-02 means +351e-2
|
||||
|
||||
class ExponentialConverter : public StreamFormatConverter
|
||||
{
|
||||
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
virtual int scanDouble(const StreamFormat&, const char*, double&);
|
||||
};
|
||||
|
||||
int ExponentialConverter::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (!scanFormat)
|
||||
{
|
||||
error("At the moment for %%m format only input is implemented\n");
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
@ -54,7 +54,16 @@ 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);
|
||||
@ -62,8 +71,17 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
|
||||
}
|
||||
else // msb first (big endian)
|
||||
{
|
||||
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--;
|
||||
@ -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 */
|
||||
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;
|
||||
|
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
|
||||
|
@ -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
|
||||
|
@ -23,6 +23,12 @@
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include <epicsExport.h>
|
||||
int showAsyncErrors = 0;
|
||||
extern "C" {
|
||||
epicsExportAddress(int, showAsyncErrors);
|
||||
}
|
||||
|
||||
enum Commands { end_cmd, in_cmd, out_cmd, wait_cmd, event_cmd, exec_cmd,
|
||||
connect_cmd, disconnect_cmd };
|
||||
const char* commandStr[] = { "end", "in", "out", "wait", "event", "exec",
|
||||
@ -30,7 +36,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 /////////////////////////////////////////////
|
||||
@ -801,6 +807,8 @@ writeCallback(StreamIoStatus status)
|
||||
flags &= ~WritePending;
|
||||
if (status != StreamIoSuccess)
|
||||
{
|
||||
error("%s: write failed: %s\n",
|
||||
name(), StreamIoStatusStr[status]);
|
||||
finishProtocol(WriteTimeout);
|
||||
return;
|
||||
}
|
||||
@ -921,7 +929,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;
|
||||
}
|
||||
@ -985,7 +997,7 @@ readCallback(StreamIoStatus status,
|
||||
}
|
||||
// try to parse what we got
|
||||
end = inputBuffer.length();
|
||||
if (!(flags & AsyncMode))
|
||||
if (!(flags & AsyncMode)||showAsyncErrors)
|
||||
{
|
||||
error("%s: Timeout after reading %ld byte%s \"%s%s\"\n",
|
||||
name(), end, end==1 ? "" : "s", end > 20 ? "..." : "",
|
||||
@ -1115,7 +1127,7 @@ matchInput()
|
||||
}
|
||||
if (consumed < 0)
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" does not match format %%%s\n",
|
||||
name(), inputLine.expand(consumedInput, 20)(),
|
||||
@ -1130,7 +1142,7 @@ matchInput()
|
||||
flags &= ~Separator;
|
||||
if (!matchValue(fmt, fieldAddress ? fieldAddress() : NULL))
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
if (flags & ScanTried)
|
||||
error("%s: Input \"%s%s\" does not match format %%%s\n",
|
||||
@ -1160,7 +1172,7 @@ matchInput()
|
||||
{
|
||||
int i = 0;
|
||||
while (commandIndex[i] >= ' ') i++;
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: Input \"%s%s\" too short."
|
||||
" No match for \"%s\"\n",
|
||||
@ -1173,7 +1185,7 @@ matchInput()
|
||||
}
|
||||
if (command != inputLine[consumedInput])
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
int i = 0;
|
||||
while (commandIndex[i] >= ' ') i++;
|
||||
@ -1198,7 +1210,7 @@ matchInput()
|
||||
long surplus = inputLine.length()-consumedInput;
|
||||
if (surplus > 0 && !(flags & IgnoreExtraInput))
|
||||
{
|
||||
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
|
||||
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
|
||||
{
|
||||
error("%s: %ld byte%s surplus input \"%s%s\"\n",
|
||||
name(), surplus, surplus==1 ? "" : "s",
|
||||
@ -1323,7 +1335,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 +1522,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);
|
||||
|
||||
|
@ -1180,11 +1180,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++;
|
||||
|
@ -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>
|
||||
|
||||
|
@ -20,7 +20,7 @@
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aaoRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <aiRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <longinRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <longoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbbiDirectRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -19,7 +19,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <mbbiRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
|
@ -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>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <stringoutRecord.h>
|
||||
#include <epicsExport.h>
|
||||
|
||||
|
@ -18,7 +18,7 @@
|
||||
* *
|
||||
***************************************************************/
|
||||
|
||||
#include <devStream.h>
|
||||
#include "devStream.h"
|
||||
#include <waveformRecord.h>
|
||||
#include <string.h>
|
||||
#include <epicsExport.h>
|
||||
|
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