version 2.2

This commit is contained in:
zimoch
2007-07-31 09:19:37 +00:00
commit 90a6fd4af9
91 changed files with 16846 additions and 0 deletions

44
Makefile Normal file
View File

@ -0,0 +1,44 @@
include /ioc/tools/driver.makefile
#EXCLUDE_VERSIONS = 3.13.2
EXCLUDE_VERSIONS = 3.13
PRIJECT=stream
DOCUDIR = doc
SOURCES += $(wildcard src/*.c)
SOURCES += $(wildcard src/*.cc)
DBDS = stream.dbd
BUSSES += AsynDriver
FORMATS += Enum
FORMATS += BCD
FORMATS += Raw
FORMATS += Binary
FORMATS += Checksum
RECORDTYPES += aai aao
RECORDTYPES += ao ai
RECORDTYPES += bo bi
RECORDTYPES += mbbo mbbi
RECORDTYPES += mbboDirect mbbiDirect
RECORDTYPES += longout longin
RECORDTYPES += stringout stringin
RECORDTYPES += waveform
RECORDTYPES += calcout
StreamCore.o: streamReferences
streamReferences:
@for i in $(BUSSES); \
do echo "extern void* ref_$${i}Interface;"; \
echo "void* p$$i = ref_$${i}Interface;"; \
done > $@
@for i in $(FORMATS); \
do echo "extern void* ref_$${i}Converter;"; \
echo "void* p$$i = ref_$${i}Converter;"; \
done >> $@
stream.dbd:
@for r in $(RECORDTYPES); \
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
done > $@
@echo "driver(stream)" >> $@

BIN
doc/EPICS.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 453 B

BIN
doc/PSI.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

BIN
doc/SLS.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

128
doc/aai.html Normal file
View File

@ -0,0 +1,128 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: aai Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: aai Records</h1>
<p>
<b>Note:</b> aai record support is disabled per default.
Enable it in <code>src/CONFIG_STREAM</code>.
</p>
<h2>Normal Operation</h2>
<p>
With aai records, the format converter is applied to
each element. Between the elements, a separator is printed
or expected as specified by the <code>Separator</code>
<a href="protocol.html#sysvar">variable</a> in the protocol.
When parsing input, a space as the first character of the
<code>Separator</code> matches any number of any whitespace
characters.
</p>
<p>
During input, a maximum of <code>NELM</code> elements is
read and <code>NORD</code> is updated accordingly.
Parsing of elements stops when the separator does not match,
conversion fails, or the end of the input is reached.
A minimum of one element must be available.
</p>
<p>
During output, the first <code>NORD</code> elements are
written.
</p>
<p>
The format data type must be convertible to or from the type
specified in the <code>FTVL</code> field.
The variable <code><i>x[i]</i></code> stands for one element of
the written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u><code><i>x[i]</i>=double(VAL[i])</code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i]</i>)</code><br>
<code>FTVL</code> must be <code>"FLOAT"</code> or <code>"DOUBLE"</code>
</dd>
<dt>LONG or ENUM format (e.g. <code>%i</code> or <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=long(VAL[i])</code><br>
<code>FTVL</code> can be
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
Signed values are sign-extended to long, unsigned values are
zero-extended to long before converting them.<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i])</i></code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
The value is truncated to the least significant bytes if
<code>FTVL</code> has a smaller data size than <code>long</code>.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<dl>
<dt>If <code>FTVL=="STRING"</code>:</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=VAL[i]</code><br>
<u>Input:</u> <code>VAL[i]=<i>x[i]</i></code><br>
Note that this is an array of strings, not an array of characters.
</dd>
<dt>If <code>FTVL=="CHAR"</code> or <code>FTVL="UCHAR"</code>:</dt>
<dd>
In this case, the complete aai is treated as a large
single string of size <code>NORD</code>.
No separators are printed or expected.<br>
<u>Output:</u> <code><i>x</i>=range(VAL,0,NORD)</code><br>
The first <code>NORD</code> characters are printed,
which might be less than <code>NELM</code>.<br>
<u>Input:</u> <code>VAL=<i>x</i>, NORD=length(<i>x</i>)</code><br>
A maximum of <code>NELM-1</code> characters can be read.
<code>NORD</code> is updated to the index of the first of the
trailing zeros.
Usually, this is the same as the string length.
</dd>
</dl>
Other values of <code>FTVL</code> are not allowed for this format.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

128
doc/aao.html Normal file
View File

@ -0,0 +1,128 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: aao Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: aao Records</h1>
<p>
<b>Note:</b> aao record support is disabled per default.
Enable it in <code>src/CONFIG_STREAM</code>.
</p>
<h2>Normal Operation</h2>
<p>
With aao records, the format converter is applied to
each element. Between the elements, a separator is printed
or expected as specified by the <code>Separator</code>
<a href="protocol.html#sysvar">variable</a> in the protocol.
When parsing input, a space as the first character of the
<code>Separator</code> matches any number of any whitespace
characters.
</p>
<p>
During output, the first <code>NORD</code> elements are
written.
</p>
<p>
During input, a maximum of <code>NELM</code> elements is
read and <code>NORD</code> is updated accordingly.
Parsing of elements stops when the separator does not match,
conversion fails, or the end of the input is reached.
A minimum of one element must be available.
</p>
<p>
The format data type must be convertible to or from the type
specified in the <code>FTVL</code> field.
The variable <code><i>x[i]</i></code> stands for one element of
the written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u><code><i>x[i]</i>=double(VAL[i])</code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i]</i>)</code><br>
<code>FTVL</code> must be <code>"FLOAT"</code> or <code>"DOUBLE"</code>
</dd>
<dt>LONG or ENUM format (e.g. <code>%i</code> or <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=long(VAL[i])</code><br>
<code>FTVL</code> can be
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
Signed values are sign-extended to long, unsigned values are
zero-extended to long before converting them.<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i])</i></code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
The value is truncated to the least significant bytes if
<code>FTVL</code> has a smaller data size than <code>long</code>.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<dl>
<dt>If <code>FTVL=="STRING"</code>:</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=VAL[i]</code><br>
<u>Input:</u> <code>VAL[i]=<i>x[i]</i></code><br>
Note that this is an array of strings, not an array of characters.
</dd>
<dt>If <code>FTVL=="CHAR"</code> or <code>FTVL="UCHAR"</code>:</dt>
<dd>
In this case, the complete aao is treated as a large
single string of size <code>NORD</code>.
No separators are printed or expected.<br>
<u>Output:</u> <code><i>x</i>=range(VAL,0,NORD)</code><br>
The first <code>NORD</code> characters are printed,
which might be less than <code>NELM</code>.<br>
<u>Input:</u> <code>VAL=<i>x</i>, NORD=length(<i>x</i>)</code><br>
A maximum of <code>NELM-1</code> characters can be read.
<code>NORD</code> is updated to the index of the first of the
trailing zeros.
Usually, this is the same as the string length.
</dd>
</dl>
Other values of <code>FTVL</code> are not allowed for this format.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

81
doc/ai.html Normal file
View File

@ -0,0 +1,81 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: ai Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: ai Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=(VAL-AOFF)/ASLO</code><br>
<u>Input:</u> <code>VAL=(<i>x</i>*ASLO+AOFF)*(1.0-SMOO)+VAL*SMOO</code><br>
In both cases, if <code>ASLO==0.0</code>, it is treated as <code>1.0</code>.
Default values are <code>ASLO=1.0</code>, <code>AOFF=0.0</code>,
<code>SMOO=0.0</code>.<br>
If input is successful, <code>UDF</code> is cleared.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL</code><br>
<u>Input:</u> <code>RVAL=<i>x</i></code><br>
Note that the record calculates
<code>VAL=(((RVAL+ROFF)*ASLO+AOFF)*ESLO+EOFF)*(1.0-SMOO)+VAL*SMOO</code>
if <code>LINR=="LINEAR"</code>.
<code>ESLO</code> and <code>EOFF</code> might be set in the record
definition. <em>StreamDevice</em> does not set it. For example,
<code>EOFF=-10</code> and <code>ESLO=0.000305180437934</code>
(=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if present.
In contrast to normal operation, in DOUBLE input <code>SMOO</code> is ignored
(treated as <code>0.0</code>).
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

82
doc/ao.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: ao Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: ao Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=(OVAL-AOFF)/ASLO</code><br>
<u>Input:</u> <code>VAL=<i>x</i>*ASLO+AOFF</code><br>
In both cases, if <code>ASLO==0.0</code>, it is treated as <code>1.0</code>.
Default values are <code>ASLO=1.0</code>, <code>AOFF=0.0</code>.<br>
Note that <code>OVAL</code> is not necessarily equal to <code>VAL</code>
if <code>OROC!=0.0</code>.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL</code><br>
<u>Input:</u> <code>RBV=<i>x</i></code><br>
Note that the record calculates
<code>RVAL=(((OVAL-EOFF)/ESLO)-AOFF)/ASLO</code> if
<code>LINR=="LINEAR"</code>. <code>ESLO</code> and <code>EOFF</code>
might be set in the record definition. <em>StreamDevice</em> does not set it.
For example, <code>EOFF=-10</code> and <code>ESLO=0.000305180437934</code>
(=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. In contrast to normal operation, output in DOUBLE format uses
<code>VAL</code> instead of <code>OVAL</code>. Note that the record
initializes <code>VAL</code> from <code>DOL</code> if that is a constant.
LONG input is put to <code>RVAL</code> as well as to <code>RBV</code> and
converted by the record.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

BIN
doc/bg.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 868 B

77
doc/bi.html Normal file
View File

@ -0,0 +1,77 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: bi Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: bi Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL</code><br>
<u>Input:</u> <code>RVAL=<i>x</i>&amp;MASK</code><br>
<code>MASK</code> can be set be set in the record definition. Stream
Device does not set it. If <code>MASK==0</code>, it is ignored
(i.e. <code>RVAL=<i>x</i></code>). The record sets
<code>VAL=(RVAL!=0)</code>, i.e. <code>1</code> if <code>RVAL!=0</code>
and <code>0</code> if <code>RVAL==0</code>.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=(<i>x</i>!=0)</code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> Depending on <code>VAL</code>, <code>ZNAM</code> or
<code>ONAM</code> is written, i.e. <code><i>x</i>=VAL?ONAM:ZNAM</code>.<br>
<u>Input:</u> If input is equal to <code>ZNAM</code> or <code>ONAM</code>,
<code>VAL</code> is set accordingly. Other input strings are not accepted.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

76
doc/bo.html Normal file
View File

@ -0,0 +1,76 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: bo Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: bo Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL</code><br>
<u>Input:</u> <code>RBV=<i>x</i>&amp;MASK</code><br>
<code>MASK</code> can be set be set in the record definition. Stream
Device does not set it. If <code>MASK==0</code>, it is ignored
(i.e. <code>RBV=<i>x</i></code>).
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=(<i>x</i>!=0)</code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> Depending on <code>VAL</code>, <code>ZNAM</code> or
<code>ONAM</code> is written, i.e. <code><i>x</i>=VAL?ONAM:ZNAM</code>.<br>
<u>Input:</u> If input is equal to <code>ZNAM</code> or <code>ONAM</code>,
<code>VAL</code> is set accordingly. Other input strings are not accepted.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. In contrast to normal operation, LONG input is put to
<code>RVAL</code> as well as to <code>RBV</code> and converted by the record.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

634
doc/businterface.html Normal file
View File

@ -0,0 +1,634 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Bus API</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>Bus API</h1>
<a name="class"></a>
<h2>Bus Interface Class</h2>
<p>
<em>StreamDevice</em> already comes with an interface to <a target="ex"
href="http://www.aps.anl.gov/epics/modules/soft/asyn/">
<em>asynDriver</em></a>.
You should first try to implement your bus driver compatible to
<em>asynDriver</em>.
Then it can be used by <em>StreamDevice</em> automatically.
Only if that does not work, write your own bus interface.
</p>
<p>
A bus interface is a C++ class that inherits from
<em>StreamBusInterface</em>.
Its purpose is to provide an interface to <em>StreamDevice</em> for a
low-level I/O bus driver.
<em>StreamDevice</em> acts as a client of the interface, calling interface
methods and receiving replies via callbacks.
Since the internal details of <em>StreamDevice</em> are not of
interest to a bus interface, I will reference it simply as
<em>client</em> in this chapter.
The interface class must be registered via a call to
<code><a href="#registration">
RegisterStreamBusInterface</a>()</code>
in the global context of the C++ file (not in a header file).
</p>
<p>
Interface methods called by the client must not block for arbitrary
long times.
That means the interface is allowed to take mutex semaphores to protect
its internal data structures but it must not take event semaphores to
wait for external I/O or similar.
</p>
<p>
It is assumed that the interface creates a separate thread to handle
blocking I/O and to call the callback methods in the context of that
thread when I/O has completed or timed out.
The callback methods don't block but may in turn call interface methods.
Much of the actual work will be done in the context of those callbacks,
i.e. in the interface thread, thus be generous with stack.
</p>
<h3>Example bus interface class declaration</h3>
<pre>
#include &lt;StreamBusInterface.h&gt;
class MyInterface : StreamBusInterface
{
// ... (internally used attributes and methods)
<a href="#create">MyInterface</a>(Client* client);
<a href="#create">~MyInterface</a>();
// StreamBusInterface virtual methods
bool <a href="#lock">lockRequest</a>(unsigned long lockTimeout_ms);
bool <a href="#lock">unlock</a>();
bool <a href="#write">writeRequest</a>(const void* output, size_t size,
unsigned long writeTimeout_ms);
bool <a href="#read">readRequest</a>(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms,
long expectedLength, bool async);
bool <a href="#read">supportsAsyncRead</a>();
bool <a href="#event">supportsEvent</a>();
bool <a href="#event">acceptEvent</a>(unsigned long mask,
unsigned long replytimeout_ms);
bool <a href="#connect">connectRequest</a>(unsigned long connecttimeout_ms);
bool <a href="#connect">disconnect</a>();
void <a href="#lock">finish</a>();
public:
// creator method
static StreamBusInterface* <a href="#create">getBusInterface</a>(
Client* client, const char* busname,
int addr, const char* param);
};
<a href="#registration">RegisterStreamBusInterface</a>(MyInterface);
// ... (implementation)
</pre>
<h3>Methods to implement</h3>
<p>
The interface class must implement a public static creator method:
</p>
<div class="indent"><code>
static StreamBusInterface*
<a href="#create">getBusInterface</a>(Client*&nbsp;client,
const&nbsp;char*&nbsp;busname, int&nbsp;addr,
const&nbsp;char*&nbsp;param);
</code></div>
<p>
And it must implement the following pure virtual methods:
</p>
<div class="indent"><code>
bool <a href="#lock">lockRequest</a>(unsigned&nbsp;long&nbsp;lockTimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#lock">unlock</a>();
</code></div>
<p>
It may implement additional virtual methods if the bus supports it:
</p>
<div class="indent"><code>
bool <a href="#write">writeRequest</a>(const&nbsp;void*&nbsp;output,
size_t&nbsp;size, unsigned&nbsp;long&nbsp;writeTimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#read">readRequest</a>(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
long&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
bool <a href="#read">supportsAsyncRead</a>();
</code></div>
<div class="indent"><code>
bool <a href="#event">supportsEvent</a>();
</code></div>
<div class="indent"><code>
bool <a href="#event">acceptEvent</a>(unsigned&nbsp;long&nbsp;mask,
unsigned&nbsp;long&nbsp;replytimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#connect">connectRequest</a>(unsigned&nbsp;long&nbsp;connecttimeout_ms);
</code></div>
<div class="indent"><code>
bool <a href="#connect">disconnect</a>();
</code></div>
<div class="indent"><code>
void <a href="#lock">finish</a>();
</code></div>
<p>
It also may override the following virtual method:
</p>
<div class="indent"><code>
void <a href="#create">release</a>();
</code></div>
<h3>Callback methods provided</h3>
<p>
The base class <em>StreamBusInterface</em> implements a set of protected
callback methods which must be called in response to the above request
methods (most probably from another thread):
</p>
<div class="indent"><code>
void <a href="#lock">lockCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#write">writeCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
long <a href="#read">readCallback</a>(StreamIoStatus&nbsp;status,
const&nbsp;void*&nbsp;input&nbsp;=&nbsp;NULL,
long&nbsp;size&nbsp;=&nbsp;0);
</code></div>
<div class="indent"><code>
void <a href="#event">eventCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
void <a href="#connect">connectCallback</a>(StreamIoStatus&nbsp;status);
</code></div>
<h3>Other provided methods, attibutes, and types</h3>
<div class="indent"><code>
<a href="#create">StreamBusInterface</a>(Client*&nbsp;client);
</code></div>
<div class="indent"><code>
long <a href="#lock">priority</a>();
</code></div>
<div class="indent"><code>
const char* <a href="#create">clientName</a>();
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;<a href="#write">getOutTerminator</a>(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;<a href="#read">getInTerminator</a>(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
enum&nbsp;StreamIoStatus {StreamIoSuccess, StreamIoTimeout, StreamIoNoReply, StreamIoEnd, StreamIoFault};
</code></div>
<a name="theory"></a>
<h2>Theory of Operation</h2>
<a name="registration"></a>
<h3>Registration</h3>
<div class="indent"><code>
RegisterStreamBusInterface(<i>interfaceClass</i>);
</code></div>
<p>
During initialization, the macro <code>RegisterStreamBusInterface()</code>
registers the bus interface.
It must be called exactly once for each bus interface class in global
file context.
</p>
<a name="create"></a>
<h3>Creation and deletion</h3>
<div class="indent"><code>
static StreamBusInterface* getBusInterface(Client*&nbsp;client,
const&nbsp;char*&nbsp;busname, int&nbsp;addr,
const&nbsp;char*&nbsp;param);
</code></div>
<div class="indent"><code>
StreamBusInterface(Client*&nbsp;client);
</code></div>
<div class="indent"><code>
void release();
</code></div>
<div class="indent"><code>
const char* clientName();
</code></div>
<p>
During startup, each client instance searches for its bus interface
by name.
It does so by calling the static <code>getBusInterface()</code> method
of every registered interface class.
This method should check by <code>busname</code> if its interface class
is responsible for that bus.
If yes, it should check if the address <code>addr</code> is valid and
associate a <em>device</em> with <code>busname</code>/<code>addr</code>.
Some busses do not have addresses and allow only one device
(e.g. RS232).
Interfaces to such busses can ignore <code>addr</code>.
The bus interface may then try to connect to the device, but it should
allow it to be disconnected or switched off at that time.
If the bus interface requires additional parameters, parse the
<code>param</code> string.
Your constructor should pass <code>client</code> to the base class
constructor <code>StreamBusInterface(Client* client)</code>.
</p>
<p>
On success, <code>getBusInterface</code> should then return a pointer
to a bus interface instance.
Note that many client instances may want to connect to the same device.
Each needs its own bus interface instance.
The bus interface can get a string containing the name of the
client instance from <code>clientName()</code>.
This name is for use in error and log messages.
</p>
<p>
On failure, or if this interface class is not responsible for that bus,
<code>getBusInterface</code> should return <code>NULL</code>.
The client will then try other bus interface classes.
</p>
<p>
When the client does not need the interface any more, it calls
<code>release()</code>.
The default implementation of <code>release()</code> assumes that
<code>getBusInterface()</code> has allocated a new bus interface
and just calls <code>delete</code>.
You should change <code>release()</code> if that assumption is not
correct.
</p>
<a name="connect"></a>
<h3>Connecting and disconnecting</h3>
<div class="indent"><code>
bool connectRequest(unsigned&nbsp;long&nbsp;connecttimeout_ms);
</code></div>
<div class="indent"><code>
bool disconnect();
</code></div>
<div class="indent"><code>
void connectCallback(IoStatus&nbsp;status);
</code></div>
<p>
Connection should be handled automatically.
If the device is disconnected, each attempt to access the
device should try to (re-)connect.
Normally, the interface should not try to disconnect unless
the device does so.
</p>
<p>
However, sometimes the client wants to connect or
disconnect explicitely.
To connect, the client calls <code>connectRequest()</code>.
This function should return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
The interface should call <code>connectCallback(StreamIoSuccess)</code>
once the bus could be connected.
If the bus cannot be connected within <code>connecttimeout_ms</code>
milliseconds, the bus interface should call
<code>connectCallback(StreamIoTimeout)</code>.
</p>
<p>
If a device cannot be connected, for example because there is
something wrong with the I/O hardware,
<code>connectCallback(StreamIoFault)</code> may be called.
</p>
<p>
To disconnect, the client calls <code>disconnect()</code>;
This function should return <code>true</code> immediately or
<code>false</code> if disconnecting is impossible.
There is no callback for <code>disconnect()</code>.
</p>
<a name="lock"></a>
<h3>Bus locking</h3>
<div class="indent"><code>
bool lockRequest(unsigned&nbsp;long&nbsp;lockTimeout_ms);
</code></div>
<div class="indent"><code>
void lockCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
bool unlock();
</code></div>
<div class="indent"><code>
long priority();
</code></div>
<div class="indent"><code>
void finish();
</code></div>
<p>
Before doing output, the client calls <code>lockRequest()</code> to get
exclusive access to the device.
This function should return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
If the device is already locked, the bus interface should add itself to
a queue, sorted by <code>priority()</code>.
As soon as the device is available, the bus interface should call
<code>lockCallback(StreamIoSuccess)</code>.
If the bus cannot be locked within <code>lockTimeout_ms</code>
milliseconds, the bus interface should call
<code>lockCallback(StreamIoTimeout)</code>.
</p>
<p>
If a device cannot be locked, for example because there is
something wrong with the I/O hardware,
<code>lockCallback(StreamIoFault)</code> may be called.
</p>
<p>
Normally, it is not necessary to lock the complete bus but only one
device (i.e. one address).
Other clients should still be able to talk to other devices on the same bus.
</p>
<p>
The client may perform several read and write operations when it has
locked the device.
When the protocol ends and the device is locked, the client calls
<code>unlock()</code>.
If other bus interfaces are in the lock queue, the next one should
call <code>lockCallback(StreamIoSuccess)</code> now.
</p>
<p>
The client calls <code>finish()</code> when the protocol ends.
This allows the bus interface to clean up.
The bus interface should also cancel any outstanding requests of
this client.
</p>
<a name="write"></a>
<h3>Writing output</h3>
<div class="indent"><code>
bool writeRequest(const&nbsp;void*&nbsp;output,
size_t&nbsp;size, unsigned&nbsp;long&nbsp;writeTimeout_ms);
</code></div>
<div class="indent"><code>
void writeCallback(IoStatus&nbsp;status);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;getOutTerminator(size_t&&nbsp;length);
</code></div>
<p>
To start output, the client calls <code>writeRequest()</code>.
You can safely assume that the device has already been locked at this
time.
That means, no other client will call <code>writeRequest()</code>
for this device and no other output is currently active for this device
until it has been unlocked.
</p>
<p>
The function should arrange transmission of <code>size</code> bytes of
<code>output</code> but return <code>true</code> immediately
or <code>false</code> if the request cannot be accepted.
It must not block until output has completed.
After all output has been successfully transmitted, but not earlier, the
interface should call <code>writeCallback(StreamIoSuccess)</code>.
</p>
<p>
If output blocks for <code>writeTimeout_ms</code> milliseconds,
the interface should abort the transmision and call
<code>writeCallback(StreamIoTimeout)</code>.
</p>
<p>
If output is impossible, for example because there is
something wrong with the I/O hardware,
<code>writeCallback(StreamIoFault)</code> may be called.
</p>
<p>
The interface must transmit excactly the <code>size</code> bytes
from <code>output</code>.
It must not change anything and it should not assume that
any bytes have a special meaning.
In particular, a null byte does not terminate <code>output</code>.
</p>
<p>
A call to <code>getOutTerminator</code> tells the interface which
terminator has already been added to the output.
If <code>NULL</code> was returned, the client is not aware of a
terminator (no outTerminator was defined in the protocol).
In this case, the interface may add a terminator which it knows from
other sources.
An interface is not required to support <code>NULL</code> results
and may not add any terminator in this case.
</p>
<p>
The buffer referenced by <code>output</code> stays valid until
<code>writeCallback()</code> is called.
</p>
<p>
The client may request more I/O or call <code>unlock()</code> after
<code>writeCallback()</code> has been called.
</p>
<a name="read"></a>
<h3>Reading input</h3>
<div class="indent"><code>
bool readRequest(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
long&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
long readCallback(IoStatus&nbsp;status,
const&nbsp;void*&nbsp;input&nbsp;=&nbsp;NULL,
long&nbsp;size&nbsp;=&nbsp;0);
</code></div>
<div class="indent"><code>
const&nbsp;char*&nbsp;getInTerminator(size_t&&nbsp;length);
</code></div>
<div class="indent"><code>
bool supportsAsyncRead();
</code></div>
<p>
The client calls <code>readRequest()</code> to tell the bus interface
that it expects input.
Depending on the bus, this function might have to set the bus hardware
into receive mode.
If <code>expectedLength&gt;0</code>, the the bus interface should stop
input after this number of bytes have been received.
In opposite to writing, the device may be in a non-locked status when
<code>readRequest()</code> is called.
</p>
<p>
This function must not block until input is available.
Instead, it should arrange for
<code>readCallback(StreamIoSuccess, buffer, size)</code> to be called
when input has been received and return <code>true</code>
immediately or <code>false</code> if the request cannot be accepted.
</p>
<p>
Here, <code>buffer</code> is a pointer to
<code>size</code> input bytes.
The bus interface is responsible for the buffer.
The client copies its contents. It does not modify or free it.
</p>
<p>
It is not necessary to wait until all data has been received.
The bus interface can call <code>n=readCallback()</code> after
any amount of input has been received.
If the client needs more input, <code>readCallback()</code>
returns a non-zero value.
A positive <code>n</code> means, the client needs another
<code>n</code> bytes of input.
A negative <code>n</code> means, the client needs an unspecified
amount of additional input.
</p>
<p>
With some bus interfaces, <code>readRequest()</code> might not have to
do anything because the bus is always receiving.
It might also be that the bus has no local buffer associated to store
input before it is fetched with some <code>read()</code> call.
In this case, a race condition between device and client can occure.
To avoid loss of data, <code>readCallback(StreamIoSuccess, buffer, size)</code>
may be called in this case even before <code>readRequest()</code>.
If the client is expecting input in the next future, it will store it.
Otherwise the input is dropped.
</p>
<p>
The <code>replyTimeout_ms</code> parameter defines how many milliseconds
to wait for the first byte of a reply before the device is considered
offline.
If no input has been received after <code>replyTimeout_ms</code>
milliseconds, the bus interface should call
<code>readCallback(StreamIoNoReply)</code>.
</p>
<p>
The <code>readTimeout_ms</code> parameter is the maximum time to wait
for further input.
If input stops for longer than <code>readTimeout_ms</code> milliseconds
the bus interface should call
<code>readCallback(StreamIoTimeout,buffer,size)</code>.
The client decides if this timeout is an error or a legal termination.
Thus, pass all input received so far.
</p>
<p>
A call to <code>getInTerminator(length)</code> tells the interface which
terminator is expected for input and <code>length</code> is set to the
number of bytes of the terminator. The result is a hint to the bus
interface to recognize the end of an input.
Once the terminator string is found, the bus interface should stop
receiving input and call
<code>readCallback(StreamIoSuccess, buffer, size)</code>.
It is not necessary to remove the terminator string from the received input.
An empty terminator string (<code>length==0</code>) means:
Don't look for terminators.
</p>
<p>
If <code>NULL</code> was returned, the client is not aware of a
terminator (no inTerminator was defined in the protocol).
In this case, the interface may look for a terminator which it knows from
other sources, reduce size by the terminator length and call
<code>readCallback(StreamIoEnd, buffer, size)</code>.
A bus interface is not required to support <code>NULL</code> results and
may treat them as empty terminator (see above).
</p>
<p>
Some busses (e.g. GPIB) support special "end of message" signals.
If such a signal is received, the bus interface should call
<code>readCallback(StreamIoEnd, buffer, size)</code>.
Use it to indicate a special "end of message" signal which is not
visible in the normal byte data stream.
If <code>getInTerminator()</code> has not returned <code>NULL</code>
it it not necessary to remove a terminator which may come in
addition to the "end of message" signal.
</p>
<p>
If input is impossible, for example because there is
something wrong with the I/O hardware,
<code>readCallback(StreamIoFault)</code> may be called.
</p>
<p>
If the <code>async</code> flag is <code>true</code>, the client
wants to read input asyncronously without any timeout.
That means, the bus interface should call <code>readCallback()</code>
even if the input was requested by another client.
</p>
<p>
If a client wishes to receive asynchonous input, it first calls
<code>supportsAsyncRead()</code>.
The default implementation of this method always returns
<code>false</code>.
A bus interface may overwrite this method to return <code>true</code>
and eventually prepare for asynchonous input.
The client is then allowed to call <code>readRequest()</code> with
the <code>async==true</code>.
This means that the client is interested in any input.
It should receive a <code>readCallback()</code> of all input which came
in response to a synchonous (<code>async==false</code>) request from
another client (which of course should receive the input, too).
The interface should also receive asynchonous input when no
synchonous client is active at the moment.
Many asynchonous <code>readRequest()</code> calls from different clients
may be active at the same time.
All of them should receive the same input.
</p>
<p>
For asynchonous requests, <code>replyTimeout_ms</code> has a different
meaning: If the bus interface has to poll the bus for input, it may take
<code>replyTimeout_ms</code> as a hint for the poll period.
If many asynchonous requests are active at the same time, it should poll
with the shortest period of all clients.
An asynchonous request does not time out.
It stays active until the next input arrives.
The client may reissue the asynchronous <code>readRequest()</code>
from within the <code>readCallback()</code> if it wants to continue
receiving asynchonous input.
</p>
<p>
If the client calls <code>finish()</code> at any time, the bus
interface should cancel all outstanding requests, including
asynchonous read requests.
</p>
<a name="event"></a>
<h3>Handling events</h3>
<div class="indent"><code>
bool supportsEvent();
</code></div>
<div class="indent"><code>
bool acceptEvent(unsigned long mask, unsigned long replytimeout_ms);
</code></div>
<div class="indent"><code>
void eventCallback(StreamIoStatus status);
</code></div>
<p>
An event is a sort of input from a device which is not part of
the normal byte stream.
One example is the SRQ line of GPIB.
Not all bus types have events.
To support events, the bus interface must overwrite
<code>supportsEvent()</code> to return <code>true</code>.
The default implementation always returns <code>false</code>.
</p>
<p>
If <code>true</code> is returned, the client is allowed to call
<code>acceptEvent()</code>, where <code>mask</code> defines the
(bus dependent) type of event or events to wait for.
If <code>mask</code> is illegal, <code>acceptEvent()</code> should
return <code>false</code>.
The call to <code>acceptEvent()</code> must not block.
It should arrange to call <code>eventCallback(StreamIoSuccess)</code>
when the event matching <code>mask</code> arrives within
<code>replytimeout_ms</code> milliseconds.
If no such event arrives within this time, the bus interface
should call <code>eventCallback(StreamIoTimeout)</code>.
</p>
<p>
To avoid race conditions, the bus interface should buffer events and
also report a matching event which occured before the actual call
to <code>acceptEvent()</code> but after any previous call of any
other request method like <code>writeRequest()</code>.
</p>
<hr>
<p><small>Dirk Zimoch, 2007</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

83
doc/calcout.html Normal file
View File

@ -0,0 +1,83 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: calcout Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: calcout Records</h1>
<p>
<b>Note:</b> Device support for calcout records is only available for
EPICS base R3.14.5 or higher.
</p>
<h2>Normal Operation</h2>
<p>
Different record fields are used for output and input. The variable
<code><i>x</i></code> stands for the written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=OVAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
Note that the record calculates <code>OVAL</code> from <code>CALC</code>
or <code>OCAL</code> depending on <code>DOPT</code>.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=int(OVAL)</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=int(OVAL)</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<p>
For calcout records, it is probably more useful to access fields
<code>A</code> to <code>L</code> directly (e.g. <code>"%(A)f"</code>).
However, even if <code>OVAL</code> is not used, it is calculated by the
record. Thus, <code>CALC</code> must always contain a valid expression
(e.g. <code>"0"</code>).
</p>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

224
doc/epics3_13.html Normal file
View File

@ -0,0 +1,224 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Using EPICS 3.13</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Using EPICS 3.13</h1>
<a name="pre"></a>
<h2>1. Prerequisites</h2>
<p>
<em>StreamDevice</em> version 2.2 and higher can run on EPICS 3.13.
However, this requires some preparation, because EPICS 3.13 is missing
some libraries and header files.
Also <em>asynDriver</em></a> needs to be modified to compile with EPICS 3.13.
Due to the limitations of EPICS 3.13, you can build streamDevice only for
vxWorks systems.
</p>
<p>
Of course, you need an installation of <a target="ex"
href="http://www.aps.anl.gov/epics/base/R3-13.php">EPICS 3.13</a>.
I guess you already have that, otherwhise you would want to
<a href="setup">install <em>StreamDevice</em> on EPICS 3.14</a>.
I have tested <em>StreamDevice</em> with EPICS versions 3.13.7 up to 3.13.10
with vxWorks 5.3.1 and 5.5 on a ppc604 processor.
</p>
<p>
Download my <a
href="http://epics.web.psi.ch/software/streamdevice/compat-1-0.tgz">
compatibility package</a>,
<a target="ex"
href="http://www.aps.anl.gov/epics/modules/soft/asyn/"><em>asynDriver</em></a>
version 4-3 or higher,
and my <a
href="http://epics.web.psi.ch/software/streamdevice/configure.tgz">
configure patches</a>.
</p>
<a name="compat"></a>
<h2>2. Build the Compatibility Package</h2>
<p>
Unpack <kbd>compat-1-0.tgz</kbd> in the <kbd>&lt;top&gt;</kbd> directory of
your application build area.
(Please refer to the <a target="ex"
href="http://www.aps.anl.gov/epics/EpicsDocumentation/AppDevManuals/iocScm-3.13.2/managingATop.html#3">
<em>EPICS IOC Software Configuration Management</em></a> document.)
</p>
<p>
Change to the <kbd>compat</kbd> directory and run <kbd>make</kbd>.
This installs many EPICS 3.14-style header files and a small library
(<kbd>compatLib</kbd>).
</p>
<a name="asyn"></a>
<h2>3. Build the <em>asynDriver</em> Library</h2>
<p>
Unpack the <em>asynDriver</em> package and change to its top directory.
</p>
<p>
Unpack <kbd>configure.tgz</kbd> here.
This will modify files in the <kbd>configure</kbd> directory.
Change to the <kbd>configure</kbd> directory and edit <kbd>CONFIG_APP</kbd>.
Set <code>COMPAT=...</code> to the <kbd>&lt;top&gt;</kbd>
directory where you have installed the compatibility package before.
(This patch might also allow you to compile other 3.14-style drivers for 3.13.
It has absolutely no effect if you use EPICS 3.14.)
</p>
<p>
Edit <kbd>RELEASE</kbd> and comment out <code>IPAC=...</code>
(unless you have the <em>ipac</em> package and somehow made it
compatible to EPICS 3.13).
Set <code>EPICS_BASE</code> to your EPICS 3.13 installation.
</p>
<p>
Run <kbd>make</kbd> in the <kbd>configure</kbd> directory.
</p>
<p>
Change to <kbd>../asyn/devGpib</kbd> and edit
<kbd>devGpib.h</kbd> and <kbd>devSupportGpib.c</kbd>.
Change all occurrences of <code>static&nbsp;gDset</code> to
<code>gDset</code>.
</p>
<p>
Go one directory up (to <kbd>asyn</kbd>) and run <kbd>make</kbd> twice!
(The first run will just create <kbd>Makefile.Vx</kbd>.)
Ignore all compiler warnings.
</p>
<p>
Do not try to build the test applications. It will not work.
</p>
<a name="lib"></a>
<h2>4. Build the <em>StreamDevice</em> Library</h2>
<p>
Go to the <kbd>&lt;top&gt;</kbd> directory of your application build area.
</p>
<p>
Edit <kbd>config/RELEASE</kbd> and add the variable <code>ASYN</code>.
Set it to the location of the <em>asynDriver</em> installation.
Also set the <code>COMPAT</code> variable to the location of the
compatibility package.
Run <kbd>make</kbd> in the <kbd>config</kbd> directory.
<p>
Unpack the <em>StreamDevice</em> package in your <kbd>&lt;top&gt;</kbd>
directory.
Change to the newly created <em>StreamDevice</em> directory
and run <kbd>make</kbd>.
</p>
<a name="app"></a>
<h2>5. Build an Application</h2>
<p>
To use <em>StreamDevice</em>, your application must be built with the
<em>asyn</em>, <em>stream</em>, and <em>compat</em> libraries and must load
<kbd>asyn.dbd</kbd> and <kbd>stream.dbd</kbd>.
Also, as the <em>stream</em> library contains C++ code, the application
must be munched.
Therefore, include <kbd>$(TOP)/config/RULES.munch</kbd>.
(Put your application in the same <kbd>&lt;top&gt;</kbd> as the
<em>StreamDevice</em> installation.)
</p>
<p>
Include the following lines in your <kbd>Makefile.Vx</kbd>:
</p>
<pre>
LDLIBS += $(COMPAT_BIN)/compatLib
LDLIBS += $(ASYN_BIN)/asynLib
LDLIBS += $(INSTALL_BIN)/streamLib
include $(TOP)/config/RULES.munch
</pre>
<p>
Include the following lines in your <kbd>xxxAppInclude.dbd</kbd> file to use
<em>stream</em> and <em>asyn</em> (you also need a <kbd>base.dbd</kbd>):
</p>
<pre>
include "base.dbd"
include "stream.dbd"
include "asyn.dbd"
</pre>
<p>
You can find an example application in the <kbd>streamApp</kbd>
subdirectory.
</p>
<a name="sta"></a>
<h2>6. The Startup Script</h2>
<p>
<em>StreamDevice</em> is based on <a
href="protocol.html"><em>protocol files</em></a>.
To tell <em>StreamDevice</em> where to search for protocol files,
set the environment variable <code>STREAM_PROTOCOL_PATH</code> to a
list of directories to search.
Directories are separated by <code>:</code>.
The default value is <code>STREAM_PROTOCOL_PATH=.</code>,
i.e. the current directory.
</p>
<p>
Also configure the buses (in <em>asynDriver</em> terms: ports) you want
to use with <em>StreamDevice</em>.
You can give the buses any name you want, like <kbd>COM1</kbd> or
<kbd>socket</kbd>, but I recommend to use names related to the
connected device.
</p>
<h3>Example:</h3>
<p>
A power supply with serial communication (9600 baud, 8N1) is connected to
<kbd>/dev/ttyS1</kbd>.
The name of the power supply is <tt>PS1</tt>.
Protocol files are either in the current working directory or in the
<kbd>../protocols</kbd> directory.
</p>
<p>
Then the startup script must contain lines like this:
</p>
<pre>
ld < iocCore
ld < streamApp.munch
dbLoadDatabase ("streamApp.dbd")
putenv ("STREAM_PROTOCOL_PATH=.:../protocols")
drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
asynSetOption ("PS1", 0, "baud", "9600")
asynSetOption ("PS1", 0, "bits", "8")
asynSetOption ("PS1", 0, "parity", "none")
asynSetOption ("PS1", 0, "stop", "1")
</pre>
<p>
An alternative approach is to skip step 5 (do not build an application)
and load all components explicitely in the startup script.
The <code>STREAM_PROTOCOL_PATH</code> variable can also be a vxWorks shell
variable.
</p>
<pre>
ld < iocCore
ld < compatLib
ld < asynLib
ld < streamLib.munch
dbLoadDatabase ("asyn.dbd")
dbLoadDatabase ("stream.dbd")
STREAM_PROTOCOL_PATH=".:../protocols"
drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
asynSetOption ("PS1", 0, "baud", "9600")
asynSetOption ("PS1", 0, "bits", "8")
asynSetOption ("PS1", 0, "parity", "none")
asynSetOption ("PS1", 0, "stop", "1")
</pre>
<h2>7. <a href="setup.html#pro">Continue as with EPICS 3.14.</a></h2>
<hr>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

BIN
doc/ex.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

BIN
doc/exr.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 187 B

20
doc/formatconverter.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Format Converter API</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Format Converter API</h1>
<h2>Sorry, this documentation is still missing.</h2>
<hr>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

410
doc/formats.html Normal file
View File

@ -0,0 +1,410 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Format Converters</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Format Converters</h1>
<a name="syntax"></a>
<h2>1. Format Syntax</h2>
<p>
<em>StreamDevice</em> format converters work very similar to the format
converters of the C functions <em>printf()</em> and <em>scanf()</em>.
But <em>StreamDevice</em> provides more different converters and you can
also write your own converters.
Formats are specified in <a href="protocol.html#str">quoted strings</a>
as arguments of <code>out</code> or <code>in</code>
<a href="protocol.html#cmd">commands</a>.
</p>
<p>
A format converter consists of
</p>
<ul>
<li>The <code>%</code> character</li>
<li>Optionally a field 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>
<li>A conversion character</li>
<li>Additional information required by some converters</li>
</ul>
<p>
The <code>*</code> flag skips data in input formats.
Input is consumed and parsed, a mismatch is an error, but the read
data is dropped.
This is useful if input contains more than one value.
Example: <code>in "%*f%f";</code> reads the second floating point
number.
</p>
<p>
The <code>#</code> flag may alter the format, depending on the
converter (see below).
</p>
<p>
The '<code> </code>' (space) and <code>+</code> flags print a space
or a <code>+</code> sign before positive numbers, where negative
numbers would have a <code>-</code>.
</p>
<p>
The <code>0</code> flag says that numbers should be left padded with
<code>0</code> if <em>width</em> is larger than required.
</p>
<p>
The <code>-</code> flag specifies that output is left justified if
<em>width</em> is larger than required.
</p>
<h3>Examples:</h3>
<table>
<tr>
<td><code>in "%f";</code></td>
<td>float</td>
</tr>
<tr>
<td><code>out "%(HOPR)7.4f";</code></td>
<td>the HOPR field as 7 char float with precision 4</td>
</tr>
<tr>
<td><code>out "%#010x";</code></td>
<td>0-padded 10 char alternate hex (with leading 0x)</td>
</tr>
<tr>
<td><code>in "%[_a-zA-Z0-9]";</code></td>
<td>string of chars out of a charset</td>
</tr>
<tr>
<td><code>in "%*i";</code></td>
<td>skipped integer number</td>
</tr>
</table>
<a name="types"></a>
<h2>2. Data Types and Record Fields</h2>
<h3>Default fields</h3>
<p>
Every conversion character corresponds to one of the data types DOUBLE,
LONG, ENUM, or STRING.
In opposite to to <em>printf()</em> and <em>scanf()</em>, it is not
required to specify a variable for the conversion.
The variable is typically the <code>VAL</code> or <code>RVAL</code> field
of the record, selected automatically depending on the data type.
Not all data types make sense for all record types.
Refer to the description of <a href="recordtypes.html">supported record
types</a> for details.
</p>
<p>
<em>StreamDevice</em> makes no difference between <code>float</code>
and <code>double</code> nor between <code>short</code>, <code>int</code>
and <code>long</code> values.
Thus, data type modifiers like <code>l</code> or <code>h</code> do not
exist in <em>StreamDevice</em> formats.
</p>
<h3>Accessing record fields directly</h3>
<p>
To use other fields of the record or even fields of other records on the
same IOC for the conversion, write the field name in parentheses directly
after the <code>%</code>.
For example <code>out&nbsp;"%(EGU)s";</code> outputs the <code>EGU</code>
field formatted as a string.
Use <code>in&nbsp;"%(<i>otherrecord</i>.VAL)f";</code> to write the floating
point input value into the <code>VAL</code> field of
<code><i>otherrecord</i></code>.
(You can't skip <code>.VAL</code> here.)
This is very useful when one line of input contains many values that should
be distributed to many records.
If <code><i>otherrecord</i></code> is passive and the field has the PP
attribute (see
<a href="http://www.aps.anl.gov/asd/controls/epics/EpicsDocumentation/AppDevManuals/RecordRef/Recordref-1.html"
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.
Note that using this syntax is by far not as efficient as using the
default field.
</p>
<h3>Pseudo-converters</h3>
<p>
Some formats are not actually converters.
They format data which is not stored in a record field, such as a
<a href="#chksum">checksum</a>.
No data type corresponds to those <em>pseudo-converters</em> and the
<code>%(<em>FIELD</em>)</code> syntax cannot be used.
</p>
<a name="stdd"></a>
<h2>3. Standard DOUBLE Converters (<code>%f</code>, <code>%e</code>,
<code>%E</code>, <code>%g</code>, <code>%G</code>)</h2>
<p>
In output, <code>%f</code> prints fixed point, <code>%e</code> prints
exponential notation and <code>%g</code> prints either fixed point or
exponential depending on the magnitude of the value.
<code>%E</code> and <code>%G</code> use <code>E</code> instead of
<code>e</code> to separate the exponent.
With the <code>#</code> flag, output always contains a period character.
</p>
<p>
In input, all these formats are equivalent.
Leading whitespaces are skipped.
</p>
<a name="stdl"></a>
<h2>4. Standard LONG Converters (<code>%d</code>, <code>%i</code>,
<code>%u</code>, <code>%o</code>, <code>%x</code>, <code>%X</code>)</h2>
<p>
In output, <code>%d</code> and <code>%i</code> print signed decimal,
<code>%u</code> unsigned decimal, <code>%o</code> unsigned octal, and
<code>%x</code> or <code>%X</code> unsigned hexadecimal.
<code>%X</code> uses upper case letters.
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>
<p>
In input, <code>%d</code> matches signed decimal, <code>%u</code> matches
unsigned decimal, <code>%o</code> unsigned octal.
<code>%x</code> and <code>%X</code> both match upper or lower case unsigned
hexadecimal.
Octal and hexadecimal values can optionally be prefixed.
<code>%i</code> matches any integer in decimal, or prefixed octal or
hexadecimal notation.
Leading whitespaces are skipped.
</p>
<a name="stds"></a>
<h2>5. Standard STRING Converters (<code>%s</code>, <code>%c</code>)</h2>
<p>
In output, <code>%s</code> prints a string.
If <em>precision</em> is specified, this is the maximum string length.
<code>%c</code> is a LONG format in output, printing one character!
</p>
<p>
In input, <code>%s</code> matches a sequence of non-whitespace characters
and <code>%c</code> 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> but not with
<code>%c</code>.
The empty string matches.
</p>
<a name="cset"></a>
<h2>6. Standard Charset STRING Converter (<code>%[<em>charset</em>]</code>)</h2>
<p>
This is an input-only format.
It matches a sequence of characters from <em>charset</em>.
If <em>charset</em> starts with <code>^</code>, the format matches
all characters <u>not</u> in <em>charset</em>.
Leading whitespaces are not skipped.
</p>
<p>
Example: <code>%[_a-z]</code> matches a string consisting
entirely of <code>_</code> (underscore) or letters from <code>a</code>
to <code>z</code>.
</p>
<a name="enum"></a>
<h2>7. ENUM Converter (<code>%{<em>string0</em>|<em>string1</em>|...}</code>)</h2>
<p>
This format maps an unsigned integer value on a set of strings.
The value 0 corresponds to <em>string0</em> and so on.
The strings are separated by <code>|</code>.
If one of the strings contains <code>|</code> or <code>}</code>,
a <code>\</code> must be used to escape the character.
</p>
<p>
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>
In output, depending on the value, one of the strings is printed.
</p>
<p>
In input, if any of the strings matches the value is set accordingly.
</p>
<a name="bin"></a>
<h2>8. Binary LONG Converter (<code>%b</code>, <code>%B<em>zo</em></code>)</h2>
<p>
This format prints or scans an unsigned integer represented as a binary
string (one character per bit).
The <code>%b</code> format uses the characters <code>0</code> and
<code>1</code>.
With the <code>%B</code> format, you can choose two other characters
to represent zero and one.
</p>
<p>
Examples: <code>%B.!</code> or <code>%B\x00\xff</code>.
<code>%B01</code> is equivalent to <code>%b</code>.
</p>
<p>
If in output <em>width</em> is larger than the number of significant bits,
then the flag <code>0</code> means that the value should be padded with
with the chosen zero character instead of spaces.
If <em>precision</em> is set, it means the number of significant bits.
Otherwise, the highest 1 bit defines the number of significant bits.
There is no alternate format when <code>#</code> is used.
</p>
<p>
In input, leading spaces are skipped.
A maximum of <em>width</em> characters is read.
Conversion stops with the first character that is not the zero or the
one character.
</p>
<a name="raw"></a>
<h2>9. Raw LONG Converter (<code>%r</code>)</h2>
<p>
The raw converter does not really "convert".
A signed integer value is written or read in the internal
(usually two's complement) representation of the computer.
The normal byte order is <em>big endian</em>, i.e. most significant byte
first.
With the <code>#</code> flag, the byte order is changed to <em>little
endian</em>, i.e. least significant byte first.
</p>
<p>
In output, the <em>width</em> least significant bytes of the value
are written.
If <em>width</em> is larger than the size of a <code>long</code>,
the value is sign extended.
</p>
<p>
In input, <em>width</em> bytes are read and put into the value.
If <em>width</em> is longer than the size of a <code>long</code>, only
the least significant bytes are used.
</p>
<a name="bcd"></a>
<h2>10. Packed BCD (Binary Coded Decimal) LONG Converter (<code>%D</code>)</h2>
<p>
Packed BCD is a format where each byte contains two binary coded
decimal digits (<code>0</code> ... <code>9</code>).
Thus a BCD byte is in the range from <code>0x00</code> to <code>0x99</code>.
The normal byte order is <em>big endian</em>, i.e. most significant byte
first.
With the <code>#</code> flag, the byte order is changed to <em>little
endian</em>, i.e. least significant byte first.
The <code>+</code> flag defines that the value is signed, using the
upper half of the most significant byte for the sign.
Otherwise the value is unsigned.
</p>
<p>
In output, <em>precision</em> decimal digits are printed in at least
<em>width</em> output bytes.
Signed negative values have <code>0xF</code> in their most significant half
byte followed by the absolute value.
</p>
<p>
In input, <em>width</em> bytes are read.
If the value is signed, a one in the most significant bit is interpreted as
a negative sign.
Input stops with the first byte (after the sign) that does not represent a
BCD value, i.e. where either the upper or the lower half byte is larger
than 9.
</p>
<a name="chksum"></a>
<h2>11. Checksum Pseudo-Converter (<code>%&lt;<em>checksum</em>&gt;</code>)</h2>
<p>
This is not a normal "converter", because no user data is converted.
Instead, a checksum is calculated from the input or output.
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).
For example in <code>"abcdefg%&lt;xor&gt;"</code> the checksum is calculated
from <code>abcdefg</code>,
but in <code>"abcdefg%2.1&lt;xor&gt;"</code> only from <code>cdef</code>.
</p>
<p>
Normally, multi-byte checksums are in <em>big endian</em> byteorder,
i.e. most significant byte first.
With the <code>#</code> flag, the byte order is changed to <em>little
endian</em>, i.e. least significant byte first.
</p>
<p>
The <code>0</code> flag changes the checksum representation from
binary to hexadecimal ASCII (2 bytes per checksum byte).
<!--
In output, the case of the ASCII checksum matches the case of first
letter of the function name.
E.g. <code>out&nbsp;"123456789%&lt;sum&gt;"</code> writes <code>dd</code>
but <code>out&nbsp;"123456789%&lt;Sum&gt;"</code> writes <code>DD</code>.
In input, case is ignored.
-->
</p>
<p>
In output, the checksum is appended.
</p>
<p>
In input, the next byte or bytes must match the checksum.
</p>
<h3>Implemented checksum functions</h3>
<dl>
<dt><code>%&lt;SUM&gt;</code> or <code>%&lt;SUM8&gt;</code></dt>
<dd>One byte. The sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;SUM16&gt;</code></dt>
<dd>Two bytes. The sum of all characters modulo 2<sup>16</sup>.</dd>
<dt><code>%&lt;SUM32&gt;</code></dt>
<dd>Four bytes. The sum of all characters modulo 2<sup>32</sup>.</dd>
<dt><code>%&lt;NEGSUM&gt;</code> or <code>%&lt;NSUM&gt;</code> or <code>%&lt;-SUM&gt;</code></dt>
<dd>One byte. The negative of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;NOTSUM&gt;</code> or <code>%&lt;~SUM&gt;</code></dt>
<dd>One byte. The bitwise inverse of the sum of all characters modulo 2<sup>8</sup>.</dd>
<dt><code>%&lt;XOR&gt;</code></dt>
<dd>One byte. All characters xor'ed.</dd>
<dt><code>%&lt;CRC8&gt;</code></dt>
<dd>One byte. An often used 8 bit CRC checksum
(poly=0x07, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CCITT8&gt;</code></dt>
<dd>One byte. The CCITT standard 8 bit CRC checksum
(poly=0x31, init=0x00, xorout=0x00).</dd>
<dt><code>%&lt;CRC16&gt;</code></dt>
<dd>Two bytes. An often used 16 bit CRC checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CRC16R&gt;</code></dt>
<dd>Two bytes. An often used reflected 16 bit CRC checksum
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
<dt><code>%&lt;CCITT16&gt;</code></dt>
<dd>Two bytes. The usual (but <a target="ex"
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
implementation of the CCITT standard 16 bit CRC checksum
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
<dt><code>%&lt;CCITT16A&gt;</code></dt>
<dd>Two bytes. The unusual (but <a target="ex"
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
implementation of the CCITT standard 16 bit CRC checksum with augment.
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
<dt><code>%&lt;CRC32&gt;</code></dt>
<dd>Four bytes. The standard 32 bit CRC checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;CRC32R&gt;</code></dt>
<dd>Four bytes. The standard reflected 32 bit CRC checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
<dt><code>%&lt;JAMCRC&gt;</code></dt>
<dd>Four bytes. Another reflected 32 bit CRC checksum.
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
<dt><code>%&lt;ADLER32&gt;</code></dt>
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
<dt><code>%&lt;HEXSUM8&gt;</code></dt>
<dd>One byte. The sum of all hex digits. (Other characters are ignored.)</dd>
</dl>
<hr>
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
<p><small>Dirk Zimoch, 2007</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

32
doc/head.html Normal file
View File

@ -0,0 +1,32 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Headbar</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
<style type="text/css">
<!--
body {margin:0px 0px 0px 0px;}
big {font-size: 30px; font-family:serif;}
-->
</style>
</head>
<body bgcolor="white">
<table cellspacing=0 cellpadding=0 border=0 width="100%">
<tr><td bgcolor="#267ab8" colspan=6 valign="bottom" height=9 width="100%"></td></tr>
<tr><td bgcolor="#000000" colspan=6 valign="bottom" height=1 width="100%"></td></tr>
<tr>
<td><img src="space.gif" width=180 height=1 alt=""><center><a href="http://www.psi.ch" target="ex"><img src="PSI.gif" width=150 height=55 alt="PSI" border=0></a></center></td>
<td><img src="space.gif" width=1 height=65 alt=""></td>
<td><a href="http://www.aps.anl.gov/epics" target="ex"><img
src="EPICS.gif" border=0 width=50 height=50 alt="EPICS"></a></td>
<td width="100%" align="center" nowrap>
<big><b><em id="subtitle">StreamDevice</em></b></big></td>
<td><img src="space.gif" width=166 height=1 alt=""><center><a href="http://sls.web.psi.ch/view.php/about/index.html" target="ex"><img src="SLS.gif" width=146 height=55 alt="SLS" border=0></a></center></td>
</tr>
<tr><td bgcolor="#267ab8" colspan=6 valign="bottom" height=4 width="100%"></td></tr>
<tr><td bgcolor="#000000" colspan=6 valign="bottom" height=1 width="100%"></td></tr>
</table>
</body>
</html>

20
doc/index.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Frameset//EN"
"http://www.w3.org/TR/html4/frameset.dtd">
<html>
<head>
<title>StreamDevice Documentation</title>
<link rel="shortcut icon" href="sls_icon.ico">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<frameset rows="80,*" framespacing=0 frameborder=0 border=0>
<frame src="head.html" name="head" scrolling="no" noresize>
<frameset cols="180,*" >
<frame src="nav.html" name="nav" noresize>
<frame src="stream.html" name="text">
</frameset>
<noframes>
<a href="stream.html">Click here for no-frames version.</a>
</noframes>
</frameset>
</html>

68
doc/longin.html Normal file
View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: longin Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: longin Records</h1>
<h2>Normal Operation</h2>
<p>
The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

68
doc/longout.html Normal file
View File

@ -0,0 +1,68 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: longout Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: longout Records</h1>
<h2>Normal Operation</h2>
<p>
The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

94
doc/mbbi.html Normal file
View File

@ -0,0 +1,94 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: mbbi Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: mbbi Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<dl>
<dt>If any of <code>ZRVL</code> ... <code>FFVL</code> is set
(is not <code>0</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL&amp;MASK</code><br>
<u>Input:</u> <code>RVAL=<i>x</i>&amp;MASK</code><br>
Note that the record shifts <code>RVAL</code> right by
<code>SHFT</code> bits, compares the result with all of
<code>ZRVL</code> ... <code>FFVL</code>, and sets <code>VAL</code>
to the index of the first match.
<code>MASK</code> is initialized to <code>NOBT</code> 1-bits shifted
left by <code>SHFT</code>. If <code>MASK==0</code> (because
<code>NOBT</code> was not set) it is ignored, i.e.
<code><i>x</i>=RVAL</code> and <code>RVAL=<i>x</i></code>.
</dd>
<dt>If none of <code>ZRVL</code> ... <code>FFVL</code> is set
(all are <code>0</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
</dl>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> Depending on <code>VAL</code>, one of <code>ZRST</code>
or <code>FFST</code> is written. <code>VAL</code> must be in the range
0 ... 15.<br>
<u>Input:</u> If input is equal one of <code>ZRST</code> ...
<code>FFST</code>, <code>VAL</code> is set accordingly.
Other input strings are not accepted.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

80
doc/mbbiDirect.html Normal file
View File

@ -0,0 +1,80 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: mbbiDirect Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: mbbiDirect Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<dl>
<dt>If <code>MASK==0</code> (because <code>NOBT</code> is not set):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>If <code>MASK!=0</code>:</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL&amp;MASK</code><br>
<u>Input:</u> <code>RVAL=<i>x</i>&amp;MASK</code><br>
</dd>
</dl>
<code>MASK</code> is initialized to <code>NOBT</code> 1-bits shifted
left by <code>SHFT</code>.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">calcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

95
doc/mbbo.html Normal file
View File

@ -0,0 +1,95 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: mbbo Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: mbbo Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<dl>
<dt>If any of <code>ZRVL</code> ... <code>FFVL</code> is set
(is not <code>0</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL&amp;MASK</code><br>
Note that the record calculates <code>RVAL</code> by choosing one of
<code>ZRVL</code> ... <code>FFVL</code> depending on <code>VAL</code>
and by shifting it left by <code>SHFT</code> bits.<br>
<u>Input:</u> <code>RBV=<i>x</i>&amp;MASK</code><br>
<code>MASK</code> is initialized to <code>NOBT</code> 1-bits shifted
left by <code>SHFT</code>. If <code>MASK==0</code> (because
<code>NOBT</code> was not set) it is ignored, i.e.
<code><i>x</i>=RVAL</code> and <code>RBV=<i>x</i></code>.
</dd>
<dt>If none of <code>ZRVL</code> ... <code>FFVL</code> is set
(all are <code>0</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
</dl>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> Depending on <code>VAL</code>, one of <code>ZRST</code>
... <code>FFST</code> is written. <code>VAL</code> must be in the
range 0 ... 15.<br>
<u>Input:</u> If input is equal one of <code>ZRST</code> ...
<code>FFST</code>, <code>VAL</code> is set accordingly.
Other input strings are not accepted.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. In contrast to normal operation, LONG input is put to
<code>RVAL</code> as well as to <code>RBV</code> and converted by the
record.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

82
doc/mbboDirect.html Normal file
View File

@ -0,0 +1,82 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: mbboDirect Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: mbboDirect Records</h1>
<h2>Normal Operation</h2>
<p>
Depending on the format type, different record fields are used
for output and input. The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<dl>
<dt>If <code>MASK==0</code> (because <code>NOBT</code> is not set):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>If <code>MASK!=0</code>:</dt>
<dd>
<u>Output:</u> <code><i>x</i>=RVAL&amp;MASK</code><br>
<u>Input:</u> <code>RBV=<i>x</i>&amp;MASK</code><br>
</dd>
</dl>
<code>MASK</code> is initialized to <code>NOBT</code> 1-bits shifted
left by <code>SHFT</code>.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
Not allowed.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. In contrast to normal operation, input is put to
<code>RVAL</code> as well as to <code>RBV</code> and converted by the
record if <code>MASK!=0</code>.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

218
doc/nav.html Normal file
View File

@ -0,0 +1,218 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Navbar</title>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
<script type="text/javascript">
<!--
active="";
function show(name) {
var m = document.getElementById(name);
var b = document.getElementById(name+"Button");
if (active != "") { hide(active); }
m.style.display = "block";
b.innerHTML="[&laquo;]";
b.href="javascript:hide(\'"+name+"\')"
active=name;
}
function hide(name) {
var m = document.getElementById(name);
var b = document.getElementById(name+"Button");
m.style.display = "none";
b.innerHTML="[&raquo;]";
b.href="javascript:show(\'"+name+"\')"
active="";
}
function printview() {
w = window.open(parent.text.location, "_parent", "dependent=yes,width=800,menubar=yes,toolbar=yes,location=yes");
}
-->
</script>
<style type="text/css">
<!--
body {margin:0px 0px 0px 0px;
font-family:sans-serif;
font-weight:bold;
font-size:13px;}
a {text-decoration:none;}
a:link {color:#333333;}
a:visited {color:#333333;}
a:hover {color:#FF0000;}
a:active {color:#FF0000;}
.top {margin-bottom: 0px;
margin-top: 10px;
border-bottom: solid 1px black;}
.top ul {margin-left: 5px;
margin-top: 2px;
margin-bottom: 2px;
font-size:95%;
line-height:120%;
padding-left:1em;}
.subsub {margin-left: 15px;
line-height:120%;
font-size:80%;}
h1 {font-size:120%;
margin-top: 20px;
margin-right: 20px;
margin-bottom: 10px;
color:#267ab8;}
-->
</style>
</head>
<body background="bg.gif">
<p align="center">
</p>
<div style="margin-left: 20px;">
<h1>User's Guide</h1>
<div class="top">
<a target="text" href="stream.html">Intro</a>
</div>
<div class="top">
<a target="text" href="setup.html">Setup</a>
<a href="javascript:show('setup')" id="setupButton"></a>
<ul id="setup">
<li><a target="text" href="setup.html#pre">Prerequisites</a>
<ul>
<li><a target="text" href="epics3_13.html">Using EPICS 3.13</a></li>
</ul></li>
<li><a target="text" href="setup.html#lib">Build Library</a></li>
<li><a target="text" href="setup.html#app">Build Application</a></li>
<li><a target="text" href="setup.html#sta">Startup Script</a></li>
<li><a target="text" href="setup.html#pro">Protocol File</a>
<ul>
<li><a target="text" href="setup.html#reload">Reloading</a></li>
</ul></li>
<li><a target="text" href="setup.html#rec">Records</a></li>
</ul>
</div>
<div class="top">
<a target="text" href="protocol.html">Protocol Files</a>
<a href="javascript:show('protocol')" id="protocolButton"></a>
<ul id="protocol">
<li><a target="text" href="protocol.html#gen">General</a></li>
<li><a target="text" href="protocol.html#proto">Protocols</a></li>
<li><a target="text" href="protocol.html#cmd">Commands</a></li>
<li><a target="text" href="protocol.html#str">Strings</a></li>
<li><a target="text" href="protocol.html#var">Variables</a>
<ul>
<li><a target="text" href="protocol.html#sysvar">System variables</a></li>
<li><a target="text" href="protocol.html#argvar">Protocol Arguments</a></li>
<li><a target="text" href="protocol.html#usrvar">User variables</a></li>
</ul></li>
<li><a target="text" href="protocol.html#except">Exception Handlers</a></li>
</ul>
</div>
<div class="top">
<a target="text" href="formats.html">Format Converters</a>
<a href="javascript:show('formats')" id="formatsButton"></a>
<ul id="formats">
<li><a target="text" href="formats.html#syntax">Syntax</a></li>
<li><a target="text" href="formats.html#types">Types &amp; Fields</a></li>
<li><a target="text" href="formats.html#stdd">%f %e %g</a></li>
<li><a target="text" href="formats.html#stdl">%i %d %u %o %x</a></li>
<li><a target="text" href="formats.html#stds">%s %c</a></li>
<li><a target="text" href="formats.html#cset">%[<em>charset</em>]</a></li>
<li><a target="text" href="formats.html#enum">%{<em>s0</em>|<em>s1</em>|...}</a></li>
<li><a target="text" href="formats.html#bin">%b %B</a></li>
<li><a target="text" href="formats.html#raw">%r</a></li>
<li><a target="text" href="formats.html#bcd">%D</a></li>
<li><a target="text" href="formats.html#chksum">%&lt;<em>checksum</em>&gt;</a></li>
</ul>
</div>
<div class="top">
<a target="text" href="processing.html">Record Processing</a>
<a href="javascript:show('proc')" id="procButton"></a>
<ul id="proc">
<li><a target="text" href="processing.html#proc">Normal Processing</a></li>
<li><a target="text" href="processing.html#init">Initialization</a></li>
<li><a target="text" href="processing.html#iointr">I/O Intr</a></li>
</ul>
</div>
<div class="top">
<a target="text" href="recordtypes.html">Record Types</a>
<a href="javascript:show('records')" id="recordsButton"></a>
<ul id="records" class="sub">
<li><a target="text" href="aai.html">aai</a></li>
<li><a target="text" href="aao.html">aao</a></li>
<li><a target="text" href="ai.html">ai</a></li>
<li><a target="text" href="ao.html">ao</a></li>
<li><a target="text" href="bi.html">bi</a></li>
<li><a target="text" href="bo.html">bo</a></li>
<li><a target="text" href="mbbi.html">mbbi</a></li>
<li><a target="text" href="mbbo.html">mbbo</a></li>
<li><a target="text" href="mbbiDirect.html">mbbiDirect</a></li>
<li><a target="text" href="mbboDirect.html">mbboDirect</a></li>
<li><a target="text" href="stringin.html">stringin</a></li>
<li><a target="text" href="stringout.html">stringout</a></li>
<li><a target="text" href="longin.html">longin</a></li>
<li><a target="text" href="longout.html">longout</a></li>
<li><a target="text" href="waveform.html">waveform</a></li>
<li><a target="text" href="calcout.html">calcout</a></li>
<li><a target="text" href="scalcout.html">scalcout</a></li>
</ul>
</div>
<div class="top">
<a target="text" href="tipsandtricks.html">Tips &amp; Tricks</a>
<a href="javascript:show('tipsandtricks')" id="tipsandtricksButton"></a>
<ul id="tipsandtricks">
<li><a target="text" href="tipsandtricks.html#argvar">Many almost identical protocols</a></li>
<li><a target="text" href="tipsandtricks.html#iointr">Read unsolicited input</a></li>
<li><a target="text" href="tipsandtricks.html#multiline">Read multi-line messages</a></li>
<li><a target="text" href="tipsandtricks.html#writemany">Write more than one value in one message</a></li>
<li><a target="text" href="tipsandtricks.html#readmany">Read more than one value from one message</a></li>
<li><a target="text" href="tipsandtricks.html#mixed">Read values of mixed data type</a></li>
</ul>
</div>
<h1>Programmer's Guide</h1>
<div class="top">
<a target="text" href="recordinterface.html">Record API</a>
</div>
<div class="top">
<a target="text" href="businterface.html">Bus API</a>
<a href="javascript:show('bus')" id="busButton"></a>
<ul id="bus">
<li><a target="text" href="businterface.html#class">Interface Class</a></li>
<li><a target="text" href="businterface.html#theory">Theory of Operation</a>
<ul>
<li><a target="text" href="businterface.html#registration">Registration</a></li>
<li><a target="text" href="businterface.html#create">Creation &amp; deletion</a></li>
<li><a target="text" href="businterface.html#connect">Connecting</a></li>
<li><a target="text" href="businterface.html#lock">Bus locking</a></li>
<li><a target="text" href="businterface.html#write">Writing output</a></li>
<li><a target="text" href="businterface.html#read">Reading input</a></li>
<li><a target="text" href="businterface.html#event">Handling events</a></li>
</ul></li>
</ul>
</div>
<div class="top">
<a target="text" href="formatconverter.html">Format Converter API</a>
</div>
<div class="top">
<a target="text" href="osinterface.html">Operating System API</a>
</div>
</div>
<p align="center" style="margin-top:20px;">
<script type="text/javascript">
<!--
document.writeln('<a href="javascript:printview()">' +
'<img src="printer.gif" alt="Printer friendly" border=0><br>' +
'<small>Printer friendly<\/small><\/a>');
hide('setup');
hide('protocol');
hide('formats');
hide('proc');
hide('records');
hide('bus');
hide('tipsandtricks');
chapter=decodeURIComponent(parent.location.search.substr(1));
if (chapter) parent.text.location.href=chapter;
//-->
</script>
</p>
</body>
</html>

20
doc/osinterface.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Operating System API</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Operating System API</h1>
<h2>Sorry, this documentation is still missing.</h2>
<hr>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

BIN
doc/printer.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

244
doc/processing.html Normal file
View File

@ -0,0 +1,244 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Record Processing</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Record Processing</h1>
<a name="proc"></a>
<h2>1. Normal Processing</h2>
<p>
<em>StreamDevice</em> is an asynchronous device support
(see <a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/AppDevGuide.pdf"
target="ex">IOC Application Developer's Guide</a> chapter 12:
Device Support).
Whenever the record is processed, the <a href="protocol.html">protocol</a>
is scheduled to start and the record is left active (<code>PACT=1</code>).
The protocol itself runs in another thread.
That means that any waiting in the protocol does not delay any other
part of the IOC.
</p>
<p>
After the protocol has finished, the record is processed again, leaving
<code>PACT=0</code> this time, triggering monitors and processing
the forward link <code>FLNK</code>.
Note that input links with PP flag pointing to a <em>StreamDevice</em>
record will read the <u>old</u> value first and start the protocol
afterward.
This is a problem all asynchronous EPICS device supports have.
</p>
<p>
The first <code>out</code> command in the protocol locks the device
for exclusive access.
That means that no other record can communicate with that device.
This ensures that replies given by the device reach the record
which has sent the request.
On a bus with many devices on different addresses, this
normally locks only one device.
The device is unlocked when the protocol terminates.
Another record trying to lock the same device has to wait and
might get a <code>LockTimeout</code>.
<p>
</p>
<p>If any error happens, the protocol is aborted. The record will have
its <code>SEVR</code> field set to <code>INVALID</code> and its
<code>STAT</code> field to something describing the error:
</p>
<dl>
<dt><code>TIMEOUT</code></dt>
<dd>
The device could not be locked (<code>LockTimeout</code>) or the
device did not reply (<code>ReplyTimeout</code>).
</dd>
<dt><code>WRITE</code></dt>
<dd>
Output could not be written to the device (<code>WriteTimeout</code>).
</dd>
<dt><code>READ</code></dt>
<dd>
Input from the device started but stopped unexpectedly
(<code>ReadTimeout</code>).
</dd>
<dt><code>COMM</code></dt>
<dd>
The device driver reported some other communication error (e.g.
unplugged cable).
</dd>
<dt><code>CALC</code></dt>
<dd>
Input did not match the argument string of the <code>in</code> command
or it contained values the record did not accept.
</dd>
<dt><code>UDF</code></dt>
<dd>
Some fatal error happened or the record has not been initialized
correctly (e.g. because the protocol is erroneous).
</dd>
</dl>
<p>
If the protocol is aborted, an
<a href="protocol.html#except">exception handler</a> might be executed
if defined.
Even if the exception handler can complete with no further error, the
protocol will not resume and <code>SEVR</code> and <code>STAT</code>
will be set according to the original error.
</p>
<a name="init"></a>
<h2>2. Initialization</h2>
<p>
Often, it is required to initialize records from the hardware after
booting the IOC, especially output records.
For this purpose, initialization is formally handled as an
<a href="protocol.html#except">exception</a>.
The <code>@init</code> handler is called as part of the
<code>initRecord()</code> function during <code>iocInit</code>
before any scan task starts.
</p>
<p>
In contrast to <a href="#proc">normal processing</a>, the protocol
is handled synchronously.
That means that <code>initRecord()</code> does not return before the
<code>@init</code> handler has finished.
Thus, the records initialize one after the other.
The scan tasks are not started and <code>iocInit</code> does not
return before all <code>@init</code> handlers have finished.
If the handler fails, the record remains uninitialized:
<code>UDF=1</code>, <code>SEVR=INVALID</code>,
<code>STAT=UDF</code>.
</p>
<p>
The <code>@init</code> handler has nothing to do with the
<code>PINI</code> field.
The handler does <u>not</u> process the record nor does it trigger
forward links or other PP links.
It runs <u>before</u> <code>PINI</code> is handled.
If the record has <code>PINI=YES</code>, the <code>PINI</code>
processing is a <a href="#proc">normal processing</a> after the
<code>@init</code> handlers of all records have completed.
</p>
<p>
Depending on the record type, format converters might work
slightly different from normal processing.
Refer to the description of
<a href="recordtypes.html">supported record types</a> for details.
</p>
<p>
If the <code>@init</code>handler has read a value and has completed
without error, the record starts in a defined state.
That means <code>UDF=0</code>, <code>SEVR=NO_ALARM</code>,
<code>STAT=NO_ALARM</code> and the <code>VAL</code> field contains
the value read from the device.
</p>
<p>
If no <code>@init</code> handler is installed, <code>VAL</code> and
<code>RVAL</code> fields remain untouched.
That means they contain the value defined in the record definition,
read from a constant <code>INP</code> or <code>DOL</code> field,
or restored from a bump-less reboot system
(e.g. <em>autosave</em> from the <em>synApps</em> package).
</p>
<a name="iointr"></a>
<h2>3. I/O Intr</h2>
<p>
<em>StreamDevice</em> supports I/O event scanning.
This is a mode where record processing is triggered by the device
whenever the device sends input.
</p>
<p>
In terms of protocol execution this means:
When the <code>SCAN</code> field is set to <code>I/O Intr</code>
(during <code>iocInit</code> or later),
the protocol starts without processing the record.
With the first <code>in</code> command, the protocol is suspended.
If the device has been locked (i.e there was an <code>out</code>
command earlier in the protocol), it is unlocked now.
That means that other records can communicate to the device while
this record is waiting for input.
This <code>in</code> command ignores <code>replyTimeout</code>,
it waits forever.
</p>
<p>
The protocol now receives any input from the device.
It also gets a copy of all input directed to other records.
Non-matching input does not generate a mismatch
<a href="protocol.html#except">exception</a>.
It just restarts the <code>in</code> command until matching input
is received.
</p>
<p>
After receiving matching input, the protocol continues normally.
All other <code>in</code> commands are handled normally.
When the protocol has completed, the record is processed.
It then triggers monitors, forward links, etc.
After the record has been processed, the protocol restarts.
</p>
<p>
This mode is useful in two cases:
First for devices that send data automatically without being asked.
Second to distribute multiple values in one message to different
records.
In this case, one record would send a request to the device and pick
only one value out of the reply.
The other values are read by records in <code>I/O Intr</code> mode.
</p>
<h3>Example:</h3>
<p>
Device <em>dev1</em> has a "region of interest" (ROI) defined by
a start value and an end value. When asked "<code>ROI?</code>",
it replies something like "<code>ROI 17.3 58.7</code>",
i.e. a string containing both values.
</p>
<p>
We need two ai records to store the two values. Whenever record
<code>ROI:start</code> is processed, it requests ROI from the device.
Record <code>ROI:end</code> updates automatically.
</p>
<pre>
record (ai "ROI:start") {
field (DTYP, "stream")
field (INP, "@myDev.proto getROIstart dev1")
}
record (ai "ROI:end") {
field (DTYP, "stream")
field (INP, "@myDev.proto getROIend dev1")
field (SCAN, "I/O Intr")
}
</pre>
<p>
Only one of the two protocols sends a request, but both read
their part of the same reply message.
</p>
<pre>
getROIstart {
out "ROI?";
in "ROI %f %*f";
}
getROIend {
in "ROI %*f %f";
}
</pre>
<p>
Note that the other value is also parsed by each protocol, but skipped
because of the <code>%*</code> <a href="formats.html">format</a>.
Even though the <code>getROIend</code> protocol may receive input
from other requests, it silently ignores every message that does not start
with "<code>ROI</code>", followed by two floating point numbers.
</p>
<hr>
<p align="right"><a href="recordtypes.html">Next: Supported Record Types</a></p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

552
doc/protocol.html Normal file
View File

@ -0,0 +1,552 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Protocol Files</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Protocol Files</h1>
<a name="gen"></a>
<h2>1. General Information</h2>
<p>
A protocol file describes the communication with one device type.
It contains <a href="#proto"><em>protocols</em></a> for each function
of the device type and <a href="#var"><em>variables</em></a> which affect
how the <a href="#cmd"><em>commands</em></a> in a protocol work.
It does not contain information about the individual device or the used
communication bus.
</p>
<p>
Each device type should have its own protocol file.
I suggest to choose a file name that contains the name of the device type.
Don't use spaces in the file name and keep it short.
The file will be referenced by its name in the <code>INP</code>
or <code>OUT</code> link of the records which use it.
The protocol file must be stored in one of the directories listed
in the environment variable <code>STREAM_PROTOCOL_PATH</code>
(see chapter <a href="setup.html#sta">Setup</a>).
</p>
<p>
The protocol file is a plain text file.
Everything not enclosed in quotes
(single <code>'</code> or double <code>"</code>) is not case sensitive.
This includes the names of <a href="#cmd">commands</a>,
<a href="#proto">protocols</a> and <a href="#var">variables</a>.
There may be any amount of whitespaces (space, tab, newline, ...) or
comments between names, <a href="#str">quoted strings</a> and special
characters, such as <code>={};</code>.
A comment is everything starting from an unquoted <code>#</code>
until the end of the line.
</p>
<h3>Example Protocol File:</h3>
<pre>
# This is an example protocol file
Terminator = CR LF;
# Frequency is a float
# use ai and ao records
getFrequency {
out "FREQ?"; in "%f";
}
setFrequency {
out "FREQ %f";
@init { getFrequency; }
}
# Switch is an enum, either OFF or ON
# use bi and bo records
getSwitch {
out "SW?"; in "SW %{OFF|ON}";
}
setSwitch {
out "SW %{OFF|ON}";
@init { getSwitch; }
}
# Connect a stringout record to this to get
# a generic command interface.
# After processing finishes, the record contains the reply.
debug {
ExtraInput = Ignore;
out "%s"; in "%39c"
}
</pre>
<a name="proto"></a>
<h2>2. Protocols</h2>
<p>
For each function of the device type, define one protocol.
A protocol consists of a name followed by a body in braces <code>{}</code>.
The name must be unique within the protocol file.
It is used to reference the protocol in the
<code>INP</code> or <code>OUT</code> link of the record,
thus keep it short.
It should describe the function of the protocol.
It must not contain spaces or any of the characters
<code>,;={}()$'"\#</code>.
</p>
<p>
The protocol body contains a sequence of <a href="#cmd">commands</a> and
optionally <a href="#var">variable assignments</a> separated by
<code>;</code>.
</p>
<h3>Referencing other protocols</h3>
<p>
To save some typing, a previously defined protocol can be called inside
another protocol like a <a href="#cmd">command</a> without parameters.
The protocol name is replaced by the commands in the referenced protocol.
However, this does not include any
<a href="#var">variable assignments</a> or
<a href="#except">exception handlers</a> from the referenced protocol.
See the <code>@init</code> handlers in the above example.
</p>
<h3>Limitations</h3>
<p>
The <em>StreamDevice</em> protocol is not a programming language.
It has neither loops nor conditionals
(in this version of <em>StreamDevice</em>).
However, if an error occurs, e.g. a timeout or a mismatch in input
parsing, an <a href="#excep">exception handler</a> can be called to
clean up.
</p>
<a name="cmd"></a>
<h2>3. Commands</h2>
<p>
Seven different commands can be used in a protocol:
<code>out</code>, <code>in</code>, <code>wait</code>, <code>event</code>,
<code>exec</code>, <code>disconnect</code>, and <code>connect</code>.
Most protocols will consist only of a single <code>out</code> command to
write some value,
or an <code>out</code> command followed by an <code>in</code> command to
read a value.
But there can be any number of commands in a protocol.
</p>
<dl>
<dt><code>out <i>string</i>;</code></dt>
<dd>
Write output to the device.
The argument <a href="#str">string</a> may contain
<a href="formats.html">format converters</a> which are replaced by the
formatted value of the record before sending.
</dd>
<dt><code>in <i>string</i>;</code></dt>
<dd>
Read and parse input from the device.
The argument <a href="#str">string</a> may contain
<a href="formats.html">format converters</a> which specify how to
interpret data to be put into the record.
Input must match the argument string.
Any input from the device should be consumed with an
<code>in</code> command.
If a device, for example, acknowledges a setting, use an
<code>in</code> command to check the acknowledge, even though
it contains no user data.
</dd>
<dt><code>wait <i>milliseconds</i>;</code></dt>
<dd>
Just wait for some milliseconds.
Depending on the resolution of the timer system, the actual delay
can be slightly longer than specified.
</dd>
<dt><code>event(<i>eventcode</i>) <i>milliseconds</i>;</code></dt>
<dd>
Wait for event <code><i>eventcode</i></code> with some timeout.
What an event actually means depends on the used
<a href="businterface.html#event">bus</a>.
Some buses do not support events at all, some provide many different
events.
If the bus supports only one event, <code>(<i>eventcode</i>)</code>
is dispensable.
</dd>
<dt><code>exec <i>string</i>;</code></dt>
<dd>
The argument <a href="#str">string</a> is passed to the IOC shell
as a command to execute.
</dd>
<dt><code>disconnect;</code></dt>
<dd>
Disconnect from the hardware.
This is probably not supported by all busses.
Any <code>in</code> or <code>out</code> command will automatically
reconnect.
Only records reading in
<a href="processing.html#iointr">"I/O Intr"</a> mode
will not cause a reconnect.
</dd>
<dt><code>connect <i>milliseconds</i>;</code></dt>
<dd>
Explicitely connect to the hardware with <code><i>milliseconds</i></code>
timeout.
Since connection is handled automatically, this command is normally not
needed.
It may be useful after a <code>disconnect</code>.
</dd>
</dl>
<a name="str"></a>
<h2>4. Strings</h2>
<p>
In a <em>StreamDevice</em> protocol file, strings can be written
as quoted literals (single quotes or double quotes), as
a sequence of bytes values, or as a combination of both.
</p>
<p>
Examples for quoted literals are:<br>
<code>"That's a string."</code><br>
<code>'Say "Hello"'</code>
</p>
<p>
There is no difference between double quoted and single quoted
literals, it just makes it easier to use quotes of the other type
in a string.
To break long strings into multiple lines of the protocol file,
close the quotes before the line break and reopen them in the next line.
Don't use a line break inside quotes.
</p>
<p>
As arguments of <code>out</code> or <code>in</code>
<a href="#cmd">commands</a>, string literals can contain
<a href="formats.html">format converters</a>.
A format converter starts with <code>%</code> and works similar
to formats in the C functions <em>printf()</em> and <em>scanf()</em>.
</p>
<p>
<em>StreamDevice</em> uses the backslash character <code>\</code> to
define some escape sequences in quoted string literals:<br>
<code>\"</code>, <code>\'</code>, <code>\%</code>, and <code>\\</code>
mean literal <code>"</code>, <code>'</code>, <code>%</code>, and
<code>\</code>.<br>
<code>\a</code> means <em>alarm bell</em> (ASCII code 7).<br>
<code>\b</code> means <em>backspace</em> (ASCII code 8).<br>
<code>\t</code> means <em>tab</em> (ASCII code 9).<br>
<code>\n</code> means <em>new line</em> (ASCII code 10).<br>
<code>\r</code> means <em>carriage return</em> (ASCII code 13).<br>
<code>\e</code> means <em>escape</em> (ASCII code 27).<br>
<code>\x</code> followed by up to two hexadecimal digits means a byte with
that hex value.<br>
<code>\0</code> followed by up to three octal digits means a byte with
that octal value.<br>
<code>\1</code> to <code>\9</code> followed by up to two more decimal
digits means a byte with that decimal value.<br>
<code>\?</code> in the argument string of an <code>in</code>
<a href="#cmd">command</a> matches any input byte<br>
<code>\$</code> followed by the name of a
<a href="#var">protocol varible</a> is replaced by the contents of that
variable.
</p>
<p>
For non-printable characters, it is often easier to write sequences of
byte values instead of escaped quoted string literals.
A byte is written as an unquoted decimal, hexadecimal, or octal
number in the range of -128 to 255 (-0x80 to 0xff, -0200 to 0377).
<em>StreamDevice</em> also defines some symbolic names for frequently
used byte codes as aliases for the numeric byte value:<br>
<code>EOT</code> means <em>end of transmission</em> (ASCII code 4).<br>
<code>ACK</code> means <em>acknowledge</em> (ASCII code 6).<br>
<code>BEL</code> means <em>bell</em> (ASCII code 7).<br>
<code>BS</code> means <em>backspace</em> (ASCII code 8).<br>
<code>HT</code> or <code>TAB</code> mean <em>horizontal tabulator</em>
(ASCII code 9).<br>
<code>LF</code> or <code>NL</code> mean <em>line feed</em> /
<em>new line</em> (ASCII code 10).<br>
<code>CR</code> means <em>carriage return</em> (ASCII code 13).<br>
<code>ESC</code> means <em>escape</em> (ASCII code 27).<br>
<code>DEL</code> means <em>delete</em> (ASCII code 127).<br>
<code>SKIP</code> in the argument string of an <code>in</code>
<a href="#cmd">command</a> matches any input byte.
</p>
<p>
A single string can be built from several quoted literals and byte values
by writing them separated by whitespaces or comma.
</p>
<h3>Example:</h3>
<p>
The following lines represent the same string:<br>
<code>"Hello world\r\n"</code><br>
<code>'Hello',0x20,"world",CR,LF</code><br>
<code>72 101 108 108 111 32 119 111 114 108 100 13 10</code>
</p>
<a name="var"></a>
<h2>5. Protocol Variables</h2>
<p>
<em>StreamDevice</em> uses three types of variables in a protocol file.
<a href="#sysvar"><em>System variables</em></a> influence the behavior
of <code>in</code> and <code>out</code> <a href="#cmd">commands</a>.
<a href="#argvar"><em>Protocol arguments</em></a> work like function
arguments and can be specified in the <code>INP</code> or
<code>OUT</code> link of the record.
<a href="#usrvar"><em>User variables</em></a> can be defined and used
in the protocol as abbreviations for often used values.
</p>
<p>
System and user variables can be set in the global context of the
protocol file or locally inside protocols.
When set globally, a variable keeps its value until overwritten.
When set locally, a variable is valid inside the protocol only.
To set a variable use the syntax:<br>
<code><i>variable</i> = <i>value</i>;</code>
</p>
<p>
Set variables can be referenced outside of
<a href="#str">quoted strings</a> by
<code>$<i>variable</i></code> or <code>${<i>variable</i>}</code>
and inside quoted strings by
<code>\$<i>variable</i></code> or <code>\${<i>variable</i>}</code>.
The reference will be replaced by the value of the variable at
this point.
</p>
<a name="sysvar"></a>
<h3>System variables</h3>
<p>
This is a list of system variables, their default settings and
what they influence.
</p>
<dl>
<dt><code>LockTimeout = 5000;</code></dt>
<dd>
Integer. Affects first <code>out</code> command in a protocol.<br>
If other records currently use the device, how many milliseconds
to wait for exclusive access to the device before giving up?
</dd>
<dt><code>WriteTimeout = 100;</code></dt>
<dd>
Integer. Affects <code>out</code> commands.<br>
If we have access to the device but output cannot be written
immediately, how many milliseconds to wait before giving up?
</dd>
<dt><code>ReplyTimeout = 1000;</code></dt>
<dd>
Integer. Affects <code>in</code> commands.<br>
Different devices need different times to calculate
a reply and start sending it.
How many milliseconds to wait for the first byte of the input
from the device?
Since several other records may be waiting to access the device
during this time, <code>LockTimeout</code> should be larger than
<code>ReplyTimeout</code>.
</dd>
<dt><code>ReadTimeout = 100;</code></dt>
<dd>
Integer. Affects <code>in</code> commands.<br>
The device may send input in pieces (e.g. bytes).
When it stops sending, how many milliseconds to wait for more
input bytes before giving up?
If <code>InTerminator = ""</code>, a read timeout is not an error
but a valid input termination.
</dd>
<dt><code>PollPeriod = $ReplyTimeout;</code></dt>
<dd>
Integer. Affects first <code>in</code> command in
<code>I/O Intr</code> mode (see chapter
<a href="processing.html#iointr">Record Processing</a>).<br>
In that mode, some buses require periodic polling to get asynchronous
input if no other record executes an <code>in</code> command at
the moment.
How many milliseconds to wait after last poll or last received
input before polling again?
If not set the same value as for <code>ReplyTimeout</code> is
used.
</dd>
<dt><code>Terminator</code></dt>
<dd>
String. Affects <code>out</code> and <code>in</code> commands.<br>
Most devices send and expect terminators after each message,
e.g. <code>CR LF</code>.
The value of the <code>Terminator</code> variable is automatically
appended to any output.
It is also used to find the end of input.
It is removed before the input is passed to the <code>in</code>
command.
If no <code>Terminator</code> or <code>InTerminator</code> is defined,
the underlying driver may use its own terminator settings.
For example, <i>asynDriver</i> defines its own terminator settings.
</dd>
<dt><code>OutTerminator = $Terminator;</code></dt>
<dd>
String. Affects <code>out</code> commands.<br>
If a device has different terminators for input and output,
use this for the output terminator.
</dd>
<dt><code>InTerminator = $Terminator;</code></dt>
<dd>
String. Affects <code>in</code> commands.<br>
If a device has different terminators for input and output,
use this for the input terminator.
If no <code>Terminator</code> or <code>InTerminator</code> is defined,
the underlying driver may use its own terminator settings.
If <code>InTerminator = ""</code>, a read timeout is not an error
but a valid input termination.
</dd>
<dt><code>MaxInput = 0;</code></dt>
<dd>
Integer. Affects <code>in</code> commands.<br>
Some devices don't send terminators but always send a fixed message
size. How many bytes to read before terminating input even without
input terminator or read timeout?
The value <code>0</code> means "infinite".
</dd>
<dt><code>Separator = "";</code></dt>
<dd>
String. Affects <code>out</code> and <code>in</code> commands.<br>
When formatting or parsing array values in a format converter
(see <a href="formats.html">formats</a> and
<a href="waveform.html">waveform record</a>), what string
to write or to expect between values?
If the first character of the <code>Separator</code> is a
space, it matches any number of any whitespace characters in
an <code>in</code> command.
</dd>
<dt><code>ExtraInput = Error;</code></dt>
<dd>
<code>Error</code> or <code>Ignore</code>.
Affects <code>in</code> commands.<br>
Normally, when input parsing has completed, any bytes left in the
input are treated as parse error.
If extra input bytes should be ignored, set
<code>ExtraInput = Ignore;</code>
</dd>
</dl>
<a name="argvar"></a>
<h3>Protocol arguments</h3>
<p>
Sometimes, protocols differ only very little.
In that case it can be convenient to write only one protocol
and use <em>protocol arguments</em> for the difference.
For example a motor controller for the 3 axes X, Y, Z requires
three protocols to set a position.
</p>
<pre>
moveX { out "X GOTO %d"; }
moveY { out "Y GOTO %d"; }
moveZ { out "Z GOTO %d"; }
</pre>
<p>
It also needs three versions of any other protocol.
That means basically writing everything three times.
To make this easier, <em>protocol arguments</em> can be used:
</p>
<pre>
move { out "\$1 GOTO %d"; }
</pre>
<p>
Now, the protocol can be references in the <code>OUT</code> link
of three different records as <code>move(X)</code>,
<code>move(Y)</code> and <code>move(Z)</code>.
Up to 9 parameters, referenced as <code>$1</code> ... <code>$9</code>
can be specified in parentheses, separated by comma.
The variable <code>$0</code> is replaced by the name of the protocol.
</p>
<a name="usrvar"></a>
<h3>User variables</h3>
<p>
User defined variables are just a means to save some typing.
Once set, a user variable can be referenced later in the protocol.
</p>
<pre>
f = "FREQ"; # sets f to "FREQ" (including the quotes)
f1 = $f " %f"; # sets f1 to "FREQ %f"
getFrequency {
out $f "?"; # same as: out "FREQ?";
in $f1; # same as: in "FREQ %f";
}
setFrequency {
out $f1; # same as: out "FREQ %f";
}
</pre>
<a name="except"></a>
<h2>6. Exception Handlers</h2>
<p>
When an error happens, an exception handler may be called.
Exception handlers are a kind of sub-protocols in a protocol.
They consist of the same set of commands and are intended to
reset the device or to finish the protocol cleanly in case of
communication problems.
Like variables, exception handlers can be defined globally or
locally.
Globally defined handlers are used for all following protocols
unless overwritten by a local handler.
There is a fixed set of exception handler names starting with
<code>@</code>.
</p>
<dl>
<dt><code>@mismatch</code></dt>
<dd>
Called when input does not match in an <code>in</code> command.<br>
It means that the device has sent something else than what the
protocol expected.
If the handler starts with an <code>in</code> command, then this
command reparses the old input from the unsuccessful <code>in</code>.
Error messages from the unsuccessful <code>in</code> are suppressed.
Nevertheless, the record will end up in <code>INVALID/CALC</code>
state (see chapter <a href="processing.html#proc">Record Processing</a>).
</dd>
<dt><code>@writetimeout</code></dt>
<dd>
Called when a write timeout occurred in an <code>out</code> command.<br>
It means that output cannot be written to the device.
Note that <code>out</code> commands in the handler are
also likely to fail in this case.
</dd>
<dt><code>@replytimeout</code></dt>
<dd>
Called when a reply timeout occurred in an <code>in</code> command.<br>
It means that the device does not send any data.
Note that <code>in</code> commands in the handler are
also likely to fail in this case.
</dd>
<dt><code>@readtimeout</code></dt>
<dd>
Called when a read timeout occurred in an <code>in</code> command.<br>
It means that the device stopped sending data unexpectedly after
sending at least one byte.
</dd>
<dt><code>@init</code></dt>
<dd>
Not really an exception but formally specified in the same syntax.
This handler is called from <code>iocInit</code> during record
initialization.
It can be used to initialize an output record with a value read from
the device.
Also see chapter <a href="processing.html#init">Record Processing</a>.
</dd>
</dl>
<h3>Example:</h3>
<pre>
setPosition {
out "POS %f";
@init { out "POS?"; in "POS %f"; }
}
</pre>
<p>
After executing the exception handler, the protocol terminates.
If any exception occurs within an exception handler, no other handler
is called but the protocol terminates immediately.
An exception handler uses all <a href="#sysvar">system variable</a>
settings from the protocol in which the exception occurred.
</p>
<hr>
<p align="right"><a href="formats.html">Next: Format Converters</a></p>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

20
doc/recordinterface.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Record API</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Record API</h1>
<h2>Sorry, this documentation is still missing.</h2>
<hr>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

54
doc/recordtypes.html Normal file
View File

@ -0,0 +1,54 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Record Types</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Record Types</h1>
<h2>Supported Record Types</h2>
<p>
<em>StreamDevice</em> comes with support for all standard record types
in EPICS base which can have device support.
</p>
<p>
There is a separate page for each supported record type:<br>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p>
Each page describes which record fields are used in input and output
for different <a href="formats.html#types">format data types</a>
during <a href="processing.html#proc">normal record processing</a>
and <a href="processing.html#init">initialization</a>.
</p>
<p>
It is also possible to
<a href="recordinterface.html">write support for other recordtypes</a>.
</p>
<hr>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

101
doc/scalcout.html Normal file
View File

@ -0,0 +1,101 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: scalcout Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: scalcout Records</h1>
<p>
<b>Note:</b> The scalcout record is part of the <i>calc</i> module of
the <a target="ex"
href="http://www.aps.anl.gov/aod/bcda/synApps/index.php"
><em>synApps</em></a> package.
Device support for scalcout records is only available for <i>calc</i>
module release 2-4 or higher.
You also need the synApps modules <i>genSub</i> and <i>sscan</i> to
build <i>calc</i>.
</p>
<p>
Up to release 2-6 (synApps release 5.1), the scalcout record needs a fix.
In sCalcout.c at the end of <code>init_record</code> add
before the final <code>return(0)</code>:
</p>
<pre class="box">
if(pscalcoutDSET->init_record ) {
return (*pscalcoutDSET->init_record)(pcalc);
}
</pre>
<h2>Normal Operation</h2>
<p>
Different record fields are used for output and input. The variable
<code><i>x</i></code> stands for the written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=OVAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
Note that the record calculates <code>OVAL</code> from <code>CALC</code>
or <code>OCAL</code> depending on <code>DOPT</code>.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=int(OVAL)</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=int(OVAL)</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code><br>
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=OSV</code><br>
<u>Input:</u> <code>SVAL=<i>x</i></code><br>
</dd>
</dl>
<p>
For scalcout records, it is probably more useful to access fields
<code>A</code> to <code>L</code> and <code>AA</code> to <code>LL</code>
directly (e.g. <code>"%(A)f"</code> or <code>"%(BB)s"</code>).
However, even if <code>OVAL</code> is not used, it is calculated by the
record. Thus, <code>CALC</code> must always contain a valid expression
(e.g. <code>"0"</code>).
</p>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

290
doc/setup.html Normal file
View File

@ -0,0 +1,290 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Setup</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Setup</h1>
<a name="pre"></a>
<h2>1. Prerequisites</h2>
<p>
<em>StreamDevice</em> requires either
<a href="http://www.aps.anl.gov/epics/base/R3-14/index.php"
target="ex">EPICS base R3.14.6 or higher</a> or
<a href="http://www.aps.anl.gov/epics/base/R3-13.php"
target="ex">EPICS base R3.13.7 or higher</a>.
How to use <em>StreamDevice</em> on EPICS R3.13 is described on a
<a href="epics3_13.html">separate page</a>.
Because <em>StreamDevice</em> comes with an interface to
<a href="http://www.aps.anl.gov/epics/modules/soft/asyn/"
target="ex"><em>asynDriver</em> version R4-3 or higher</a> as the
underlying driver layer,
you should have <em>asynDriver</em> installed first.
</p>
<p>
<em>StreamDevice</em> has support for the
<a href="scalcout.html"><em>scalcout</em></a> record from the
<em>calc</em> module of <a target="ex"
href="http://www.aps.anl.gov/aod/bcda/synApps/index.php"
><em>synApps</em></a>.
Up to <em>calc</em> release R2-6 (<em>synApps</em> release R5_1),
the <em>scalcout</em> record needs a fix.
(See separate <a href="scalcout.html"><em>scalcout</em> page</a>.)
</p>
<p>
Up to release R3.14.8.2, a fix in EPICS base is required to build
<em>StreamDevice</em> on Windows (not cygwin).
In <kbd>src/iocsh/iocsh.h</kbd>, add the following line
and rebuild base.
</p>
<pre>
epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
</pre>
<p>
Make sure that the <em>asyn</em> library and the <em>calc</em> module of
<em>synApps</em> can be found, e.g. by
adding <code>ASYN</code>
and (if installed) <code>CALC</code> or <code>SYNAPPS</code>
to your <kbd>&lt;top&gt;/configure/RELEASE</kbd> file:
<pre>
ASYN=/home/epics/asyn/4-5
CALC=/home/epics/synApps/calc/2-7
</pre>
<p>
For details on <kbd>&lt;top&gt;</kbd> directories and RELEASE files,
please refer to the
<a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/AppDevGuide.pdf"
target="ex"><em>IOC Application Developer's Guide</em></a> chapter 4:
EPICS Build Facility.
</p>
<a name="lib"></a>
<h2>2. Build the <em>StreamDevice</em> Library</h2>
<p>
Unpack the
<a href="http://epics.web.psi.ch/software/streamdevice/StreamDevice-2.tgz"
><em>StreamDevice</em> package</a> in a <kbd>&lt;top&gt;</kbd> directory
of your application build area.
(You might probably have done this already.)
Go to the newly created <em>StreamDevice</em> directory
and run <kbd>make</kbd> (or <kbd>gmake</kbd>).
This will create and install the <em>stream</em> library and the
<kbd>stream.dbd</kbd> file.
</p>
<a name="app"></a>
<h2>3. Build an Application</h2>
<p>
To use <em>StreamDevice</em>, your application must be built with the
<em>asyn</em> and <em>stream</em> libraries and must load
<kbd>asyn.dbd</kbd> and <kbd>stream.dbd</kbd>.
</p>
<p>
Include the following lines in your application Makefile:
</p>
<pre>
PROD_LIBS += stream
PROD_LIBS += asyn
</pre>
<p>
Include the following lines in your xxxAppInclude.dbd file to use
<em>stream</em> and <em>asyn</em> with serial lines and IP sockets:
</p>
<pre>
include "base.dbd"
include "stream.dbd"
include "asyn.dbd"
registrar(drvAsynIPPortRegisterCommands)
registrar(drvAsynSerialPortRegisterCommands)
</pre>
<p>
You can find an example application in the <kbd>streamApp</kbd>
subdirectory.
</p>
<a name="sta"></a>
<h2>4. The Startup Script</h2>
<p>
<em>StreamDevice</em> is based on <a
href="protocol.html"><em>protocol files</em></a>.
To tell <em>StreamDevice</em> where to search for protocol files,
set the environment variable <code>STREAM_PROTOCOL_PATH</code> to a
list of directories to search.
On Unix and vxWorks systems, directories are separated by <code>:</code>,
on Windows systems by <code>;</code>.
The default value is <code>STREAM_PROTOCOL_PATH=.</code>,
i.e. the current directory.
</p>
<p>
Also configure the buses (in <em>asynDriver</em> terms: ports) you want
to use with <em>StreamDevice</em>.
You can give the buses any name you want, like <kbd>COM1</kbd> or
<kbd>socket</kbd>, but I recommend to use names related to the
connected device.
</p>
<h3>Example:</h3>
<p>
A power supply with serial communication (9600 baud, 8N1) is connected to
<kbd>/dev/ttyS1</kbd>.
The name of the power supply is <tt>PS1</tt>.
Protocol files are either in the current working directory or in the
<kbd>../protocols</kbd> directory.
</p>
<p>
Then the startup script must contain lines like this:
</p>
<pre>
epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
asynSetOption ("PS1", 0, "baud", "9600")
asynSetOption ("PS1", 0, "bits", "8")
asynSetOption ("PS1", 0, "parity", "none")
asynSetOption ("PS1", 0, "stop", "1")
asynSetOption ("PS1", 0, "clocal", "Y")
asynSetOption ("PS1", 0, "crtscts", "N")
</pre>
<p>If the power supply was connected via telnet-style TCP/IP
at address 192.168.164.10 on port 23,
the startupscript would contain:
</p>
<pre>
epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
drvAsynIPPortConfigure ("PS1", "192.168.164.10:23")
</pre>
<p>
With a VXI11 (GPIB via TCP/IP) connection, e.g. a
HP E2050A on IP address 192.168.164.10, it would look like this:
</p>
<pre>
epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
vxi11Configure ("PS1","192.168.164.10",1,1000,"hpib")
</pre>
<a name="pro"></a>
<h2>5. The Protocol File</h2>
<p>
For each different type of hardware, create a protocol file
which defines protocols for all needed functions of the device.
The file name is arbitrary, but I recommend that it contains
the device type.
It must not contain spaces and should be short.
During <code>iocInit</code>, <em>streamDevice</em> loads and parses
the required protocol files.
If the files contain errors, they are printed on the IOC shell.
Put the protocol file in one of the directories listed in
<code>STREAM_PROTOCOL_PATH</code>.
</p>
<h3>Example:</h3>
<p>
<tt>PS1</tt> is an <em>ExamplePS</em> power supply.
It communicates via ASCII strings which are terminated by
&lt;carriage&nbsp;return&gt; &lt;line&nbsp;feed&gt; (ASCII codes 13, 10).
The output current can be set by sending a string like
<code>"CURRENT 5.13"</code>.
When asked with the string <code>"CURRENT?"</code>, the device returns
the last set value in a string like <code>"CURRENT 5.13 A"</code>.
</p>
<p>
Normally, an analog output record should write its value to the device.
But during startup, the record should be initialized from the the device.
The protocol file <kbd>ExamplePS.proto</kbd> defines the protocol
<code>setCurrent</code>.
</p>
<pre>
Terminator = CR LF;
setCurrent {
out "CURRENT %.2f";
@init {
out "CURRENT?";
in "CURRENT %f A";
}
}
</pre>
<a name="reload"></a>
<h3>Reloading the Protocol File</h3>
<p>
During development, the protocol files might change frequently.
To prevent restarting the IOC all the time, it is possible to reload
the protocol file of one or all records with the shell function
<code>streamReload("<var>record</var>")</code>.
If <code>"<var>record</var>"</code> is not given, all records using
<em>StreamDevice</em> reload their protocols.
Furthermore, the <code>streamReloadSub</code> function can be used
with a subroutine record to reload all protocols.
</p>
<p>
Reloading the protocol file aborts currently running protocols.
This might set <code>SEVR=INVALID</code> and <code>STAT=UDF</code>.
If a record can't reload its protocol file (e.g. because of a syntax
error), it stays <code>INVALID</code>/<code>UDF</code> until a valid
protocol is loaded.
</p>
<p>
See the <a href="protocol.html">next chapter</a> for protocol files in depth.
</p>
<a name="rec"></a>
<h2>6. Configure the Records</h2>
<p>
To make a record use <em>StreamDevice</em>, set its <code>DTYP</code> field to
<code>"stream"</code>.
The <code>INP</code> or <code>OUT</code> link has the form
<code>"@<var>file&nbsp;protocol&nbsp;bus</var>&nbsp;[<var>address</var>&nbsp;[<var>parameters</var>]]"</code>.
</p>
<p>
Here, <code><var>file</var></code> is the name of the protocol file and
<code><var>protocol</var></code> is the name of a protocol defined in this file.
If the protocol requires <a href="protocol.html#argvar">arguments</a>,
specify them enclosed in parentheses:
<code><var>protocol</var>(<var>arg1,arg2,...</var>)</code>.
</p>
<p>
The communication channel is specified with <code><var>bus</var></code> and
<code><var>addr</var></code>.
If the bus does not have addresses, <code><var>addr</var></code> is dispensable.
Optional <code><var>parameters</var></code> are passed to the bus driver.
</p>
<h3>Example:</h3>
<p>
Create an output record to set the current of <tt>PS1</tt>.
Use protocol <em>setCurrent</em> from file <em>ExamplePS.proto</em>.
The bus is called <em>PS1</em> like the device.
</p>
<pre>
record (ao, "PS1:I-set")
{
field (DESC, "Set current of PS1")
field (DTYP, "stream")
field (OUT, "@ExamplePS.proto setCurrent PS1")
field (EGU, "A")
field (PREC, "2")
field (DRVL, "0")
field (DRVH, "60")
field (LOPR, "0")
field (HOPR, "60")
}
</pre>
<hr>
<p align="right"><a href="protocol.html">Next: Protocol Files</a></p>
<p><small>Dirk Zimoch, 2007</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

BIN
doc/sls_icon.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 B

BIN
doc/space.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 807 B

79
doc/stream.css Normal file
View File

@ -0,0 +1,79 @@
a:link {color: #0000D0; text-decoration:none;}
a:visited {color: #0000D0; text-decoration:none;}
a:hover {color: #FF0000; text-decoration:none;}
body {
margin-right:20px;
font-family:sans-serif;
font-size:14px;
background-color:#ffffff;
}
pre {
background-color:#f4f4f4;
padding:1ex;
border:1px solid #000000;
white-space:pre;
margin:2ex;
}
dt {
margin-top:0.5ex;
}
h1 {
font-size:225%;
margin-top:0;
font-style:italic;
font-family:serif;
}
h2 {
font-size:150%;
margin-bottom:0.5ex;
}
h3 {
font-size:100%;
margin-bottom:0.25ex;
}
p {
margin-top:0.75ex;
margin-bottom:0.75ex;
}
small {
font-size:75%;
}
code {
color: #008000;
}
.indent {
text-indent:-4ex;
margin-left:4ex;
margin-top:0.5ex;
text-align:left;
}
/*
a[target=ex] {
background-image:url(ex.png);
background-repeat:no-repeat;
background-position:right center;
padding-right: 12px;
}
a[target=ex]:hover {
background-image:url(exr.png);
}
*/
@media print {
a:link {color: black; text-decoration:none;}
a:visited {color: black; text-decoration:none;}
a:hover {color: black; text-decoration:none;}
code {color: black; }
}

92
doc/stream.html Normal file
View File

@ -0,0 +1,92 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>EPICS <em>StreamDevice</em> Documentation</h1>
<h2>What is <em>StreamDevice</em>?</h2>
<p>
<em>StreamDevice</em> is a generic
<a href="http://www.aps.anl.gov/epics" target="ex">EPICS</a>
device support for devices with a "byte stream" based
communication interface.
That means devices that can be controlled by sending and
receiving strings (in the broadest sense, including non-printable
characters and even null-bytes).
Examples for this type of communication interface are
serial line (RS-232, RS-485, ...),
IEEE-488 (also known as GPIB or HP-IB), and TCP/IP.
<em>StreamDevice</em> comes with an interface to<a target="ex"
href="http://www.aps.anl.gov/epics/modules/soft/asyn/">
<em>asynDriver</em></a>
but can be extended to
<a href="businterface.html">support other bus drivers</a>.
</p>
<p>
If the device can be controlled with strings like
"<code>RF:FREQ 499.655 MHZ</code>", <em>StreamDevice</em> can be used.
How the strings exactly look like is defined in <em>protocols</em>.
Formatting and interpretation of values is done with
<a href="formats.html"><em>format converters</em></a> similar to those
known from the C functions <em>printf()</em> and <em>scanf()</em>.
To support other formats, it is possible to
<a href="formatconverter.html">write your own converters</a>.
</p>
<p>
Each record with <em>StreamDevice</em> support runs one <em>protocol</em>
to read or write its value.
All <em>protocols</em> are defined in
<a href="protocol.html"><em>protocol files</em></a> in plain ASCII text.
No compiling is necessary to change a protocol or to support new devices.
<em>Protocols</em> can be as simple as just one output string or can
consist of many strings sent to and read from the device.
However, a protocol is linear.
That means it runs from start to end each time the record is
<a href="processing.html">processed</a>.
It does not provide loops or branches.
</p>
<p>
<em>StreamDevice</em> supports all
<a href="recordtypes.html">standard records</a> of EPICS base
which can have device support.
It is also possible to
<a href="recordinterface.html">write support for new record types</a>.
</p>
<h2>What is <em>StreamDevice</em> not?</h2>
<p>
It is not a programming language for a high-level application.
It is, for example, not possible to write a complete scanning program
in a <em>protocol</em>.
Use other tools for that and use <em>StreamDevice</em> only for the
primitive commands.
</p>
<p>
It is not a block oriented device support.
It is not possible to send or receive huge blocks of data that contain
many process variables distributed over many records.
</p>
<h2>Recommended Readings</h2>
<p>
<a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/AppDevGuide.pdf"
target="ex">IOC Application Developer's Guide (PDF)</a>
</p>
<p>
<a href="http://www.aps.anl.gov/asd/controls/epics/EpicsDocumentation/AppDevManuals/RecordRef/Recordref-1.html"
target="ex">EPICS Record Reference Manual</a>
</p>
<hr>
<p align="right"><a href="setup.html">Next: Setup</a></p>
<p><small>Dirk Zimoch, 2006</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

5
doc/stream.js Normal file
View File

@ -0,0 +1,5 @@
if (parent.head) {
parent.head.document.getElementById('subtitle').innerHTML=document.title;
parent.document.title=document.title;
document.getElementsByTagName('h1')[0].style.display="none";
}

67
doc/stringin.html Normal file
View File

@ -0,0 +1,67 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: stringin Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: stringin Records</h1>
<h2>Normal Operation</h2>
<p>
The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringout.html">stringout</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

67
doc/stringout.html Normal file
View File

@ -0,0 +1,67 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: stringout Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: stringout Records</h1>
<h2>Normal Operation</h2>
<p>
The variable <code><i>x</i></code> stands for the
written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>LONG format (e.g. <code>%i</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>ENUM format (e.g. <code>%{</code>):</dt>
<dd>
Not allowed.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<u>Output:</u> <code><i>x</i>=VAL</code><br>
<u>Input:</u> <code>VAL=<i>x</i></code>
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="waveform.html">waveform</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

297
doc/tipsandtricks.html Normal file
View File

@ -0,0 +1,297 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: Tips and Tricks</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: Tips and Tricks</h1>
<a name="argvar"></a>
<h2>I have many almost identical protocols</h2>
<p>
You can give <a href="protocol.html#argvar">arguments</a> to a protocol.
In the <code>INP</code> or <code>OUT</code> link, write:
</p>
<p>
<code>
field (OUT, "@protocolfile protocol(arg1,arg2,arg3) bus")
</code>
</p>
<p>
In the protocol, reference arguments as <code>$1 $2 $3</code> or inside strings
as <code>"\$1 \$2 \$3"</code>.
</p>
<p>
<code>
moveaxis {out "move\$1 %.6f";}<br>
field (OUT, "@motor.proto moveaxis(X) motor1")
</code>
</p>
<p>
<code>
readpressure {out 0x02 0x00 $1; in 0x82 0x00 $1 "%2r";}<br>
field (INP, "@vacuumgauge.proto readpressure(0x84) gauge3")
</code>
</p>
<a name="iointr"></a>
<h2>I have a device that sends unsolicited data</h2>
<p>
Use <a href="processing.html#iointr"><code>I/O Intr</code> processing</a>.
The record receives any input and processes only when the input matches.
</p>
<p>
<code>
read {in "new value = %f";}
</code>
</p>
<p>
<code>
record (ai, "$(RECORD)") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@(DEVICETYPE).proto read $(BUS)")<br>
&nbsp;&nbsp;field (SCAN, "I/O Intr")<br>
}
</code>
</p>
<a name="multiline"></a>
<h2>I have a device that sends multi-line messages</h2>
<pre>
Here is the value:
3.1415
</pre>
<p>
Use as many <code>in</code> commands as you get input lines.
</p>
<p>
<code>
read_value {in "Here is the value:"; in "%f";}
</code>
</p>
<a name="writemany"></a>
<h2>I need to write more than one value in one message</h2>
<p>
There is more than one solution to this problem.
Different approaches have different requirements.
</p>
<h3>A) All values have the same type and are separated by the same string</h3>
<p>
Use array records (e.g. <a href="waveform.html">waveform</a>,
<a href="aao.html">aao</a>).
</p>
<p>
<code>
array_out {separator=", "; out "an array: (%.2f)";}
</code>
</p>
<p>
The format <code>%.2f</code> is repeated for each element of the array.
All elements are separated by <code>", "</code>.<br>
Output will look like this:
<pre>
an array: (3.14, 17.30, -12.34)
</pre>
</p>
<h3>B) We have up to 12 numeric values</h3>
<p>
Use a <a href="calcout.html">calcout</a> record and
<a href="formats.html#types">field references</a> in the format.
</p>
<p>
<code>
write_ABC {out "A=%(A).2f B=%(B).6f C=%(C).0f";}
</code>
</p>
<p>
You must specify a valid expression in the <code>CALC</code> field
even if you don't use it.
</p>
<p>
<code>
record (calcout, "$(RECORD)") {<br>
&nbsp;&nbsp;field (INPA, "$(A_RECORD)")<br>
&nbsp;&nbsp;field (INPB, "$(B_RECORD)")<br>
&nbsp;&nbsp;field (INPC, "$(C_RECORD)")<br>
&nbsp;&nbsp;field (CALC, "0")<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (OUT, "@(DEVICETYPE).proto write_ABC $(BUS)")<br>
}
</code>
</p>
<h3>C) Values are in other records on the same IOC</h3>
<p>
Use <a href="formats.html#types">record references</a> in the format.
</p>
<p>
<code>
acquire {out 'ACQUIRE "%(\$1:directory.VAL)s/%s",%(\$1:time.VAL).3f;';}
</code>
</p>
<p>
You must specify the full <code>record.FIELD</code> name,
even for <code>VAL</code> fields.
To avoid record names in protocol files use
<a href="protocol.html#argvar">protocol arguments</a>.
In the link, specify the record name or just the basename of the
other records (device name) in parentheses.
</p>
<p>
<code>
record (stringout, "$(DEVICE):getimage") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (OUT, "@(DEVICETYPE).proto acquire($(DEVICE)) $(BUS)")<br>
}
</code>
</p>
<a name="readmany"></a>
<h2>I need to read more than one value from one message</h2>
<p>
Again, there is more than one solution to this problem.
</p>
<h3>A) All values have the same type and are separated by the same string</h3>
<p>
Use array records (e.g. <a href="waveform.html">waveform</a>,
<a href="aai.html">aai</a>).<br>
</p>
<p>
<code>
array_in {separator=","; in "array = (%f)";}
</code>
</p>
<p>
The format <code>%f</code> is repeated for each element of the array.
A <code>","</code> is expected beween element.<br>
Input may look like this:
</p>
<pre>
array = (3.14, 17.30, -12.34)
</pre>
<h3>B) The message and the values in it can be filtered easily</h3>
<p>
Use <a href="processing.html#iointr"><code>I/O Intr</code> processing</a>
and <a href="formats.html#syntax">value skipping</a> (<code>%*</code>)<br>
</p>
<p>
<code>
read_A {out "GET A,B"; in "A=%f, B=%*f";}<br>
read_B {in "A=%*f, B=%f";}
</code>
</p>
<p>
<code>
record (ai, "$(DEVICE):A") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@(DEVICETYPE).proto read_A $(BUS)")<br>
&nbsp;&nbsp;field (SCAN, "1 second")<br>
}<br>
record (ai, "$(DEVICE):B") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@(DEVICETYPE).proto read_B $(BUS)")<br>
&nbsp;&nbsp;field (SCAN, "I/O Intr")<br>
}
</code>
</p>
<p>
Record A actively requests values every second.
The reply contains values A and B.
Record A filters only value A from the input and ignores value B
by using the <code>*</code> flag.
Nevertheless, a complete syntax check is performed: B must be a
valid floating point number.
Record B is <code>I/O Intr</code> and gets (a copy of) any input, including
input that was directed to record A.
If it finds a matching string it ignores value A, reads value B and
then processes.
Any non-matching input is ignored by record B.
</p>
<h3>C) Values should be stored in other records on the same IOC</h3>
<p>
Use <a href="formats.html#types">record references</a> in the format.
To avoid record names in protocol files, use
<a href="protocol.html#argvar">protocol arguments</a>.
</p>
<p>
<code>
read_AB {out "GET A,B"; in "A=%f, B=%(\$1.VAL)f";}
</code>
</p>
<p>
<code>
record (ai, "$(DEVICE):A") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@(DEVICETYPE).proto read_AB($(DEVICE):B) $(BUS)")<br>
&nbsp;&nbsp;field (SCAN, "1 second")<br>
}<br>
record (ai, "$(DEVICE):B") {<br>
}
</code>
</p>
<p>
Whenever record A reads input, it stores the first value in its own VAL field
as usual and the second in the VAL field of record B.
Because the VAL field of record B has the PP attribute, this automatically
processes record B.
</p>
<a name="mixed"></a>
<h2>I have a device that sends mixed data types: numbers and strings</h2>
<p>
Use a <code>@mismatch</code>
<a href="protocol.html#except">exception handler</a> and
<a href="formats.html#types">record references</a> in the format.
To avoid record names in protocol files, use
<a href="protocol.html#argvar">protocol arguments</a>.
</p>
<h3>Example</h3>
<p>
When asked "<code>CURRENT?</code>", the device send something like
"<code>CURRENT&nbsp;3.24&nbsp;A</code>" or a message like
"<code>device&nbsp;switched&nbsp;off</code>".
</p>
<p>
<code>
read_current {out "CURRENT?"; in "CURRENT %f A"; @mismatch {in "%(\1.VAL)39c";}}
</code>
</p>
<p>
<code>
record (ai, "$(DEVICE):readcurrent") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@(DEVICETYPE).proto read_current($(DEVICE):message) $(BUS)")<br>
}<br>
record (stringin, "$(DEVICE):message") {<br>
}
</code>
</p>
<p>
After <a href="processing.html#proc">processing</a> the readcurrent record, you can see from
SEVR/STAT if the read was successful or not.
With some more records, you can clean the message record if SEVR is not INVALID.
</p>
<code>
record (calcout, "$(DEVICE):clean_1") {<br>
&nbsp;&nbsp;field (INPA, "$(DEVICE):readcurrent.SEVR CP")<br>
&nbsp;&nbsp;field (CALC, "A!=2")<br>
&nbsp;&nbsp;field (OOPT, "When Non-zero")<br>
&nbsp;&nbsp;field (OUT, "$(DEVICE):clean_2.PROC")<br>
}<br>
record (stringout, "$(DEVICE):clean_2") {<br>
&nbsp;&nbsp;field (VAL, "OK")<br>
&nbsp;&nbsp;field (OUT, "$(DEVICE):message PP")<br>
}<br>
</code>
<hr>
<p><small>Dirk Zimoch, 2007</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

126
doc/waveform.html Normal file
View File

@ -0,0 +1,126 @@
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>StreamDevice: waveform Records</title>
<link rel="shortcut icon" href="sls_icon.ico">
<link rel="stylesheet" type="text/css" href="stream.css">
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<meta name="author" content="Dirk Zimoch">
</head>
<body>
<h1>StreamDevice: waveform Records</h1>
<h2>Normal Operation</h2>
<p>
With waveform records, the format converter is applied to
each element. Between the elements, a separator is printed
or expected as specified by the <code>Separator</code>
<a href="protocol.html#sysvar">variable</a> in the
protocol.
When parsing input, a space as the first character of the
<code>Separator</code> matches any number of any whitespace
characters.
</p>
<p>
During input, a maximum of <code>NELM</code> elements is
read and <code>NORD</code> is updated accordingly.
Parsing of elements stops when the separator does not match,
conversion fails, or the end of the input is reached.
A minimum of one element must be available.
</p>
<p>
During output, the first <code>NORD</code> elements are
written.
</p>
<p>
The format data type must be convertible to or from the type
specified in the <code>FTVL</code> field.
The variable <code><i>x[i]</i></code> stands for one element of
the written or read value.
</p>
<dl>
<dt>DOUBLE format (e.g. <code>%f</code>):</dt>
<dd>
<u>Output:</u><code><i>x[i]</i>=double(VAL[i])</code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i]</i>)</code><br>
<code>FTVL</code> must be <code>"FLOAT"</code> or <code>"DOUBLE"</code>
</dd>
<dt>LONG or ENUM format (e.g. <code>%i</code> or <code>%{</code>):</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=long(VAL[i])</code><br>
<code>FTVL</code> can be
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
Signed values are sign-extended to long, unsigned values are
zero-extended to long before converting them.<br>
<u>Input:</u> <code>VAL[i]=FTVL(<i>x[i])</i></code><br>
<code>FTVL</code> can be <code>"DOUBLE"</code>, <code>"FLOAT"</code>,
<code>"LONG"</code>, <code>"ULONG"</code>, <code>"SHORT"</code>,
<code>"USHORT"</code>, <code>"CHAR"</code>, <code>"UCHAR"</code>,
or <code>"ENUM"</code> (which is treated as <code>"USHORT"</code>).<br>
The value is truncated to the least significant bytes if
<code>FTVL</code> has a smaller data size than <code>long</code>.
</dd>
<dt>STRING format (e.g. <code>%s</code>):</dt>
<dd>
<dl>
<dt>If <code>FTVL=="STRING"</code>:</dt>
<dd>
<u>Output:</u> <code><i>x[i]</i>=VAL[i]</code><br>
<u>Input:</u> <code>VAL[i]=<i>x[i]</i></code><br>
Note that this is an array of strings, not an array of characters.
</dd>
<dt>If <code>FTVL=="CHAR"</code> or <code>FTVL="UCHAR"</code>:</dt>
<dd>
In this case, the complete waveform is treated as a large
single string of size <code>NORD</code>.
No separators are printed or expected.<br>
<u>Output:</u> <code><i>x</i>=range(VAL,0,NORD)</code><br>
The first <code>NORD</code> characters are printed,
which might be less than <code>NELM</code>.<br>
<u>Input:</u> <code>VAL=<i>x</i>, NORD=length(<i>x</i>)</code><br>
A maximum of <code>NELM-1</code> characters can be read.
<code>NORD</code> is updated to the index of the first of the
trailing zeros.
Usually, this is the same as the string length.
</dd>
</dl>
Other values of <code>FTVL</code> are not allowed for this format.
</dd>
</dl>
<h2>Initialization</h2>
<p>
During <a href="processing.html#init">initialization</a>, the <code>@init</code> handler is executed, if
present. All format converters work like in normal operation.
</p>
<hr>
<p>
<a href="aai.html">aai</a>
<a href="aao.html">aao</a>
<a href="ai.html">ai</a>
<a href="ao.html">ao</a>
<a href="bi.html">bi</a>
<a href="bo.html">bo</a>
<a href="mbbi.html">mbbi</a>
<a href="mbbo.html">mbbo</a>
<a href="mbbiDirect.html">mbbiDirect</a>
<a href="mbboDirect.html">mbboDirect</a>
<a href="longin.html">longin</a>
<a href="longout.html">longout</a>
<a href="stringin.html">stringin</a>
<a href="stringout.html">stringout</a>
<a href="calcout.html">calcout</a>
<a href="scalcout.html">scalcout</a>
</p>
<p><small>Dirk Zimoch, 2005</small></p>
<script src="stream.js" type="text/javascript"></script>
</body>
</html>

1324
src/AsynDriverInterface.cc Normal file

File diff suppressed because it is too large Load Diff

155
src/BCDConverter.cc Normal file
View File

@ -0,0 +1,155 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the BCD format converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamError.h"
// packed BCD (0x00 - 0x99)
class BCDConverter : public StreamFormatConverter
{
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printLong(const StreamFormat&, StreamBuffer&, long);
int scanLong(const StreamFormat&, const char*, long&);
};
int BCDConverter::
parse(const StreamFormat&, StreamBuffer&, const char*&, bool)
{
return long_format;
}
bool BCDConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
unsigned char bcd[6]={0,0,0,0,0,0}; // sufficient for 2^32
int i;
int prec = fmt.prec; // number of nibbles
if (prec == -1)
{
prec = 2 * sizeof (value);
}
int width = (prec + (fmt.flags & sign_flag ? 2 : 1)) / 2;
if (fmt.width > width) width = fmt.width;
if (fmt.flags & sign_flag && value < 0)
{
// negative BCD value, I hope "F" as "-" is OK
bcd[5] = 0xF0;
value = -value;
}
if (prec > 10) prec = 10;
for (i = 0; i < prec; i++)
{
bcd[i/2] |= (value % 10) << (4 * (i & 1));
value /= 10;
}
if (fmt.flags & alt_flag)
{
// least significant byte first (little endian)
for (i = 0; i < (prec + 1) / 2; i++)
{
output.append(bcd[i]);
}
for (; i < width; i++)
{
output.append('\0');
}
output[-1] |= bcd[5];
}
else
{
// most significant byte first (big endian)
int firstbyte = output.length();
for (i = 0; i < width - (prec + 1) / 2; i++)
{
output.append('\0');
}
for (i = (prec - 1) / 2; i >= 0; i--)
{
output.append(bcd[i]);
}
output[firstbyte] |= bcd[5];
}
return true;
}
int BCDConverter::
scanLong(const StreamFormat& fmt, const char* input, long& value)
{
int length = 0;
int val = 0;
unsigned char bcd1, bcd10;
int width = fmt.width;
if (width == 0) width = 1;
if (fmt.flags & alt_flag)
{
// little endian
int shift = 1;
while (width--)
{
bcd1 = bcd10 = (unsigned char) input[length++];
bcd1 &= 0x0F;
bcd10 >>= 4;
if (bcd1 > 9 || shift * bcd1 < bcd1) break;
if (width == 0 && fmt.flags & sign_flag)
{
val += bcd1 * shift;
if (bcd10 != 0) val = -val;
break;
}
if (bcd10 > 9 || shift * bcd10 < bcd10) break;
val += (bcd1 + 10 * bcd10) * shift;
if (shift <= 100000000) shift *= 100;
else shift = 0;
}
}
else
{
// big endian
int sign = 1;
while (width--)
{
long temp;
bcd1 = bcd10 = (unsigned char) input[length];
bcd1 &= 0x0F;
bcd10 >>= 4;
if (length == 0 && fmt.flags & sign_flag && bcd10)
{
sign = -1;
bcd10 = 0;
}
if (bcd1 > 9 || bcd10 > 9) break;
temp = val * 100 + (bcd1 + 10 * bcd10);
if (temp < val)
{
length = 0;
break;
}
val = temp;
length++;
}
val *= sign;
}
if (length == 0) return -1;
value = val;
return length;
}
RegisterConverter (BCDConverter, "D");

118
src/BinaryConverter.cc Normal file
View File

@ -0,0 +1,118 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the binary format converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <ctype.h>
#include "StreamFormatConverter.h"
#include "StreamError.h"
// Binary ASCII Converter %b and %B
class BinaryConverter : public StreamFormatConverter
{
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printLong(const StreamFormat&, StreamBuffer&, long);
int scanLong(const StreamFormat&, const char*, long&);
};
int BinaryConverter::
parse(const StreamFormat& format, StreamBuffer& info,
const char*& source, bool)
{
if (format.conv == 'B')
{
// user defined characters for %B (next 2 in source)
if (!*source )
{
if (*source == esc) source++;
info.append(*source++);
if (!*source)
{
if (*source == esc) source++;
info.append(*source++);
return long_format;
}
}
error("Missing characters after %%B format conversion\n");
return false;
}
// default characters for %b
info.append("01");
return long_format;
}
bool BinaryConverter::
printLong(const StreamFormat& format, StreamBuffer& output, long value)
{
int prec = format.prec;
if (prec == -1)
{
// find number of significant bits
prec = sizeof (value) * 8;
while (prec && (value & (1 << (prec - 1))) == 0) prec--;
}
if (prec == 0) prec++; // print at least one bit
int width = prec;
if (format.width > width) width = format.width;
char zero = format.info[0];
char one = format.info[1];
if (!(format.flags & left_flag))
{
// pad left
char fill = (format.flags & zero_flag) ? zero : ' ';
while (width > prec)
{
output.append(fill);
width--;
}
}
while (prec--)
{
output.append((value & (1 << prec)) ? one : zero);
width--;
}
while (width--)
{
// pad right
output.append(' ');
}
return true;
}
int BinaryConverter::
scanLong(const StreamFormat& format, const char* input, long& value)
{
long val = 0;
int width = format.width;
if (width == 0) width = -1;
int length = 0;
while (isspace(input[++length])); // skip whitespaces
char zero = format.info[0];
char one = format.info[1];
if (input[length] != zero && input[length] != one) return -1;
while (width-- && (input[length] == zero || input[length] == one))
{
val <<= 1;
if (input[length++] == one) val |= 1;
}
value = val;
return length;
}
RegisterConverter (BinaryConverter, "bB");

65
src/CONFIG_STREAM Normal file
View File

@ -0,0 +1,65 @@
# Want debugging?
# HOST_OPT = NO
# You may add more record interfaces
# This requires the naming conventions
# dev$(RECORD)Stream.c
RECORDS += ao ai
RECORDS += bo bi
RECORDS += mbbo mbbi
RECORDS += mbboDirect mbbiDirect
RECORDS += longout longin
RECORDS += stringout stringin
RECORDS += waveform
RECORDS += calcout
#RECORDS += aai aao
# Do you have synApps and want support for scalcout?
# Then define CALC or SYNAPPS in your RELEASE file
# pointing to the 'calc' module of synApps.
# Due to strange cross dependencies in synApps
# you have to build the 'sscan' and 'genSub'
# modules before building 'calc'.
# See doc/scalcout.html for a required fix in scalcout.
SYNAPPS_RECORDS += scalcout
# You may add more bus interfaces
# This requires the naming convention
# $(BUS)Interface.cc
# asynDriver interface is added automatically
# if ASYN is defined in your RELEASE file.
# BUSSES += Debug
# You may add more format converters
# This requires the naming convention
# $(FORMAT)Converter.cc
FORMATS += Enum
FORMATS += BCD
FORMATS += Raw
FORMATS += Binary
FORMATS += Checksum
# Want a loadable module?
# For Tornado 2.0.2, a fix is needed in the
# registerRecordDeviceDriver.pl script in base:
# at the end replace the line "IoccrfReg iocshReg;"
# by "static IoccrfReg iocshReg;"
# LOADABLE_MODULE = YES
# Want to add some memory tracing
# to find memory leaks?
# USE_MEMGUARD = YES
# Sources of Stream Kernel
# Do not modify
STREAM_SRCS += StreamVersion.c
STREAM_SRCS += StreamBuffer.cc
STREAM_SRCS += StreamError.cc
STREAM_SRCS += StreamProtocol.cc
STREAM_SRCS += StreamFormatConverter.cc
STREAM_SRCS += StreamCore.cc
STREAM_SRCS += StreamBusInterface.cc
STREAM_SRCS += StreamEpics.cc

645
src/ChecksumConverter.cc Normal file
View File

@ -0,0 +1,645 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the checksum pseudo-converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamError.h"
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
// These systems have no strncasecmp
#include <epicsString.h>
#define strncasecmp epicsStrnCaseCmp
#endif
#include <ctype.h>
typedef unsigned long ulong;
typedef unsigned char uchar;
typedef ulong (*checksumFunc)(const uchar* data, ulong len, ulong init);
static ulong sum(const uchar* data, ulong len, ulong sum)
{
while (len--)
{
sum += *data++;
}
return sum;
}
static ulong xor8(const uchar* data, ulong len, ulong sum)
{
while (len--)
{
sum ^= *data++;
}
return sum;
}
static ulong crc_0x07(const uchar* data, ulong len, ulong crc)
{
// x^8 + x^2 + x^1 + x^0 (0x07)
const static uchar table[256] = {
0x00, 0x07, 0x0E, 0x09, 0x1C, 0x1B, 0x12, 0x15,
0x38, 0x3F, 0x36, 0x31, 0x24, 0x23, 0x2A, 0x2D,
0x70, 0x77, 0x7E, 0x79, 0x6C, 0x6B, 0x62, 0x65,
0x48, 0x4F, 0x46, 0x41, 0x54, 0x53, 0x5A, 0x5D,
0xE0, 0xE7, 0xEE, 0xE9, 0xFC, 0xFB, 0xF2, 0xF5,
0xD8, 0xDF, 0xD6, 0xD1, 0xC4, 0xC3, 0xCA, 0xCD,
0x90, 0x97, 0x9E, 0x99, 0x8C, 0x8B, 0x82, 0x85,
0xA8, 0xAF, 0xA6, 0xA1, 0xB4, 0xB3, 0xBA, 0xBD,
0xC7, 0xC0, 0xC9, 0xCE, 0xDB, 0xDC, 0xD5, 0xD2,
0xFF, 0xF8, 0xF1, 0xF6, 0xE3, 0xE4, 0xED, 0xEA,
0xB7, 0xB0, 0xB9, 0xBE, 0xAB, 0xAC, 0xA5, 0xA2,
0x8F, 0x88, 0x81, 0x86, 0x93, 0x94, 0x9D, 0x9A,
0x27, 0x20, 0x29, 0x2E, 0x3B, 0x3C, 0x35, 0x32,
0x1F, 0x18, 0x11, 0x16, 0x03, 0x04, 0x0D, 0x0A,
0x57, 0x50, 0x59, 0x5E, 0x4B, 0x4C, 0x45, 0x42,
0x6F, 0x68, 0x61, 0x66, 0x73, 0x74, 0x7D, 0x7A,
0x89, 0x8E, 0x87, 0x80, 0x95, 0x92, 0x9B, 0x9C,
0xB1, 0xB6, 0xBF, 0xB8, 0xAD, 0xAA, 0xA3, 0xA4,
0xF9, 0xFE, 0xF7, 0xF0, 0xE5, 0xE2, 0xEB, 0xEC,
0xC1, 0xC6, 0xCF, 0xC8, 0xDD, 0xDA, 0xD3, 0xD4,
0x69, 0x6E, 0x67, 0x60, 0x75, 0x72, 0x7B, 0x7C,
0x51, 0x56, 0x5F, 0x58, 0x4D, 0x4A, 0x43, 0x44,
0x19, 0x1E, 0x17, 0x10, 0x05, 0x02, 0x0B, 0x0C,
0x21, 0x26, 0x2F, 0x28, 0x3D, 0x3A, 0x33, 0x34,
0x4E, 0x49, 0x40, 0x47, 0x52, 0x55, 0x5C, 0x5B,
0x76, 0x71, 0x78, 0x7F, 0x6A, 0x6D, 0x64, 0x63,
0x3E, 0x39, 0x30, 0x37, 0x22, 0x25, 0x2C, 0x2B,
0x06, 0x01, 0x08, 0x0F, 0x1A, 0x1D, 0x14, 0x13,
0xAE, 0xA9, 0xA0, 0xA7, 0xB2, 0xB5, 0xBC, 0xBB,
0x96, 0x91, 0x98, 0x9F, 0x8A, 0x8D, 0x84, 0x83,
0xDE, 0xD9, 0xD0, 0xD7, 0xC2, 0xC5, 0xCC, 0xCB,
0xE6, 0xE1, 0xE8, 0xEF, 0xFA, 0xFD, 0xF4, 0xF3 };
while (len--) crc = table[(crc ^ *data++) & 0xFF];
return crc;
}
static ulong crc_0x31(const uchar* data, ulong len, ulong crc)
{
// x^8 + x^5 + x^4 + x^0 (0x31)
const static uchar table[256] = {
0x00, 0x5e, 0xbc, 0xe2, 0x61, 0x3f, 0xdd, 0x83,
0xc2, 0x9c, 0x7e, 0x20, 0xa3, 0xfd, 0x1f, 0x41,
0x9d, 0xc3, 0x21, 0x7f, 0xfc, 0xa2, 0x40, 0x1e,
0x5f, 0x01, 0xe3, 0xbd, 0x3e, 0x60, 0x82, 0xdc,
0x23, 0x7d, 0x9f, 0xc1, 0x42, 0x1c, 0xfe, 0xa0,
0xe1, 0xbf, 0x5d, 0x03, 0x80, 0xde, 0x3c, 0x62,
0xbe, 0xe0, 0x02, 0x5c, 0xdf, 0x81, 0x63, 0x3d,
0x7c, 0x22, 0xc0, 0x9e, 0x1d, 0x43, 0xa1, 0xff,
0x46, 0x18, 0xfa, 0xa4, 0x27, 0x79, 0x9b, 0xc5,
0x84, 0xda, 0x38, 0x66, 0xe5, 0xbb, 0x59, 0x07,
0xdb, 0x85, 0x67, 0x39, 0xba, 0xe4, 0x06, 0x58,
0x19, 0x47, 0xa5, 0xfb, 0x78, 0x26, 0xc4, 0x9a,
0x65, 0x3b, 0xd9, 0x87, 0x04, 0x5a, 0xb8, 0xe6,
0xa7, 0xf9, 0x1b, 0x45, 0xc6, 0x98, 0x7a, 0x24,
0xf8, 0xa6, 0x44, 0x1a, 0x99, 0xc7, 0x25, 0x7b,
0x3a, 0x64, 0x86, 0xd8, 0x5b, 0x05, 0xe7, 0xb9,
0x8c, 0xd2, 0x30, 0x6e, 0xed, 0xb3, 0x51, 0x0f,
0x4e, 0x10, 0xf2, 0xac, 0x2f, 0x71, 0x93, 0xcd,
0x11, 0x4f, 0xad, 0xf3, 0x70, 0x2e, 0xcc, 0x92,
0xd3, 0x8d, 0x6f, 0x31, 0xb2, 0xec, 0x0e, 0x50,
0xaf, 0xf1, 0x13, 0x4d, 0xce, 0x90, 0x72, 0x2c,
0x6d, 0x33, 0xd1, 0x8f, 0x0c, 0x52, 0xb0, 0xee,
0x32, 0x6c, 0x8e, 0xd0, 0x53, 0x0d, 0xef, 0xb1,
0xf0, 0xae, 0x4c, 0x12, 0x91, 0xcf, 0x2d, 0x73,
0xca, 0x94, 0x76, 0x28, 0xab, 0xf5, 0x17, 0x49,
0x08, 0x56, 0xb4, 0xea, 0x69, 0x37, 0xd5, 0x8b,
0x57, 0x09, 0xeb, 0xb5, 0x36, 0x68, 0x8a, 0xd4,
0x95, 0xcb, 0x29, 0x77, 0xf4, 0xaa, 0x48, 0x16,
0xe9, 0xb7, 0x55, 0x0b, 0x88, 0xd6, 0x34, 0x6a,
0x2b, 0x75, 0x97, 0xc9, 0x4a, 0x14, 0xf6, 0xa8,
0x74, 0x2a, 0xc8, 0x96, 0x15, 0x4b, 0xa9, 0xf7,
0xb6, 0xe8, 0x0a, 0x54, 0xd7, 0x89, 0x6b, 0x35 };
while (len--) crc = table[(crc ^ *data++) & 0xFF];
return crc;
}
static ulong crc_0x8005(const uchar* data, ulong len, ulong crc)
{
// 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 };
while (len--) crc = table[((crc>>8) ^ *data++) & 0xFF] ^ (crc << 8);
return crc;
}
static ulong crc_0x8005_r(const uchar* data, ulong len, ulong crc)
{
// 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 };
while (len--) crc = table[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
return crc;
}
static ulong crc_0x1021(const uchar* data, ulong len, ulong crc)
{
// x^16 + x^12 + x^5 + x^0 (0x1021)
const static unsigned short table[256] = {
0x0000, 0x1021, 0x2042, 0x3063, 0x4084, 0x50A5, 0x60C6, 0x70E7,
0x8108, 0x9129, 0xA14A, 0xB16B, 0xC18C, 0xD1AD, 0xE1CE, 0xF1EF,
0x1231, 0x0210, 0x3273, 0x2252, 0x52B5, 0x4294, 0x72F7, 0x62D6,
0x9339, 0x8318, 0xB37B, 0xA35A, 0xD3BD, 0xC39C, 0xF3FF, 0xE3DE,
0x2462, 0x3443, 0x0420, 0x1401, 0x64E6, 0x74C7, 0x44A4, 0x5485,
0xA56A, 0xB54B, 0x8528, 0x9509, 0xE5EE, 0xF5CF, 0xC5AC, 0xD58D,
0x3653, 0x2672, 0x1611, 0x0630, 0x76D7, 0x66F6, 0x5695, 0x46B4,
0xB75B, 0xA77A, 0x9719, 0x8738, 0xF7DF, 0xE7FE, 0xD79D, 0xC7BC,
0x48C4, 0x58E5, 0x6886, 0x78A7, 0x0840, 0x1861, 0x2802, 0x3823,
0xC9CC, 0xD9ED, 0xE98E, 0xF9AF, 0x8948, 0x9969, 0xA90A, 0xB92B,
0x5AF5, 0x4AD4, 0x7AB7, 0x6A96, 0x1A71, 0x0A50, 0x3A33, 0x2A12,
0xDBFD, 0xCBDC, 0xFBBF, 0xEB9E, 0x9B79, 0x8B58, 0xBB3B, 0xAB1A,
0x6CA6, 0x7C87, 0x4CE4, 0x5CC5, 0x2C22, 0x3C03, 0x0C60, 0x1C41,
0xEDAE, 0xFD8F, 0xCDEC, 0xDDCD, 0xAD2A, 0xBD0B, 0x8D68, 0x9D49,
0x7E97, 0x6EB6, 0x5ED5, 0x4EF4, 0x3E13, 0x2E32, 0x1E51, 0x0E70,
0xFF9F, 0xEFBE, 0xDFDD, 0xCFFC, 0xBF1B, 0xAF3A, 0x9F59, 0x8F78,
0x9188, 0x81A9, 0xB1CA, 0xA1EB, 0xD10C, 0xC12D, 0xF14E, 0xE16F,
0x1080, 0x00A1, 0x30C2, 0x20E3, 0x5004, 0x4025, 0x7046, 0x6067,
0x83B9, 0x9398, 0xA3FB, 0xB3DA, 0xC33D, 0xD31C, 0xE37F, 0xF35E,
0x02B1, 0x1290, 0x22F3, 0x32D2, 0x4235, 0x5214, 0x6277, 0x7256,
0xB5EA, 0xA5CB, 0x95A8, 0x8589, 0xF56E, 0xE54F, 0xD52C, 0xC50D,
0x34E2, 0x24C3, 0x14A0, 0x0481, 0x7466, 0x6447, 0x5424, 0x4405,
0xA7DB, 0xB7FA, 0x8799, 0x97B8, 0xE75F, 0xF77E, 0xC71D, 0xD73C,
0x26D3, 0x36F2, 0x0691, 0x16B0, 0x6657, 0x7676, 0x4615, 0x5634,
0xD94C, 0xC96D, 0xF90E, 0xE92F, 0x99C8, 0x89E9, 0xB98A, 0xA9AB,
0x5844, 0x4865, 0x7806, 0x6827, 0x18C0, 0x08E1, 0x3882, 0x28A3,
0xCB7D, 0xDB5C, 0xEB3F, 0xFB1E, 0x8BF9, 0x9BD8, 0xABBB, 0xBB9A,
0x4A75, 0x5A54, 0x6A37, 0x7A16, 0x0AF1, 0x1AD0, 0x2AB3, 0x3A92,
0xFD2E, 0xED0F, 0xDD6C, 0xCD4D, 0xBDAA, 0xAD8B, 0x9DE8, 0x8DC9,
0x7C26, 0x6C07, 0x5C64, 0x4C45, 0x3CA2, 0x2C83, 0x1CE0, 0x0CC1,
0xEF1F, 0xFF3E, 0xCF5D, 0xDF7C, 0xAF9B, 0xBFBA, 0x8FD9, 0x9FF8,
0x6E17, 0x7E36, 0x4E55, 0x5E74, 0x2E93, 0x3EB2, 0x0ED1, 0x1EF0 };
while (len--) crc = table[((crc>>8) ^ *data++) & 0xFF] ^ (crc << 8);
return crc;
}
static ulong crc_0x04C11DB7(const uchar* data, ulong len, ulong crc)
{
// x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 +
// x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7)
const static ulong table[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd,
0x4c11db70, 0x48d0c6c7, 0x4593e01e, 0x4152fda9,
0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011,
0x791d4014, 0x7ddc5da3, 0x709f7b7a, 0x745e66cd,
0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5,
0xbe2b5b58, 0xbaea46ef, 0xb7a96036, 0xb3687d81,
0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49,
0xc7361b4c, 0xc3f706fb, 0xceb42022, 0xca753d95,
0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d,
0x34867077, 0x30476dc0, 0x3d044b19, 0x39c556ae,
0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16,
0x018aeb13, 0x054bf6a4, 0x0808d07d, 0x0cc9cdca,
0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02,
0x5e9f46bf, 0x5a5e5b08, 0x571d7dd1, 0x53dc6066,
0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e,
0xbfa1b04b, 0xbb60adfc, 0xb6238b25, 0xb2e29692,
0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a,
0xe0b41de7, 0xe4750050, 0xe9362689, 0xedf73b3e,
0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686,
0xd5b88683, 0xd1799b34, 0xdc3abded, 0xd8fba05a,
0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb,
0x4f040d56, 0x4bc510e1, 0x46863638, 0x42472b8f,
0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47,
0x36194d42, 0x32d850f5, 0x3f9b762c, 0x3b5a6b9b,
0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623,
0xf12f560e, 0xf5ee4bb9, 0xf8ad6d60, 0xfc6c70d7,
0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f,
0xc423cd6a, 0xc0e2d0dd, 0xcda1f604, 0xc960ebb3,
0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b,
0x9b3660c6, 0x9ff77d71, 0x92b45ba8, 0x9675461f,
0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640,
0x4e8ee645, 0x4a4ffbf2, 0x470cdd2b, 0x43cdc09c,
0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24,
0x119b4be9, 0x155a565e, 0x18197087, 0x1cd86d30,
0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088,
0x2497d08d, 0x2056cd3a, 0x2d15ebe3, 0x29d4f654,
0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c,
0xe3a1cbc1, 0xe760d676, 0xea23f0af, 0xeee2ed18,
0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0,
0x9abc8bd5, 0x9e7d9662, 0x933eb0bb, 0x97ffad0c,
0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4 };
while (len--) crc = table[((crc>>24) ^ *data++) & 0xFF] ^ (crc << 8);
return crc;
}
static ulong crc_0x04C11DB7_r(const uchar* data, ulong len, ulong crc)
{
// x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 +
// x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7)
// reflected
const static ulong table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d };
while (len--) crc = table[(crc ^ *data++) & 0xFF] ^ (crc >> 8);
return crc;
}
static ulong adler32(const uchar* data, ulong len, ulong init)
{
ulong a = init & 0xFFFF;
ulong b = (init >> 16) & 0xFFFF;
while (len) {
ulong tlen = len > 5550 ? 5550 : len;
len -= tlen;
do {
a += *data++;
b += a;
} while (--tlen);
a = (a & 0xFFFF) + (a >> 16) * 15;
b = (b & 0xFFFF) + (b >> 16) * 15;
}
if (a >= 65521) a -= 65521;
b = (b & 0xFFFF) + (b >> 16) * 15;
if (b >= 65521) b -= 65521;
return b << 16 | a;
}
static ulong hexsum(const uchar* data, ulong len, ulong sum)
{
// Add all hex digits, ignore all other bytes.
ulong d;
while (len--)
{
d = toupper(*data++);
if (isxdigit(d))
{
if (isdigit(d)) d -= '0';
else d -= 'A' - 0x0A;
sum += d;
}
}
return sum;
}
struct checksum
{
const char* name;
checksumFunc func;
ulong init;
ulong xorout;
signed char bytes;
};
static checksum checksumMap[] =
// You may add your own checksum functions to this map.
{
// name func init xorout bytes chk("123456789")
{"sum", sum, 0x00, 0x00, 1}, // 0xDD
{"sum8", sum, 0x00, 0x00, 1}, // 0xDD
{"sum16", sum, 0x0000, 0x0000, 2}, // 0x01DD
{"sum32", sum, 0x00000000, 0x00000000, 4}, // 0x000001DD
{"nsum", sum, 0xff, 0xff, 1}, // 0x23
{"negsum", sum, 0xff, 0xff, 1}, // 0x23
{"-sum", sum, 0xff, 0xff, 1}, // 0x23
{"notsum", sum, 0x00, 0xff, 1}, // 0x22
{"~sum", sum, 0x00, 0xff, 1}, // 0x22
{"xor", xor8, 0x00, 0x00, 1}, // 0x31
{"crc8", crc_0x07, 0x00, 0x00, 1}, // 0xF4
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
{"crc16", crc_0x8005, 0x0000, 0x0000, 2}, // 0xFEE8
{"crc16r", crc_0x8005_r, 0x0000, 0x0000, 2}, // 0xBB3D
{"ccitt16", crc_0x1021, 0xFFFF, 0x0000, 2}, // 0x29B1
{"ccitt16a",crc_0x1021, 0x1D0F, 0x0000, 2}, // 0xE5CC
{"crc32", crc_0x04C11DB7, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFC891918
{"crc32r", crc_0x04C11DB7_r, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xCBF43926
{"jamcrc", crc_0x04C11DB7_r, 0xFFFFFFFF, 0x00000000, 4}, // 0x340BC6D9
{"adler32", adler32, 0x00000001, 0x00000000, 4}, // 0x091E01DE
{"hexsum8", hexsum, 0x00, 0x00, 1} // 0x2D
};
static ulong mask[5] = {0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF};
class ChecksumConverter : public StreamFormatConverter
{
int parse (const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printPseudo(const StreamFormat&, StreamBuffer&);
int scanPseudo(const StreamFormat&, StreamBuffer&, long& cursor);
};
int ChecksumConverter::
parse(const StreamFormat&, StreamBuffer& info, const char*& source, bool)
{
const char* p = strchr(source, '>');
if (!p)
{
error ("Missing terminating '>' in checksum format.\n");
return false;
}
size_t fnum;
for (fnum = 0; fnum < sizeof(checksumMap)/sizeof(checksum); fnum++)
{
if (strncasecmp(source, checksumMap[fnum].name, p-source) == 0)
{
info.append(fnum);
source = p+1;
return pseudo_format;
}
}
error ("Unknown checksum algorithm \"%.*s\"\n",
static_cast<int>(p-source), source);
return false;
}
bool ChecksumConverter::
printPseudo(const StreamFormat& format, StreamBuffer& output)
{
ulong sum;
int fnum = format.info[0];
int start = format.width;
int length = output.length()-format.width;
if (format.prec > 0) length -= format.prec;
debug("ChecksumConverter %s: output to check: \"%s\"\n",
checksumMap[fnum].name, output.expand(start,length)());
sum = checksumMap[fnum].xorout ^ checksumMap[fnum].func(
reinterpret_cast<uchar*>(output(start)), length,
checksumMap[fnum].init) & mask[checksumMap[fnum].bytes];
debug("ChecksumConverter %s: output checksum is 0x%lX\n",
checksumMap[fnum].name, sum);
int i;
unsigned outchar;
if (format.flags & alt_flag) // lsb first (little endian)
{
for (i = 0; i < checksumMap[fnum].bytes; i++)
{
outchar = sum & 0xff;
debug("ChecksumConverter %s: little endian appending 0x%X\n",
checksumMap[fnum].name, outchar);
if (format.flags & zero_flag) // ASCII
output.printf("%02X", outchar);
else // binary
output.append(outchar);
sum >>= 8;
}
}
else // msb first (big endian)
{
sum <<= 8*(4-checksumMap[fnum].bytes);
for (i = 0; i < checksumMap[fnum].bytes; i++)
{
outchar = (sum >> 24) & 0xff;
debug("ChecksumConverter %s: big endian appending 0x%X\n",
checksumMap[fnum].name, outchar);
if (format.flags & zero_flag) // ASCII
output.printf("%02X", outchar);
else // binary
output.append(outchar);
sum <<= 8;
}
}
return true;
}
int ChecksumConverter::
scanPseudo(const StreamFormat& format, StreamBuffer& input, long& cursor)
{
int fnum = format.info[0];
ulong sum;
int start = format.width;
int length = cursor-format.width;
if (format.prec > 0) length -= format.prec;
debug("ChecksumConverter %s: input to check: \"%s\n",
checksumMap[fnum].name, input.expand(start,length)());
if (input.length() - cursor <
(format.flags & zero_flag ? 2 : 1) * checksumMap[fnum].bytes)
{
error("Input too short for checksum\n");
return -1;
}
sum = checksumMap[fnum].xorout ^ checksumMap[fnum].func(
reinterpret_cast<uchar*>(input(start)), length,
checksumMap[fnum].init) & mask[checksumMap[fnum].bytes];
debug("ChecksumConverter %s: input checksum is 0x%0*lX\n",
checksumMap[fnum].name, 2*checksumMap[fnum].bytes, sum);
int i,j;
unsigned inchar;
if (format.flags & alt_flag) // lsb first (little endian)
{
for (i = 0; i < checksumMap[fnum].bytes; i++)
{
if (format.flags & zero_flag) // ASCII
{
sscanf(input(cursor+2*i), "%2X", &inchar);
}
else // binary
{
inchar = input[cursor+i] & 0xff;
}
if (inchar != ((sum >> 8*i) & 0xff))
{
error("Input byte 0x%02X does not match checksum 0x%0*lX\n",
inchar, 2*checksumMap[fnum].bytes, sum);
return -1;
}
}
}
else // msb first (big endian)
{
for (i = checksumMap[fnum].bytes-1, j = 0; i >= 0; i--, j++)
{
if (format.flags & zero_flag) // ASCII
{
sscanf(input(cursor+2*i), "%2x", &inchar);
}
else // binary
{
inchar = input[cursor+i] & 0xff;
}
if (inchar != ((sum >> 8*j) & 0xff))
{
error("Input byte 0x%02X does not match checksum 0x%0*lX\n",
inchar, 2*checksumMap[fnum].bytes, sum);
return -1;
}
}
}
if (format.flags & zero_flag) // ASCII
return 2*checksumMap[fnum].bytes;
return checksumMap[fnum].bytes;
}
RegisterConverter (ChecksumConverter, "<");

229
src/DebugInterface.cc Normal file
View File

@ -0,0 +1,229 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the interface to a debug and example bus drivers for *
* StreamDevice. Please refer to the HTML files in ../doc/ for *
* a detailed documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamBusInterface.h"
#include "StreamError.h"
#include "StreamBuffer.h"
// This is a non-blocking bus interface for debugging purpose.
// Normally, a bus interface will use blocking I/O and thus require
// a separate thread.
class DebugInterface : StreamBusInterface
{
DebugInterface(Client* client);
// StreamBusInterface methods
bool lockRequest(unsigned long lockTimeout_ms);
bool unlock();
bool writeRequest(const void* output, size_t size,
unsigned long writeTimeout_ms);
bool readRequest(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms, long expectedLength, bool async);
protected:
~DebugInterface();
public:
// static creator method
static StreamBusInterface* getBusInterface(Client* client,
const char* busname, int addr, const char* param);
};
RegisterStreamBusInterface(DebugInterface);
DebugInterface::
DebugInterface(Client* client) : StreamBusInterface(client)
{
// Create or attach to interface thread
}
DebugInterface::
~DebugInterface()
{
// Abort any running I/O
// Disconnect low-level bus driver
// Free all resources
}
// Interface method getBusInterface():
// Do we have this bus/addr ?
StreamBusInterface* DebugInterface::
getBusInterface(Client* client,
const char* busname, int addr, const char*)
{
if (strcmp(busname, "debug") == 0)
{
DebugInterface* interface = new DebugInterface(client);
debug ("DebugInterface::getBusInterface(%s, %d): "
"new Interface allocated\n",
busname, addr);
// Connect to low-level bus driver here or in constructor
// Delete interface and return NULL if connect fails.
return interface;
}
return NULL;
}
// Interface method lockRequest():
// We want exclusive access to the device
// lockTimeout_ms=0 means "block here" (used in @init)
// Return false if the lock request cannot be accepted.
bool DebugInterface::
lockRequest(unsigned long lockTimeout_ms)
{
debug("DebugInterface::lockRequest(%s, %ld msec)\n",
clientName(), lockTimeout_ms);
// Debug interface is non-blocking,
// thus we can call lockCallback() immediately.
lockCallback(StreamIoSuccess);
// A blocking interface would send a message that requests
// exclusive access to the interface thread.
// Once the interface is available, the interface thread
// would call lockCallback(StreamIoSuccess).
// If lockTimeout_ms expires, the interface would
// call lockCallback(ioTimeout).
// Only if lockTimeout_ms==0, a blocking interface is allowed,
// even required, to block here until the interface is available.
return true;
}
// Interface method unlock():
// We don't need exclusive access any more
// Return false if the unlock fails. (How can that be?)
bool DebugInterface::
unlock()
{
debug("DebugInterface::unlock(%s)\n",
clientName());
// debug interface is non-blocking, thus unlock does nothing.
// A blocking interface would call lockCallback(StreamIoSuccess) now
// for the next interface that had called lockRequest.
return true;
}
// Interface method writeRequest():
// We want to write something
// This method will only be called by StreamDevice when locked.
// I.e. lockRequest() has been called and the interface
// has called lockCallback(StreamIoSuccess).
// It may be called several times before unlock() is called.
// Return false if the write request cannot be accepted.
bool DebugInterface::
writeRequest(const void* output, size_t size, unsigned long writeTimeout_ms)
{
debug("DebugInterface::writeRequest(%s, \"%.*s\", %ld msec)\n",
clientName(), (int)size, (char*)output, writeTimeout_ms);
// Debug interface is non-blocking,
// thus we can call writeCallback() immediately.
writeCallback(StreamIoSuccess);
// A blocking interface would send a message that requests
// write access to the interface thread.
// That thread would handle the actual write.
// Writing all output might take some time and might be
// done in several chunks.
// If any chunk cannot be written within writeTimeout_ms milliseconds,
// the interface thread would call writeCallback(ioTimeout).
// If anything is wrong with the bus (e.g. unpugged cable),
// the interface would return false now or call writeCallback(ioFault)
// later when it finds out.
// Once the interface has completed writing, the interface thread
// would call writeCallback(StreamIoSuccess).
return true;
}
// Interface method readRequest():
// We want to read something
// This method may be called in async mode, if a previous call to
// supportsAsyncRead() had returned true.
// It may be called unlocked.
// If expectedLength!=0, it is a hint when to stop reading input.
// Return false if the read request cannot be accepted.
bool DebugInterface::
readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
long expectedLength, bool async)
{
debug("DebugInterface::readRequest(%s, %ld msec reply, %ld msec read, expect %ld bytes, asyn=%s)\n",
clientName(), replyTimeout_ms, readTimeout_ms, expectedLength, async?"yes":"no");
// Debug interface does not support async mode.
// Since we have not implemented supportsAsyncRead(),
// this method is never called with async=true.
if (async) return false;
// Debug interface is non-blocking,
// thus we can call writeCallback() immediately.
const char input [] = "Receviced input 3.1415\r\n";
readCallback(StreamIoEnd, input, sizeof(input));
// A blocking interface would send a message that requests
// read access to the interface thread.
// That thread would handle the actual read.
// Reading all input might take some time and might be
// done in several chunks.
// If no data arrives within replyTimeout_ms milliseconds,
// the interface thread would call
// readCallback(ioNoReply).
// If any following chunk cannot be read within readTimeout_ms
// milliseconds, the interface thread would call
// readCallback(ioTimeout, input, inputlength)
// with all input received so far.
// For each reveived chunk, the interface would call
// readCallback(StreamIoSuccess, input, inputlength)
// and check its return value.
// The return value is 0 if no more input is expected, i.e.
// the interface would tell the low-level driver that
// reading has finished (if necessary) and return.
// If the return value is >0, this number of additional bytes is
// expected at maximum (this replaces the original expectedLengh).
// If the return value is -1, an unknown number of additional
// bytes is expected.
// In the both cases where more input is expected, the interface
// would do another read and call readCallback() again.
// The return value needs only to be checked if readCallback()
// is called with StreamIoSuccess as the first argument.
// If the interface received a signal from the low-level driver
// telling that input has terminated (e.g. EOS in GPIB, EOF when
// reading file or socket, ...) it would call
// readCallback(StreamIoEnd, input, inputlength).
// If anything is wrong with the bus (e.g. unpugged cable),
// the interface would immediately return false now or
// call readCallback(ioFault) later when it finds out.
// On some busses, input may arrive even before readRequest()
// has been called, e.g. when a device replies very fast.
// In this case, the interface would buffer the input or call
// readCallback() even without beeing asked to do so.
// In async mode, the interface would arrange that the client
// gets a copy of the next input on the same bus/address,
// even when the input is meant for an other client.
return true;
}

142
src/EnumConverter.cc Normal file
View File

@ -0,0 +1,142 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the enum format converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamError.h"
#include "StreamProtocol.h"
// Enum %{string0|string1|...}
class EnumConverter : public StreamFormatConverter
{
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printLong(const StreamFormat&, StreamBuffer&, long);
int scanLong(const StreamFormat&, const char*, long&);
};
int EnumConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool)
{
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag|alt_flag))
{
error("Use of modifiers '-', '+', ' ', '0', '#'"
"not allowed with %%{ conversion\n");
return false;
}
int i = info.length(); // put maxValue here later
info.append('\0');
int maxValue = 0;
while (*source)
{
switch (*source)
{
case '|':
info.append('\0');
if (++maxValue > 255)
{
error("Too many enums (max 256)\n");
return false;
}
break;
case '}':
source++;
info.append('\0');
info[i] = maxValue;
debug("EnumConverter::parse %d choices: %s\n",
maxValue+1, info.expand(i+1)());
return enum_format;
case esc:
info.append(*source++);
default:
info.append(*source);
}
source++;
}
error("Missing '}' after %%{ format conversion\n");
return false;
}
bool EnumConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
long maxValue = fmt.info[0]; // number of enums
const char* s = fmt.info+1; // first enum string
if (value < 0 || value > maxValue)
{
error("Value %li out of range [0...%li]\n", value, maxValue);
return false;
}
while (value--)
{
while(*s)
{
if (*s == esc) s++;
s++;
}
s++;
}
while(*s)
{
if (*s == esc) s++;
output.append(*s++);
}
return true;
}
int EnumConverter::
scanLong(const StreamFormat& fmt, const char* input, long& value)
{
debug("EnumConverter::scanLong(%%%c, \"%s\")\n",
fmt.conv, input);
long maxValue = fmt.info[0]; // number of enums
const char* s = fmt.info+1; // first enum string
int length;
long val;
bool match;
for (val = 0; val <= maxValue; val++)
{
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", val, s);
length = 0;
match = true;
while(*s)
{
if (*s == StreamProtocolParser::skip)
{
s++;
length++;
continue;
}
if (*s == esc) s++;
if (*s++ != input[length++]) match = false;
}
if (match)
{
debug("EnumConverter::scanLong: value %ld matches\n", val);
value = val;
return length;
}
s++;
}
debug("EnumConverter::scanLong: no value matches\n");
return -1;
}
RegisterConverter (EnumConverter, "{");

100
src/Makefile Normal file
View File

@ -0,0 +1,100 @@
################################################################
# StreamDevice Support #
# #
# (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) #
# (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) #
# #
# This is the EPICS 3.14 Makefile of StreamDevice. #
# Normally it should not be necessary to modify this file. #
# All configuration can be done in CONFIG_STREAM #
# #
# If you do any changes in this file, you are not allowed to #
# redistribute it any more. If there is a bug or a missing #
# feature, send me an email and/or your patch. If I accept #
# your changes, they will go to the next release. #
# #
# DISCLAIMER: If this software breaks something or harms #
# someone, it's your problem. #
# #
################################################################
TOP=../..
# Look if we have EPICS R3.13 or R3.14
ifeq ($(wildcard $(TOP)/configure),)
# EPICS R3.13
include $(TOP)/config/CONFIG_APP
# The real work is in Makefile.Vx
include $(TOP)/config/RULES_ARCHS
else
# EPICS R3.14
include $(TOP)/configure/CONFIG
-include CONFIG_STREAM
-include ../CONFIG_STREAM
LIBRARY_DEFAULT = stream
DBD += $(LIBRARY_DEFAULT).dbd
ifdef ASYN
LIB_LIBS += asyn
BUSSES += AsynDriver
endif
ifdef T_A
ifndef BUSSES
$(error No bus interface defined! Didn't you set ASYN in your RELEASE file?)
endif
endif
ifeq ($(LOADABLE_MODULE),YES)
SRCS += $(LIBRARY_DEFAULT)_registerRecordDeviceDriver.cpp
endif
SRCS += $(BUSSES:%=%Interface.cc)
SRCS += $(FORMATS:%=%Converter.cc)
SRCS += $(RECORDS:%=dev%Stream.c)
SRCS += $(STREAM_SRCS)
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
ifeq ($(USE_MEMGUARD),YES)
# memguard looks for memory leaks (gcc only)
CPPFLAGS += -include ../memguard.h
LIB_SRCS += memguard.cc
endif
INC += devStream.h
include $(TOP)/configure/RULES
# Update version string (contains __DATE__ and __TIME__)
# each time make runs.
StreamVersion$(OBJ): FORCE
FORCE:
# Add references to all registrars to main file to avoid
# missing initialization.
StreamCore$(OBJ): streamReferences
streamReferences: ../CONFIG_STREAM
@for i in $(BUSSES); \
do echo "extern void* ref_$${i}Interface;"; \
echo "void* p$$i = ref_$${i}Interface;"; \
done > $@
@for i in $(FORMATS); \
do echo "extern void* ref_$${i}Converter;"; \
echo "void* p$$i = ref_$${i}Converter;"; \
done >> $@
# create stream.dbd from all RECORDS
$(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
@for r in $(RECORDS); \
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
done > $@
@echo "driver(stream)" >> $@
@echo "variable(streamDebug, int)" >> $@
@echo "registrar(streamRegistrar)" >> $@
endif

45
src/Makefile.Host Normal file
View File

@ -0,0 +1,45 @@
################################################################
# StreamDevice Support #
# #
# (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) #
# (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) #
# #
# This is the EPICS 3.13 Makefile of StreamDevice. #
# Normally it should not be necessary to modify this file. #
# All configuration can be done in CONFIG_STREAM #
# #
# If you do any changes in this file, you are not allowed to #
# redistribute it any more. If there is a bug or a missing #
# feature, send me an email and/or your patch. If I accept #
# your changes, they will go to the next release. #
# #
# DISCLAIMER: If this software breaks something or harms #
# someone, it's your problem. #
# #
################################################################
TOP = ../../..
include $(TOP)/config/CONFIG_APP
include ../CONFIG_STREAM
# In 3.13, calcout has no device support
RECORDS_3_13 = $(filter-out calcout,$(RECORDS))
DBDNAME = stream.dbd
INC += devStream.h
# This is the munch.pl taken from EPICS 3.14.8.2
# Install script and rule.
CONFIGS = RULES.munch
SCRIPTS = munch.pl
include $(TOP)/config/RULES.Host
# create stream.dbd from all RECORDS
stream.dbd: ../CONFIG_STREAM
@for r in $(RECORDS_3_13); \
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
done > $@
@echo "driver(stream)" >> $@

58
src/Makefile.Vx Normal file
View File

@ -0,0 +1,58 @@
################################################################
# StreamDevice Support #
# #
# (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) #
# (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) #
# #
# This is the EPICS 3.13 Makefile of StreamDevice. #
# Normally it should not be necessary to modify this file. #
# All configuration can be done in CONFIG_STREAM #
# #
# If you do any changes in this file, you are not allowed to #
# redistribute it any more. If there is a bug or a missing #
# feature, send me an email and/or your patch. If I accept #
# your changes, they will go to the next release. #
# #
# DISCLAIMER: If this software breaks something or harms #
# someone, it's your problem. #
# #
################################################################
TOP = ../../..
include $(TOP)/config/CONFIG_APP
include ../CONFIG_STREAM
LIBNAME = streamLib
# In 3.13, calcout has no device support
RECORDS_3_13 = $(filter-out calcout,$(RECORDS))
ifdef ASYN
BUSSES += AsynDriver
endif
SRCS.cc += $(patsubst %,../%,$(filter %.cc,$(STREAM_SRCS)))
SRCS.cc += $(BUSSES:%=../%Interface.cc)
SRCS.cc += $(FORMATS:%=../%Converter.cc)
SRCS.c += $(patsubst %,../%,$(filter %.c,$(STREAM_SRCS)))
SRCS.c += $(RECORDS_3_13:%=../dev%Stream.c)
LIBOBJS = $(patsubst ../%,%.o,$(basename $(SRCS.cc) $(SRCS.c)))
include $(TOP)/config/RULES.Vx
include $(TOP)/config/RULES.munch
build:: depends
-include DEPENDS
# Update version string (contains __DATE__ and __TIME__)
# each time make runs.
StreamVersion.o: FORCE
FORCE:
StreamCore.o: streamReferences
streamReferences:
touch $@

14
src/RULES.munch Normal file
View File

@ -0,0 +1,14 @@
MUNCH = $(PERL) $(INSTALL_LOCATION)/bin/$(HOST_ARCH)/munch.pl
# The original 3.13.10 munching rule does not really work well
build:: $(LIBNAME).munch
buildInstall:: $(INSTALL_BIN)/$(LIBNAME).munch
%.munch: %
$(RM) $*_ctct.o $*_ctdt.c
$(NM) $< | $(MUNCH) > $*_ctdt.c
$(GCC) -traditional $(CFLAGS) -fdollars-in-identifiers -c $(SOURCE_FLAG) $*_ctdt.c
$(LINK.c) $@ $< $*_ctdt.o

119
src/RawConverter.cc Normal file
View File

@ -0,0 +1,119 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the raw format converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamError.h"
// Raw Bytes Converter %r
class RawConverter : public StreamFormatConverter
{
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printLong(const StreamFormat&, StreamBuffer&, long);
int scanLong(const StreamFormat&, const char*, long&);
};
int RawConverter::
parse(const StreamFormat&, StreamBuffer&,
const char*&, bool)
{
return long_format;
}
bool RawConverter::
printLong(const StreamFormat& format, StreamBuffer& output, long value)
{
int prec = format.prec; // number of bytes from value
if (prec == -1) prec = 1; // default: 1 byte
int width = prec; // number of bytes in output
if (format.width > width) width = format.width;
char byte = 0;
if (format.flags & alt_flag) // lsb first (little endian)
{
while (prec--)
{
byte = static_cast<char>(value);
output.append(byte);
value >>= 8;
width--;
}
byte = (byte & 0x80) ? 0xFF : 0x00; // fill with sign
while (width--)
{
output.append(byte);
}
}
else // msb first (big endian)
{
byte = ((value >> (8 * (prec-1))) & 0x80) ? 0xFF : 0x00;
while (width > prec) // fill with sign
{
output.append(byte);
width--;
}
while (prec--)
{
output.append(static_cast<char>(value >> (8 * prec)));
}
}
return true;
}
int RawConverter::
scanLong(const StreamFormat& format, const char* input, long& value)
{
long length = 0;
long val = 0;
int width = format.width;
if (width == 0) width = 1; // default: 1 byte
if (format.flags & skip_flag)
{
return width; // just skip input
}
if (format.flags & alt_flag)
{
// little endian (sign extended)*/
unsigned int shift = 0;
while (--width && shift < sizeof(long)*8)
{
val |= ((unsigned char) input[length++]) << shift;
shift += 8;
}
if (width == 0)
{
val |= ((signed char) input[length++]) << shift;
}
length += width; // ignore upper bytes not fitting in long
}
else
{
// big endian (sign extended)*/
val = (signed char) input[length++];
while (--width)
{
val <<= 8;
val |= (unsigned char) input[length++];
}
}
value = val;
return length;
}
RegisterConverter (RawConverter, "r");

317
src/StreamBuffer.cc Normal file
View File

@ -0,0 +1,317 @@
/***************************************************************
* StreamBuffer *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is a buffer class used in StreamDevice for I/O. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamBuffer.h"
#include "StreamError.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#if defined(__vxworks) || defined(vxWorks) || defined(_WIN32) || defined(__rtems__)
// These systems have no vsnprintf
#include <epicsStdio.h>
#define vsnprintf epicsVsnprintf
#endif
void StreamBuffer::
init(const void* s, long minsize)
{
len = 0;
offs = 0;
buffer = local;
cap = sizeof(local);
if (minsize < 0) minsize = 0;
if (minsize >= cap)
{
// use allocated buffer
grow(minsize);
}
else
{
// clear local buffer
memset(buffer+minsize, 0, cap-minsize);
}
if (s) {
len = minsize;
memcpy(buffer, s, minsize);
}
}
void StreamBuffer::
grow(long minsize)
{
// make space for minsize + 1 (for termination) bytes
char* newbuffer;
long newcap;
if (minsize > 10000)
{
// crude trap against infinite grow
error ("StreamBuffer exploded growing from %ld to %ld chars. Exiting\n",
cap, minsize);
int i;
char c;
fprintf(stderr, "String contents (len=%ld):\n", len);
for (i = offs; i < len; i++)
{
c = buffer[i];
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
{
fprintf(stderr, "<%02x>", c & 0xff);
}
else
{
fprintf(stderr, "%c", c);
}
}
fprintf(stderr, "\n");
abort();
}
if (minsize < cap)
{
// just move contents to start of buffer and clear end
// to avoid reallocation
memmove(buffer, buffer+offs, len);
memset(buffer+len, 0, offs);
offs = 0;
return;
}
// allocate new buffer
for (newcap = sizeof(local)*2; newcap <= minsize; newcap *= 2);
newbuffer = new char[newcap];
// copy old buffer to new buffer and clear end
memcpy(newbuffer, buffer+offs, len);
memset(newbuffer+len, 0, newcap-len);
if (buffer != local)
{
delete buffer;
}
buffer = newbuffer;
cap = newcap;
offs = 0;
}
StreamBuffer& StreamBuffer::
append(const void* s, long size)
{
if (size <= 0)
{
// append negative number of bytes? let's delete some
if (size < -len) size = -len;
memset (buffer+offs+len+size, 0, -size);
}
else
{
check(size);
memcpy(buffer+offs+len, s, size);
}
len += size;
return *this;
}
long int StreamBuffer::
find(const void* m, long size, long start) const
{
if (start < 0)
{
start += len;
if (start < 0) start = 0;
}
if (start >= len-size+1) return -1; // find nothing after end
if (!m || size <= 0) return start; // find empty string at start
const char* s = static_cast<const char*>(m);
char* b = buffer+offs;
char* p = b+start;
long i;
while ((p = static_cast<char*>(memchr(p, s[0], b-p+len-size+1))))
{
i = 1;
while (p[i] == s[i])
{
if (++i >= size) return p-b;
}
p++;
}
return -1;
}
StreamBuffer& StreamBuffer::
replace(long remstart, long remlen, const void* ins, long inslen)
{
if (remstart < 0)
{
// remove from end
remstart += len;
}
if (remlen < 0)
{
// remove left of remstart
remstart += remlen;
remlen = -remlen;
}
if (inslen < 0)
{
// handle negative inserts as additional remove
remstart += inslen;
remlen -= inslen;
inslen = 0;
}
if (remstart < 0)
{
// truncate remove before bufferstart
remlen += remstart;
remstart = 0;
}
if (remstart > len)
{
// remove begins after bufferend
remstart = len;
}
if (remlen >= len-remstart)
{
// truncate remove after bufferend
remlen = len-remstart;
}
if (inslen == 0 && remstart == 0)
{
// optimize remove of bufferstart
offs += remlen;
return *this;
}
if (inslen < 0) inslen = 0;
long remend = remstart+remlen;
long newlen = len+inslen-remlen;
if (cap <= newlen)
{
// buffer too short
long newcap;
for (newcap = sizeof(local)*2; newcap <= newlen; newcap *= 2);
char* newbuffer = new char[newcap];
memcpy(newbuffer, buffer+offs, remstart);
memcpy(newbuffer+remstart, ins, inslen);
memcpy(newbuffer+remstart+inslen, buffer+offs+remend, len-remend);
memset(newbuffer+newlen, 0, newcap-newlen);
if (buffer != local)
{
delete buffer;
}
buffer = newbuffer;
cap = newcap;
offs = 0;
}
else
{
if (newlen+offs<=cap)
{
// move to start of buffer
memmove(buffer+offs+remstart+inslen, buffer+offs+remend, len-remend);
memcpy(buffer+offs+remstart, ins, inslen);
if (newlen<len) memset(buffer+offs+newlen, 0, len-newlen);
}
else
{
memmove(buffer,buffer+offs,remstart);
memmove(buffer+remstart+inslen, buffer+offs+remend, len-remend);
memcpy(buffer+remstart, ins, inslen);
if (newlen<len) memset(buffer+newlen, 0, len-newlen);
offs = 0;
}
}
len = newlen;
return *this;
}
StreamBuffer& StreamBuffer::
printf(const char* fmt, ...)
{
va_list va;
int printed;
while (1)
{
va_start(va, fmt);
printed = vsnprintf(buffer+offs+len, cap-offs-len, fmt, va);
va_end(va);
if (printed > -1 && printed < (int)(cap-offs-len))
{
len += printed;
return *this;
}
if (printed > -1) grow(len+printed);
else grow(len);
}
}
StreamBuffer StreamBuffer::expand(long start, long length) const
{
long end;
if (start < 0)
{
start += len;
if (start < 0) start = 0;
}
end = length >= 0 ? start+length : len;
if (end > len) end = len;
StreamBuffer result((end-start)*2);
start += offs;
end += offs;
long i;
char c;
for (i = start; i < end; i++)
{
c = buffer[i];
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
{
result.printf("<%02x>", c & 0xff);
}
else
{
result.append(c);
}
}
return result;
}
StreamBuffer StreamBuffer::
dump() const
{
StreamBuffer result(256+cap*5);
result.append("\033[0m");
long i;
result.printf("%ld,%ld,%ld:\033[37m", offs, len, cap);
for (i = 0; i < cap; i++)
{
if (i == offs) result.append("\033[34m[\033[0m");
if ((buffer[i] & 0x7f) < 0x20 || (buffer[i] & 0x7f) == 0x7f)
{
if (i < offs || i >= offs+len)
result.printf(
"<%02x>", buffer[i] & 0xff);
else
result.printf(
"\033[34m<%02x>\033[37m", buffer[i] & 0xff);
}
else
{
result.append(buffer[i]);
}
if (i == offs+len-1) result.append("\033[34m]\033[37m");
}
result.append("\033[0m");
return result;
}

208
src/StreamBuffer.h Normal file
View File

@ -0,0 +1,208 @@
/***************************************************************
* StreamBuffer *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is a buffer class used in StreamDevice for I/O. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamBuffer_h
#define StreamBuffer_h
#include <string.h>
#include <stdio.h>
#ifndef __GNUC__
#define __attribute__(x)
#endif
class StreamBuffer
{
long len;
long cap;
long offs;
char* buffer;
char local[64];
void grow(long);
void init(const void*, long);
void check(long size)
{if (len+offs+size >= cap) grow(len+size);}
public:
// Hints:
// * Any index parameter (long) can be negative
// meaning "count from end" (-1 is the last byte)
// * Any returned char* pointer becomes invalid when
// the StreamBuffer is modified.
StreamBuffer()
{init(NULL, 0);}
StreamBuffer(const void*s, long size)
{init(s, size);}
StreamBuffer(const char*s)
{init(s, s?strlen(s):0);}
StreamBuffer(const StreamBuffer& s)
{init(s.buffer+s.offs, s.len);}
StreamBuffer(long size)
{init(NULL, size);}
~StreamBuffer()
{if (buffer != local) delete buffer;}
// operator (): get char* pointing to index
const char* operator()(long index=0) const
{buffer[offs+len]=0; return buffer+offs+(index<0?index+len:index);}
char* operator()(long index=0)
{buffer[offs+len]=0; return buffer+offs+(index<0?index+len:index);}
// operator []: get byte at index
char operator[](long index) const
{return buffer[offs+(index<0?index+len:index)];}
char& operator[](long index)
{return buffer[offs+(index<0?index+len:index)];}
// cast to bool: not empty?
operator bool() const
{return len>0;}
// length: get current data length
long length() const
{return len;}
// capacity: get current max data length (spare one byte for end)
long capacity() const
{return cap-1;}
// end: get pointer to byte after last data byte
const char* end() const
{return buffer+offs+len;}
// clear: set length to 0, don't free or blank memory (fast!)
StreamBuffer& clear()
{offs+=len; len=0; return *this;}
// reserve: reserve size bytes of memory and return
// pointer to that memory (for copying something to it)
char* reserve(long size)
{check(size); char* p=buffer+offs+len; len+=size; return p; }
// append: append data at the end of the buffer
StreamBuffer& append(char c)
{check(1); buffer[offs+len++]=c; return *this;}
StreamBuffer& append(const void* s, long size);
StreamBuffer& append(const char* s)
{return append(s, s?strlen(s):0);}
StreamBuffer& append(const StreamBuffer& s)
{return append(s.buffer+s.offs, s.len);}
// set: clear buffer and fill with new data
StreamBuffer& set(const void* s, long size)
{clear(); return append(s, size);}
StreamBuffer& set(const char* s)
{clear(); return append(s, s?strlen(s):0);}
StreamBuffer& set(const StreamBuffer& s)
{clear(); return append(s.buffer+s.offs, s.len);}
// operator =: alias for set
StreamBuffer& operator=(const char* s)
{return set(s);}
StreamBuffer& operator=(const StreamBuffer& s)
{return set(s);}
// replace: delete part of buffer (pos/length) and insert new data
StreamBuffer& replace(
long pos, long length, const void* s, long size);
StreamBuffer& replace(long pos, long length, const char* s)
{return replace(pos, length, s, s?strlen(s):0);}
StreamBuffer& replace(long pos, long length, const StreamBuffer& s)
{return replace(pos, length, s.buffer+s.offs, s.len);}
// replace: delete part of buffer
StreamBuffer& remove(long pos, long length)
{return replace(pos, length, NULL, 0);}
StreamBuffer& remove(long length)
{if (length>len) length=len;
offs+=length; len-=length; return *this;}
// replace: delete end of buffer
StreamBuffer& truncate(long pos)
{return replace(pos, len, NULL, 0);}
// insert: insert new data into buffer
StreamBuffer& insert(long pos, const void* s, long size)
{return replace(pos, 0, s, size);}
StreamBuffer& insert(long pos, const char* s)
{return replace(pos, 0, s, s?strlen(s):0);}
StreamBuffer& insert(long pos, const StreamBuffer& s)
{return replace(pos, 0, s.buffer+s.offs, s.len);}
StreamBuffer& insert(long pos, char c)
{return replace(pos, 0, &c, 1);}
StreamBuffer& printf(const char* fmt, ...)
__attribute__ ((format(printf,2,3)));
// find: get index of data in buffer or -1
long find(char c, long start=0) const
{char* p;
return (p = static_cast<char*>(
memchr(buffer+offs+(start<0?start+len:start),
c, start<0?-start:len-start)))?
p-(buffer+offs) : -1;}
long find(const void* s, long size, long start=0) const;
long find(const char* s, long start=0) const
{return find(s, s?strlen(s):0, start);}
long int find(const StreamBuffer& s, long start=0) const
{return find(s.buffer+s.offs, s.len, start);}
// equals: returns true if first size bytes are equal
bool equals(const void* s, long size) const
{return len>=size ? memcmp(buffer+offs, s, size) == 0 : false;}
// equals: returns true if first string is equal (empty string match)
bool equals(const char* s) const
{return len ? strcmp(buffer+offs, s) == 0 : !s || !*s;}
// expand: create copy of StreamBuffer where all nonprintable characters
// are replaced by <xx> with xx being the hex code of the characters
StreamBuffer expand(long start=0, long length=-1) const;
// dump: debug function, like expand but also show the 'hidden' memory
// before and after the real data. Uses colours.
StreamBuffer dump() const;
};
#endif

144
src/StreamBusInterface.cc Normal file
View File

@ -0,0 +1,144 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the interface to bus drivers for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamBusInterface.h"
const char* StreamIoStatusStr[] = {
"StreamIoSuccess", "ioTimeout", "ioNoReply", "ioEnd", "ioFault"
};
StreamBusInterfaceRegistrarBase* StreamBusInterfaceRegistrarBase::first;
StreamBusInterfaceRegistrarBase::
StreamBusInterfaceRegistrarBase(const char* name) : name(name)
{
next = NULL;
StreamBusInterfaceRegistrarBase** pr;
for (pr = &first; *pr; pr = &(*pr)->next);
*pr = this;
}
StreamBusInterfaceRegistrarBase::
~StreamBusInterfaceRegistrarBase()
{
}
StreamBusInterface::
StreamBusInterface(Client* client) :
client(client)
{
}
bool StreamBusInterface::
supportsEvent()
{
return false;
}
bool StreamBusInterface::
supportsAsyncRead()
{
return false;
}
StreamBusInterface* StreamBusInterface::
find(Client* client, const char* busname, int addr, const char* param)
{
StreamBusInterfaceRegistrarBase* r;
StreamBusInterface* bus;
for (r = r->first; r; r = r->next)
{
bus = r->find(client, busname, addr, param);
if (bus) return bus;
}
return NULL;
}
bool StreamBusInterface::
acceptEvent(unsigned long, unsigned long)
{
return false;
}
void StreamBusInterface::
release ()
{
delete this;
}
bool StreamBusInterface::
connectRequest (unsigned long)
{
return false;
}
bool StreamBusInterface::
disconnect ()
{
return false;
}
bool StreamBusInterface::
writeRequest(const void*, size_t, unsigned long)
{
return false;
}
bool StreamBusInterface::
readRequest(unsigned long, unsigned long, long, bool)
{
return false;
}
void StreamBusInterface::
finish()
{
}
StreamBusInterface::Client::
~Client()
{
}
void StreamBusInterface::Client::
writeCallback(StreamIoStatus)
{
}
long StreamBusInterface::Client::
readCallback(StreamIoStatus, const void*, long)
{
return 0;
}
void StreamBusInterface::Client::
eventCallback(StreamIoStatus)
{
}
void StreamBusInterface::Client::
connectCallback(StreamIoStatus)
{
}
long StreamBusInterface::Client::
priority()
{
return 0;
}

200
src/StreamBusInterface.h Normal file
View File

@ -0,0 +1,200 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the interface to bus drivers for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamBusInterface_h
#define StreamBusInterface_h
#include <stddef.h>
enum StreamIoStatus {
StreamIoSuccess, StreamIoTimeout, StreamIoNoReply,
StreamIoEnd, StreamIoFault
};
extern const char* StreamIoStatusStr[];
class StreamBusInterface
{
public:
class Client
{
friend class StreamBusInterface;
virtual void lockCallback(StreamIoStatus status) = 0;
virtual void writeCallback(StreamIoStatus status);
virtual long readCallback(StreamIoStatus status,
const void* input, long size);
virtual void eventCallback(StreamIoStatus status);
virtual void connectCallback(StreamIoStatus status);
virtual long priority();
virtual const char* name() = 0;
virtual const char* getInTerminator(size_t& length) = 0;
virtual const char* getOutTerminator(size_t& length) = 0;
public:
virtual ~Client();
protected:
StreamBusInterface* businterface;
bool busSupportsEvent() {
return businterface->supportsEvent();
}
bool busSupportsAsyncRead() {
return businterface->supportsAsyncRead();
}
bool busAcceptEvent(unsigned long mask,
unsigned long replytimeout_ms) {
return businterface->acceptEvent(mask, replytimeout_ms);
}
void busRelease() {
businterface->release();
}
bool busLockRequest(unsigned long timeout_ms) {
return businterface->lockRequest(timeout_ms);
}
bool busUnlock() {
return businterface->unlock();
}
bool busWriteRequest(const void* output, size_t size,
unsigned long timeout_ms) {
return businterface->writeRequest(output, size, timeout_ms);
}
bool busReadRequest(unsigned long replytimeout_ms,
unsigned long readtimeout_ms, long expectedLength,
bool async) {
return businterface->readRequest(replytimeout_ms,
readtimeout_ms, expectedLength, async);
}
void busFinish() {
businterface->finish();
}
bool busConnectRequest(unsigned long timeout_ms) {
return businterface->connectRequest(timeout_ms);
}
bool busDisconnect() {
return businterface->disconnect();
}
};
private:
friend class StreamBusInterfaceClass; // the iterator
friend class Client;
public:
Client* client;
virtual ~StreamBusInterface() {};
protected:
StreamBusInterface(Client* client);
// map client functions into StreamBusInterface namespace
void lockCallback(StreamIoStatus status)
{ client->lockCallback(status); }
void writeCallback(StreamIoStatus status)
{ client->writeCallback(status); }
long readCallback(StreamIoStatus status,
const void* input = NULL, long size = 0)
{ return client->readCallback(status, input, size); }
void eventCallback(StreamIoStatus status)
{ client->eventCallback(status); }
void connectCallback(StreamIoStatus status)
{ client->connectCallback(status); }
const char* getInTerminator(size_t& length)
{ return client->getInTerminator(length); }
const char* getOutTerminator(size_t& length)
{ return client->getOutTerminator(length); }
long priority() { return client->priority(); }
const char* clientName() { return client->name(); }
// default implementations
virtual bool writeRequest(const void* output, size_t size,
unsigned long timeout_ms);
virtual bool readRequest(unsigned long replytimeout_ms,
unsigned long readtimeout_ms, long expectedLength,
bool async);
virtual bool supportsEvent(); // defaults to false
virtual bool supportsAsyncRead(); // defaults to false
virtual bool acceptEvent(unsigned long mask, // implement if
unsigned long replytimeout_ms); // supportsEvents() returns true
virtual void release();
virtual bool connectRequest(unsigned long connecttimeout_ms);
virtual bool disconnect();
virtual void finish();
// pure virtual
virtual bool lockRequest(unsigned long timeout_ms) = 0;
virtual bool unlock() = 0;
public:
// static methods
static StreamBusInterface* find(Client*, const char* busname,
int addr, const char* param);
};
class StreamBusInterfaceRegistrarBase
{
friend class StreamBusInterfaceClass; // the iterator
friend class StreamBusInterface; // the implementation
static StreamBusInterfaceRegistrarBase* first;
StreamBusInterfaceRegistrarBase* next;
virtual StreamBusInterface* find(StreamBusInterface::Client* client,
const char* busname, int addr, const char* param) = 0;
protected:
const char* name;
StreamBusInterfaceRegistrarBase(const char* name);
virtual ~StreamBusInterfaceRegistrarBase();
};
template <class C>
class StreamBusInterfaceRegistrar : protected StreamBusInterfaceRegistrarBase
{
StreamBusInterface* find(StreamBusInterface::Client* client,
const char* busname, int addr, const char* param)
{ return C::getBusInterface(client, busname, addr, param); }
public:
StreamBusInterfaceRegistrar(const char* name) :
StreamBusInterfaceRegistrarBase(name) {};
};
#define RegisterStreamBusInterface(interface) \
template class StreamBusInterfaceRegistrar<interface>; \
StreamBusInterfaceRegistrar<interface> \
registrar_##interface(#interface); \
void* ref_##interface = &registrar_##interface\
// Interface class iterator
class StreamBusInterfaceClass
{
StreamBusInterfaceRegistrarBase* ptr;
public:
StreamBusInterfaceClass () {
ptr = StreamBusInterfaceRegistrarBase::first;
}
StreamBusInterfaceClass& operator ++ () {
ptr = ptr->next; return *this;
}
const char* name() {
return ptr->name;
}
operator bool () {
return ptr != NULL;
}
};
#endif

1517
src/StreamCore.cc Normal file

File diff suppressed because it is too large Load Diff

224
src/StreamCore.h Normal file
View File

@ -0,0 +1,224 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the kernel of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamCore_h
#define StreamCore_h
#include "StreamProtocol.h"
#include "StreamFormatConverter.h"
#include "StreamBusInterface.h"
/**************************************
virtual methods:
bool getFieldAddress(const char* fieldname, StreamBuffer& address)
If a format sting contains a field name (like "%(EGU)s") a value should
be taken from / put to that field instead of the default location.
This function must convert the fieldname into some address structure and
put the structure into the address buffer (e.g. address.set(addressStruct)).
The address structure must be suitable for bytewise copying. I.e. memcpy()
to so some other memory location must not invalidate its contents. No
constructor, destructor or copy operator will be called.
formatValue() and matchValue() get a pointer to the structure.
getFieldAddress() must return true on success and false on failure.
bool formatValue(const StreamFormat& format, const void* fieldaddress)
This function should first get a value from fieldaddress (if not NULL) or
the default location (which may depend on format.type).
The printValue(format,XXX) function suitable for format.type should be
called to print value. If value is an array, printValue() should be called
for each element. The separator string will be added automatically.
formatValue() must return true on success and false on failure.
bool matchValue(const StreamFormat& format, const void* fieldaddress)
This function should first call the scanValue(format,XXX) function
suitable for format.type. If scanValue() returns true, the scanned value
should be put into fieldaddress (if not NULL) or the default location
(which may depend on format.type).
If value is an array, scanValue() should be called for each element. It
returns false if there is no more element available. The separator string
is matched automatically.
matchValue() must return true on success and false on failure.
void protocolStartHook()
void protocolFinishHook(ProtocolResult)
void startTimer(unsigned short timeout)
void lockRequest(unsigned short timeout)
void unlock()
void writeRequest(unsigned short timeout)
void readRequest(unsigned short replytimeout, unsigned short timeout)
void readAsyn(unsigned short timeout)
void acceptEvent(unsigned short mask, unsigned short timeout)
***************************************/
enum Flags {
// 0x00FFFFFF reserved for StreamCore
None = 0x0000,
IgnoreExtraInput = 0x0001,
InitRun = 0x0002,
AsyncMode = 0x0004,
GotValue = 0x0008,
BusOwner = 0x0010,
Separator = 0x0020,
ScanTried = 0x0040,
AcceptInput = 0x0100,
AcceptEvent = 0x0200,
LockPending = 0x0400,
WritePending = 0x0800,
WaitPending = 0x1000,
BusPending = LockPending|WritePending|WaitPending,
ClearOnStart = InitRun|AsyncMode|GotValue|BusOwner|Separator|ScanTried|
AcceptInput|AcceptEvent|BusPending
};
struct StreamFormat;
class StreamCore :
StreamProtocolParser::Client,
StreamBusInterface::Client
{
protected:
enum ProtocolResult {
Success, LockTimeout, WriteTimeout, ReplyTimeout, ReadTimeout,
ScanError, FormatError, Abort, Fault
};
enum StartMode {
StartNormal, StartInit, StartAsync
};
class MutexLock
{
StreamCore* stream;
public:
MutexLock(StreamCore* _stream) : stream(_stream)
{ _stream->lockMutex(); }
~MutexLock()
{ stream->releaseMutex(); }
};
friend class MutexLock;
StreamCore* next;
static StreamCore* first;
char* streamname;
unsigned long flags;
bool attachBus(const char* busname, int addr, const char* param);
void releaseBus();
bool startProtocol(StartMode);
void finishProtocol(ProtocolResult);
void timerCallback();
bool printValue(const StreamFormat& format, long value);
bool printValue(const StreamFormat& format, double value);
bool printValue(const StreamFormat& format, char* value);
long scanValue(const StreamFormat& format, long& value);
long scanValue(const StreamFormat& format, double& value);
long scanValue(const StreamFormat& format, char* value, long maxlen);
long scanValue(const StreamFormat& format);
bool scanSeparator(); // disencouraged
StreamBuffer protocolname;
unsigned long lockTimeout;
unsigned long writeTimeout;
unsigned long replyTimeout;
unsigned long readTimeout;
unsigned long pollPeriod;
unsigned long maxInput;
bool inTerminatorDefined;
bool outTerminatorDefined;
StreamBuffer inTerminator;
StreamBuffer outTerminator;
StreamBuffer separator;
StreamBuffer commands; // the normal protocol
StreamBuffer onInit; // init protocol (optional)
StreamBuffer onWriteTimeout; // error handler (optional)
StreamBuffer onReplyTimeout; // error handler (optional)
StreamBuffer onReadTimeout; // error handler (optional)
StreamBuffer onMismatch; // error handler (optional)
const char* commandIndex; // current position
const char* activeCommand; // start of current command
StreamBuffer outputLine;
StreamBuffer inputBuffer;
StreamBuffer inputLine;
long consumedInput;
ProtocolResult runningHandler;
StreamBuffer fieldAddress;
StreamIoStatus lastInputStatus;
bool unparsedInput;
StreamCore(const StreamCore&); // undefined
bool compile(StreamProtocolParser::Protocol*);
bool evalCommand();
bool evalOut();
bool evalIn();
bool evalEvent();
bool evalWait();
bool evalExec();
bool evalConnect();
bool evalDisconnect();
bool formatOutput();
bool matchInput();
bool matchSeparator();
void printSeparator();
// StreamProtocolParser::Client methods
bool compileCommand(StreamProtocolParser::Protocol*,
StreamBuffer&, const char* command, const char*& args);
bool getFieldAddress(const char* fieldname,
StreamBuffer& address) = 0;
// StreamBusInterface::Client methods
void lockCallback(StreamIoStatus status);
void writeCallback(StreamIoStatus status);
long readCallback(StreamIoStatus status,
const void* input, long size);
void eventCallback(StreamIoStatus status);
void execCallback(StreamIoStatus status);
void connectCallback(StreamIoStatus status);
const char* getInTerminator(size_t& length);
const char* getOutTerminator(size_t& length);
// virtual methods
virtual void protocolStartHook() {}
virtual void protocolFinishHook(ProtocolResult) {}
virtual void startTimer(unsigned long timeout) = 0;
virtual bool formatValue(const StreamFormat&, const void* fieldaddress) = 0;
virtual bool matchValue (const StreamFormat&, const void* fieldaddress) = 0;
virtual void lockMutex() = 0;
virtual void releaseMutex() = 0;
virtual bool execute();
public:
StreamCore();
virtual ~StreamCore();
bool parse(const char* filename, const char* protocolname);
void printProtocol();
const char* name() { return streamname; }
};
#endif

1218
src/StreamEpics.cc Normal file

File diff suppressed because it is too large Load Diff

103
src/StreamError.cc Normal file
View File

@ -0,0 +1,103 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is error and debug message handling of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamError.h"
#include <string.h>
#include <time.h>
int streamDebug = 0;
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
FILE *StreamDebugFile = NULL;
}
#ifndef va_copy
#ifdef __va_copy
#define va_copy __va_copy
#endif
#endif
/* You can globally change the printTimestamp function
by setting the StreamPrintTimestampFunction variable
to your own function.
*/
static void printTimestamp(char* buffer, int size)
{
time_t t;
struct tm tm;
time(&t);
#ifdef _WIN32
tm = *localtime(&t);
#else
localtime_r(&t, &tm);
#endif
strftime(buffer, size, "%Y/%m/%d %H:%M:%S", &tm);
}
void (*StreamPrintTimestampFunction)(char* buffer, int size) = printTimestamp;
void StreamError(const char* fmt, ...)
{
va_list args;
va_start(args, fmt);
StreamVError(fmt, args);
va_end(args);
}
void StreamVError(const char* fmt, va_list args)
{
char timestamp[40];
StreamPrintTimestampFunction(timestamp, 40);
#ifdef va_copy
if (StreamDebugFile)
{
va_list args2;
va_copy(args2, args);
fprintf(StreamDebugFile, "%s ", timestamp);
vfprintf(StreamDebugFile, fmt, args2);
fflush(StreamDebugFile);
va_end(args2);
}
#endif
fprintf(stderr, "\033[31;1m");
fprintf(stderr, "%s ", timestamp);
vfprintf(stderr, fmt, args);
fprintf(stderr, "\033[0m");
}
int StreamDebugClass::
print(const char* fmt, ...)
{
va_list args;
char timestamp[40];
StreamPrintTimestampFunction(timestamp, 40);
va_start(args, fmt);
const char* f = strrchr(file, '/');
if (f) f++; else f = file;
FILE* fp = StreamDebugFile ? StreamDebugFile : stderr;
fprintf(fp, "%s ", timestamp);
fprintf(fp, "%s:%d: ", f, line);
vfprintf(fp, fmt, args);
fflush(fp);
va_end(args);
return 1;
}

62
src/StreamError.h Normal file
View File

@ -0,0 +1,62 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is error and debug message handling of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamError_h
#define StreamError_h
#include <stdio.h>
#include <stdarg.h>
#ifndef __GNUC__
#define __attribute__(x)
#endif
extern int streamDebug;
extern void (*StreamPrintTimestampFunction)(char* buffer, int size);
void StreamError(const char* fmt, ...)
__attribute__ ((format(printf,1,2)));
void StreamVError(const char* fmt, va_list args)
__attribute__ ((format(printf,1,0)));
class StreamDebugClass
{
const char* file;
int line;
public:
StreamDebugClass(const char* file, int line) :
file(file), line(line) {}
int print(const char* fmt, ...)
__attribute__ ((format(printf,2,3)));
};
inline StreamDebugClass
StreamDebugObject(const char* file, int line)
{ return StreamDebugClass(file, line); }
#define error StreamError
#define debug (!streamDebug)?0:StreamDebugObject(__FILE__,__LINE__).print
#if (__GNUC__ == 2 && __GNUC_MINOR__ == 7)
/* Bug in cygnus-2.7.2 compiler: temporary objects crash the compiler */
#define NO_TEMPORARY
#endif
#endif

55
src/StreamFormat.h Normal file
View File

@ -0,0 +1,55 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This header defines the format stucture used to interface *
* format converters and record interfaces to StreamDevice *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamFormat_h
#define StreamFormat_h
typedef enum {
left_flag = 0x01,
sign_flag = 0x02,
space_flag = 0x04,
alt_flag = 0x08,
zero_flag = 0x10,
skip_flag = 0x20
} StreamFormatFlag;
typedef enum {
long_format = 1,
enum_format = 2,
double_format = 3,
string_format = 4,
pseudo_format = 5
} StreamFormatType;
extern const char* StreamFormatTypeStr[];
typedef struct StreamFormat
{
char conv;
StreamFormatType type;
unsigned char flags;
short prec;
unsigned short width;
unsigned short infolen;
const char* info;
} StreamFormat;
#endif

View File

@ -0,0 +1,435 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the format converter base and includes the standard *
* format converters for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamFormat.h"
#include "StreamError.h"
StreamFormatConverter* StreamFormatConverter::
registered [256];
StreamFormatConverter::
~StreamFormatConverter()
{
}
void StreamFormatConverter::
provides(const char* name, const char* provided)
{
_name = name;
const unsigned char* p;
for (p = reinterpret_cast<const unsigned char*>(provided);
*p; p++)
{
registered[*p] = this;
}
}
bool StreamFormatConverter::
printLong(const StreamFormat& fmt, StreamBuffer&, long)
{
error("Unimplemented printLong method\n for %%%c format",
fmt.conv);
return false;
}
bool StreamFormatConverter::
printDouble(const StreamFormat& fmt, StreamBuffer&, double)
{
error("Unimplemented printDouble method for %%%c format\n",
fmt.conv);
return false;
}
bool StreamFormatConverter::
printString(const StreamFormat& fmt, StreamBuffer&, const char*)
{
error("Unimplemented printString method for %%%c format\n",
fmt.conv);
return false;
}
bool StreamFormatConverter::
printPseudo(const StreamFormat& fmt, StreamBuffer&)
{
error("Unimplemented printPseudo method for %%%c format\n",
fmt.conv);
return false;
}
int StreamFormatConverter::
scanLong(const StreamFormat& fmt, const char*, long&)
{
error("Unimplemented scanLong method for %%%c format\n",
fmt.conv);
return -1;
}
int StreamFormatConverter::
scanDouble(const StreamFormat& fmt, const char*, double&)
{
error("Unimplemented scanDouble method for %%%c format\n",
fmt.conv);
return -1;
}
int StreamFormatConverter::
scanString(const StreamFormat& fmt, const char*, char*, size_t)
{
error("Unimplemented scanString method for %%%c format\n",
fmt.conv);
return -1;
}
int StreamFormatConverter::
scanPseudo(const StreamFormat& fmt, StreamBuffer&, long&)
{
error("Unimplemented scanPseudo method for %%%c format\n",
fmt.conv);
return -1;
}
static void copyFormatString(StreamBuffer& info, const char* source)
{
const char* p = source - 1;
while (*p != '%' && *p != ')') p--;
info.append('%');
while (++p != source-1) info.append(*p);
}
// Standard Long Converter for 'diouxX'
class StdLongConverter : public StreamFormatConverter
{
int parse(const StreamFormat& fmt, StreamBuffer& output, const char*& value, bool scanFormat);
bool printLong(const StreamFormat& fmt, StreamBuffer& output, long value);
int scanLong(const StreamFormat& fmt, const char* input, long& value);
};
int StdLongConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (scanFormat && (fmt.flags & alt_flag))
{
error("Use of modifier '#' not allowed with %%%c input conversion\n",
fmt.conv);
return false;
}
if (scanFormat && fmt.prec >= 0)
{
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
fmt.prec, fmt.conv);
return false;
}
copyFormatString(info, source);
info.append('l');
info.append(fmt.conv);
if (scanFormat) info.append("%n");
return long_format;
}
bool StdLongConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
output.printf(fmt.info, value);
return true;
}
int StdLongConverter::
scanLong(const StreamFormat& fmt, const char* input, long& value)
{
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
}
else
{
if (sscanf(input, fmt.info, &value, &length) < 1) return -1;
}
return length;
}
RegisterConverter (StdLongConverter, "diouxX");
// Standard Double Converter for 'feEgG'
class StdDoubleConverter : public StreamFormatConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual bool printDouble(const StreamFormat&, StreamBuffer&, double);
virtual int scanDouble(const StreamFormat&, const char*, double&);
};
int StdDoubleConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (scanFormat && (fmt.flags & alt_flag))
{
error("Use of modifier '#' not allowed with %%%c input conversion\n",
fmt.conv);
return false;
}
if (scanFormat && fmt.prec >= 0)
{
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
fmt.prec, fmt.conv);
return false;
}
copyFormatString(info, source);
if (scanFormat) info.append('l');
info.append(fmt.conv);
if (scanFormat) info.append("%n");
return double_format;
}
bool StdDoubleConverter::
printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
{
output.printf(fmt.info, value);
return true;
}
int StdDoubleConverter::
scanDouble(const StreamFormat& fmt, const char* input, double& value)
{
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
}
else
{
if (sscanf(input, fmt.info, &value, &length) < 1) return -1;
}
return length;
}
RegisterConverter (StdDoubleConverter, "feEgG");
// Standard String Converter for 's'
class StdStringConverter : public StreamFormatConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual bool printString(const StreamFormat&, StreamBuffer&, const char*);
virtual int scanString(const StreamFormat&, const char*, char*, size_t);
};
int StdStringConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (fmt.flags & (sign_flag|space_flag|zero_flag|alt_flag))
{
error("Use of modifiers '+', ' ', '0', '#' "
"not allowed with %%%c conversion\n",
fmt.conv);
return false;
}
if (scanFormat && fmt.prec >= 0)
{
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
fmt.prec, fmt.conv);
return false;
}
copyFormatString(info, source);
info.append(fmt.conv);
if (scanFormat) info.append("%n");
return string_format;
}
bool StdStringConverter::
printString(const StreamFormat& fmt, StreamBuffer& output, const char* value)
{
output.printf(fmt.info, value);
return true;
}
int StdStringConverter::
scanString(const StreamFormat& fmt, const char* input,
char* value, size_t maxlen)
{
int length = -1;
if (*input == '\0')
{
// match empty string
value[0] = '\0';
return 0;
}
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
}
else
{
char tmpformat[10];
const char* f;
if (maxlen <= fmt.width || fmt.width == 0)
{
// assure not to read too much
sprintf(tmpformat, "%%%d%c%%n", maxlen-1, fmt.conv);
f = tmpformat;
}
else
{
f = fmt.info;
}
if (sscanf(input, f, value, &length) < 1) return -1;
if (length < 0) return -1;
value[length] = '\0';
#ifndef NO_TEMPORARY
debug("StdStringConverter::scanString: length=%d, value=%s\n",
length, StreamBuffer(value,length).expand()());
#endif
}
return length;
}
RegisterConverter (StdStringConverter, "s");
// Standard Characters Converter for 'c'
class StdCharsConverter : public StdStringConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual bool printLong(const StreamFormat&, StreamBuffer&, long);
// scanString is inherited from %s format
};
int StdCharsConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (fmt.flags & (sign_flag|space_flag|zero_flag|alt_flag))
{
error("Use of modifiers '+', ' ', '0', '#' "
"not allowed with %%%c conversion\n",
fmt.conv);
return false;
}
if (scanFormat && fmt.prec >= 0)
{
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
fmt.prec, fmt.conv);
return false;
}
copyFormatString(info, source);
info.append(fmt.conv);
if (scanFormat)
{
info.append("%n");
return string_format;
}
return long_format;
}
bool StdCharsConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
output.printf(fmt.info, value);
return true;
}
RegisterConverter (StdCharsConverter, "c");
// Standard Charset Converter for '['
class StdCharsetConverter : public StreamFormatConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual int scanString(const StreamFormat&, const char*, char*, size_t);
// no print method, %[ is readonly
};
int StdCharsetConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (!scanFormat)
{
error("Format conversion %%[ is only allowed in input formats\n");
return false;
}
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag|alt_flag))
{
error("Use of modifiers '-', '+', ' ', '0', '#'"
"not allowed with %%%c conversion\n",
fmt.conv);
return false;
}
if (scanFormat && fmt.prec >= 0)
{
error("Use of precision field '.%d' not allowed with %%%c input conversion\n",
fmt.prec, fmt.conv);
return false;
}
info.printf("%%%d[", fmt.width);
while (*source && *source != ']')
{
if (*source == esc) source++;
info.append(*source++);
}
if (!*source) {
error("Missing ']' after %%[ format conversion\n");
return false;
}
source++; // consume ']'
info.append("]%n");
return string_format;
}
int StdCharsetConverter::
scanString(const StreamFormat& fmt, const char* input,
char* value, size_t maxlen)
{
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf (input, fmt.info, &length) < 0) return -1;
}
else
{
char tmpformat[256];
const char* f;
if (maxlen <= fmt.width || fmt.width == 0)
{
const char *p = strchr (fmt.info, '[');
// assure not to read too much
sprintf(tmpformat, "%%%d%s", maxlen-1, p);
f = tmpformat;
}
else
{
f = fmt.info;
}
if (sscanf(input, f, value, &length) < 1) return -1;
if (length < 0) return -1;
value[length] = '\0';
debug("StdCharsetConverter::scanString: length=%d, value=%s\n",
length, value);
}
return length;
}
RegisterConverter (StdCharsetConverter, "[");

162
src/StreamFormatConverter.h Normal file
View File

@ -0,0 +1,162 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the format converter header of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamFormatConverter_h
#define StreamFormatConverter_h
#include "StreamFormat.h"
#include "StreamBuffer.h"
#define esc (0x1b)
template <class C>
class StreamFormatConverterRegistrar
{
public:
StreamFormatConverterRegistrar(const char* name, const char* provided) {
static C prototype;
prototype.provides(name, provided);
}
};
class StreamFormatConverter
{
static StreamFormatConverter* registered [];
const char* _name;
public:
virtual ~StreamFormatConverter();
const char* name() { return _name; }
void provides(const char* name, const char* provided);
static StreamFormatConverter* find(unsigned char c);
virtual int parse(const StreamFormat& fmt,
StreamBuffer& info, const char*& source, bool scanFormat) = 0;
virtual bool printLong(const StreamFormat& fmt,
StreamBuffer& output, long value);
virtual bool printDouble(const StreamFormat& fmt,
StreamBuffer& output, double value);
virtual bool printString(const StreamFormat& fmt,
StreamBuffer& output, const char* value);
virtual bool printPseudo(const StreamFormat& fmt,
StreamBuffer& output);
virtual int scanLong(const StreamFormat& fmt,
const char* input, long& value);
virtual int scanDouble(const StreamFormat& fmt,
const char* input, double& value);
virtual int scanString(const StreamFormat& fmt,
const char* input, char* value, size_t maxlen);
virtual int scanPseudo(const StreamFormat& fmt,
StreamBuffer& inputLine, long& cursor);
};
inline StreamFormatConverter* StreamFormatConverter::
find(unsigned char c) {
return registered[c];
}
#define RegisterConverter(converter, conversions) \
template class StreamFormatConverterRegistrar<converter>; \
StreamFormatConverterRegistrar<converter> \
registrar_##converter(#converter,conversions); \
void* ref_##converter = &registrar_##converter\
/****************************************************************************
* A user defined converter class inherits public from StreamFormatConverter
* and handles one or more conversion characters.
* Print and scan of the same conversion character must be handled by the
* same class but not both need to be supported.
*
* parse()
* =======
* This function is called when the protocol is parsed (at initialisation)
* whenever one of the conversions handled by your converter is found.
* The fields fmt.conv, fmt.flags, fmt.prec, and fmt.width have
* already been filled in. If a scan format is parsed, scanFormat is true. If
* a print format is parsed, scanFormat is false.
*
* The source pointer points to the character of the format string just after
* the conversion character. You can parse additional characters if they
* belong to the format string handled by your class. Move the source pointer
* so that is points to the first character after the format string.
*
* Example:
*
* source source
* before after
* V V
* "%39[1-9]constant text"
* |
* conversion
* character
*
* You can write any string to info you need in print*() or scan*(). This will
* probably be necessary if you have taken characters from the format string.
*
* Return long_format, double_format, string_format, or enum_format
* depending on the datatype associated with the conversion character.
* You need not to return the same value for print and for scan formats.
* You can even return different values depending on the format string, but
* I can't imagine why anyone should do that.
*
* If the format is not a real data conversion but does other things with
* the data (append or check a checksum, encode or decode the data,...),
* return pseudo_format.
*
* Return false if there is any parse error or if print or scan is requested
* but not supported by this conversion.
*
* print[Long|Double|String|Pseudo](), scan[Long|Double|String|Pseudo]()
* =================
* Provide a print*() and/or scan*() method appropriate for the data type
* you have returned in the parse() method. That method is called whenever
* the conversion appears in an output or input, resp.
* You only need to implement the flavour of print and/or scan suitable for
* the datatype returned by parse().
*
* Now, fmt.type contains the value returned by parse(). With fmt.info()
* you will get the string you have written to info in parse() (null terminated).
* The length of the info string can be found in fmt.infolen.
*
* In print*(), append the converted value to output. Do not modify what is
* already in output (unless you really know what you're doing).
* Return true on success, false on failure.
*
* In scan*(), read the value from input and return the number of consumed
* bytes. In the string version, don't write more bytes than maxlen! If the
* skip_flag is set, you don't need to write to value, since the value will be
* discarded anyway. Return -1 on failure.
*
*
* Register your class
* ===================
* To register your class, write
*
* RegisterConverter (your_class, "conversions");
*
* in the global context of your file.
* "conversions" is a string containing all conversion characters
* handled by your class.
*
* For examples see StreamFormatConverter.cc.
*
* HINT: Do not branch depending on the conversion character.
* Provide multiple classes, that's more efficient.
*
****************************************************************************/
#endif

1661
src/StreamProtocol.cc Normal file

File diff suppressed because it is too large Load Diff

164
src/StreamProtocol.h Normal file
View File

@ -0,0 +1,164 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the protocol parser of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef StreamProtocol_h
#define StreamProtocol_h
#include "StreamBuffer.h"
#include <stdio.h>
enum FormatType {NoFormat, ScanFormat, PrintFormat};
class StreamProtocolParser
{
public:
enum Codes
{
eos = 0, skip, format, format_field, last_function_code
};
class Client;
class Protocol
{
class Variable;
friend class StreamProtocolParser;
private:
Protocol* next;
Variable* variables;
const StreamBuffer protocolname;
StreamBuffer* commands;
int line;
const char* parameter[10];
Protocol(const char* filename);
Protocol(const Protocol& p, StreamBuffer& name, int line);
StreamBuffer* createVariable(const char* name, int line);
bool compileFormat(StreamBuffer&, const char*& source,
FormatType, Client*);
bool compileCommands(StreamBuffer&, const char*& source, Client*);
bool replaceVariable(StreamBuffer&, const char* varname);
const Variable* getVariable(const char* name);
public:
const StreamBuffer filename;
bool getNumberVariable(const char* varname, unsigned long& value,
unsigned long max = 0xFFFFFFFF);
bool getEnumVariable(const char* varname, unsigned short& value,
const char ** enumstrings);
bool getStringVariable(const char* varname,StreamBuffer& value, bool* defined = NULL);
bool getCommands(const char* handlername, StreamBuffer& code, Client*);
bool compileNumber(unsigned long& number, const char*& source,
unsigned long max = 0xFFFFFFFF);
bool compileString(StreamBuffer& buffer, const char*& source,
FormatType formatType = NoFormat, Client* = NULL, int quoted = false);
bool checkUnused();
void errorMsg(int line, const char* fmt, ...)
__attribute__ ((format(printf,3,4)));
~Protocol();
void report();
};
class Client
{
friend class StreamProtocolParser::Protocol;
virtual bool compileCommand(Protocol*, StreamBuffer& buffer,
const char* command, const char*& args) = 0;
virtual bool getFieldAddress(const char* fieldname,
StreamBuffer& address) = 0;
virtual const char* name() = 0;
public:
virtual ~Client();
};
private:
StreamBuffer filename;
FILE* file;
int line;
int quote;
Protocol globalSettings;
Protocol* protocols;
StreamProtocolParser* next;
static StreamProtocolParser* parsers;
bool valid;
StreamProtocolParser(FILE* file, const char* filename);
Protocol* getProtocol(const StreamBuffer& protocolAndParams);
bool isGlobalContext(const StreamBuffer* commands);
bool isHandlerContext(Protocol&, const StreamBuffer* commands);
static StreamProtocolParser* readFile(const char* file);
bool parseProtocol(Protocol&, StreamBuffer* commands);
int readChar();
bool readToken(StreamBuffer& buffer,
const char* specialchars = NULL, bool eofAllowed = false);
bool parseAssignment(const char* variable, Protocol&);
bool parseValue(StreamBuffer& buffer, bool lazy = false);
void errorMsg(const char* fmt, ...)
__attribute__ ((format(printf,2,3)));
protected:
~StreamProtocolParser(); // get rid of cygnus-2.7.2 compiler warning
public:
static Protocol* getProtocol(const char* file,
const StreamBuffer& protocolAndParams);
static void free();
static const char* path;
static const char* printString(StreamBuffer&, const char* string);
void report();
};
inline int getLineNumber(const char* s)
{
int l;
memcpy (&l, s+strlen(s)+1, sizeof(int));
return l;
}
template<class T>
inline const T extract(const char*& string)
{
T p;
memcpy (&p, string, sizeof(T));
string += sizeof(T);
return p;
}
/*
API functions:
NAME: getProtocol()
PURPOSE: read protocol from file, create parser if necessary
RETURNS: a copy of a protocol that must be deleted by the caller
SIDEEFFECTS: file IO, memory allocation for parser
NAME: free()
PURPOSE: free all parser resources allocated by getProtocol()
Call this function once after the last getProtocol() to clean up.
*/
#endif

28
src/StreamVersion.c Normal file
View File

@ -0,0 +1,28 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This provides a version string for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "devStream.h"
#define STR2(x) #x
#define STR(x) STR2(x)
const char StreamVersion [] =
"StreamDevice " STR(STREAM_MAJOR) "." STR(STREAM_MINOR)
" built " __DATE__ " " __TIME__;

115
src/devStream.h Normal file
View File

@ -0,0 +1,115 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the header for the EPICS interface to StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifndef devStream_h
#define devStream_h
#define STREAM_MAJOR 2
#define STREAM_MINOR 2
#if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h>
#endif
#ifndef OK
#define OK 0
#endif
#ifndef ERROR
#define ERROR -1
#endif
#define DO_NOT_CONVERT 2
#define INIT_RUN (!interruptAccept)
#include <epicsVersion.h>
#if (EPICS_VERSION == 3 && EPICS_REVISION == 14)
#define EPICS_3_14
#endif
#if defined(__cplusplus) && !defined(EPICS_3_14)
extern "C" {
#endif
#include <stdio.h>
#include <dbCommon.h>
#include <dbScan.h>
#include <devSup.h>
/* #include <dbFldTypes.h> */
#include <dbAccess.h>
#if defined(__cplusplus) && !defined(EPICS_3_14)
}
#endif
typedef const struct format_s {
unsigned char type;
const struct StreamFormat* priv;
} format_t;
#ifdef __cplusplus
extern "C" {
#endif
#ifdef _WIN32
__declspec(dllimport)
#endif
extern FILE* StreamDebugFile;
extern const char StreamVersion [];
typedef long (*streamIoFunction) (dbCommon*, format_t*);
long streamInit(int after);
long streamInitRecord(dbCommon *record, struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData);
long streamReport(int interest);
long streamReadWrite(dbCommon *record);
long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt);
long streamPrintf(dbCommon *record, format_t *format, ...);
long streamScanSep(dbCommon *record);
long streamScanfN(dbCommon *record, format_t *format,
void*, size_t maxStringSize);
/* backward compatibility stuff */
#define devStreamIoFunction streamIoFunction
#define devStreamInit streamInit
#define devStreamInitRecord streamInitRecord
#define devStreamReport streamReport
#define devStreamRead streamReadWrite
#define devStreamWrite streamReadWrite
#define devStreamGetIointInfo streamGetIointInfo
#define devStreamPrintf streamPrintf
#define devStreamPrintSep(record) (0)
#define devStreamScanSep streamScanSep
#define devStreamScanf(record, format, value) \
streamScanfN(record, format, value, MAX_STRING_SIZE)
#define streamScanf(record, format, value) \
streamScanfN(record, format, value, MAX_STRING_SIZE)
#define streamRead streamReadWrite
#define streamWrite streamReadWrite
#define streamReport NULL
#ifdef __cplusplus
}
#endif
#endif

302
src/devaaiStream.c Normal file
View File

@ -0,0 +1,302 @@
/***************************************************************
* StreamDevice record interface for aai records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <string.h>
#include <stdlib.h>
#include <devStream.h>
#include <aaiRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
aaiRecord *aai = (aaiRecord *) record;
double dval;
long lval;
for (aai->nord = 0; aai->nord < aai->nelm; aai->nord++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
if (streamScanf (record, format, &dval) != OK)
{
return aai->nord ? OK : ERROR;
}
switch (aai->ftvl)
{
case DBF_DOUBLE:
((double *)aai->bptr)[aai->nord] = dval;
break;
case DBF_FLOAT:
((float *)aai->bptr)[aai->nord] = (float)dval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from double to %s\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_LONG:
case DBF_ENUM:
{
if (streamScanf (record, format, &lval) != OK)
{
return aai->nord ? OK : ERROR;
}
switch (aai->ftvl)
{
case DBF_DOUBLE:
((double *)aai->bptr)[aai->nord] = lval;
break;
case DBF_FLOAT:
((float *)aai->bptr)[aai->nord] = (float)lval;
break;
case DBF_LONG:
case DBF_ULONG:
((long *)aai->bptr)[aai->nord] = lval;
break;
case DBF_SHORT:
case DBF_USHORT:
case DBF_ENUM:
((short *)aai->bptr)[aai->nord] = (short)lval;
break;
case DBF_CHAR:
case DBF_UCHAR:
((char *)aai->bptr)[aai->nord] = (char)lval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from long to %s\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_STRING:
{
switch (aai->ftvl)
{
case DBF_STRING:
if (streamScanfN (record, format,
(char *)aai->bptr + aai->nord * MAX_STRING_SIZE,
MAX_STRING_SIZE) != OK)
{
return aai->nord ? OK : ERROR;
}
break;
case DBF_CHAR:
case DBF_UCHAR:
memset (aai->bptr, 0, aai->nelm);
aai->nord = 0;
if (streamScanfN (record, format,
(char *)aai->bptr, aai->nelm) != OK)
{
return ERROR;
}
((char*)aai->bptr)[aai->nelm] = 0;
for (lval = aai->nelm;
lval >= 0 && ((char*)aai->bptr)[lval] == 0;
lval--);
aai->nord = lval+1;
return OK;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from string to %s\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogMajor,
"readData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[format->type].strvalue,
pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
}
}
return OK;
}
static long writeData (dbCommon *record, format_t *format)
{
aaiRecord *aai = (aaiRecord *) record;
double dval;
long lval;
unsigned long nowd;
for (nowd = 0; nowd < aai->nord; nowd++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
switch (aai->ftvl)
{
case DBF_DOUBLE:
dval = ((double *)aai->bptr)[nowd];
break;
case DBF_FLOAT:
dval = ((float *)aai->bptr)[nowd];
break;
case DBF_LONG:
dval = ((long *)aai->bptr)[nowd];
break;
case DBF_ULONG:
dval = ((unsigned long *)aai->bptr)[nowd];
break;
case DBF_SHORT:
dval = ((short *)aai->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
dval = ((unsigned short *)aai->bptr)[nowd];
break;
case DBF_CHAR:
dval = ((char *)aai->bptr)[nowd];
break;
case DBF_UCHAR:
dval = ((unsigned char *)aai->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to double\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, dval))
return ERROR;
break;
}
case DBF_LONG:
case DBF_ENUM:
{
switch (aai->ftvl)
{
case DBF_LONG:
case DBF_ULONG:
lval = ((long *)aai->bptr)[nowd];
break;
case DBF_SHORT:
lval = ((short *)aai->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
lval = ((unsigned short *)aai->bptr)[nowd];
break;
case DBF_CHAR:
lval = ((char *)aai->bptr)[nowd];
break;
case DBF_UCHAR:
lval = ((unsigned char *)aai->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to long\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, lval))
return ERROR;
break;
}
case DBF_STRING:
{
switch (aai->ftvl)
{
case DBF_STRING:
if (streamPrintf (record, format,
((char *)aai->bptr) + nowd * MAX_STRING_SIZE))
return ERROR;
break;
case DBF_CHAR:
case DBF_UCHAR:
/* print aai as a null-terminated string */
if (aai->nord < aai->nelm)
{
((char *)aai->bptr)[aai->nord] = 0;
}
else
{
((char *)aai->bptr)[aai->nelm-1] = 0;
}
if (streamPrintf (record, format,
((char *)aai->bptr)))
return ERROR;
return OK;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to string\n",
record->name, pamapdbfType[aai->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[aai->ftvl].strvalue,
pamapdbfType[format->type].strvalue);
return ERROR;
}
}
}
return OK;
}
static long initRecord (dbCommon *record)
{
static const int typesize[] = {MAX_STRING_SIZE,1,1,2,2,4,4,4,8,2};
aaiRecord *aai = (aaiRecord *) record;
aai->bptr = calloc(aai->nelm, typesize[aai->ftvl]);
if (aai->bptr == NULL)
{
errlogSevPrintf (errlogFatal,
"initRecord %s: can't allocate memory for data array\n",
record->name);
return ERROR;
}
return streamInitRecord (record, &aai->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devaaiStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devaaiStream);

302
src/devaaoStream.c Normal file
View File

@ -0,0 +1,302 @@
/***************************************************************
* StreamDevice record interface for aao records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2006 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <string.h>
#include <stdlib.h>
#include <devStream.h>
#include <aaoRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
aaoRecord *aao = (aaoRecord *) record;
double dval;
long lval;
for (aao->nord = 0; aao->nord < aao->nelm; aao->nord++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
if (streamScanf (record, format, &dval) != OK)
{
return aao->nord ? OK : ERROR;
}
switch (aao->ftvl)
{
case DBF_DOUBLE:
((double *)aao->bptr)[aao->nord] = dval;
break;
case DBF_FLOAT:
((float *)aao->bptr)[aao->nord] = (float)dval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from double to %s\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_LONG:
case DBF_ENUM:
{
if (streamScanf (record, format, &lval) != OK)
{
return aao->nord ? OK : ERROR;
}
switch (aao->ftvl)
{
case DBF_DOUBLE:
((double *)aao->bptr)[aao->nord] = lval;
break;
case DBF_FLOAT:
((float *)aao->bptr)[aao->nord] = (float)lval;
break;
case DBF_LONG:
case DBF_ULONG:
((long *)aao->bptr)[aao->nord] = lval;
break;
case DBF_SHORT:
case DBF_USHORT:
case DBF_ENUM:
((short *)aao->bptr)[aao->nord] = (short)lval;
break;
case DBF_CHAR:
case DBF_UCHAR:
((char *)aao->bptr)[aao->nord] = (char)lval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from long to %s\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_STRING:
{
switch (aao->ftvl)
{
case DBF_STRING:
if (streamScanfN (record, format,
(char *)aao->bptr + aao->nord * MAX_STRING_SIZE,
MAX_STRING_SIZE) != OK)
{
return aao->nord ? OK : ERROR;
}
break;
case DBF_CHAR:
case DBF_UCHAR:
memset (aao->bptr, 0, aao->nelm);
aao->nord = 0;
if (streamScanfN (record, format,
(char *)aao->bptr, aao->nelm) != OK)
{
return ERROR;
}
((char*)aao->bptr)[aao->nelm] = 0;
for (lval = aao->nelm;
lval >= 0 && ((char*)aao->bptr)[lval] == 0;
lval--);
aao->nord = lval+1;
return OK;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from string to %s\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogMajor,
"readData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[format->type].strvalue,
pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
}
}
return OK;
}
static long writeData (dbCommon *record, format_t *format)
{
aaoRecord *aao = (aaoRecord *) record;
double dval;
long lval;
unsigned long nowd;
for (nowd = 0; nowd < aao->nord; nowd++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
switch (aao->ftvl)
{
case DBF_DOUBLE:
dval = ((double *)aao->bptr)[nowd];
break;
case DBF_FLOAT:
dval = ((float *)aao->bptr)[nowd];
break;
case DBF_LONG:
dval = ((long *)aao->bptr)[nowd];
break;
case DBF_ULONG:
dval = ((unsigned long *)aao->bptr)[nowd];
break;
case DBF_SHORT:
dval = ((short *)aao->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
dval = ((unsigned short *)aao->bptr)[nowd];
break;
case DBF_CHAR:
dval = ((char *)aao->bptr)[nowd];
break;
case DBF_UCHAR:
dval = ((unsigned char *)aao->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to double\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, dval))
return ERROR;
break;
}
case DBF_LONG:
case DBF_ENUM:
{
switch (aao->ftvl)
{
case DBF_LONG:
case DBF_ULONG:
lval = ((long *)aao->bptr)[nowd];
break;
case DBF_SHORT:
lval = ((short *)aao->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
lval = ((unsigned short *)aao->bptr)[nowd];
break;
case DBF_CHAR:
lval = ((char *)aao->bptr)[nowd];
break;
case DBF_UCHAR:
lval = ((unsigned char *)aao->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to long\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, lval))
return ERROR;
break;
}
case DBF_STRING:
{
switch (aao->ftvl)
{
case DBF_STRING:
if (streamPrintf (record, format,
((char *)aao->bptr) + nowd * MAX_STRING_SIZE))
return ERROR;
break;
case DBF_CHAR:
case DBF_UCHAR:
/* print aao as a null-terminated string */
if (aao->nord < aao->nelm)
{
((char *)aao->bptr)[aao->nord] = 0;
}
else
{
((char *)aao->bptr)[aao->nelm-1] = 0;
}
if (streamPrintf (record, format,
((char *)aao->bptr)))
return ERROR;
return OK;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to string\n",
record->name, pamapdbfType[aao->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[aao->ftvl].strvalue,
pamapdbfType[format->type].strvalue);
return ERROR;
}
}
}
return OK;
}
static long initRecord (dbCommon *record)
{
static const int typesize[] = {MAX_STRING_SIZE,1,1,2,2,4,4,4,8,2};
aaoRecord *aao = (aaoRecord *) record;
aao->bptr = calloc(aao->nelm, typesize[aao->ftvl]);
if (aao->bptr == NULL)
{
errlogSevPrintf (errlogFatal,
"initRecord %s: can't allocate memory for data array\n",
record->name);
return ERROR;
}
return streamInitRecord (record, &aao->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devaaoStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devaaoStream);

98
src/devaiStream.c Normal file
View File

@ -0,0 +1,98 @@
/***************************************************************
* StreamDevice record interface for analog input records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <aiRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
aiRecord *ai = (aiRecord *) record;
double val;
switch (format->type)
{
case DBF_DOUBLE:
{
if (streamScanf (record, format, &val)) return ERROR;
if (ai->aslo != 0.0) val *= ai->aslo;
val += ai->aoff;
if (!INIT_RUN && ai->smoo != 0.0)
{
val = ai->val * ai->smoo + val * (1.0 - ai->smoo);
}
ai->val = val;
return DO_NOT_CONVERT;
}
case DBF_LONG:
{
return streamScanf (record, format, &ai->rval);
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
aiRecord *ai = (aiRecord *) record;
double val;
switch (format->type)
{
case DBF_DOUBLE:
{
val = ai->val - ai->aoff;
if (ai->aslo != 0) val /= ai->aslo;
return streamPrintf (record, format, val);
}
case DBF_LONG:
{
return streamPrintf (record, format, ai->rval);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
aiRecord *ai = (aiRecord *) record;
return streamInitRecord (record, &ai->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
DEVSUPFUN special_linconv;
} devaiStream = {
6,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead,
NULL
};
epicsExportAddress(dset,devaiStream);

99
src/devaoStream.c Normal file
View File

@ -0,0 +1,99 @@
/***************************************************************
* Stream Device record interface for analog output records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <aoRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
aoRecord *ao = (aoRecord *) record;
switch (format->type)
{
case DBF_DOUBLE:
{
double val;
if (streamScanf (record, format, &val)) return ERROR;
if (ao->aslo != 0) val *= ao->aslo;
ao->val = val + ao->aoff;
return DO_NOT_CONVERT;
}
case DBF_LONG:
{
long rval;
if (streamScanf (record, format, &rval)) return ERROR;
ao->rbv = rval;
if (INIT_RUN) ao->rval = rval;
return OK;
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
aoRecord *ao = (aoRecord *) record;
double val;
switch (format->type)
{
case DBF_DOUBLE:
{
if (INIT_RUN) val = ao->val;
else val = ao->oval;
val -= ao->aoff;
if (ao->aslo != 0) val /= ao->aslo;
return streamPrintf (record, format, val);
}
case DBF_LONG:
{
return streamPrintf (record, format, ao->rval);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
aoRecord *ao = (aoRecord *) record;
return streamInitRecord (record, &ao->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
DEVSUPFUN special_linconv;
} devaoStream = {
6,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite,
NULL
};
epicsExportAddress(dset,devaoStream);

112
src/devbiStream.c Normal file
View File

@ -0,0 +1,112 @@
/***************************************************************
* Stream Device record interface for binary input records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <biRecord.h>
#include <string.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
biRecord *bi = (biRecord *) record;
unsigned long val;
switch (format->type)
{
case DBF_LONG:
{
if (streamScanf (record, format, &val)) return ERROR;
if (bi->mask) val &= bi->mask;
bi->rval = val;
return OK;
}
case DBF_ENUM:
{
if (streamScanf (record, format, &val)) return ERROR;
bi->val = (val != 0);
return DO_NOT_CONVERT;
}
case DBF_STRING:
{
char buffer[sizeof(bi->znam)];
if (streamScanfN (record, format, buffer, sizeof(buffer)))
return ERROR;
if (strcmp (bi->znam, buffer) == 0)
{
bi->val = 0;
return DO_NOT_CONVERT;
}
if (strcmp (bi->onam, buffer) == 0)
{
bi->val = 1;
return DO_NOT_CONVERT;
}
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
biRecord *bi = (biRecord *) record;
switch (format->type)
{
case DBF_LONG:
{
return streamPrintf (record, format, bi->rval);
}
case DBF_ENUM:
{
return streamPrintf (record, format, (long) bi->val);
}
case DBF_STRING:
{
return streamPrintf (record, format,
bi->val ? bi->onam : bi->znam);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
biRecord *bi = (biRecord *) record;
return streamInitRecord (record, &bi->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devbiStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devbiStream);

113
src/devboStream.c Normal file
View File

@ -0,0 +1,113 @@
/***************************************************************
* Stream Device record interface for binary output records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <boRecord.h>
#include <string.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
boRecord *bo = (boRecord *) record;
unsigned long val;
switch (format->type)
{
case DBF_LONG:
{
if (streamScanf (record, format, &val)) return ERROR;
if (bo->mask) val &= bo->mask;
bo->rbv = val;
if (INIT_RUN) bo->rval = val;
return OK;
}
case DBF_ENUM:
{
if (streamScanf (record, format, &val)) return ERROR;
bo->val = (val != 0);
return DO_NOT_CONVERT;
}
case DBF_STRING:
{
char buffer[sizeof(bo->znam)];
if (streamScanfN (record, format, buffer, sizeof(buffer)))
return ERROR;
if (strcmp (bo->znam, buffer) == 0)
{
bo->val = 0;
return DO_NOT_CONVERT;
}
if (strcmp (bo->onam, buffer) == 0)
{
bo->val = 1;
return DO_NOT_CONVERT;
}
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
boRecord *bo = (boRecord *) record;
switch (format->type)
{
case DBF_LONG:
{
return streamPrintf (record, format, bo->rval);
}
case DBF_ENUM:
{
return streamPrintf (record, format, (long) bo->val);
}
case DBF_STRING:
{
return streamPrintf (record, format,
bo->val ? bo->onam : bo->znam);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
boRecord *bo = (boRecord *) record;
return streamInitRecord (record, &bo->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
} devboStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devboStream);

92
src/devcalcoutStream.c Normal file
View File

@ -0,0 +1,92 @@
/***************************************************************
* Stream Device record interface for calcout records *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <postfix.h>
#include <calcoutRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
calcoutRecord *co = (calcoutRecord *) record;
switch (format->type)
{
case DBF_DOUBLE:
{
return streamScanf (record, format, &co->val);
}
case DBF_LONG:
case DBF_ENUM:
{
long lval;
if (streamScanf (record, format, &lval)) return ERROR;
co->val = lval;
return OK;
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
calcoutRecord *co = (calcoutRecord *) record;
switch (format->type)
{
case DBF_DOUBLE:
{
return streamPrintf (record, format, co->oval);
}
case DBF_LONG:
case DBF_ENUM:
{
return streamPrintf (record, format, (long)co->oval);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
calcoutRecord *co = (calcoutRecord *) record;
return streamInitRecord (record, &co->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
DEVSUPFUN special_linconv;
} devcalcoutStream = {
6,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite,
NULL
};
epicsExportAddress(dset,devcalcoutStream);

70
src/devlonginStream.c Normal file
View File

@ -0,0 +1,70 @@
/***************************************************************
* Stream Device record interface for long input records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <longinRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
longinRecord *li = (longinRecord *) record;
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamScanf (record, format, &li->val);
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
longinRecord *li = (longinRecord *) record;
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamPrintf (record, format, li->val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
longinRecord *li = (longinRecord *) record;
return streamInitRecord (record, &li->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devlonginStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devlonginStream);

71
src/devlongoutStream.c Normal file
View File

@ -0,0 +1,71 @@
/***************************************************************
* Stream Device record interface for long output records *
* *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <longoutRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
longoutRecord *lo = (longoutRecord *) record;
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamScanf (record, format, &lo->val);
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
longoutRecord *lo = (longoutRecord *) record;
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamPrintf (record, format, lo->val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
longoutRecord *lo = (longoutRecord *) record;
return streamInitRecord (record, &lo->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
} devlongoutStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devlongoutStream);

88
src/devmbbiDirectStream.c Normal file
View File

@ -0,0 +1,88 @@
/***************************************************************
* StreamDevice record interface for *
* multibit binary input direct records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <mbbiDirectRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
long val;
if (format->type == DBF_LONG)
{
if (streamScanf (record, format, &val)) return ERROR;
if (mbbiD->mask)
{
val &= mbbiD->mask;
mbbiD->rval = val;
return OK;
}
else
{
/* No MASK, (NOBT = 0): use VAL field */
mbbiD->val = (short)val;
return DO_NOT_CONVERT;
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
long val;
if (format->type == DBF_LONG)
{
if (mbbiD->mask) val = mbbiD->rval & mbbiD->mask;
else val = mbbiD->val;
return streamPrintf (record, format, val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
mbbiDirectRecord *mbbiD = (mbbiDirectRecord *) record;
mbbiD->mask <<= mbbiD->shft;
return streamInitRecord (record, &mbbiD->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devmbbiDirectStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devmbbiDirectStream);

136
src/devmbbiStream.c Normal file
View File

@ -0,0 +1,136 @@
/***************************************************************
* StreamDevice record interface for *
* multibit binary input records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <mbbiRecord.h>
#include <string.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
mbbiRecord *mbbi = (mbbiRecord *) record;
long val;
int i;
switch (format->type)
{
case DBF_LONG:
{
if (streamScanf (record, format, &val)) return ERROR;
/* read VAL or RBV? Look if any value is defined */
if (mbbi->sdef) for (i=0; i<16; i++)
{
if ((&mbbi->zrvl)[i])
{
if (mbbi->mask) val &= mbbi->mask;
mbbi->rval = val;
return OK;
}
}
mbbi->val = (short)val;
return DO_NOT_CONVERT;
}
case DBF_ENUM:
{
if (streamScanf (record, format, &val)) return ERROR;
mbbi->val = (short)val;
return DO_NOT_CONVERT;
}
case DBF_STRING:
{
char buffer[sizeof(mbbi->zrst)];
if (streamScanfN (record, format, buffer, sizeof(buffer)))
return ERROR;
for (val = 0; val < 16; val++)
{
if (strcmp ((&mbbi->zrst)[val], buffer) == 0)
{
mbbi->val = (short)val;
return DO_NOT_CONVERT;
}
}
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
mbbiRecord *mbbi = (mbbiRecord *) record;
long val;
int i;
switch (format->type)
{
case DBF_LONG:
{
/* print VAL or RVAL ? Look if any value is defined */
val = mbbi->val;
if (mbbi->sdef) for (i=0; i<16; i++)
{
if ((&mbbi->zrvl)[i])
{
val = mbbi->rval;
if (mbbi->mask) val &= mbbi->mask;
break;
}
}
return streamPrintf (record, format, val);
}
case DBF_ENUM:
{
return streamPrintf (record, format, (long) mbbi->val);
}
case DBF_STRING:
{
if (mbbi->val >= 16) return ERROR;
return streamPrintf (record, format,
mbbi->zrst + sizeof(mbbi->zrst) * mbbi->val);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
mbbiRecord *mbbi = (mbbiRecord *) record;
mbbi->mask <<= mbbi->shft;
return streamInitRecord (record, &mbbi->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devmbbiStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devmbbiStream);

89
src/devmbboDirectStream.c Normal file
View File

@ -0,0 +1,89 @@
/***************************************************************
* StreamDevice record interface for *
* multibit binary output direct records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <mbboDirectRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
long val;
if (format->type == DBF_LONG)
{
if (streamScanf (record, format, &val)) return ERROR;
if (mbboD->mask)
{
val &= mbboD->mask;
mbboD->rbv = val;
if (INIT_RUN) mbboD->rval = val;
return OK;
}
else
{
/* No MASK, (NOBT = 0): use VAL field */
mbboD->val = (short)val;
return DO_NOT_CONVERT;
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
long val;
if (format->type == DBF_LONG)
{
if (mbboD->mask) val = mbboD->rval & mbboD->mask;
else val = mbboD->val;
return streamPrintf (record, format, val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
mbboDirectRecord *mbboD = (mbboDirectRecord *) record;
mbboD->mask <<= mbboD->shft;
return streamInitRecord (record, &mbboD->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
} devmbboDirectStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devmbboDirectStream);

138
src/devmbboStream.c Normal file
View File

@ -0,0 +1,138 @@
/***************************************************************
* StreamDevice record interface for *
* multibit binary output records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <mbboRecord.h>
#include <string.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
mbboRecord *mbbo = (mbboRecord *) record;
long val;
int i;
switch (format->type)
{
case DBF_LONG:
{
if (streamScanf (record, format, &val)) return ERROR;
/* read VAL or RBV? Look if any value is defined */
if (mbbo->sdef) for (i=0; i<16; i++)
{
if ((&mbbo->zrvl)[i])
{
if (mbbo->mask) val &= mbbo->mask;
mbbo->rbv = val;
if (INIT_RUN) mbbo->rval = val;
return OK;
}
}
mbbo->val = (short)val;
return DO_NOT_CONVERT;
}
case DBF_ENUM:
{
if (streamScanf (record, format, &val)) return ERROR;
mbbo->val = (short)val;
return DO_NOT_CONVERT;
}
case DBF_STRING:
{
char buffer[sizeof(mbbo->zrst)];
if (streamScanfN (record, format, buffer, sizeof(buffer)))
return ERROR;
for (val = 0; val < 16; val++)
{
if (strcmp ((&mbbo->zrst)[val], buffer) == 0)
{
mbbo->val = (short)val;
return DO_NOT_CONVERT;
}
}
}
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
mbboRecord *mbbo = (mbboRecord *) record;
long val;
int i;
switch (format->type)
{
case DBF_LONG:
{
/* print VAL or RVAL ? */
val = mbbo->val;
if (mbbo->sdef) for (i=0; i<16; i++)
{
if ((&mbbo->zrvl)[i])
{
/* any values defined ? */
val = mbbo->rval;
if (mbbo->mask) val &= mbbo->mask;
break;
}
}
return streamPrintf (record, format, val);
}
case DBF_ENUM:
{
return streamPrintf (record, format, (long) mbbo->val);
}
case DBF_STRING:
{
if (mbbo->val >= 16) return ERROR;
return streamPrintf (record, format,
mbbo->zrst + sizeof(mbbo->zrst) * mbbo->val);
}
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
mbboRecord *mbbo = (mbboRecord *) record;
mbbo->mask <<= mbbo->shft;
return streamInitRecord (record, &mbbo->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
} devmbboStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devmbboStream);

70
src/devstringinStream.c Normal file
View File

@ -0,0 +1,70 @@
/***************************************************************
* Stream Device record interface for string input records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <stringinRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
stringinRecord *si = (stringinRecord *) record;
if (format->type == DBF_STRING)
{
return streamScanfN (record, format, si->val, sizeof(si->val));
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
stringinRecord *si = (stringinRecord *) record;
if (format->type == DBF_STRING)
{
return streamPrintf (record, format, si->val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
stringinRecord *si = (stringinRecord *) record;
return streamInitRecord (record, &si->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devstringinStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devstringinStream);

70
src/devstringoutStream.c Normal file
View File

@ -0,0 +1,70 @@
/***************************************************************
* Stream Device record interface for string output records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <stringoutRecord.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
stringoutRecord *so = (stringoutRecord *) record;
if (format->type == DBF_STRING)
{
return streamScanfN (record, format, so->val, sizeof(so->val));
}
return ERROR;
}
static long writeData (dbCommon *record, format_t *format)
{
stringoutRecord *so = (stringoutRecord *) record;
if (format->type == DBF_STRING)
{
return streamPrintf (record, format, so->val);
}
return ERROR;
}
static long initRecord (dbCommon *record)
{
stringoutRecord *so = (stringoutRecord *) record;
return streamInitRecord (record, &so->out, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN write;
} devstringoutStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamWrite
};
epicsExportAddress(dset,devstringoutStream);

293
src/devwaveformStream.c Normal file
View File

@ -0,0 +1,293 @@
/***************************************************************
* StreamDevice record interface for waveform records *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is an EPICS record Interface for StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include <devStream.h>
#include <waveformRecord.h>
#include <string.h>
#include <epicsExport.h>
static long readData (dbCommon *record, format_t *format)
{
waveformRecord *wf = (waveformRecord *) record;
double dval;
long lval;
wf->rarm = 0;
for (wf->nord = 0; wf->nord < wf->nelm; wf->nord++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
if (streamScanf (record, format, &dval) != OK)
{
return wf->nord ? OK : ERROR;
}
switch (wf->ftvl)
{
case DBF_DOUBLE:
((double *)wf->bptr)[wf->nord] = dval;
break;
case DBF_FLOAT:
((float *)wf->bptr)[wf->nord] = (float)dval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from double to %s\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_LONG:
case DBF_ENUM:
{
if (streamScanf (record, format, &lval) != OK)
{
return wf->nord ? OK : ERROR;
}
switch (wf->ftvl)
{
case DBF_DOUBLE:
((double *)wf->bptr)[wf->nord] = (double)lval;
break;
case DBF_FLOAT:
((float *)wf->bptr)[wf->nord] = (float)lval;
break;
case DBF_LONG:
case DBF_ULONG:
((long *)wf->bptr)[wf->nord] = lval;
break;
case DBF_SHORT:
case DBF_USHORT:
case DBF_ENUM:
((short *)wf->bptr)[wf->nord] = (short)lval;
break;
case DBF_CHAR:
case DBF_UCHAR:
((char *)wf->bptr)[wf->nord] = (char)lval;
break;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from long to %s\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
break;
}
case DBF_STRING:
{
switch (wf->ftvl)
{
case DBF_STRING:
if (streamScanfN (record, format,
(char *)wf->bptr + wf->nord * MAX_STRING_SIZE,
MAX_STRING_SIZE) != OK)
{
return wf->nord ? OK : ERROR;
}
break;
case DBF_CHAR:
case DBF_UCHAR:
memset (wf->bptr, 0, wf->nelm);
wf->nord = 0;
if (streamScanfN (record, format,
(char *)wf->bptr, wf->nelm) != OK)
{
return ERROR;
}
((char*)wf->bptr)[wf->nelm] = 0;
for (lval = wf->nelm;
lval >= 0 && ((char*)wf->bptr)[lval] == 0;
lval--);
wf->nord = lval+1;
return OK;
default:
errlogSevPrintf (errlogFatal,
"readData %s: can't convert from string to %s\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogMajor,
"readData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[format->type].strvalue,
pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
}
}
return OK;
}
static long writeData (dbCommon *record, format_t *format)
{
waveformRecord *wf = (waveformRecord *) record;
double dval;
long lval;
unsigned long nowd;
for (nowd = 0; nowd < wf->nord; nowd++)
{
switch (format->type)
{
case DBF_DOUBLE:
{
switch (wf->ftvl)
{
case DBF_DOUBLE:
dval = ((double *)wf->bptr)[nowd];
break;
case DBF_FLOAT:
dval = ((float *)wf->bptr)[nowd];
break;
case DBF_LONG:
dval = ((long *)wf->bptr)[nowd];
break;
case DBF_ULONG:
dval = ((unsigned long *)wf->bptr)[nowd];
break;
case DBF_SHORT:
dval = ((short *)wf->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
dval = ((unsigned short *)wf->bptr)[nowd];
break;
case DBF_CHAR:
dval = ((char *)wf->bptr)[nowd];
break;
case DBF_UCHAR:
dval = ((unsigned char *)wf->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to double\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, dval))
return ERROR;
break;
}
case DBF_LONG:
case DBF_ENUM:
{
switch (wf->ftvl)
{
case DBF_LONG:
case DBF_ULONG:
lval = ((long *)wf->bptr)[nowd];
break;
case DBF_SHORT:
lval = ((short *)wf->bptr)[nowd];
break;
case DBF_USHORT:
case DBF_ENUM:
lval = ((unsigned short *)wf->bptr)[nowd];
break;
case DBF_CHAR:
lval = ((char *)wf->bptr)[nowd];
break;
case DBF_UCHAR:
lval = ((unsigned char *)wf->bptr)[nowd];
break;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to long\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
if (streamPrintf (record, format, lval))
return ERROR;
break;
}
case DBF_STRING:
{
switch (wf->ftvl)
{
case DBF_STRING:
if (streamPrintf (record, format,
((char *)wf->bptr) + nowd * MAX_STRING_SIZE))
return ERROR;
break;
case DBF_CHAR:
case DBF_UCHAR:
/* print waveform as a null-terminated string */
if (wf->nord < wf->nelm)
{
((char *)wf->bptr)[wf->nord] = 0;
}
else
{
((char *)wf->bptr)[wf->nelm-1] = 0;
}
if (streamPrintf (record, format,
((char *)wf->bptr)))
return ERROR;
return OK;
default:
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to string\n",
record->name, pamapdbfType[wf->ftvl].strvalue);
return ERROR;
}
break;
}
default:
{
errlogSevPrintf (errlogFatal,
"writeData %s: can't convert from %s to %s\n",
record->name, pamapdbfType[wf->ftvl].strvalue,
pamapdbfType[format->type].strvalue);
return ERROR;
}
}
}
return OK;
}
static long initRecord (dbCommon *record)
{
waveformRecord *wf = (waveformRecord *) record;
return streamInitRecord (record, &wf->inp, readData, writeData);
}
struct {
long number;
DEVSUPFUN report;
DEVSUPFUN init;
DEVSUPFUN init_record;
DEVSUPFUN get_ioint_info;
DEVSUPFUN read;
} devwaveformStream = {
5,
streamReport,
streamInit,
initRecord,
streamGetIointInfo,
streamRead
};
epicsExportAddress(dset,devwaveformStream);

204
src/memguard.cc Normal file
View File

@ -0,0 +1,204 @@
/***************************************************************
* memguard *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* Include memguard.h and link memguard.o to get some memory *
* guardiance. Allocated memory (using new) is tracked with *
* source file and line of the new operator. Deleted memory is *
* checked for duplicate deletion. *
* Call memguardReport() to see all allocated memory. *
* Calling memguardReport() shows currently allocated memory. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#undef new
#undef delete
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
struct memheader
{
static memheader* first;
memheader* next;
size_t size;
long line;
const char* file;
long fill1;
long fill2;
unsigned long magic;
};
extern "C" void memguardReport();
class memguard
{
public:
memguard() {atexit(memguardReport);}
void report();
};
memheader* memheader::first = NULL;
static memguard guard;
extern "C" void memguardReport()
{
guard.report();
}
void* operator new(size_t size, const char* file, long line) throw (std::bad_alloc)
{
memheader* header;
memheader** prev;
header = (memheader*) malloc(size + sizeof(memheader));
if (!header)
{
fprintf (stderr, "out of memory\n");
if (file) fprintf (stderr, "in %s line %ld\n", file, line);
guard.report();
abort();
}
header->magic = 0xA110caed;
for (prev = &memheader::first; *prev; prev = &(*prev)->next);
*prev = header;
header->next = NULL;
header->size = size;
header->line = line;
header->file = file;
// fprintf(stderr, "allocating %p: %d bytes for %s line %ld\n",
// header+1, size, file, line);
return header+1;
}
void* operator new[](size_t size, const char* file, long line) throw (std::bad_alloc)
{
return operator new(size, file, line);
}
void* operator new(size_t size) throw (std::bad_alloc)
{
return operator new(size, NULL, 0);
}
void* operator new[](size_t size) throw (std::bad_alloc)
{
return operator new(size, NULL, 0);
}
static const char* deleteFile = NULL;
static long deleteLine = 0;
int memguardLocation(const char* file, long line)
{
deleteFile = file;
deleteLine = line;
return 1;
}
void operator delete (void* memory) throw ()
{
memheader* header;
memheader** prev;
if (!memory)
{
deleteFile = NULL;
deleteLine = 0;
return;
}
header = ((memheader*)memory)-1;
if (header->magic == 0xDeadBeef)
{
fprintf (stderr, "memory at %p deleted twice\n", memory);
if (header->file)
fprintf (stderr, "allocated by %s line %ld\n",
header->file, header->line);
guard.report();
abort();
}
for (prev = &memheader::first; *prev; prev = &(*prev)->next)
{
if (*prev == header)
{
if (header->magic != 0xA110caed)
{
fprintf (stderr, "deleted memory at %p corrupt\n", memory);
if (header->file)
fprintf (stderr, "allocated by %s line %ld\n",
header->file, header->line);
guard.report();
abort();
}
*prev = header->next;
header->magic = 0xDeadBeef;
// fprintf(stderr, "deleting %p ", memory);
// if (header->file) fprintf(stderr, "allocated in %s line %ld\n",
// header->file, header->line);
// if (deleteFile) fprintf(stderr, "probably deleted in %s line %ld\n",
// deleteFile, deleteLine);
deleteFile = NULL;
deleteLine = 0;
free(header);
// fprintf(stderr, "done\n");
return;
}
}
if (deleteFile)
{
fprintf (stderr, "deleted memory at %p was never allocated\n", memory);
fprintf (stderr, "delete was probably called in %s line %ld\n",
deleteFile, deleteLine);
abort();
}
}
void operator delete[](void* memory) throw ()
{
operator delete(memory);
}
void memguard::report()
{
memheader* header;
unsigned long sum = 0;
unsigned long chunks = 0;
if (memheader::first)
{
fprintf(stderr, "allocated memory:\n");
for (header = memheader::first; header; header = header->next)
{
chunks++;
sum += header->size;
if (header->file)
{
fprintf (stderr, "%p: %d bytes by %s line %ld\n",
header+1, header->size, header->file, header->line);
}
else
{
fprintf (stderr, "%p: %d bytes by unknown\n",
header+1, header->size);
}
if (header->magic != 0xA110caed)
{
fprintf (stderr, "memory list is corrupt\n");
abort();
}
}
fprintf (stderr, "%lu bytes in %lu chunks\n", sum, chunks);
}
else
{
fprintf(stderr, "no memory allocated\n");
}
}

30
src/memguard.h Normal file
View File

@ -0,0 +1,30 @@
/***************************************************************
* memguard *
* *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* Include memguard.h and link memguard.o to get some memory *
* guardiance. Allocated memory (using new) is tracked with *
* source file and line of the new operator. Deleted memory is *
* checked for duplicate deletion. *
* Call memguardReport() to see all allocated memory. *
* Calling memguardReport() shows currently allocated memory. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#ifdef __cplusplus
#ifndef memguard_h
#define memguard_h
#include <new.h>
#define MEMGUARD
void* operator new(size_t, const char*, long) throw (std::bad_alloc);
void* operator new[](size_t, const char*, long) throw (std::bad_alloc);
#define new new(__FILE__,__LINE__)
int memguardLocation(const char*, long);
#define delete if (memguardLocation(__FILE__,__LINE__)) delete
extern "C" void memguardReport();
#endif
#endif

84
src/munch.pl Normal file
View File

@ -0,0 +1,84 @@
eval 'exec perl -S $0 ${1+"$@"}' # -*- Mode: perl -*-
if $running_under_some_shell; # makeConfigAppInclude.pl
#*************************************************************************
# Copyright (c) 2002 The University of Chicago, as Operator of Argonne
# National Laboratory.
# Copyright (c) 2002 The Regents of the University of California, as
# Operator of Los Alamos National Laboratory.
# EPICS BASE Versions 3.13.7
# and higher are distributed subject to a Software License Agreement found
# in file LICENSE that is included with this distribution.
#*************************************************************************
# Creates a ctdt.c file of c++ static constructors and destructors.
# munch.pl,v 1.10 2002/12/16 20:25:53 jba Exp
@ctorlist = ();
@dtorlist = ();
while ($line = <STDIN>)
{
chomp;
next if ($line =~ /__?GLOBAL_.F.+/);
next if ($line =~ /__?GLOBAL_.I._GLOBAL_.D.+/);
if ($line =~ /__?GLOBAL_.D.+/) {
($adr,$type,$name) = split ' ',$line,3;
$name =~ s/^__/_/;
@dtorlist = (@dtorlist,$name);
};
if ($line =~ /__?GLOBAL_.I.+/) {
($adr,$type,$name) = split ' ',$line,3;
$name =~ s/^__/_/;
@ctorlist = (@ctorlist,$name);
};
}
foreach $ctor (@ctorlist)
{
if ( $ctor =~ /\./ ) {
printf "void %s() __asm__ (\"_%s\");\n",convertName($ctor),$ctor;
} else {
printf "void %s();\n",$ctor;
}
}
print "extern void (*_ctors[])();\n";
print "void (*_ctors[])() = {\n";
foreach $ctor (@ctorlist)
{
if ( $ctor =~ /\./ ) {
printf " %s,\n",convertName($ctor);
} else {
printf " %s,\n",$ctor;
}
}
print " 0};\n";
foreach $dtor (@dtorlist)
{
if ( $dtor =~ /\./ ) {
printf "void %s() __asm__ (\"_%s\");\n",convertName($dtor),$dtor;
} else {
printf "void %s();\n",$dtor;
}
}
print "extern void (*_ctors[])();\n";
print "void (*_dtors[])() = {\n";
foreach $dtor (@dtorlist)
{
if ( $dtor =~ /\./ ) {
printf " %s,\n",convertName($dtor);
} else {
printf " %s,\n",$dtor;
}
}
print " 0};\n";
sub convertName {
my ($name) = @_;
$name =~ s/\./\$/g;
return $name;
}