Compare commits
31 Commits
stream_2_7
...
2-7
Author | SHA1 | Date | |
---|---|---|---|
d5dc15e321 | |||
cfa777d718 | |||
54bc78f7c7 | |||
77d110de70 | |||
19467bd5d5 | |||
d69c74bc8f | |||
e09506c2bb | |||
343eef324b | |||
a798cf2f21 | |||
23f3e806e2 | |||
56b6c9a627 | |||
2830f07324 | |||
abd8daafc3 | |||
489e783872 | |||
10d1fa8b02 | |||
c8bffebfc6 | |||
0a90eb3d9c | |||
0936ac7840 | |||
704ece6231 | |||
5c6e98127e | |||
8805437c68 | |||
3acf791409 | |||
0ba674a341 | |||
40c33abac7 | |||
bc67317b0b | |||
06e212c66e | |||
7b314ccffd | |||
1d84986ee8 | |||
2ca8a129f7 | |||
126da8c499 | |||
ea110d5047 |
@ -44,7 +44,7 @@ ifneq (${EPICS_BASETYPE},3.13)
|
|||||||
RECORDTYPES += calcout
|
RECORDTYPES += calcout
|
||||||
endif
|
endif
|
||||||
|
|
||||||
StreamCore.o: streamReferences
|
StreamCore.o StreamCore.d: streamReferences
|
||||||
|
|
||||||
streamReferences:
|
streamReferences:
|
||||||
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
||||||
|
5
MODULE
5
MODULE
@ -1,5 +0,0 @@
|
|||||||
# Please change the following email with yours.
|
|
||||||
Email: dirk.zimoch@psi.ch
|
|
||||||
Module-Name: StreamDevice2
|
|
||||||
Description: StreamDevice2
|
|
||||||
Project-Name:
|
|
117
doc/formats.html
117
doc/formats.html
@ -367,9 +367,12 @@ endian</em>, i.e. least significant byte first.
|
|||||||
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
With the <code>0</code> flag, the value is unsigned, otherwise signed.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In output, the <em>prec</em> (or sizeof(long) whatever is less) least
|
In output, the <em>precision</em> (or sizeof(long) whatever is less) least
|
||||||
significant bytes of the value are sign extended or zero extended
|
significant bytes of the value are sign extended or zero extended
|
||||||
(depending on the <code>0</code> flag) to <em>width</em> bytes.
|
(depending on the <code>0</code> flag) to <em>width</em> bytes.
|
||||||
|
The default for <em>precision</em> is 1. Thus if you do not specify
|
||||||
|
the <em>precision</em>, only the least significant byte is written!
|
||||||
|
It is common error to write <code>out "%2r";</code> instead of <code>out "%.2r";</code>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In input, <em>width</em> bytes are read and put into the value.
|
In input, <em>width</em> bytes are read and put into the value.
|
||||||
@ -380,7 +383,7 @@ the value is sign extended or zero extended, depending on the
|
|||||||
<code>0</code> flag.
|
<code>0</code> flag.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Example: <code>out "%.2r"</code>
|
Examples: <code>out "%.2r"; in "%02r";</code>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<a name="rawdouble"></a>
|
<a name="rawdouble"></a>
|
||||||
@ -434,7 +437,7 @@ The <em>width</em> field is the byte number from which to start
|
|||||||
calculating the checksum.
|
calculating the checksum.
|
||||||
Default is 0, i.e. the first byte of the input or output of the current
|
Default is 0, i.e. the first byte of the input or output of the current
|
||||||
command.
|
command.
|
||||||
The last byte is <em>prec</em> bytes before the checksum (default 0).
|
The last byte is <em>precision</em> bytes before the checksum (default 0).
|
||||||
For example in <code>"abcdefg%<xor>"</code> the checksum is calculated
|
For example in <code>"abcdefg%<xor>"</code> the checksum is calculated
|
||||||
from <code>abcdefg</code>,
|
from <code>abcdefg</code>,
|
||||||
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
||||||
@ -528,41 +531,44 @@ In input, the next byte or bytes must match the checksum.
|
|||||||
</dl>
|
</dl>
|
||||||
|
|
||||||
<a name="regex"></a>
|
<a name="regex"></a>
|
||||||
<h2>12. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
<h2>13. Regular Expresion STRING Converter (<code>%/<em>regex</em>/</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This input-only format matches <a target="ex"
|
This input-only format matches <a target="ex"
|
||||||
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
href="http://www.pcre.org/" >Perl compatible regular expressions (PCRE)</a>.
|
||||||
It is only available if a PCRE library is installed.
|
It is only available if a PCRE library is installed.
|
||||||
</p>
|
</p>
|
||||||
|
<div class="box">
|
||||||
<p>
|
<p>
|
||||||
If PCRE is not available for your host or cross architecture, download
|
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>
|
the sourcecode from <a target="ex" href="http://www.pcre.org/">www.pcre.org</a>
|
||||||
and try my EPICS compatible <a target="ex"
|
and try my EPICS compatible <a target="ex"
|
||||||
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
href="http://epics.web.psi.ch/software/streamdevice/pcre/Makefile">Makefile</a>
|
||||||
to compile it like a normal EPICS application.
|
to compile it like a normal EPICS support module.
|
||||||
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
|
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
|
In your RELEASE file define the variable <code>PCRE</code> so that
|
||||||
it points to the install location of PCRE.
|
it points to the install location of PCRE.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
If PCRE is already installed on your system, use the variables
|
If PCRE is already installed on (some of) your systems, you may add
|
||||||
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
|
architectures where PCRE can be found in standard include and library
|
||||||
the install directories of <code>pcre.h</code> and the library.
|
locations to the variable <code>WITH_SYSTEM_PCRE</code>.
|
||||||
</p>
|
If either the header file or the library are in a non-standard place,
|
||||||
<p>
|
set in your RELEASE file the variables <code>PCRE_INCLUDE_<em>arch</em></code>
|
||||||
If you have PCRE installed in different locations for different (cross)
|
and/or <code>PCRE_LIB_<em>arch</em></code> for the respective architectures
|
||||||
architectures, define the variables in RELEASE.Common.<architecture>
|
to the correct directories or set
|
||||||
instead of the global RELEASE file.
|
<code>PCRE_INCLUDE</code> and/or <code>PCRE_LIB</code>
|
||||||
|
in architecture specific RELEASE.Common.<em>arch</em> files.
|
||||||
</p>
|
</p>
|
||||||
|
</div>
|
||||||
<p>
|
<p>
|
||||||
If the regular expression is not anchored, i.e. does not start with
|
If the regular expression is not anchored, i.e. does not start with
|
||||||
<code>^</code>, leading non-matching input is skipped.
|
<code>^</code>, leading non-matching input is skipped.
|
||||||
A maximum of <em>width</em> bytes is matched, if specified.
|
A maximum of <em>width</em> bytes is matched, if specified.
|
||||||
If <em>prec</em> is given, it specifies the sub-expression whose match
|
If <em>precision</em> is given, it specifies the sub-expression in <code>()</code>
|
||||||
is retuned.
|
whose match is retuned.
|
||||||
Otherwise the complete match is returned.
|
Otherwise the complete match is returned.
|
||||||
In any case, the complete match is consumed from the input buffer.
|
In any case, the complete match is consumed from the input buffer.
|
||||||
If the expression contains a <code>/</code> it must be escaped.
|
If the expression contains a <code>/</code> it must be escaped like <code>\/</code>.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
||||||
@ -570,8 +576,81 @@ the title of an HTML page, skipps anything before the
|
|||||||
<code><title></code> tag and leaves anything after the
|
<code><title></code> tag and leaves anything after the
|
||||||
<code></title></code> tag in the input buffer.
|
<code></title></code> tag in the input buffer.
|
||||||
</p>
|
</p>
|
||||||
|
<a name="regsub"></a>
|
||||||
|
<h2>14. Regular Expresion Substitution Pseudo-Converter (<code>%#/<em>regex</em>/<em>subst</em>/</code>)</h2>
|
||||||
|
<p>
|
||||||
|
This is a variant of the previous converter (note the <code>#</code>)
|
||||||
|
but instead of returning the matching string,
|
||||||
|
it can be used as a pre-processor for input or
|
||||||
|
as a post-processor for output.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Matches of the <em>regex</em> are replaced by the string <em>subst</em> with all
|
||||||
|
<code>&</code> in <em>subst</em> replaced with the match itself and all
|
||||||
|
<code>\1</code> through <code>\9</code> replaced with the match of the corresponding
|
||||||
|
sub-expression <span class="new"> if such a sub-expression exists.
|
||||||
|
|
||||||
|
Due to limitations of the parser, <code>\1</code> and <code>\x01</code> are the same
|
||||||
|
which makes it difficult to use literal bytes with values lower than 10 in <em>subst</em>.
|
||||||
|
Therefore <code>\0</code> aways means a literal byte (incompatible change from earlier version!)
|
||||||
|
and <code>\1</code> through <code>\9</code> mean literal bytes if they are larger than
|
||||||
|
the number of sub-expressions.
|
||||||
|
</span>
|
||||||
|
|
||||||
|
To get a literal <code>&</code> or <code>\</code> or <code>/</code> in the substitution write
|
||||||
|
<code>\&</code> or <code>\\</code> or <code>\/</code>.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If <em>width</em> is specified, it limits the number of characters processed.
|
||||||
|
If the <code>-</code> flag is used (i.e. <em>width</em> looks like a negative number)
|
||||||
|
only the last <em>width</em> characters are processed, else the first.
|
||||||
|
Without <em>width</em> (or 0) all available characters are processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
If <em>precision</em> is specified, it indicates which matches to replace.
|
||||||
|
With the <code>+</code> flag given, <em>precision</em> is the maximum
|
||||||
|
number of matches to replace.
|
||||||
|
Otherwise <em>precision</em> is the index (counting from 1) of the match to replace.
|
||||||
|
Without <em>precision</em> (or 0), all matches are replaced.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In input this converter pre-processes data received from the device before
|
||||||
|
following converters read it.
|
||||||
|
Converters preceding this one will read unmodified input.
|
||||||
|
Thus place this converter before those whose input should be pre-processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
In output it post-processes data already formatted by preceding converters
|
||||||
|
before sending it to the device.
|
||||||
|
Converters following this one will send their output unmodified.
|
||||||
|
Thus place this converter after those whose output should be post-processed.
|
||||||
|
</p>
|
||||||
|
<p>
|
||||||
|
Examples:
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#+-10.2/ab/X/</code> replaces the string <code>ab</code> with <code>X</code>
|
||||||
|
maximal 2 times in the last 10 characters.
|
||||||
|
(<code>abcabcabcabc</code> becomes <code>abcXcXcabc</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/\\/\//</code> replaces all <code>\</code> with <code>/</code>
|
||||||
|
(<code>\dir\file</code> becomes <code>/dir/file</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/..\B/&:/</code> inserts <code>:</code> after every second character
|
||||||
|
which is not at the end of a word.
|
||||||
|
(<code>0b19353134</code> becomes <code>0b:19:35:31:34</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/://</code> removes all <code>:</code> characters.
|
||||||
|
(<code>0b:19:35:31:34</code> becomes <code>0b19353134</code>)
|
||||||
|
</div>
|
||||||
|
<div class="indent">
|
||||||
|
<code>%#/([^+-])*([+-])/\2\1/</code> moves a postfix sign to the front.
|
||||||
|
(<code>1.23-</code> becomes <code>-1.23</code>)<br>
|
||||||
|
</div>
|
||||||
<a name="mantexp"></a>
|
<a name="mantexp"></a>
|
||||||
<h2>13. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
<h2>15. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This exotic and experimental format matches numbers in the format
|
This exotic and experimental format matches numbers in the format
|
||||||
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
||||||
@ -591,7 +670,7 @@ the usual way (always sign, left justified, space instead of + sign).
|
|||||||
Flags <code>#</code> and <code>0</code> are unsupported.
|
Flags <code>#</code> and <code>0</code> are unsupported.
|
||||||
</p>
|
</p>
|
||||||
<a name="timestamp"></a>
|
<a name="timestamp"></a>
|
||||||
<h2>14. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
<h2>16. Timestamp DOUBLE converter (<code>%T(<em>timeformat</em>)</code>)</h2>
|
||||||
<p>
|
<p>
|
||||||
This format reads or writes timestamps and converts them to a double number.
|
This format reads or writes timestamps and converts them to a double number.
|
||||||
The value represents the number of seconds since 1970 (the UNIX epoch).
|
The value represents the number of seconds since 1970 (the UNIX epoch).
|
||||||
@ -628,7 +707,7 @@ In output, the system function <em>strftime()</em> is used to format the time.
|
|||||||
There may be differences in the implementation between operating systems.
|
There may be differences in the implementation between operating systems.
|
||||||
</p>
|
</p>
|
||||||
<p>
|
<p>
|
||||||
In input, <em>StreamDevice</em> used its own implementation because many
|
In input, <em>StreamDevice</em> uses its own implementation because many
|
||||||
systems are missing the <em>strptime()</em> function and additional formats
|
systems are missing the <em>strptime()</em> function and additional formats
|
||||||
are supported.
|
are supported.
|
||||||
</p>
|
</p>
|
||||||
|
@ -132,6 +132,7 @@ div div div a {list-style-type:circle;}
|
|||||||
<a target="_parent" href="formats.html#bcd" title="Binary coded decimal LONG converter">%D</a>
|
<a target="_parent" href="formats.html#bcd" title="Binary coded decimal LONG converter">%D</a>
|
||||||
<a target="_parent" href="formats.html#chksum" title="Checksum pseudo converter">%<<em>checksum</em>></a>
|
<a target="_parent" href="formats.html#chksum" title="Checksum pseudo converter">%<<em>checksum</em>></a>
|
||||||
<a target="_parent" href="formats.html#regex" title="Perl regular expression STRING converter">%/<em>regex</em>/</a>
|
<a target="_parent" href="formats.html#regex" title="Perl regular expression STRING converter">%/<em>regex</em>/</a>
|
||||||
|
<a target="_parent" href="formats.html#regsub" title="Perl regular expression substitution pseudo converter">%#/<em>regex</em>/<em>subst</em>/</a>
|
||||||
<a target="_parent" href="formats.html#mantexp" title="MantissaExponent DOUBLE converter">%m</a>
|
<a target="_parent" href="formats.html#mantexp" title="MantissaExponent DOUBLE converter">%m</a>
|
||||||
<a target="_parent" href="formats.html#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
<a target="_parent" href="formats.html#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -88,6 +88,16 @@ code {
|
|||||||
text-align:left;
|
text-align:left;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
margin-left:1ex;
|
||||||
|
margin-right:1ex;
|
||||||
|
margin-top:0.5ex;
|
||||||
|
padding: 0 1ex;
|
||||||
|
border: 1px solid black;
|
||||||
|
text-align:left;
|
||||||
|
background-color:#f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
#navleft {
|
#navleft {
|
||||||
position:fixed;
|
position:fixed;
|
||||||
left:0;
|
left:0;
|
||||||
|
@ -562,13 +562,7 @@ connectToAsynPort()
|
|||||||
clientName(), connected ? "already" : "not yet");
|
clientName(), connected ? "already" : "not yet");
|
||||||
if (!connected)
|
if (!connected)
|
||||||
{
|
{
|
||||||
printf ("%s: AsynDriverInterface::connectToAsynPort: "
|
|
||||||
"pasynCommon->connect(%p, %p)\n",
|
|
||||||
clientName(), pvtCommon, pasynUser);
|
|
||||||
status = pasynCommon->connect(pvtCommon, pasynUser);
|
status = pasynCommon->connect(pvtCommon, pasynUser);
|
||||||
printf ("%s: AsynDriverInterface::connectToAsynPort: "
|
|
||||||
"pasynCommon->connect(%p, %p) = %s\n",
|
|
||||||
clientName(), pvtCommon, pasynUser, asynStatusStr[status]);
|
|
||||||
debug("AsynDriverInterface::connectToAsynPort(%s): "
|
debug("AsynDriverInterface::connectToAsynPort(%s): "
|
||||||
"status=%s\n",
|
"status=%s\n",
|
||||||
clientName(), asynStatusStr[status]);
|
clientName(), asynStatusStr[status]);
|
||||||
@ -718,13 +712,19 @@ writeHandler()
|
|||||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||||
NULL, 0);
|
NULL, 0);
|
||||||
}
|
}
|
||||||
|
pasynUser->errorMessage[0] = 0;
|
||||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||||
outputBuffer, outputSize, &written);
|
outputBuffer, outputSize, &written);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
debug("AsynDriverInterface::writeHandler(%s): "
|
debug("AsynDriverInterface::writeHandler(%s): "
|
||||||
"write(..., outputSize=%ld, written=%ld) "
|
"write(..., \"%s\", outputSize=%ld, written=%ld) "
|
||||||
"[timeout=%g sec] = %s\n",
|
"[timeout=%g sec] = %s (%s)\n",
|
||||||
clientName(), (long)outputSize, (long)written,
|
clientName(),
|
||||||
pasynUser->timeout, asynStatusStr[status]);
|
StreamBuffer(outputBuffer, outputSize).expand()(),
|
||||||
|
(long)outputSize, (long)written,
|
||||||
|
pasynUser->timeout, asynStatusStr[status],
|
||||||
|
pasynUser->errorMessage);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (oldeoslen >= 0) // restore asyn terminator
|
if (oldeoslen >= 0) // restore asyn terminator
|
||||||
{
|
{
|
||||||
@ -769,34 +769,34 @@ writeHandler()
|
|||||||
writeCallback(StreamIoSuccess);
|
writeCallback(StreamIoSuccess);
|
||||||
return;
|
return;
|
||||||
case asynTimeout:
|
case asynTimeout:
|
||||||
error("%s: asynTimeout (%g sec) in write. Asyn says: %s\n",
|
error("%s: asynTimeout (%g sec) in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->timeout, pasynUser->errorMessage);
|
clientName(), pasynUser->timeout, pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoTimeout);
|
writeCallback(StreamIoTimeout);
|
||||||
return;
|
return;
|
||||||
case asynOverflow:
|
case asynOverflow:
|
||||||
error("%s: asynOverflow in write. Asyn driver says: %s\n",
|
error("%s: asynOverflow in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynError:
|
case asynError:
|
||||||
error("%s: asynError in write. Asyn driver says: %s\n",
|
error("%s: asynError in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||||
case asynDisconnected:
|
case asynDisconnected:
|
||||||
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
|
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynDisabled:
|
case asynDisabled:
|
||||||
error("%s: asynDisconnected in write. Asyn driver says: %s\n",
|
error("%s: asynDisconnected in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
error("%s: unknown asyn error in write. Asyn driver says: %s\n",
|
error("%s: unknown asyn error in write. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
writeCallback(StreamIoFault);
|
writeCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
@ -887,6 +887,11 @@ readHandler()
|
|||||||
oldeoslen = -1;
|
oldeoslen = -1;
|
||||||
} else do {
|
} else do {
|
||||||
// device (e.g. GPIB) might not accept full eos length
|
// device (e.g. GPIB) might not accept full eos length
|
||||||
|
if ((int)deveoslen == oldeoslen && strcmp(deveos, oldeos) == 0)
|
||||||
|
{
|
||||||
|
// nothing to do: old and new eos are the same
|
||||||
|
break;
|
||||||
|
}
|
||||||
if (pasynOctet->setInputEos(pvtOctet, pasynUser,
|
if (pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||||
deveos, deveoslen) == asynSuccess)
|
deveos, deveoslen) == asynSuccess)
|
||||||
{
|
{
|
||||||
@ -894,8 +899,9 @@ readHandler()
|
|||||||
if (ioAction != AsyncRead)
|
if (ioAction != AsyncRead)
|
||||||
{
|
{
|
||||||
debug("AsynDriverInterface::readHandler(%s) "
|
debug("AsynDriverInterface::readHandler(%s) "
|
||||||
"input EOS set to %s\n",
|
"input EOS changed from \"%s\" to \"%s\"\n",
|
||||||
clientName(),
|
clientName(),
|
||||||
|
StreamBuffer(oldeos, oldeoslen).expand()(),
|
||||||
StreamBuffer(deveos, deveoslen).expand()());
|
StreamBuffer(deveos, deveoslen).expand()());
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -951,6 +957,7 @@ readHandler()
|
|||||||
readMore = 0;
|
readMore = 0;
|
||||||
received = 0;
|
received = 0;
|
||||||
eomReason = 0;
|
eomReason = 0;
|
||||||
|
pasynUser->errorMessage[0] = 0;
|
||||||
|
|
||||||
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
||||||
"read(..., bytesToRead=%ld, ...) "
|
"read(..., bytesToRead=%ld, ...) "
|
||||||
@ -959,12 +966,13 @@ readHandler()
|
|||||||
bytesToRead, pasynUser->timeout);
|
bytesToRead, pasynUser->timeout);
|
||||||
status = pasynOctet->read(pvtOctet, pasynUser,
|
status = pasynOctet->read(pvtOctet, pasynUser,
|
||||||
buffer, bytesToRead, &received, &eomReason);
|
buffer, bytesToRead, &received, &eomReason);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
debug("AsynDriverInterface::readHandler(%s): "
|
debug("AsynDriverInterface::readHandler(%s): "
|
||||||
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
||||||
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
||||||
(long)received,eomReasonStr[eomReason&0x7],
|
(long)received,eomReasonStr[eomReason&0x7],
|
||||||
StreamBuffer(buffer, received).expand()());
|
StreamBuffer(buffer, received).expand()());
|
||||||
|
#endif
|
||||||
pasynManager->isConnected(pasynUser, &connected);
|
pasynManager->isConnected(pasynUser, &connected);
|
||||||
debug("AsynDriverInterface::readHandler(%s): "
|
debug("AsynDriverInterface::readHandler(%s): "
|
||||||
"device is now %sconnected\n",
|
"device is now %sconnected\n",
|
||||||
@ -1094,29 +1102,29 @@ readHandler()
|
|||||||
}
|
}
|
||||||
peeksize = inputBuffer.capacity();
|
peeksize = inputBuffer.capacity();
|
||||||
// deliver whatever we could save
|
// deliver whatever we could save
|
||||||
error("%s: asynOverflow in read. Asyn driver says: %s\n",
|
error("%s: asynOverflow in read. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault, buffer, received);
|
readCallback(StreamIoFault, buffer, received);
|
||||||
break;
|
break;
|
||||||
case asynError:
|
case asynError:
|
||||||
error("%s: asynError in read. Asyn driver says: %s\n",
|
error("%s: asynError in read. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault, buffer, received);
|
readCallback(StreamIoFault, buffer, received);
|
||||||
break;
|
break;
|
||||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||||
case asynDisconnected:
|
case asynDisconnected:
|
||||||
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
|
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
case asynDisabled:
|
case asynDisabled:
|
||||||
error("%s: asynDisconnected in read. Asyn driver says: %s\n",
|
error("%s: asynDisconnected in read. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
#endif
|
#endif
|
||||||
default:
|
default:
|
||||||
error("%s: unknown asyn error in read. Asyn driver says: %s\n",
|
error("%s: unknown asyn error in read. Asyn driver says: \"%s\"\n",
|
||||||
clientName(), pasynUser->errorMessage);
|
clientName(), pasynUser->errorMessage);
|
||||||
readCallback(StreamIoFault);
|
readCallback(StreamIoFault);
|
||||||
return;
|
return;
|
||||||
@ -1138,10 +1146,16 @@ readHandler()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// restore original EOS
|
// restore original EOS
|
||||||
if (oldeoslen >= 0)
|
if (oldeoslen >= 0 && oldeoslen != (int)deveoslen && strcmp(deveos, oldeos) != 0)
|
||||||
{
|
{
|
||||||
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||||
oldeos, oldeoslen);
|
oldeos, oldeoslen);
|
||||||
|
#ifndef NO_TEMPORARY
|
||||||
|
debug("AsynDriverInterface::readHandler(%s) "
|
||||||
|
"input EOS restored to \"%s\"\n",
|
||||||
|
clientName(),
|
||||||
|
StreamBuffer(oldeos, oldeoslen).expand()());
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
#include "string.h"
|
#include "string.h"
|
||||||
#include "pcre.h"
|
#include "pcre.h"
|
||||||
|
|
||||||
// Perl regular expressions (PCRE) %/regexp/
|
// Perl regular expressions (PCRE) %/regexp/ and %#/regexp/subst/
|
||||||
|
|
||||||
/* Notes:
|
/* Notes:
|
||||||
- Memory for compiled regexp is allocated in parse but never freed.
|
- Memory for compiled regexp is allocated in parse but never freed.
|
||||||
@ -32,61 +32,93 @@
|
|||||||
run-time leak.
|
run-time leak.
|
||||||
- A maximum of 9 subexpressions is supported. Only one of them can
|
- A maximum of 9 subexpressions is supported. Only one of them can
|
||||||
be the result of the match.
|
be the result of the match.
|
||||||
- vxWorks and maybe other OS don't have a PCRE library. Provide one?
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class RegexpConverter : public StreamFormatConverter
|
class RegexpConverter : public StreamFormatConverter
|
||||||
{
|
{
|
||||||
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
|
int parse (const StreamFormat& fmt, StreamBuffer&, const char*&, bool);
|
||||||
int scanString(const StreamFormat&, const char*, char*, size_t);
|
int scanString(const StreamFormat& fmt, const char*, char*, size_t);
|
||||||
|
int scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor);
|
||||||
|
bool printPseudo(const StreamFormat& fmt, StreamBuffer& output);
|
||||||
};
|
};
|
||||||
|
|
||||||
int RegexpConverter::
|
int RegexpConverter::
|
||||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||||
const char*& source, bool scanFormat)
|
const char*& source, bool scanFormat)
|
||||||
{
|
{
|
||||||
if (!scanFormat)
|
if (!scanFormat && !(fmt.flags & alt_flag))
|
||||||
{
|
{
|
||||||
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (fmt.prec > 9)
|
if (fmt.prec > 9)
|
||||||
{
|
{
|
||||||
error("Subexpression index %d too big (>9)\n", fmt.prec);
|
error("Sub-expression 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;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
StreamBuffer pattern;
|
StreamBuffer pattern;
|
||||||
while (*source != '/')
|
while (*source != '/')
|
||||||
{
|
{
|
||||||
if (!*source) {
|
if (!*source) {
|
||||||
error("Missing closing '/' after %%/ format conversion\n");
|
error("Missing closing '/' after %%/%s format conversion\n", pattern());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (*source == esc) {
|
if (*source == esc) { // handle escaped chars
|
||||||
source++;
|
if (*++source != '/') // just un-escape /
|
||||||
pattern.print("\\x%02x", *source++ & 0xFF);
|
{
|
||||||
continue;
|
pattern.append('\\');
|
||||||
|
if ((*source & 0x7f) < 0x30) // handle control chars
|
||||||
|
{
|
||||||
|
pattern.print("x%02x", *source++);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// fall through for PCRE codes like \B
|
||||||
|
}
|
||||||
}
|
}
|
||||||
pattern.append(*source++);
|
pattern.append(*source++);
|
||||||
}
|
}
|
||||||
source++;
|
source++;
|
||||||
debug("regexp = \"%s\"\n", pattern());
|
debug("regexp = \"%s\"\n", pattern.expand()());
|
||||||
|
|
||||||
const char* errormsg;
|
const char* errormsg;
|
||||||
int eoffset;
|
int eoffset;
|
||||||
pcre* code = pcre_compile(pattern(), 0,
|
int nsubexpr;
|
||||||
&errormsg, &eoffset, NULL);
|
|
||||||
|
pcre* code = pcre_compile(pattern(), 0, &errormsg, &eoffset, NULL);
|
||||||
if (!code)
|
if (!code)
|
||||||
{
|
{
|
||||||
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
|
error("%s after \"%s\"\n", errormsg, pattern.expand(0, eoffset)());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
pcre_fullinfo(code, NULL, PCRE_INFO_CAPTURECOUNT, &nsubexpr);
|
||||||
|
if (fmt.prec > nsubexpr)
|
||||||
|
{
|
||||||
|
error("Sub-expression index is %d but pattern has only %d sub-expression\n", fmt.prec, nsubexpr);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
info.append(&code, sizeof(code));
|
info.append(&code, sizeof(code));
|
||||||
|
|
||||||
|
if (fmt.flags & alt_flag)
|
||||||
|
{
|
||||||
|
StreamBuffer subst;
|
||||||
|
|
||||||
|
debug("check for subst in \"%s\"\n", StreamBuffer(source).expand()());
|
||||||
|
while (*source != '/')
|
||||||
|
{
|
||||||
|
if (!*source) {
|
||||||
|
error("Missing closing '/' after %%#/%s/%s format conversion\n", pattern(), subst());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (*source == esc)
|
||||||
|
subst.append(*source++);
|
||||||
|
subst.append(*source++);
|
||||||
|
}
|
||||||
|
source++;
|
||||||
|
debug("subst = \"%s\"\n", subst.expand()());
|
||||||
|
info.append(subst).append('\0');
|
||||||
|
return pseudo_format;
|
||||||
|
}
|
||||||
return string_format;
|
return string_format;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,32 +126,130 @@ int RegexpConverter::
|
|||||||
scanString(const StreamFormat& fmt, const char* input,
|
scanString(const StreamFormat& fmt, const char* input,
|
||||||
char* value, size_t maxlen)
|
char* value, size_t maxlen)
|
||||||
{
|
{
|
||||||
pcre* code;
|
|
||||||
size_t len;
|
|
||||||
int ovector[30];
|
int ovector[30];
|
||||||
int rc;
|
int rc;
|
||||||
int subexpr = 0;
|
unsigned int l;
|
||||||
|
|
||||||
memcpy (&code, fmt.info, sizeof(code));
|
const char* info = fmt.info;
|
||||||
|
pcre* code = extract<pcre*>(info);
|
||||||
len = fmt.width > 0 ? fmt.width : strlen(input);
|
int length = fmt.width > 0 ? fmt.width : strlen(input);
|
||||||
subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
int subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
||||||
rc = pcre_exec(code, NULL, input, len, 0, 0, ovector, 30);
|
|
||||||
if (rc < 1) return -1;
|
debug("input = \"%s\"\n", input);
|
||||||
if (fmt.flags & skip_flag) return ovector[1];
|
debug("length=%d\n", length);
|
||||||
len = ovector[subexpr*2+1] - ovector[subexpr*2];
|
|
||||||
if (len >= maxlen) {
|
rc = pcre_exec(code, NULL, input, length, 0, 0, ovector, 30);
|
||||||
|
debug("pcre_exec match \"%.*s\" result = %d\n", length, input, rc);
|
||||||
|
if ((subexpr && rc <= subexpr) || rc < 0)
|
||||||
|
{
|
||||||
|
// error or no match or not enough sub-expressions
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
if (fmt.flags & skip_flag) return ovector[subexpr*2+1];
|
||||||
|
|
||||||
|
l = ovector[subexpr*2+1] - ovector[subexpr*2];
|
||||||
|
if (l >= maxlen) {
|
||||||
if (!(fmt.flags & sign_flag)) {
|
if (!(fmt.flags & sign_flag)) {
|
||||||
error("Regexp: Matching string \"%s\" too long (%d>%d bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
error("Regexp: Matching string \"%s\" too long (%d>%ld bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||||
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
|
StreamBuffer(input + ovector[subexpr*2],l).expand()(),
|
||||||
(int)len, (int)maxlen-1);
|
l, (long)maxlen-1);
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
len = maxlen-1;
|
l = maxlen-1;
|
||||||
}
|
}
|
||||||
memcpy(value, input+ovector[subexpr*2], len);
|
memcpy(value, input + ovector[subexpr*2], l);
|
||||||
value[len]=0;
|
value[l] = '\0';
|
||||||
return ovector[1];
|
return ovector[1]; // consume input until end of match
|
||||||
|
}
|
||||||
|
|
||||||
|
static void regsubst(const StreamFormat& fmt, StreamBuffer& buffer, long start)
|
||||||
|
{
|
||||||
|
const char* subst = fmt.info;
|
||||||
|
pcre* code = extract<pcre*>(subst);
|
||||||
|
long length;
|
||||||
|
int rc, l, c, r, rl, n;
|
||||||
|
int ovector[30];
|
||||||
|
StreamBuffer s;
|
||||||
|
|
||||||
|
length = buffer.length() - start;
|
||||||
|
if (fmt.width && fmt.width < length)
|
||||||
|
length = fmt.width;
|
||||||
|
if (fmt.flags & left_flag)
|
||||||
|
start = buffer.length() - length;
|
||||||
|
|
||||||
|
debug("regsubst buffer=\"%s\", start=%ld, length=%ld, subst = \"%s\"\n",
|
||||||
|
buffer.expand()(), start, length, StreamBuffer(subst).expand()());
|
||||||
|
|
||||||
|
for (c = 0, n = 1; c < length; n++)
|
||||||
|
{
|
||||||
|
rc = pcre_exec(code, NULL, buffer(start+c), length-c, 0, 0, ovector, 30);
|
||||||
|
debug("pcre_exec match \"%s\" result = %d\n", buffer.expand(start+c, length-c)(), rc);
|
||||||
|
if (rc < 0) // no match
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (!(fmt.flags & sign_flag) && n < fmt.prec) // without + flag
|
||||||
|
{
|
||||||
|
// do not yet replace this match
|
||||||
|
c += ovector[1];
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// replace & by match in subst
|
||||||
|
l = ovector[1] - ovector[0];
|
||||||
|
debug("before [%d]= \"%s\"\n", ovector[0], buffer.expand(start+c,ovector[0])());
|
||||||
|
debug("match [%d]= \"%s\"\n", l, buffer.expand(start+c+ovector[0],l)());
|
||||||
|
for (r = 1; r < rc; r++)
|
||||||
|
debug("sub%d = \"%s\"\n", r, buffer.expand(start+c+ovector[r*2], ovector[r*2+1]-ovector[r*2])());
|
||||||
|
debug("after = \"%s\"\n", buffer.expand(start+c+ovector[1])());
|
||||||
|
s = subst;
|
||||||
|
debug("subs = \"%s\"\n", s.expand()());
|
||||||
|
for (r = 0; r < s.length(); r++)
|
||||||
|
{
|
||||||
|
debug("check \"%s\"\n", s.expand(r)());
|
||||||
|
if (s[r] == esc)
|
||||||
|
{
|
||||||
|
unsigned char ch = s[r+1];
|
||||||
|
if (c != 0 && ch < rc) // escaped 1 - 9 : replace with subexpr
|
||||||
|
{
|
||||||
|
ch *= 2;
|
||||||
|
rl = ovector[ch+1] - ovector[ch];
|
||||||
|
debug("replace \\%d: \"%s\"\n", ch/2, buffer.expand(start+c+ovector[ch], rl)());
|
||||||
|
s.replace(r, 2, buffer(start+c+ovector[ch]), rl);
|
||||||
|
r += rl - 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
s.remove(r, 1); // just remove escape
|
||||||
|
}
|
||||||
|
else if (s[r] == '&') // unescaped & : replace with match
|
||||||
|
{
|
||||||
|
debug("replace &: \"%s\"\n", buffer.expand(start+c+ovector[0], l)());
|
||||||
|
s.replace(r, 1, buffer(start+c+ovector[0]), l);
|
||||||
|
r += l - 1;
|
||||||
|
}
|
||||||
|
else continue;
|
||||||
|
debug("subs = \"%s\"\n", s.expand()());
|
||||||
|
}
|
||||||
|
buffer.replace(start+c+ovector[0], l, s);
|
||||||
|
length += s.length() - l;
|
||||||
|
c += ovector[0] + s.length();
|
||||||
|
if (n == fmt.prec) // max match reached
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int RegexpConverter::
|
||||||
|
scanPseudo(const StreamFormat& fmt, StreamBuffer& input, long& cursor)
|
||||||
|
{
|
||||||
|
/* re-write input buffer */
|
||||||
|
regsubst(fmt, input, cursor);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool RegexpConverter::
|
||||||
|
printPseudo(const StreamFormat& fmt, StreamBuffer& output)
|
||||||
|
{
|
||||||
|
/* re-write output buffer */
|
||||||
|
regsubst(fmt, output, 0);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
RegisterConverter (RegexpConverter, "/");
|
RegisterConverter (RegexpConverter, "/");
|
||||||
|
@ -296,7 +296,7 @@ StreamBuffer StreamBuffer::expand(ssize_t start, ssize_t length) const
|
|||||||
for (i = start; i < end; i++)
|
for (i = start; i < end; i++)
|
||||||
{
|
{
|
||||||
c = buffer[i];
|
c = buffer[i];
|
||||||
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
|
if (c < 0x20 || c >= 0x7f)
|
||||||
{
|
{
|
||||||
result.print("<%02x>", c & 0xff);
|
result.print("<%02x>", c & 0xff);
|
||||||
}
|
}
|
||||||
|
@ -460,7 +460,7 @@ finishProtocol(ProtocolResult status)
|
|||||||
status = Fault;
|
status = Fault;
|
||||||
}
|
}
|
||||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||||
if (runningHandler)
|
if (runningHandler || flags & InitRun)
|
||||||
{
|
{
|
||||||
// get original error status
|
// get original error status
|
||||||
if (status == Success) status = runningHandler;
|
if (status == Success) status = runningHandler;
|
||||||
@ -832,7 +832,7 @@ lockCallback(StreamIoStatus status)
|
|||||||
case StreamIoSuccess:
|
case StreamIoSuccess:
|
||||||
break;
|
break;
|
||||||
case StreamIoTimeout:
|
case StreamIoTimeout:
|
||||||
error("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
debug("%s: Cannot lock device within %ld ms, device seems to be busy\n",
|
||||||
name(), lockTimeout);
|
name(), lockTimeout);
|
||||||
flags &= ~BusOwner;
|
flags &= ~BusOwner;
|
||||||
finishProtocol(LockTimeout);
|
finishProtocol(LockTimeout);
|
||||||
@ -991,7 +991,7 @@ readCallback(StreamIoStatus status,
|
|||||||
evalIn();
|
evalIn();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
error("%s: No reply from device within %ld ms\n",
|
debug("StreamCore::readCallback(%s): No reply from device within %ld ms\n",
|
||||||
name(), replyTimeout);
|
name(), replyTimeout);
|
||||||
inputBuffer.clear();
|
inputBuffer.clear();
|
||||||
finishProtocol(ReplyTimeout);
|
finishProtocol(ReplyTimeout);
|
||||||
|
@ -180,6 +180,7 @@ public:
|
|||||||
#ifndef EPICS_3_13
|
#ifndef EPICS_3_13
|
||||||
extern "C" {
|
extern "C" {
|
||||||
epicsExportAddress(int, streamDebug);
|
epicsExportAddress(int, streamDebug);
|
||||||
|
epicsExportAddress(int, streamError);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@ -195,8 +196,12 @@ extern "C" long streamReload(char* recordname)
|
|||||||
dbCommon* record;
|
dbCommon* record;
|
||||||
long status;
|
long status;
|
||||||
|
|
||||||
|
int oldStreamError = streamError;
|
||||||
|
streamError = 1;
|
||||||
|
|
||||||
if(!pdbbase) {
|
if(!pdbbase) {
|
||||||
error("No database has been loaded\n");
|
error("No database has been loaded\n");
|
||||||
|
streamError = oldStreamError;
|
||||||
return ERROR;
|
return ERROR;
|
||||||
}
|
}
|
||||||
debug("streamReload(%s)\n", recordname);
|
debug("streamReload(%s)\n", recordname);
|
||||||
@ -233,6 +238,7 @@ extern "C" long streamReload(char* recordname)
|
|||||||
}
|
}
|
||||||
dbFinishEntry(&dbentry);
|
dbFinishEntry(&dbentry);
|
||||||
StreamProtocolParser::free();
|
StreamProtocolParser::free();
|
||||||
|
streamError = oldStreamError;
|
||||||
return OK;
|
return OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -407,6 +413,7 @@ long streamInit(int after)
|
|||||||
{
|
{
|
||||||
if (after)
|
if (after)
|
||||||
{
|
{
|
||||||
|
streamError = 0; // Switch off errors after init in order not to spam messages when a device is down.
|
||||||
StreamProtocolParser::free();
|
StreamProtocolParser::free();
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
@ -603,7 +610,7 @@ parseLink(const struct link *ioLink, char* filename,
|
|||||||
if (items <= 0)
|
if (items <= 0)
|
||||||
{
|
{
|
||||||
error("%s: Empty I/O link. "
|
error("%s: Empty I/O link. "
|
||||||
"Forgot the leading '@' or confused INP with OUT ?\n",
|
"Forgot the leading '@' or confused INP with OUT or link is too long ?\n",
|
||||||
name());
|
name());
|
||||||
return S_dev_badInitRet;
|
return S_dev_badInitRet;
|
||||||
}
|
}
|
||||||
@ -734,16 +741,15 @@ process()
|
|||||||
debug("Stream::process(%s) start\n", name());
|
debug("Stream::process(%s) start\n", name());
|
||||||
status = NO_ALARM;
|
status = NO_ALARM;
|
||||||
convert = OK;
|
convert = OK;
|
||||||
record->pact = true;
|
|
||||||
if (!startProtocol(StreamCore::StartNormal))
|
if (!startProtocol(StreamCore::StartNormal))
|
||||||
{
|
{
|
||||||
debug("Stream::process(%s): could not start, status=%d\n",
|
debug("Stream::process(%s): could not start, status=%d\n",
|
||||||
name(), status);
|
name(), status);
|
||||||
(void) recGblSetSevr(record, status, INVALID_ALARM);
|
(void) recGblSetSevr(record, status, INVALID_ALARM);
|
||||||
record->pact = false;
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
debug("Stream::process(%s): protocol started\n", name());
|
debug("Stream::process(%s): protocol started\n", name());
|
||||||
|
record->pact = true;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -23,6 +23,7 @@
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
int streamDebug = 0;
|
int streamDebug = 0;
|
||||||
|
int streamError = 1;
|
||||||
extern "C" {
|
extern "C" {
|
||||||
#ifdef _WIN32
|
#ifdef _WIN32
|
||||||
__declspec(dllexport)
|
__declspec(dllexport)
|
||||||
@ -74,6 +75,7 @@ void StreamError(int line, const char* file, const char* fmt, ...)
|
|||||||
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
void StreamVError(int line, const char* file, const char* fmt, va_list args)
|
||||||
{
|
{
|
||||||
char timestamp[40];
|
char timestamp[40];
|
||||||
|
if (!streamError) return; // Error logging disabled
|
||||||
StreamPrintTimestampFunction(timestamp, 40);
|
StreamPrintTimestampFunction(timestamp, 40);
|
||||||
#ifdef va_copy
|
#ifdef va_copy
|
||||||
if (StreamDebugFile)
|
if (StreamDebugFile)
|
||||||
|
@ -28,6 +28,7 @@
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
extern int streamDebug;
|
extern int streamDebug;
|
||||||
|
extern int streamError;
|
||||||
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
||||||
|
|
||||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||||
|
@ -512,20 +512,31 @@ Each time newline is read, line is incremented.
|
|||||||
buffer(token));
|
buffer(token));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (c == '$' && buffer[-1] == '\\')
|
|
||||||
{
|
|
||||||
// quoted variable reference
|
|
||||||
// terminate string here and do variable in next pass
|
|
||||||
buffer[-1] = quote;
|
|
||||||
ungetc(c, file);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
buffer.append(c);
|
buffer.append(c);
|
||||||
if (c == quote && buffer[-2] != '\\')
|
if (c == quote)
|
||||||
{
|
{
|
||||||
quote = false;
|
quote = false;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if (c == '\\')
|
||||||
|
{
|
||||||
|
c = getc(file);
|
||||||
|
if (c == '$')
|
||||||
|
{
|
||||||
|
// quoted variable reference
|
||||||
|
// terminate string here and do variable in next pass
|
||||||
|
buffer[-1] = quote;
|
||||||
|
ungetc(c, file);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (c == EOF || c == '\n')
|
||||||
|
{
|
||||||
|
error(line, filename(), "Backslash at end of line: %s\n",
|
||||||
|
buffer(token));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
buffer.append(c);
|
||||||
|
}
|
||||||
c = getc(file);
|
c = getc(file);
|
||||||
}
|
}
|
||||||
buffer.append('\0').append(&l, sizeof(l)); // append line number
|
buffer.append('\0').append(&l, sizeof(l)); // append line number
|
||||||
@ -1348,7 +1359,8 @@ compileString(StreamBuffer& buffer, const char*& source,
|
|||||||
source);
|
source);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if (formatType != NoFormat)
|
if (formatType != NoFormat &&
|
||||||
|
i > 2 /* do not escape skip */)
|
||||||
{
|
{
|
||||||
buffer.append(esc);
|
buffer.append(esc);
|
||||||
}
|
}
|
||||||
|
@ -23,7 +23,7 @@
|
|||||||
|
|
||||||
#define STREAM_MAJOR 2
|
#define STREAM_MAJOR 2
|
||||||
#define STREAM_MINOR 7
|
#define STREAM_MINOR 7
|
||||||
#define STREAM_PATCHLEVEL 1
|
#define STREAM_PATCHLEVEL 14
|
||||||
|
|
||||||
#if defined(__vxworks) || defined(vxWorks)
|
#if defined(__vxworks) || defined(vxWorks)
|
||||||
#include <vxWorks.h>
|
#include <vxWorks.h>
|
||||||
@ -58,7 +58,6 @@ extern "C" {
|
|||||||
#include <dbCommon.h>
|
#include <dbCommon.h>
|
||||||
#include <dbScan.h>
|
#include <dbScan.h>
|
||||||
#include <devSup.h>
|
#include <devSup.h>
|
||||||
/* #include <dbFldTypes.h> */
|
|
||||||
#include <dbAccess.h>
|
#include <dbAccess.h>
|
||||||
|
|
||||||
#ifdef devStream_epicsExportSharedSymbols
|
#ifdef devStream_epicsExportSharedSymbols
|
||||||
|
@ -55,11 +55,14 @@ static long readData (dbCommon *record, format_t *format)
|
|||||||
ai->rval = rval;
|
ai->rval = rval;
|
||||||
if (ai->linr == menuConvertNO_CONVERSION)
|
if (ai->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
|
double val;
|
||||||
if (format->type == DBF_ULONG)
|
if (format->type == DBF_ULONG)
|
||||||
ai->val = (unsigned long)rval;
|
val = (unsigned long)rval;
|
||||||
else
|
else
|
||||||
ai->val = rval;
|
val = rval;
|
||||||
|
if (ai->aslo != 0.0 && ai->aslo != 1.0) val *= ai->aslo;
|
||||||
|
ai->val = val + ai->aoff;
|
||||||
return DO_NOT_CONVERT;
|
return DO_NOT_CONVERT;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
|
@ -46,11 +46,14 @@ static long readData (dbCommon *record, format_t *format)
|
|||||||
ao->rval = rval;
|
ao->rval = rval;
|
||||||
if (ao->linr == menuConvertNO_CONVERSION)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
|
double val;
|
||||||
if (format->type == DBF_ULONG)
|
if (format->type == DBF_ULONG)
|
||||||
ao->val = (unsigned long)rval;
|
val = (unsigned long)rval;
|
||||||
else
|
else
|
||||||
ao->val = rval;
|
val = rval;
|
||||||
|
if (ao->aslo != 0.0 && ao->aslo != 1.0) val *= ao->aslo;
|
||||||
|
ao->val = val + ao->aoff;
|
||||||
return DO_NOT_CONVERT;
|
return DO_NOT_CONVERT;
|
||||||
}
|
}
|
||||||
return OK;
|
return OK;
|
||||||
@ -63,20 +66,21 @@ static long writeData (dbCommon *record, format_t *format)
|
|||||||
{
|
{
|
||||||
aoRecord *ao = (aoRecord *) record;
|
aoRecord *ao = (aoRecord *) record;
|
||||||
|
|
||||||
|
double val = (INIT_RUN ? ao->val : ao->oval) - ao->aoff;
|
||||||
|
if (ao->aslo != 0.0 && ao->aslo != 1.0) val /= ao->aslo;
|
||||||
|
|
||||||
switch (format->type)
|
switch (format->type)
|
||||||
{
|
{
|
||||||
case DBF_DOUBLE:
|
case DBF_DOUBLE:
|
||||||
{
|
{
|
||||||
double val = (INIT_RUN ? ao->val : ao->oval) - ao->aoff;
|
|
||||||
if (ao->aslo != 0.0 && ao->aslo != 1.0) val /= ao->aslo;
|
|
||||||
return streamPrintf (record, format, val);
|
return streamPrintf (record, format, val);
|
||||||
}
|
}
|
||||||
case DBF_ULONG:
|
case DBF_ULONG:
|
||||||
{
|
{
|
||||||
if (ao->linr == menuConvertNO_CONVERSION)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
return streamPrintf (record, format, (unsigned long)(INIT_RUN ? ao->val : ao->oval));
|
return streamPrintf (record, format, (unsigned long)val);
|
||||||
}
|
}
|
||||||
return streamPrintf (record, format, (unsigned long)ao->rval);
|
return streamPrintf (record, format, (unsigned long)ao->rval);
|
||||||
}
|
}
|
||||||
@ -84,8 +88,8 @@ static long writeData (dbCommon *record, format_t *format)
|
|||||||
{
|
{
|
||||||
if (ao->linr == menuConvertNO_CONVERSION)
|
if (ao->linr == menuConvertNO_CONVERSION)
|
||||||
{
|
{
|
||||||
/* allow more bits than 32 */
|
/* allow integers with more than 32 bits */
|
||||||
return streamPrintf (record, format, (long)(INIT_RUN ? ao->val : ao->oval));
|
return streamPrintf (record, format, (long)val);
|
||||||
}
|
}
|
||||||
return streamPrintf (record, format, (long)ao->rval);
|
return streamPrintf (record, format, (long)ao->rval);
|
||||||
}
|
}
|
||||||
|
@ -20,6 +20,7 @@
|
|||||||
***************************************************************/
|
***************************************************************/
|
||||||
|
|
||||||
#include <mbboDirectRecord.h>
|
#include <mbboDirectRecord.h>
|
||||||
|
#include "alarm.h"
|
||||||
#include "devStream.h"
|
#include "devStream.h"
|
||||||
#include <epicsExport.h>
|
#include <epicsExport.h>
|
||||||
|
|
||||||
@ -67,9 +68,27 @@ static long initRecord (dbCommon *record)
|
|||||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||||
|
|
||||||
mbboD->mask <<= mbboD->shft;
|
mbboD->mask <<= mbboD->shft;
|
||||||
|
|
||||||
|
/* Workaround for bug in mbboDirect record:
|
||||||
|
Put to VAL overwrites value to 0 if SEVR is INVALID_ALARM
|
||||||
|
Thus first write may send a wrong value.
|
||||||
|
*/
|
||||||
|
mbboD->sevr = 0;
|
||||||
return streamInitRecord (record, &mbboD->out, readData, writeData);
|
return streamInitRecord (record, &mbboD->out, readData, writeData);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Unfortunately the bug also corrupts the next write to VAL after an I/O error.
|
||||||
|
Thus make sure the record is never left in INVALID_ALARM status.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static long write(dbCommon *record)
|
||||||
|
{
|
||||||
|
long status = streamWrite(record);
|
||||||
|
if (record->nsev == INVALID_ALARM) record->nsev = MAJOR_ALARM;
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
long number;
|
long number;
|
||||||
DEVSUPFUN report;
|
DEVSUPFUN report;
|
||||||
@ -83,7 +102,7 @@ struct {
|
|||||||
streamInit,
|
streamInit,
|
||||||
initRecord,
|
initRecord,
|
||||||
streamGetIointInfo,
|
streamGetIointInfo,
|
||||||
streamWrite
|
write
|
||||||
};
|
};
|
||||||
|
|
||||||
epicsExportAddress(dset,devmbboDirectStream);
|
epicsExportAddress(dset,devmbboDirectStream);
|
||||||
|
@ -192,6 +192,7 @@ static long writeData (dbCommon *record, format_t *format)
|
|||||||
return ERROR;
|
return ERROR;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case DBF_ULONG:
|
||||||
case DBF_LONG:
|
case DBF_LONG:
|
||||||
case DBF_ENUM:
|
case DBF_ENUM:
|
||||||
{
|
{
|
||||||
|
@ -2,6 +2,7 @@ if (@ARGV[0] == "-3.13") {
|
|||||||
shift;
|
shift;
|
||||||
} else {
|
} else {
|
||||||
print "variable(streamDebug, int)\n";
|
print "variable(streamDebug, int)\n";
|
||||||
|
print "variable(streamError, int)\n";
|
||||||
print "registrar(streamRegistrar)\n";
|
print "registrar(streamRegistrar)\n";
|
||||||
}
|
}
|
||||||
print "driver(stream)\n";
|
print "driver(stream)\n";
|
||||||
|
Reference in New Issue
Block a user