Compare commits

...

30 Commits

Author SHA1 Message Date
40d9641150 comment fixed 2008-07-15 08:59:55 +00:00
9cf2770440 raw float converter added 2008-07-11 14:55:10 +00:00
616bc9a410 When not connected, call lock request with priority asynQueuePiorityConnect to avoid deadlock. 2008-07-11 14:53:28 +00:00
53abbb750b raw float added 2008-07-11 14:50:07 +00:00
ec3cbbf559 allow append of n equal bytes 2008-07-11 14:49:23 +00:00
8a3cbc1c6a output format for exponential converter defined 2008-07-11 14:48:17 +00:00
8a4cda6647 fix debug output of format string 2008-07-11 14:46:08 +00:00
2d513b96d3 %m added 2008-02-22 09:13:11 +00:00
6a0d7b4492 comments fixed 2008-02-21 17:09:31 +00:00
755df7c3ee bugfix: 1st byte was lost
feature: little endian support
2008-02-21 17:07:26 +00:00
ddb602196a comment added 2008-02-21 16:18:30 +00:00
0a5f486a9b improved error message 2008-02-21 16:08:09 +00:00
574052dcb8 alignment bug (on machines where it matters) 2008-02-21 16:05:56 +00:00
eaba256c51 generate 3.14 dbd file 2008-01-16 10:28:09 +00:00
7eab96120f don't return DO_NOT_CONVERT (2) from initRecord 2008-01-15 15:05:55 +00:00
50f69a76f4 bugfix 2008-01-09 08:44:40 +00:00
d40c9610c9 %B fixed 2007-11-30 16:48:20 +00:00
b97948a704 %B fixed 2007-11-30 16:45:32 +00:00
7ac080b8d7 version update 2007-11-30 15:36:41 +00:00
7a83d91ec9 better PCRE support 2007-11-30 15:33:40 +00:00
ad4b84fd60 disconnect callback 2007-11-30 15:33:22 +00:00
1f82b0224a *** empty log message *** 2007-11-30 15:32:10 +00:00
627035a2ea *** empty log message *** 2007-11-30 15:26:52 +00:00
6bb963e852 better PCRE support 2007-11-30 15:26:26 +00:00
e6c0b6af0e support signed 2007-11-30 15:25:00 +00:00
55c8977e7b Don't abort on big input 2007-11-30 15:24:38 +00:00
ed8b3aded1 disconnect callback 2007-11-30 15:23:13 +00:00
161f3c7d3c copy from main project 2007-11-30 15:18:16 +00:00
b1395f43f2 *** empty log message *** 2007-11-30 15:17:56 +00:00
c3979f766f update to current snapshot version 2007-08-27 12:49:48 +00:00
42 changed files with 913 additions and 404 deletions

View File

@ -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

View File

@ -172,6 +172,9 @@ void <a href="#event">eventCallback</a>(StreamIoStatus&nbsp;status);
<div class="indent"><code>
void <a href="#connect">connectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#connect">disconnectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<h3>Other provided methods, attibutes, and types</h3>
@ -280,6 +283,9 @@ bool disconnect();
<div class="indent"><code>
void connectCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void disconnectCallback(IoStatus&nbsp;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>

View File

@ -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>%&lt;SUM&gt;</code> or <code>%&lt;SUM8&gt;</code></dt>
<dt><code>%&lt;sum&gt;</code> or <code>%&lt;sum8&gt;</code></dt>
<dd>One byte. The sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;SUM16&gt;</code></dt>
<dt><code>%&lt;sum16&gt;</code></dt>
<dd>Two bytes. The sum of all characters modulo 2<sup>16</sup>.</dd>
<dt><code>%&lt;SUM32&gt;</code></dt>
<dt><code>%&lt;sum32&gt;</code></dt>
<dd>Four bytes. The sum of all characters modulo 2<sup>32</sup>.</dd>
<dt><code>%&lt;NEGSUM&gt;</code> or <code>%&lt;NSUM&gt;</code> or <code>%&lt;-SUM&gt;</code></dt>
<dt><code>%&lt;negsum&gt;</code>, <code>%&lt;nsum&gt;</code>, <code>%&lt;-sum&gt;</code>, <code>%&lt;negsum8&gt;</code>, <code>%&lt;nsum8&gt;</code>, or <code>%&lt;-sum8&gt;</code></dt>
<dd>One byte. The negative of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;NOTSUM&gt;</code> or <code>%&lt;~SUM&gt;</code></dt>
<dt><code>%&lt;negsum16&gt;</code>, <code>%&lt;nsum16&gt;</code>, or <code>%&lt;-sum16&gt;</code></dt>
<dd>Two bytes. The negative of the sum of all characters modulo 2<sup>16</sup>.</dd>
<dt><code>%&lt;negsum32&gt;</code>, <code>%&lt;nsum32&gt;</code>, or <code>%&lt;-sum32&gt;</code></dt>
<dd>Four bytes. The negative of the sum of all characters modulo 2<sup>32</sup>.</dd>
<dt><code>%&lt;notsum&gt;</code> or <code>%&lt;~sum&gt;</code></dt>
<dd>One byte. The bitwise inverse of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;XOR&gt;</code></dt>
<dt><code>%&lt;xor&gt;</code></dt>
<dd>One byte. All characters xor'ed.</dd>
<dt><code>%&lt;CRC8&gt;</code></dt>
<dd>One byte. An often used 8 bit CRC checksum
<dt><code>%&lt;xor7&gt;</code></dt>
<dd>One byte. All characters xor'ed &amp; 0x7F.</dd>
<dt><code>%&lt;crc8&gt;</code></dt>
<dd>One byte. An often used 8 bit crc checksum
(poly=0x07, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CCITT8&gt;</code></dt>
<dd>One byte. The CCITT standard 8 bit CRC checksum
<dt><code>%&lt;ccitt8&gt;</code></dt>
<dd>One byte. The CCITT standard 8 bit crc checksum
(poly=0x31, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CRC16&gt;</code></dt>
<dd>Two bytes. An often used 16 bit CRC checksum
<dt><code>%&lt;crc16&gt;</code></dt>
<dd>Two bytes. An often used 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CRC16R&gt;</code></dt>
<dd>Two bytes. An often used reflected 16 bit CRC checksum
<dt><code>%&lt;crc16r&gt;</code></dt>
<dd>Two bytes. An often used reflected 16 bit crc checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CCITT16&gt;</code></dt>
<dt><code>%&lt;ccitt16&gt;</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>%&lt;CCITT16A&gt;</code></dt>
<dt><code>%&lt;ccitt16a&gt;</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>%&lt;CRC32&gt;</code></dt>
<dd>Four bytes. The standard 32 bit CRC checksum.
<dt><code>%&lt;crc32&gt;</code></dt>
<dd>Four bytes. The standard 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;CRC32R&gt;</code></dt>
<dd>Four bytes. The standard reflected 32 bit CRC checksum.
<dt><code>%&lt;crc32r&gt;</code></dt>
<dd>Four bytes. The standard reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;JAMCRC&gt;</code></dt>
<dd>Four bytes. Another reflected 32 bit CRC checksum.
<dt><code>%&lt;jamcrc&gt;</code></dt>
<dd>Four bytes. Another reflected 32 bit crc checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
<dt><code>%&lt;ADLER32&gt;</code></dt>
<dt><code>%&lt;adler32&gt;</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>%&lt;HEXSUM8&gt;</code></dt>
<dt><code>%&lt;hexsum8&gt;</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.&lt;architecture&gt;
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/&lt;title&gt;(.*)&lt;\/title&gt;/</code> returns
the title of an HTML page, skipps anything before the
<code>&lt;title&gt</code> tag and leaves anything after the
<code>&lt;/title&gt;</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>

View File

@ -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">%&lt;<em>checksum</em>&gt;</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">

View File

@ -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>&lt;top&gt;/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>&lt;top&gt;</kbd> directories and RELEASE files,
please refer to the

View File

@ -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:

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View 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");

View File

@ -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

View File

@ -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
View 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
View 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, "/");

View File

@ -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;

View File

@ -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);

View File

@ -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()
{

View File

@ -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

View File

@ -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"

View File

@ -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);

View File

@ -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

View File

@ -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++;

View File

@ -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;

View File

@ -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>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <aoRecord.h>
#include <epicsExport.h>

View File

@ -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 {

View File

@ -18,7 +18,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <boRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbboDirectRecord.h>
#include <epicsExport.h>

View File

@ -19,7 +19,7 @@
* *
***************************************************************/
#include <devStream.h>
#include "devStream.h"
#include <mbboRecord.h>
#include <string.h>
#include <epicsExport.h>

View File

@ -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 {

View File

@ -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 {

View File

@ -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 {

View File

@ -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");
}
}

View File

@ -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