Compare commits
33 Commits
stream_2_7
...
stream_2_7
Author | SHA1 | Date | |
---|---|---|---|
23f3e806e2 | |||
56b6c9a627 | |||
2830f07324 | |||
abd8daafc3 | |||
489e783872 | |||
10d1fa8b02 | |||
c8bffebfc6 | |||
0a90eb3d9c | |||
0936ac7840 | |||
704ece6231 | |||
5c6e98127e | |||
8805437c68 | |||
3acf791409 | |||
0ba674a341 | |||
40c33abac7 | |||
bc67317b0b | |||
06e212c66e | |||
7b314ccffd | |||
1d84986ee8 | |||
2ca8a129f7 | |||
126da8c499 | |||
ea110d5047 | |||
b37fad41c6 | |||
74775996db | |||
74426aab66 | |||
690bbb13d4 | |||
871fbed2b0 | |||
85c68d2ae6 | |||
8870611d4d | |||
67af7fc1bf | |||
93bea174e4 | |||
84fc6aabc8 | |||
38c4f5bcb6 |
@ -1,3 +1,4 @@
|
||||
# Remove this file if not using the PSI build system
|
||||
include /ioc/tools/driver.makefile
|
||||
EXCLUDE_VERSIONS = 3.13.2
|
||||
PROJECT=stream
|
||||
@ -39,20 +40,17 @@ HEADERS += StreamFormatConverter.h
|
||||
HEADERS += StreamBuffer.h
|
||||
HEADERS += StreamError.h
|
||||
|
||||
ifeq (${EPICS_BASETYPE},3.13)
|
||||
# old gcc needs full path for -include
|
||||
CXXFLAGS += -include $(foreach d,${INCLUDES:-I%=%},$(wildcard $d/compat3_13.h))
|
||||
else
|
||||
ifneq (${EPICS_BASETYPE},3.13)
|
||||
RECORDTYPES += calcout
|
||||
endif
|
||||
|
||||
StreamCore.o: streamReferences
|
||||
StreamCore.o StreamCore.d: streamReferences
|
||||
|
||||
streamReferences:
|
||||
perl ../src/makeref.pl Interface $(BUSSES) > $@
|
||||
perl ../src/makeref.pl Converter $(FORMATS) >> $@
|
||||
|
||||
# have to hack a bit to work with both versions of driver.makefile
|
||||
DBDFILES = O.$${EPICSVERSION}_$${T_A}/streamSup.dbd
|
||||
../O.${EPICSVERSION}_${T_A}/streamSup.dbd:
|
||||
export DBDFILES = streamSup.dbd
|
||||
streamSup.dbd:
|
||||
@echo Creating $@
|
||||
perl ../src/makedbd.pl $(RECORDTYPES) > $@
|
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:
|
@ -41,7 +41,7 @@ written or read value.
|
||||
(=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
|
||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||
on 64 bit machines.
|
||||
<p class="new">
|
||||
<p>
|
||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>VAL</code>
|
||||
is directly converted from and to <code>long</code> without going through
|
||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||
@ -84,6 +84,6 @@ written or read value.
|
||||
<a href="calcout.html">calcout</a>
|
||||
<a href="scalcout.html">scalcout</a>
|
||||
</p>
|
||||
<p><small>Dirk Zimoch, 2005</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -40,7 +40,7 @@ written or read value.
|
||||
(=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
|
||||
Using unsigned formats with values ≥ 0x800000 gives different results
|
||||
on 64 bit machines.
|
||||
<p class="new">
|
||||
<p>
|
||||
If <code>LINR=="NO CONVERSION"</code> (the default), <code>OVAL</code>
|
||||
is directly converted to <code>long</code> without going through
|
||||
<code>RVAL</code>. This allows for more bits on 64 bit machines.
|
||||
@ -85,6 +85,6 @@ written or read value.
|
||||
<a href="calcout.html">calcout</a>
|
||||
<a href="scalcout.html">scalcout</a>
|
||||
</p>
|
||||
<p><small>Dirk Zimoch, 2005</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
||||
|
180
doc/formats.html
180
doc/formats.html
@ -28,8 +28,8 @@ A format converter consists of
|
||||
</p>
|
||||
<ul>
|
||||
<li>The <code>%</code> character</li>
|
||||
<li>Optionally a field <span class="new">or record</span> name in <code>()</code></li>
|
||||
<li>Optionally flags out of the characters <code>*# +0-<span class="new">?=</span></code></li>
|
||||
<li>Optionally a field or record name in <code>()</code></li>
|
||||
<li>Optionally flags out of the characters <code>*# +0-?=!</code></li>
|
||||
<li>Optionally an integer <em>width</em> field</li>
|
||||
<li>Optionally a period character (<code>.</code>) followed
|
||||
by an integer <em>precision</em> field (input ony for most formats)</li>
|
||||
@ -40,7 +40,7 @@ A format converter consists of
|
||||
<p>
|
||||
The flags <code>*# +0-</code> work like in the C functions
|
||||
<em>printf()</em> and <em>scanf()</em>.
|
||||
The flags <code>?</code> and <code>=</code> are extensions.
|
||||
The flags <code>?</code>, <code>=</code> and <code>!</code> are extensions.
|
||||
</p>
|
||||
<p>
|
||||
The <code>*</code> flag skips data in input formats.
|
||||
@ -67,16 +67,24 @@ The <code>0</code> flag says that numbers should be left padded with
|
||||
The <code>-</code> flag specifies that output is left justified if
|
||||
<em>width</em> is larger than required.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
The <code>?</code> flag makes failing input conversions succeed with
|
||||
a default zero value (0, 0.0, or "", depending on the format type).
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
The <code>=</code> flag allows to compare input with current values.
|
||||
It is only allowed in input formats.
|
||||
Instead of reading a new value from input, the current value is
|
||||
formatted (like for output) and then compared to the input.
|
||||
</p>
|
||||
<p>
|
||||
The <code>!</code> flag demands that input is exactly <em>width</em>
|
||||
bytes long (normally <em>width</em> defines the maximum number of
|
||||
bytes read in many formats).
|
||||
For example <code>in "%!5d";</code> expects exactly 5 digits.
|
||||
Fewer digits are considered loss of data and make the format fail.
|
||||
This feature has been added by Klemen Vodopivec, SNS.
|
||||
</p>
|
||||
|
||||
<h3>Examples:</h3>
|
||||
<table>
|
||||
@ -143,11 +151,8 @@ field formatted as a string.
|
||||
Use <code>in "%(<i>otherrecord</i>.RVAL)f";</code> to write the floating
|
||||
point input value into the <code>RVAL</code> field of
|
||||
<code><i>otherrecord</i></code>.
|
||||
<span class="new">
|
||||
If no field is given for an other record .VAL is assumed.
|
||||
When a record name conflicts with a field name use .VAL explicitly.
|
||||
</span>
|
||||
|
||||
</p>
|
||||
<p>
|
||||
This feature is very useful when one line of input contains many values that should
|
||||
@ -158,7 +163,7 @@ attribute (see
|
||||
target="ex">Record Reference Manual</a>), the record will be processed.
|
||||
It is your responsibility that the data type of the record field is
|
||||
compatible to the the data type of the converter.
|
||||
<span class="new">STRING formats are compatible with arrays of CHAR or UCHAR.<span>
|
||||
STRING formats are compatible with arrays of CHAR or UCHAR.
|
||||
</p>
|
||||
<p>
|
||||
Note that using this syntax is by far not as efficient as using the
|
||||
@ -192,11 +197,11 @@ With the <code>#</code> flag, output always contains a period character.
|
||||
<p>
|
||||
<b>Input:</b> All these formats are equivalent. Leading whitespaces are skipped.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag additional whitespace between sign and number
|
||||
is accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
When a maximum field width is given, leading whitespace only counts to the
|
||||
field witdth when the space flag is used.
|
||||
</p>
|
||||
@ -213,7 +218,7 @@ field witdth when the space flag is used.
|
||||
<p>
|
||||
With the <code>#</code> flag, octal values are prefixed with <code>0</code>
|
||||
and hexadecimal values with <code>0x</code> or <code>0X</code>.
|
||||
<p class="new">
|
||||
<p>
|
||||
Unlike printf, <code>%x</code> and <code>%X</code> truncate the
|
||||
output to the the given width (number of least significant half bytes).
|
||||
</p>
|
||||
@ -228,14 +233,14 @@ Octal and hexadecimal values can optionally be prefixed.
|
||||
hexadecimal notation.
|
||||
Leading whitespaces are skipped.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>-</code> negative octal and hexadecimal values are accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag additional whitespace between sign and number
|
||||
is accepted.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
When a maximum field width is given, leading whitespace only counts to the
|
||||
field witdth when the space flag is used.
|
||||
</p>
|
||||
@ -253,13 +258,11 @@ and <code>%c</code> matches a sequence of not-null characters.
|
||||
The maximum string length is given by <em>width</em>.
|
||||
The default <em>width</em> is infinite for <code>%s</code> and
|
||||
1 for <code>%c</code>.
|
||||
Leading whitespaces are skipped with <code>%s</code>
|
||||
<span class="new">
|
||||
except when the space flag is used</span>
|
||||
but not with <code>%c</code>.
|
||||
Leading whitespaces are skipped with <code>%s</code> except when
|
||||
the space flag is used but not with <code>%c</code>.
|
||||
The empty string matches.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
With the <code>#</code> flag <code>%s</code> matches a sequence of not-null
|
||||
characters instead of non-whitespace characters.
|
||||
</p>
|
||||
@ -290,18 +293,18 @@ The strings are separated by <code>|</code>.
|
||||
Example: <code>%{OFF|STANDBY|ON}</code> mapps the string <code>OFF</code>
|
||||
to the value 0, <code>STANDBY</code> to 1 and <code>ON</code> to 2.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
When using the <code>#</code> flag it is allowed to assign integer values
|
||||
to the strings using <code>=</code>.
|
||||
Unassigned strings increment their values by 1 as usual.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
If one string is the initial substing of another, the substing must come
|
||||
later to ensure correct matching.
|
||||
In particular if one string is the emptry string, it must be the last one.
|
||||
Use <code>#</code> and <code>=</code> to renumber if necessary.
|
||||
</p>
|
||||
<p class="new">
|
||||
<p>
|
||||
Use the assignment <code>=?</code> for the last string to make it the
|
||||
default value for output formats.
|
||||
</p>
|
||||
@ -310,12 +313,12 @@ Example: <code>%#{neg=-1|stop|pos|fast=10|rewind=-10}</code>.
|
||||
</p>
|
||||
<p>
|
||||
If one of the strings contains <code>|</code> or <code>}</code>
|
||||
<span class="new">(or <code>=</code> if the <code>#</code> flag is used)</span>
|
||||
(or <code>=</code> if the <code>#</code> flag is used)
|
||||
a <code>\</code> must be used to escape the character.
|
||||
</p>
|
||||
<p>
|
||||
<b>Output:</b> Depending on the value, one of the strings is printed,
|
||||
<span class="new">or the default if no value matches</span>.
|
||||
or the default if no value matches.
|
||||
</p>
|
||||
<p>
|
||||
<b>Input:</b> If any of the strings matches, the value is set accordingly.
|
||||
@ -364,9 +367,12 @@ 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>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
|
||||
(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>
|
||||
In input, <em>width</em> bytes are read and put into the value.
|
||||
@ -377,7 +383,7 @@ the value is sign extended or zero extended, depending on the
|
||||
<code>0</code> flag.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>out "%.2r"</code>
|
||||
Examples: <code>out "%.2r"; in "%02r";</code>
|
||||
</p>
|
||||
|
||||
<a name="rawdouble"></a>
|
||||
@ -431,7 +437,7 @@ The <em>width</em> field is the byte number from which to start
|
||||
calculating the checksum.
|
||||
Default is 0, i.e. the first byte of the input or output of the current
|
||||
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
|
||||
from <code>abcdefg</code>,
|
||||
but in <code>"abcdefg%2.1<xor>"</code> only from <code>cdef</code>.
|
||||
@ -485,21 +491,24 @@ In input, the next byte or bytes must match the checksum.
|
||||
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<ccitt8></code></dt>
|
||||
<dd>One byte. The CCITT standard 8 bit crc checksum
|
||||
(poly=0x31, init=0x00, xorout=0x00).</dd>
|
||||
(poly=0x31, init=0x00, xorout=0x00, reflected).</dd>
|
||||
<dt><code>%<crc16></code></dt>
|
||||
<dd>Two bytes. An often used 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<crc16r></code></dt>
|
||||
<dd>Two bytes. An often used reflected 16 bit crc checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000, reflected).</dd>
|
||||
<dt><code>%<modbus></code></dt>
|
||||
<dd>Two bytes. The modbus 16 bit crc checksum
|
||||
(poly=0x8005, init=0xffff, xorout=0x0000, reflected)</dd>
|
||||
<dt><code>%<ccitt16></code></dt>
|
||||
<dd>Two bytes. The usual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
|
||||
href="http://srecord.sourceforge.net/crc16-ccitt.html">wrong?</a>)
|
||||
implementation of the CCITT standard 16 bit crc checksum
|
||||
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
|
||||
<dt><code>%<ccitt16a></code></dt>
|
||||
<dd>Two bytes. The unusual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
|
||||
href="http://srecord.sourceforge.net/crc16-ccitt.html">correct?</a>)
|
||||
implementation of the CCITT standard 16 bit crc checksum with augment.
|
||||
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
|
||||
<dt><code>%<ccitt16x></code> or <code>%<crc16c></code> or <code>%<xmodem></code></dt>
|
||||
@ -510,10 +519,10 @@ In input, the next byte or bytes must match the checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<crc32r></code></dt>
|
||||
<dd>Four bytes. The standard reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF, reflected).</dd>
|
||||
<dt><code>%<jamcrc></code></dt>
|
||||
<dd>Four bytes. Another reflected 32 bit crc checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000, reflected).</dd>
|
||||
<dt><code>%<adler32></code></dt>
|
||||
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
||||
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
||||
@ -522,41 +531,44 @@ In input, the next byte or bytes must match the checksum.
|
||||
</dl>
|
||||
|
||||
<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>
|
||||
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>
|
||||
<div class="box">
|
||||
<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.
|
||||
to compile it like a normal EPICS support module.
|
||||
The Makefile is known to work with EPICS 3.14.8 and PCRE 7.2.
|
||||
In your RELEASE file define the variable <code>PCRE</code> so that
|
||||
it points to the install location of PCRE.
|
||||
</p>
|
||||
<p>
|
||||
If PCRE is already installed on your system, use the variables
|
||||
<code>PCRE_INCLUDE</code> and <code>PCRE_LIB</code> instead to provide
|
||||
the install directories of <code>pcre.h</code> and the library.
|
||||
</p>
|
||||
<p>
|
||||
If you have PCRE installed in different locations for different (cross)
|
||||
architectures, define the variables in RELEASE.Common.<architecture>
|
||||
instead of the global RELEASE file.
|
||||
If PCRE is already installed on (some of) your systems, you may add
|
||||
architectures where PCRE can be found in standard include and library
|
||||
locations to the variable <code>WITH_SYSTEM_PCRE</code>.
|
||||
If either the header file or the library are in a non-standard place,
|
||||
set in your RELEASE file the variables <code>PCRE_INCLUDE_<em>arch</em></code>
|
||||
and/or <code>PCRE_LIB_<em>arch</em></code> for the respective architectures
|
||||
to the correct directories or set
|
||||
<code>PCRE_INCLUDE</code> and/or <code>PCRE_LIB</code>
|
||||
in architecture specific RELEASE.Common.<em>arch</em> files.
|
||||
</p>
|
||||
</div>
|
||||
<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
|
||||
If <em>precision</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> it must be escaped.
|
||||
If the expression contains a <code>/</code> it must be escaped like <code>\/</code>.
|
||||
</p>
|
||||
<p>
|
||||
Example: <code>%.1/<title>(.*)<\/title>/</code> returns
|
||||
@ -564,8 +576,74 @@ the title of an HTML page, skipps anything before the
|
||||
<code><title></code> tag and leaves anything after the
|
||||
<code></title></code> tag in the input buffer.
|
||||
</p>
|
||||
<a name="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> or <code>\0</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.
|
||||
To get a literal <code>&</code> or <code>\</code> or <code>/</code> in the substitution write
|
||||
<code>\&</code> or <code>\\</code> or <code>\/</code>.
|
||||
There is no way to specify literal bytes with values less or equal to 9 in the
|
||||
substitution!
|
||||
</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>
|
||||
<h2>13. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||
<h2>15. MantissaExponent DOUBLE converter (<code>%m</code>)</h2>
|
||||
<p>
|
||||
This exotic and experimental format matches numbers in the format
|
||||
<i>[sign] mantissa sign exponent</i>, e.g <code>+123-4</code> meaning
|
||||
@ -584,9 +662,8 @@ Format flags <code>+</code>, <code>-</code>, and space are supported in
|
||||
the usual way (always sign, left justified, space instead of + sign).
|
||||
Flags <code>#</code> and <code>0</code> are unsupported.
|
||||
</p>
|
||||
<div class="new">
|
||||
<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>
|
||||
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).
|
||||
@ -623,7 +700,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.
|
||||
</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
|
||||
are supported.
|
||||
</p>
|
||||
@ -637,9 +714,8 @@ Because of the complexity of the problem, locales are not supported.
|
||||
Thus, only the English month names can be used (week day names are
|
||||
ignored anyway).
|
||||
</p>
|
||||
</div>
|
||||
<hr>
|
||||
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
|
||||
<p><small>Dirk Zimoch, 2011</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -104,13 +104,8 @@ This marks text you typically type in configuration files etc.
|
||||
<pre>
|
||||
Longer code segments are often set in a box.
|
||||
</pre>
|
||||
<p>
|
||||
Important modifications and new features are
|
||||
<span class="new">highlighted with a yellow background</span>.
|
||||
Obsolete features are <strike class="new">highlighted and crossed out</strike>.
|
||||
</p>
|
||||
<hr>
|
||||
<p align="right"><a href="setup.html">Next: Setup</a></p>
|
||||
<p><small>Dirk Zimoch, 2011</small></p>
|
||||
<p><small>Dirk Zimoch, 2015</small></p>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -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#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#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#timestamp" title="Timestamp DOUBLE converter">%T</a>
|
||||
</div>
|
||||
|
@ -88,6 +88,16 @@ code {
|
||||
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 {
|
||||
position:fixed;
|
||||
left:0;
|
||||
|
@ -562,13 +562,7 @@ connectToAsynPort()
|
||||
clientName(), connected ? "already" : "not yet");
|
||||
if (!connected)
|
||||
{
|
||||
printf ("%s: AsynDriverInterface::connectToAsynPort: "
|
||||
"pasynCommon->connect(%p, %p)\n",
|
||||
clientName(), 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): "
|
||||
"status=%s\n",
|
||||
clientName(), asynStatusStr[status]);
|
||||
@ -718,13 +712,19 @@ writeHandler()
|
||||
pasynOctet->setOutputEos(pvtOctet, pasynUser,
|
||||
NULL, 0);
|
||||
}
|
||||
pasynUser->errorMessage[0] = 0;
|
||||
status = pasynOctet->write(pvtOctet, pasynUser,
|
||||
outputBuffer, outputSize, &written);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::writeHandler(%s): "
|
||||
"write(..., outputSize=%ld, written=%ld) "
|
||||
"[timeout=%g sec] = %s\n",
|
||||
clientName(), (long)outputSize, (long)written,
|
||||
pasynUser->timeout, asynStatusStr[status]);
|
||||
"write(..., \"%s\", outputSize=%ld, written=%ld) "
|
||||
"[timeout=%g sec] = %s (%s)\n",
|
||||
clientName(),
|
||||
StreamBuffer(outputBuffer, outputSize).expand()(),
|
||||
(long)outputSize, (long)written,
|
||||
pasynUser->timeout, asynStatusStr[status],
|
||||
pasynUser->errorMessage);
|
||||
#endif
|
||||
|
||||
if (oldeoslen >= 0) // restore asyn terminator
|
||||
{
|
||||
@ -769,34 +769,34 @@ writeHandler()
|
||||
writeCallback(StreamIoSuccess);
|
||||
return;
|
||||
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);
|
||||
writeCallback(StreamIoTimeout);
|
||||
return;
|
||||
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);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
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);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
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);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
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);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
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);
|
||||
writeCallback(StreamIoFault);
|
||||
return;
|
||||
@ -887,6 +887,11 @@ readHandler()
|
||||
oldeoslen = -1;
|
||||
} else do {
|
||||
// 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,
|
||||
deveos, deveoslen) == asynSuccess)
|
||||
{
|
||||
@ -894,8 +899,9 @@ readHandler()
|
||||
if (ioAction != AsyncRead)
|
||||
{
|
||||
debug("AsynDriverInterface::readHandler(%s) "
|
||||
"input EOS set to %s\n",
|
||||
"input EOS changed from \"%s\" to \"%s\"\n",
|
||||
clientName(),
|
||||
StreamBuffer(oldeos, oldeoslen).expand()(),
|
||||
StreamBuffer(deveos, deveoslen).expand()());
|
||||
}
|
||||
#endif
|
||||
@ -951,6 +957,7 @@ readHandler()
|
||||
readMore = 0;
|
||||
received = 0;
|
||||
eomReason = 0;
|
||||
pasynUser->errorMessage[0] = 0;
|
||||
|
||||
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
|
||||
"read(..., bytesToRead=%ld, ...) "
|
||||
@ -959,12 +966,13 @@ readHandler()
|
||||
bytesToRead, pasynUser->timeout);
|
||||
status = pasynOctet->read(pvtOctet, pasynUser,
|
||||
buffer, bytesToRead, &received, &eomReason);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
|
||||
clientName(), asynStatusStr[status], ioActionStr[ioAction],
|
||||
(long)received,eomReasonStr[eomReason&0x7],
|
||||
StreamBuffer(buffer, received).expand()());
|
||||
|
||||
#endif
|
||||
pasynManager->isConnected(pasynUser, &connected);
|
||||
debug("AsynDriverInterface::readHandler(%s): "
|
||||
"device is now %sconnected\n",
|
||||
@ -1094,29 +1102,29 @@ readHandler()
|
||||
}
|
||||
peeksize = inputBuffer.capacity();
|
||||
// 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);
|
||||
readCallback(StreamIoFault, buffer, received);
|
||||
break;
|
||||
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);
|
||||
readCallback(StreamIoFault, buffer, received);
|
||||
break;
|
||||
#ifdef ASYN_VERSION // asyn >= 4.14
|
||||
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);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
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);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
#endif
|
||||
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);
|
||||
readCallback(StreamIoFault);
|
||||
return;
|
||||
@ -1138,10 +1146,16 @@ readHandler()
|
||||
}
|
||||
|
||||
// restore original EOS
|
||||
if (oldeoslen >= 0)
|
||||
if (oldeoslen >= 0 && oldeoslen != (int)deveoslen && strcmp(deveos, oldeos) != 0)
|
||||
{
|
||||
pasynOctet->setInputEos(pvtOctet, pasynUser,
|
||||
oldeos, oldeoslen);
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("AsynDriverInterface::readHandler(%s) "
|
||||
"input EOS restored to \"%s\"\n",
|
||||
clientName(),
|
||||
StreamBuffer(oldeos, oldeoslen).expand()());
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -22,9 +22,21 @@
|
||||
#include "StreamError.h"
|
||||
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
|
||||
// These systems have no strncasecmp
|
||||
#include <epicsVersion.h>
|
||||
#ifdef BASE_VERSION
|
||||
// 3.13
|
||||
#include <ctype.h>
|
||||
static int strncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int r=0;
|
||||
while (n && (r = toupper(*s1)-toupper(*s2)) == 0) { n--; s1++; s2++; };
|
||||
return r;
|
||||
}
|
||||
#else
|
||||
#include <epicsString.h>
|
||||
#define strncasecmp epicsStrnCaseCmp
|
||||
#endif
|
||||
#endif
|
||||
#include <ctype.h>
|
||||
|
||||
typedef unsigned int (*checksumFunc)(const unsigned char* data, unsigned int len, unsigned int init);
|
||||
@ -138,38 +150,38 @@ static unsigned int crc_0x8005(const unsigned char* data, unsigned int len, unsi
|
||||
{
|
||||
// x^16 + x^15 + x^2 + x^0 (0x8005)
|
||||
const static unsigned short table[256] = {
|
||||
0x0000,0x8005,0x800f,0x000a,0x801b,0x001e,0x0014,0x8011,
|
||||
0x8033,0x0036,0x003c,0x8039,0x0028,0x802d,0x8027,0x0022,
|
||||
0x8063,0x0066,0x006c,0x8069,0x0078,0x807d,0x8077,0x0072,
|
||||
0x0050,0x8055,0x805f,0x005a,0x804b,0x004e,0x0044,0x8041,
|
||||
0x80c3,0x00c6,0x00cc,0x80c9,0x00d8,0x80dd,0x80d7,0x00d2,
|
||||
0x00f0,0x80f5,0x80ff,0x00fa,0x80eb,0x00ee,0x00e4,0x80e1,
|
||||
0x00a0,0x80a5,0x80af,0x00aa,0x80bb,0x00be,0x00b4,0x80b1,
|
||||
0x8093,0x0096,0x009c,0x8099,0x0088,0x808d,0x8087,0x0082,
|
||||
0x8183,0x0186,0x018c,0x8189,0x0198,0x819d,0x8197,0x0192,
|
||||
0x01b0,0x81b5,0x81bf,0x01ba,0x81ab,0x01ae,0x01a4,0x81a1,
|
||||
0x01e0,0x81e5,0x81ef,0x01ea,0x81fb,0x01fe,0x01f4,0x81f1,
|
||||
0x81d3,0x01d6,0x01dc,0x81d9,0x01c8,0x81cd,0x81c7,0x01c2,
|
||||
0x0140,0x8145,0x814f,0x014a,0x815b,0x015e,0x0154,0x8151,
|
||||
0x8173,0x0176,0x017c,0x8179,0x0168,0x816d,0x8167,0x0162,
|
||||
0x8123,0x0126,0x012c,0x8129,0x0138,0x813d,0x8137,0x0132,
|
||||
0x0110,0x8115,0x811f,0x011a,0x810b,0x010e,0x0104,0x8101,
|
||||
0x8303,0x0306,0x030c,0x8309,0x0318,0x831d,0x8317,0x0312,
|
||||
0x0330,0x8335,0x833f,0x033a,0x832b,0x032e,0x0324,0x8321,
|
||||
0x0360,0x8365,0x836f,0x036a,0x837b,0x037e,0x0374,0x8371,
|
||||
0x8353,0x0356,0x035c,0x8359,0x0348,0x834d,0x8347,0x0342,
|
||||
0x03c0,0x83c5,0x83cf,0x03ca,0x83db,0x03de,0x03d4,0x83d1,
|
||||
0x83f3,0x03f6,0x03fc,0x83f9,0x03e8,0x83ed,0x83e7,0x03e2,
|
||||
0x83a3,0x03a6,0x03ac,0x83a9,0x03b8,0x83bd,0x83b7,0x03b2,
|
||||
0x0390,0x8395,0x839f,0x039a,0x838b,0x038e,0x0384,0x8381,
|
||||
0x0280,0x8285,0x828f,0x028a,0x829b,0x029e,0x0294,0x8291,
|
||||
0x82b3,0x02b6,0x02bc,0x82b9,0x02a8,0x82ad,0x82a7,0x02a2,
|
||||
0x82e3,0x02e6,0x02ec,0x82e9,0x02f8,0x82fd,0x82f7,0x02f2,
|
||||
0x02d0,0x82d5,0x82df,0x02da,0x82cb,0x02ce,0x02c4,0x82c1,
|
||||
0x8243,0x0246,0x024c,0x8249,0x0258,0x825d,0x8257,0x0252,
|
||||
0x0270,0x8275,0x827f,0x027a,0x826b,0x026e,0x0264,0x8261,
|
||||
0x0220,0x8225,0x822f,0x022a,0x823b,0x023e,0x0234,0x8231,
|
||||
0x8213,0x0216,0x021c,0x8219,0x0208,0x820d,0x8207,0x0202 };
|
||||
0x0000, 0x8005, 0x800f, 0x000a, 0x801b, 0x001e, 0x0014, 0x8011,
|
||||
0x8033, 0x0036, 0x003c, 0x8039, 0x0028, 0x802d, 0x8027, 0x0022,
|
||||
0x8063, 0x0066, 0x006c, 0x8069, 0x0078, 0x807d, 0x8077, 0x0072,
|
||||
0x0050, 0x8055, 0x805f, 0x005a, 0x804b, 0x004e, 0x0044, 0x8041,
|
||||
0x80c3, 0x00c6, 0x00cc, 0x80c9, 0x00d8, 0x80dd, 0x80d7, 0x00d2,
|
||||
0x00f0, 0x80f5, 0x80ff, 0x00fa, 0x80eb, 0x00ee, 0x00e4, 0x80e1,
|
||||
0x00a0, 0x80a5, 0x80af, 0x00aa, 0x80bb, 0x00be, 0x00b4, 0x80b1,
|
||||
0x8093, 0x0096, 0x009c, 0x8099, 0x0088, 0x808d, 0x8087, 0x0082,
|
||||
0x8183, 0x0186, 0x018c, 0x8189, 0x0198, 0x819d, 0x8197, 0x0192,
|
||||
0x01b0, 0x81b5, 0x81bf, 0x01ba, 0x81ab, 0x01ae, 0x01a4, 0x81a1,
|
||||
0x01e0, 0x81e5, 0x81ef, 0x01ea, 0x81fb, 0x01fe, 0x01f4, 0x81f1,
|
||||
0x81d3, 0x01d6, 0x01dc, 0x81d9, 0x01c8, 0x81cd, 0x81c7, 0x01c2,
|
||||
0x0140, 0x8145, 0x814f, 0x014a, 0x815b, 0x015e, 0x0154, 0x8151,
|
||||
0x8173, 0x0176, 0x017c, 0x8179, 0x0168, 0x816d, 0x8167, 0x0162,
|
||||
0x8123, 0x0126, 0x012c, 0x8129, 0x0138, 0x813d, 0x8137, 0x0132,
|
||||
0x0110, 0x8115, 0x811f, 0x011a, 0x810b, 0x010e, 0x0104, 0x8101,
|
||||
0x8303, 0x0306, 0x030c, 0x8309, 0x0318, 0x831d, 0x8317, 0x0312,
|
||||
0x0330, 0x8335, 0x833f, 0x033a, 0x832b, 0x032e, 0x0324, 0x8321,
|
||||
0x0360, 0x8365, 0x836f, 0x036a, 0x837b, 0x037e, 0x0374, 0x8371,
|
||||
0x8353, 0x0356, 0x035c, 0x8359, 0x0348, 0x834d, 0x8347, 0x0342,
|
||||
0x03c0, 0x83c5, 0x83cf, 0x03ca, 0x83db, 0x03de, 0x03d4, 0x83d1,
|
||||
0x83f3, 0x03f6, 0x03fc, 0x83f9, 0x03e8, 0x83ed, 0x83e7, 0x03e2,
|
||||
0x83a3, 0x03a6, 0x03ac, 0x83a9, 0x03b8, 0x83bd, 0x83b7, 0x03b2,
|
||||
0x0390, 0x8395, 0x839f, 0x039a, 0x838b, 0x038e, 0x0384, 0x8381,
|
||||
0x0280, 0x8285, 0x828f, 0x028a, 0x829b, 0x029e, 0x0294, 0x8291,
|
||||
0x82b3, 0x02b6, 0x02bc, 0x82b9, 0x02a8, 0x82ad, 0x82a7, 0x02a2,
|
||||
0x82e3, 0x02e6, 0x02ec, 0x82e9, 0x02f8, 0x82fd, 0x82f7, 0x02f2,
|
||||
0x02d0, 0x82d5, 0x82df, 0x02da, 0x82cb, 0x02ce, 0x02c4, 0x82c1,
|
||||
0x8243, 0x0246, 0x024c, 0x8249, 0x0258, 0x825d, 0x8257, 0x0252,
|
||||
0x0270, 0x8275, 0x827f, 0x027a, 0x826b, 0x026e, 0x0264, 0x8261,
|
||||
0x0220, 0x8225, 0x822f, 0x022a, 0x823b, 0x023e, 0x0234, 0x8231,
|
||||
0x8213, 0x0216, 0x021c, 0x8219, 0x0208, 0x820d, 0x8207, 0x0202 };
|
||||
|
||||
while (len--) crc = table[((crc>>8) ^ *data++) & 0xFF] ^ (crc << 8);
|
||||
return crc;
|
||||
@ -180,38 +192,38 @@ static unsigned int crc_0x8005_r(const unsigned char* data, unsigned int len, un
|
||||
// x^16 + x^15 + x^2 + x^0 (0x8005)
|
||||
// reflected
|
||||
const static unsigned short table[256] = {
|
||||
0x0000,0xC0C1,0xC181,0x0140,0xC301,0x03C0,0x0280,0xC241,
|
||||
0xC601,0x06C0,0x0780,0xC741,0x0500,0xC5C1,0xC481,0x0440,
|
||||
0xCC01,0x0CC0,0x0D80,0xCD41,0x0F00,0xCFC1,0xCE81,0x0E40,
|
||||
0x0A00,0xCAC1,0xCB81,0x0B40,0xC901,0x09C0,0x0880,0xC841,
|
||||
0xD801,0x18C0,0x1980,0xD941,0x1B00,0xDBC1,0xDA81,0x1A40,
|
||||
0x1E00,0xDEC1,0xDF81,0x1F40,0xDD01,0x1DC0,0x1C80,0xDC41,
|
||||
0x1400,0xD4C1,0xD581,0x1540,0xD701,0x17C0,0x1680,0xD641,
|
||||
0xD201,0x12C0,0x1380,0xD341,0x1100,0xD1C1,0xD081,0x1040,
|
||||
0xF001,0x30C0,0x3180,0xF141,0x3300,0xF3C1,0xF281,0x3240,
|
||||
0x3600,0xF6C1,0xF781,0x3740,0xF501,0x35C0,0x3480,0xF441,
|
||||
0x3C00,0xFCC1,0xFD81,0x3D40,0xFF01,0x3FC0,0x3E80,0xFE41,
|
||||
0xFA01,0x3AC0,0x3B80,0xFB41,0x3900,0xF9C1,0xF881,0x3840,
|
||||
0x2800,0xE8C1,0xE981,0x2940,0xEB01,0x2BC0,0x2A80,0xEA41,
|
||||
0xEE01,0x2EC0,0x2F80,0xEF41,0x2D00,0xEDC1,0xEC81,0x2C40,
|
||||
0xE401,0x24C0,0x2580,0xE541,0x2700,0xE7C1,0xE681,0x2640,
|
||||
0x2200,0xE2C1,0xE381,0x2340,0xE101,0x21C0,0x2080,0xE041,
|
||||
0xA001,0x60C0,0x6180,0xA141,0x6300,0xA3C1,0xA281,0x6240,
|
||||
0x6600,0xA6C1,0xA781,0x6740,0xA501,0x65C0,0x6480,0xA441,
|
||||
0x6C00,0xACC1,0xAD81,0x6D40,0xAF01,0x6FC0,0x6E80,0xAE41,
|
||||
0xAA01,0x6AC0,0x6B80,0xAB41,0x6900,0xA9C1,0xA881,0x6840,
|
||||
0x7800,0xB8C1,0xB981,0x7940,0xBB01,0x7BC0,0x7A80,0xBA41,
|
||||
0xBE01,0x7EC0,0x7F80,0xBF41,0x7D00,0xBDC1,0xBC81,0x7C40,
|
||||
0xB401,0x74C0,0x7580,0xB541,0x7700,0xB7C1,0xB681,0x7640,
|
||||
0x7200,0xB2C1,0xB381,0x7340,0xB101,0x71C0,0x7080,0xB041,
|
||||
0x5000,0x90C1,0x9181,0x5140,0x9301,0x53C0,0x5280,0x9241,
|
||||
0x9601,0x56C0,0x5780,0x9741,0x5500,0x95C1,0x9481,0x5440,
|
||||
0x9C01,0x5CC0,0x5D80,0x9D41,0x5F00,0x9FC1,0x9E81,0x5E40,
|
||||
0x5A00,0x9AC1,0x9B81,0x5B40,0x9901,0x59C0,0x5880,0x9841,
|
||||
0x8801,0x48C0,0x4980,0x8941,0x4B00,0x8BC1,0x8A81,0x4A40,
|
||||
0x4E00,0x8EC1,0x8F81,0x4F40,0x8D01,0x4DC0,0x4C80,0x8C41,
|
||||
0x4400,0x84C1,0x8581,0x4540,0x8701,0x47C0,0x4680,0x8641,
|
||||
0x8201,0x42C0,0x4380,0x8341,0x4100,0x81C1,0x8081,0x4040 };
|
||||
0x0000, 0xC0C1, 0xC181, 0x0140, 0xC301, 0x03C0, 0x0280, 0xC241,
|
||||
0xC601, 0x06C0, 0x0780, 0xC741, 0x0500, 0xC5C1, 0xC481, 0x0440,
|
||||
0xCC01, 0x0CC0, 0x0D80, 0xCD41, 0x0F00, 0xCFC1, 0xCE81, 0x0E40,
|
||||
0x0A00, 0xCAC1, 0xCB81, 0x0B40, 0xC901, 0x09C0, 0x0880, 0xC841,
|
||||
0xD801, 0x18C0, 0x1980, 0xD941, 0x1B00, 0xDBC1, 0xDA81, 0x1A40,
|
||||
0x1E00, 0xDEC1, 0xDF81, 0x1F40, 0xDD01, 0x1DC0, 0x1C80, 0xDC41,
|
||||
0x1400, 0xD4C1, 0xD581, 0x1540, 0xD701, 0x17C0, 0x1680, 0xD641,
|
||||
0xD201, 0x12C0, 0x1380, 0xD341, 0x1100, 0xD1C1, 0xD081, 0x1040,
|
||||
0xF001, 0x30C0, 0x3180, 0xF141, 0x3300, 0xF3C1, 0xF281, 0x3240,
|
||||
0x3600, 0xF6C1, 0xF781, 0x3740, 0xF501, 0x35C0, 0x3480, 0xF441,
|
||||
0x3C00, 0xFCC1, 0xFD81, 0x3D40, 0xFF01, 0x3FC0, 0x3E80, 0xFE41,
|
||||
0xFA01, 0x3AC0, 0x3B80, 0xFB41, 0x3900, 0xF9C1, 0xF881, 0x3840,
|
||||
0x2800, 0xE8C1, 0xE981, 0x2940, 0xEB01, 0x2BC0, 0x2A80, 0xEA41,
|
||||
0xEE01, 0x2EC0, 0x2F80, 0xEF41, 0x2D00, 0xEDC1, 0xEC81, 0x2C40,
|
||||
0xE401, 0x24C0, 0x2580, 0xE541, 0x2700, 0xE7C1, 0xE681, 0x2640,
|
||||
0x2200, 0xE2C1, 0xE381, 0x2340, 0xE101, 0x21C0, 0x2080, 0xE041,
|
||||
0xA001, 0x60C0, 0x6180, 0xA141, 0x6300, 0xA3C1, 0xA281, 0x6240,
|
||||
0x6600, 0xA6C1, 0xA781, 0x6740, 0xA501, 0x65C0, 0x6480, 0xA441,
|
||||
0x6C00, 0xACC1, 0xAD81, 0x6D40, 0xAF01, 0x6FC0, 0x6E80, 0xAE41,
|
||||
0xAA01, 0x6AC0, 0x6B80, 0xAB41, 0x6900, 0xA9C1, 0xA881, 0x6840,
|
||||
0x7800, 0xB8C1, 0xB981, 0x7940, 0xBB01, 0x7BC0, 0x7A80, 0xBA41,
|
||||
0xBE01, 0x7EC0, 0x7F80, 0xBF41, 0x7D00, 0xBDC1, 0xBC81, 0x7C40,
|
||||
0xB401, 0x74C0, 0x7580, 0xB541, 0x7700, 0xB7C1, 0xB681, 0x7640,
|
||||
0x7200, 0xB2C1, 0xB381, 0x7340, 0xB101, 0x71C0, 0x7080, 0xB041,
|
||||
0x5000, 0x90C1, 0x9181, 0x5140, 0x9301, 0x53C0, 0x5280, 0x9241,
|
||||
0x9601, 0x56C0, 0x5780, 0x9741, 0x5500, 0x95C1, 0x9481, 0x5440,
|
||||
0x9C01, 0x5CC0, 0x5D80, 0x9D41, 0x5F00, 0x9FC1, 0x9E81, 0x5E40,
|
||||
0x5A00, 0x9AC1, 0x9B81, 0x5B40, 0x9901, 0x59C0, 0x5880, 0x9841,
|
||||
0x8801, 0x48C0, 0x4980, 0x8941, 0x4B00, 0x8BC1, 0x8A81, 0x4A40,
|
||||
0x4E00, 0x8EC1, 0x8F81, 0x4F40, 0x8D01, 0x4DC0, 0x4C80, 0x8C41,
|
||||
0x4400, 0x84C1, 0x8581, 0x4540, 0x8701, 0x47C0, 0x4680, 0x8641,
|
||||
0x8201, 0x42C0, 0x4380, 0x8341, 0x4100, 0x81C1, 0x8081, 0x4040 };
|
||||
|
||||
while (len--) crc = table[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
|
||||
return crc;
|
||||
@ -469,6 +481,7 @@ static checksum checksumMap[] =
|
||||
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
|
||||
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
|
||||
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
|
||||
{"modbus", crc_0x8005_r, 0xFFFF, 0x0000, 2}, // 0x4B37
|
||||
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
|
||||
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
|
||||
{"ccitt16x",crc_0x1021, 0x0000, 0x0000, 2}, // 0x31C3
|
||||
|
@ -23,7 +23,7 @@
|
||||
#include "string.h"
|
||||
#include "pcre.h"
|
||||
|
||||
// Perl regular expressions (PCRE) %/regexp/
|
||||
// Perl regular expressions (PCRE) %/regexp/ and %#/regexp/subst/
|
||||
|
||||
/* Notes:
|
||||
- Memory for compiled regexp is allocated in parse but never freed.
|
||||
@ -37,15 +37,17 @@
|
||||
|
||||
class RegexpConverter : public StreamFormatConverter
|
||||
{
|
||||
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
|
||||
int scanString(const StreamFormat&, const char*, char*, size_t);
|
||||
int parse (const StreamFormat& fmt, StreamBuffer&, const char*&, bool);
|
||||
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::
|
||||
parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
const char*& source, bool scanFormat)
|
||||
{
|
||||
if (!scanFormat)
|
||||
if (!scanFormat && !(fmt.flags & alt_flag))
|
||||
{
|
||||
error("Format conversion %%/regexp/ is only allowed in input formats\n");
|
||||
return false;
|
||||
@ -55,28 +57,31 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
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");
|
||||
error("Missing closing '/' after %%/%s format conversion\n", pattern());
|
||||
return false;
|
||||
}
|
||||
if (*source == esc) {
|
||||
source++;
|
||||
pattern.print("\\x%02x", *source++ & 0xFF);
|
||||
continue;
|
||||
if (*source == esc) { // handle escaped chars
|
||||
if (*++source != '/') // just un-escape /
|
||||
{
|
||||
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++);
|
||||
}
|
||||
source++;
|
||||
debug("regexp = \"%s\"\n", pattern());
|
||||
debug("regexp = \"%s\"\n", pattern.expand()());
|
||||
|
||||
const char* errormsg;
|
||||
int eoffset;
|
||||
pcre* code = pcre_compile(pattern(), 0,
|
||||
@ -87,6 +92,26 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
|
||||
return false;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
@ -94,32 +119,130 @@ 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;
|
||||
unsigned int l;
|
||||
|
||||
memcpy (&code, fmt.info, sizeof(code));
|
||||
const char* info = fmt.info;
|
||||
pcre* code = extract<pcre*>(info);
|
||||
int length = fmt.width > 0 ? fmt.width : strlen(input);
|
||||
int subexpr = fmt.prec > 0 ? fmt.prec : 0;
|
||||
|
||||
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) {
|
||||
debug("input = \"%s\"\n", input);
|
||||
debug("length=%d\n", length);
|
||||
|
||||
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)) {
|
||||
error("Regexp: Matching string \"%s\" too long (%d>%d bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
|
||||
(int)len, (int)maxlen-1);
|
||||
error("Regexp: Matching string \"%s\" too long (%d>%ld bytes). You may want to try the + flag: \"%%+/.../\"\n",
|
||||
StreamBuffer(input + ovector[subexpr*2],l).expand()(),
|
||||
l, (long)maxlen-1);
|
||||
return -1;
|
||||
}
|
||||
len = maxlen-1;
|
||||
l = maxlen-1;
|
||||
}
|
||||
memcpy(value, input+ovector[subexpr*2], len);
|
||||
value[len]=0;
|
||||
return ovector[1];
|
||||
memcpy(value, input + ovector[subexpr*2], l);
|
||||
value[l] = '\0';
|
||||
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 & sign_flag)
|
||||
start = buffer.length() - length;
|
||||
|
||||
debug("regsubst buffer=\"%s\", start=%ld, length=%ld, subst = \"%s\"\n",
|
||||
buffer.expand()(), start, length, subst);
|
||||
|
||||
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", (int)length-c, buffer(start+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("start = \"%s\"\n", buffer(start+c));
|
||||
debug("match = \"%.*s\"\n", l, buffer(start+c+ovector[0]));
|
||||
for (r = 1; r < rc; r++)
|
||||
debug("sub%d = \"%.*s\"\n", r, ovector[r*2+1]-ovector[r*2], buffer(start+c+ovector[r*2]));
|
||||
debug("rest = \"%s\"\n", buffer(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 (ch < 9) // escaped 0 - 9 : replace with subexpr
|
||||
{
|
||||
ch *= 2;
|
||||
rl = ovector[ch+1] - ovector[ch];
|
||||
debug("replace \\%d: \"%.*s\"\n", ch/2, rl, buffer(start+c+ovector[ch]));
|
||||
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", l, buffer(start+c+ovector[0]));
|
||||
s.replace(r, 1, buffer(start+c+ovector[0]), l);
|
||||
r += l - 1;
|
||||
}
|
||||
else continue;
|
||||
debug("subs = \"%s\"\n", s());
|
||||
}
|
||||
buffer.replace(start+c+ovector[0], l, s);
|
||||
length += s.length() - l;
|
||||
c += 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, "/");
|
||||
|
@ -296,7 +296,7 @@ StreamBuffer StreamBuffer::expand(ssize_t start, ssize_t length) const
|
||||
for (i = start; i < end; i++)
|
||||
{
|
||||
c = buffer[i];
|
||||
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
|
||||
if (c < 0x20 || c >= 0x7f)
|
||||
{
|
||||
result.print("<%02x>", c & 0xff);
|
||||
}
|
||||
|
@ -460,7 +460,7 @@ finishProtocol(ProtocolResult status)
|
||||
status = Fault;
|
||||
}
|
||||
//// flags &= ~(AcceptInput|AcceptEvent);
|
||||
if (runningHandler)
|
||||
if (runningHandler || flags & InitRun)
|
||||
{
|
||||
// get original error status
|
||||
if (status == Success) status = runningHandler;
|
||||
@ -832,7 +832,7 @@ lockCallback(StreamIoStatus status)
|
||||
case StreamIoSuccess:
|
||||
break;
|
||||
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);
|
||||
flags &= ~BusOwner;
|
||||
finishProtocol(LockTimeout);
|
||||
@ -991,7 +991,7 @@ readCallback(StreamIoStatus status,
|
||||
evalIn();
|
||||
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);
|
||||
inputBuffer.clear();
|
||||
finishProtocol(ReplyTimeout);
|
||||
@ -1479,6 +1479,7 @@ scanValue(const StreamFormat& fmt, long& value)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %li\n",
|
||||
name(), value);
|
||||
@ -1510,6 +1511,7 @@ scanValue(const StreamFormat& fmt, double& value)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && (consumed != (fmt.width + fmt.prec + 1))) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
debug("StreamCore::scanValue(%s) scanned %#g\n",
|
||||
name(), value);
|
||||
@ -1542,6 +1544,7 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
|
||||
}
|
||||
else return -1;
|
||||
}
|
||||
if (fmt.flags & fix_width_flag && consumed != fmt.width) return -1;
|
||||
if (consumed > inputLine.length()-consumedInput) return -1;
|
||||
#ifndef NO_TEMPORARY
|
||||
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
|
||||
|
@ -115,7 +115,6 @@ class Stream : protected StreamCore
|
||||
epicsMutex mutex;
|
||||
epicsEvent initDone;
|
||||
#endif
|
||||
StreamBuffer fieldBuffer;
|
||||
int status;
|
||||
int convert;
|
||||
long currentValueLength;
|
||||
@ -181,6 +180,7 @@ public:
|
||||
#ifndef EPICS_3_13
|
||||
extern "C" {
|
||||
epicsExportAddress(int, streamDebug);
|
||||
epicsExportAddress(int, streamError);
|
||||
}
|
||||
#endif
|
||||
|
||||
@ -604,7 +604,7 @@ parseLink(const struct link *ioLink, char* filename,
|
||||
if (items <= 0)
|
||||
{
|
||||
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());
|
||||
return S_dev_badInitRet;
|
||||
}
|
||||
@ -990,6 +990,7 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to get value
|
||||
// from field of this or other record.
|
||||
StreamBuffer fieldBuffer;
|
||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||
|
||||
/* Handle time stamps special. %T converter takes double. */
|
||||
@ -1134,6 +1135,7 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
|
||||
{
|
||||
// Format like "%([record.]field)..." has requested to put value
|
||||
// to field of this or other record.
|
||||
StreamBuffer fieldBuffer;
|
||||
DBADDR* pdbaddr = (DBADDR*)fieldaddress;
|
||||
long nord;
|
||||
long nelem = pdbaddr->no_elements;
|
||||
|
@ -23,6 +23,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
int streamDebug = 0;
|
||||
int streamError = 0;
|
||||
extern "C" {
|
||||
#ifdef _WIN32
|
||||
__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)
|
||||
{
|
||||
char timestamp[40];
|
||||
if (!streamError) return; // Error logging disabled
|
||||
StreamPrintTimestampFunction(timestamp, 40);
|
||||
#ifdef va_copy
|
||||
if (StreamDebugFile)
|
||||
|
@ -28,6 +28,7 @@
|
||||
#endif
|
||||
|
||||
extern int streamDebug;
|
||||
extern int streamError;
|
||||
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
|
||||
|
||||
void StreamError(int line, const char* file, const char* fmt, ...)
|
||||
|
@ -30,7 +30,8 @@ typedef enum {
|
||||
zero_flag = 0x10,
|
||||
skip_flag = 0x20,
|
||||
default_flag = 0x40,
|
||||
compare_flag = 0x80
|
||||
compare_flag = 0x80,
|
||||
fix_width_flag = 0x100,
|
||||
} StreamFormatFlag;
|
||||
|
||||
typedef enum {
|
||||
@ -48,7 +49,7 @@ typedef struct StreamFormat
|
||||
{
|
||||
char conv;
|
||||
StreamFormatType type;
|
||||
unsigned char flags;
|
||||
unsigned short flags;
|
||||
short prec;
|
||||
unsigned short width;
|
||||
unsigned short infolen;
|
||||
|
@ -38,7 +38,7 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
||||
{
|
||||
/*
|
||||
source := [flags] [width] ['.' prec] conv [extra]
|
||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '='
|
||||
flags := '-' | '+' | ' ' | '#' | '0' | '*' | '?' | '=' | '!'
|
||||
width := integer
|
||||
prec := integer
|
||||
conv := character
|
||||
@ -85,6 +85,15 @@ parseFormat(const char*& source, FormatType formatType, StreamFormat& streamForm
|
||||
}
|
||||
streamFormat.flags |= default_flag;
|
||||
break;
|
||||
case '!':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
error("Use of fixed width modifier '!' "
|
||||
"only allowed in input formats\n");
|
||||
return false;
|
||||
}
|
||||
streamFormat.flags |= fix_width_flag;
|
||||
break;
|
||||
case '=':
|
||||
if (formatType != ScanFormat)
|
||||
{
|
||||
|
@ -22,8 +22,8 @@
|
||||
#define devStream_h
|
||||
|
||||
#define STREAM_MAJOR 2
|
||||
#define STREAM_MINOR 6
|
||||
#define STREAM_PATCHLEVEL 6
|
||||
#define STREAM_MINOR 7
|
||||
#define STREAM_PATCHLEVEL 10
|
||||
|
||||
#if defined(__vxworks) || defined(vxWorks)
|
||||
#include <vxWorks.h>
|
||||
@ -58,7 +58,6 @@ extern "C" {
|
||||
#include <dbCommon.h>
|
||||
#include <dbScan.h>
|
||||
#include <devSup.h>
|
||||
/* #include <dbFldTypes.h> */
|
||||
#include <dbAccess.h>
|
||||
|
||||
#ifdef devStream_epicsExportSharedSymbols
|
||||
|
@ -55,11 +55,14 @@ static long readData (dbCommon *record, format_t *format)
|
||||
ai->rval = rval;
|
||||
if (ai->linr == menuConvertNO_CONVERSION)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
/* allow integers with more than 32 bits */
|
||||
double val;
|
||||
if (format->type == DBF_ULONG)
|
||||
ai->val = (unsigned long)rval;
|
||||
val = (unsigned long)rval;
|
||||
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 OK;
|
||||
|
@ -46,11 +46,14 @@ static long readData (dbCommon *record, format_t *format)
|
||||
ao->rval = rval;
|
||||
if (ao->linr == menuConvertNO_CONVERSION)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
/* allow integers with more than 32 bits */
|
||||
double val;
|
||||
if (format->type == DBF_ULONG)
|
||||
ao->val = (unsigned long)rval;
|
||||
val = (unsigned long)rval;
|
||||
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 OK;
|
||||
@ -63,20 +66,21 @@ static long writeData (dbCommon *record, format_t *format)
|
||||
{
|
||||
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)
|
||||
{
|
||||
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);
|
||||
}
|
||||
case DBF_ULONG:
|
||||
{
|
||||
if (ao->linr == menuConvertNO_CONVERSION)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
return streamPrintf (record, format, (unsigned long)(INIT_RUN ? ao->val : ao->oval));
|
||||
/* allow integers with more than 32 bits */
|
||||
return streamPrintf (record, format, (unsigned long)val);
|
||||
}
|
||||
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)
|
||||
{
|
||||
/* allow more bits than 32 */
|
||||
return streamPrintf (record, format, (long)(INIT_RUN ? ao->val : ao->oval));
|
||||
/* allow integers with more than 32 bits */
|
||||
return streamPrintf (record, format, (long)val);
|
||||
}
|
||||
return streamPrintf (record, format, (long)ao->rval);
|
||||
}
|
||||
|
@ -20,6 +20,7 @@
|
||||
***************************************************************/
|
||||
|
||||
#include <mbboDirectRecord.h>
|
||||
#include "alarm.h"
|
||||
#include "devStream.h"
|
||||
#include <epicsExport.h>
|
||||
|
||||
@ -67,9 +68,27 @@ static long initRecord (dbCommon *record)
|
||||
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
/* 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 {
|
||||
long number;
|
||||
DEVSUPFUN report;
|
||||
@ -83,7 +102,7 @@ struct {
|
||||
streamInit,
|
||||
initRecord,
|
||||
streamGetIointInfo,
|
||||
streamWrite
|
||||
write
|
||||
};
|
||||
|
||||
epicsExportAddress(dset,devmbboDirectStream);
|
||||
|
@ -2,6 +2,7 @@ if (@ARGV[0] == "-3.13") {
|
||||
shift;
|
||||
} else {
|
||||
print "variable(streamDebug, int)\n";
|
||||
print "variable(streamError, int)\n";
|
||||
print "registrar(streamRegistrar)\n";
|
||||
}
|
||||
print "driver(stream)\n";
|
||||
|
Reference in New Issue
Block a user