version 2.2
44
Makefile
Normal 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
After Width: | Height: | Size: 453 B |
BIN
doc/PSI.gif
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
doc/SLS.gif
Normal file
After Width: | Height: | Size: 2.2 KiB |
128
doc/aai.html
Normal 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
@ -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
@ -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
@ -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
After Width: | Height: | Size: 868 B |
77
doc/bi.html
Normal 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>&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
@ -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>&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
@ -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 <StreamBusInterface.h>
|
||||
|
||||
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* client,
|
||||
const char* busname, int addr,
|
||||
const char* 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 long 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 void* output,
|
||||
size_t size, unsigned long writeTimeout_ms);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
bool <a href="#read">readRequest</a>(unsigned long replyTimeout_ms,
|
||||
unsigned long readTimeout_ms,
|
||||
long expectedLength, bool 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 long mask,
|
||||
unsigned long replytimeout_ms);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
bool <a href="#connect">connectRequest</a>(unsigned long 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 status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void <a href="#write">writeCallback</a>(StreamIoStatus status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
long <a href="#read">readCallback</a>(StreamIoStatus status,
|
||||
const void* input = NULL,
|
||||
long size = 0);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void <a href="#event">eventCallback</a>(StreamIoStatus status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void <a href="#connect">connectCallback</a>(StreamIoStatus status);
|
||||
</code></div>
|
||||
|
||||
<h3>Other provided methods, attibutes, and types</h3>
|
||||
|
||||
<div class="indent"><code>
|
||||
<a href="#create">StreamBusInterface</a>(Client* 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 char* <a href="#write">getOutTerminator</a>(size_t& length);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
const char* <a href="#read">getInTerminator</a>(size_t& length);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
enum 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* client,
|
||||
const char* busname, int addr,
|
||||
const char* param);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
StreamBusInterface(Client* 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 long connecttimeout_ms);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
bool disconnect();
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void connectCallback(IoStatus 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 long lockTimeout_ms);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void lockCallback(IoStatus 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 void* output,
|
||||
size_t size, unsigned long writeTimeout_ms);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
void writeCallback(IoStatus status);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
const char* getOutTerminator(size_t& 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 long replyTimeout_ms,
|
||||
unsigned long readTimeout_ms,
|
||||
long expectedLength, bool async);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
long readCallback(IoStatus status,
|
||||
const void* input = NULL,
|
||||
long size = 0);
|
||||
</code></div>
|
||||
<div class="indent"><code>
|
||||
const char* getInTerminator(size_t& 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>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
@ -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
@ -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><top></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><top></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 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><top></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><top></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><top></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
After Width: | Height: | Size: 187 B |
BIN
doc/exr.png
Normal file
After Width: | Height: | Size: 187 B |
20
doc/formatconverter.html
Normal 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
@ -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 "%(EGU)s";</code> outputs the <code>EGU</code>
|
||||
field formatted as a string.
|
||||
Use <code>in "%(<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>%<<em>checksum</em>></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%<xor>"</code> the checksum is calculated
|
||||
from <code>abcdefg</code>,
|
||||
but in <code>"abcdefg%2.1<xor>"</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 "123456789%<sum>"</code> writes <code>dd</code>
|
||||
but <code>out "123456789%<Sum>"</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>%<SUM></code> or <code>%<SUM8></code></dt>
|
||||
<dd>One byte. The sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<SUM16></code></dt>
|
||||
<dd>Two bytes. The sum of all characters modulo 2<sup>16</sup>.</dd>
|
||||
<dt><code>%<SUM32></code></dt>
|
||||
<dd>Four bytes. The sum of all characters modulo 2<sup>32</sup>.</dd>
|
||||
<dt><code>%<NEGSUM></code> or <code>%<NSUM></code> or <code>%<-SUM></code></dt>
|
||||
<dd>One byte. The negative of the sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<NOTSUM></code> or <code>%<~SUM></code></dt>
|
||||
<dd>One byte. The bitwise inverse of the sum of all characters modulo 2<sup>8</sup>.</dd>
|
||||
<dt><code>%<XOR></code></dt>
|
||||
<dd>One byte. All characters xor'ed.</dd>
|
||||
<dt><code>%<CRC8></code></dt>
|
||||
<dd>One byte. An often used 8 bit CRC checksum
|
||||
(poly=0x07, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<CCITT8></code></dt>
|
||||
<dd>One byte. The CCITT standard 8 bit CRC checksum
|
||||
(poly=0x31, init=0x00, xorout=0x00).</dd>
|
||||
<dt><code>%<CRC16></code></dt>
|
||||
<dd>Two bytes. An often used 16 bit CRC checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<CRC16R></code></dt>
|
||||
<dd>Two bytes. An often used reflected 16 bit CRC checksum
|
||||
(poly=0x8005, init=0x0000, xorout=0x0000).</dd>
|
||||
<dt><code>%<CCITT16></code></dt>
|
||||
<dd>Two bytes. The usual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">wrong?</a>)
|
||||
implementation of the CCITT standard 16 bit CRC checksum
|
||||
(poly=0x1021, init=0xFFFF, xorout=0x0000).</dd>
|
||||
<dt><code>%<CCITT16A></code></dt>
|
||||
<dd>Two bytes. The unusual (but <a target="ex"
|
||||
href="http://www.joegeluso.com/software/articles/ccitt.htm">correct?</a>)
|
||||
implementation of the CCITT standard 16 bit CRC checksum with augment.
|
||||
(poly=0x1021, init=0x1D0F, xorout=0x0000).</dd>
|
||||
<dt><code>%<CRC32></code></dt>
|
||||
<dd>Four bytes. The standard 32 bit CRC checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<CRC32R></code></dt>
|
||||
<dd>Four bytes. The standard reflected 32 bit CRC checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0xFFFFFFFF).</dd>
|
||||
<dt><code>%<JAMCRC></code></dt>
|
||||
<dd>Four bytes. Another reflected 32 bit CRC checksum.
|
||||
(poly=0x04C11DB7, init=0xFFFFFFFF, xorout=0x00000000).</dd>
|
||||
<dt><code>%<ADLER32></code></dt>
|
||||
<dd>Four bytes. The Adler32 checksum according to <a target="ex"
|
||||
href="http://www.ietf.org/rfc/rfc1950.txt">RFC 1950</a>.</dd>
|
||||
<dt><code>%<HEXSUM8></code></dt>
|
||||
<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
@ -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
@ -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
@ -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
@ -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
@ -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&MASK</code><br>
|
||||
<u>Input:</u> <code>RVAL=<i>x</i>&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
@ -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&MASK</code><br>
|
||||
<u>Input:</u> <code>RVAL=<i>x</i>&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
@ -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&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>&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
@ -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&MASK</code><br>
|
||||
<u>Input:</u> <code>RBV=<i>x</i>&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
@ -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="[«]";
|
||||
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="[»]";
|
||||
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 & 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">%<<em>checksum</em>></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 & 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 & 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
@ -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
After Width: | Height: | Size: 1.2 KiB |
244
doc/processing.html
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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><top>/configure/RELEASE</kbd> file:
|
||||
<pre>
|
||||
ASYN=/home/epics/asyn/4-5
|
||||
CALC=/home/epics/synApps/calc/2-7
|
||||
</pre>
|
||||
<p>
|
||||
For details on <kbd><top></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><top></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
|
||||
<carriage return> <line feed> (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 protocol bus</var> [<var>address</var> [<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
After Width: | Height: | Size: 318 B |
BIN
doc/space.gif
Normal file
After Width: | Height: | Size: 807 B |
79
doc/stream.css
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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>
|
||||
field (DTYP, "stream")<br>
|
||||
field (INP, "@(DEVICETYPE).proto read $(BUS)")<br>
|
||||
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>
|
||||
field (INPA, "$(A_RECORD)")<br>
|
||||
field (INPB, "$(B_RECORD)")<br>
|
||||
field (INPC, "$(C_RECORD)")<br>
|
||||
field (CALC, "0")<br>
|
||||
field (DTYP, "stream")<br>
|
||||
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>
|
||||
field (DTYP, "stream")<br>
|
||||
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>
|
||||
field (DTYP, "stream")<br>
|
||||
field (INP, "@(DEVICETYPE).proto read_A $(BUS)")<br>
|
||||
field (SCAN, "1 second")<br>
|
||||
}<br>
|
||||
record (ai, "$(DEVICE):B") {<br>
|
||||
field (DTYP, "stream")<br>
|
||||
field (INP, "@(DEVICETYPE).proto read_B $(BUS)")<br>
|
||||
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>
|
||||
field (DTYP, "stream")<br>
|
||||
field (INP, "@(DEVICETYPE).proto read_AB($(DEVICE):B) $(BUS)")<br>
|
||||
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 3.24 A</code>" or a message like
|
||||
"<code>device switched 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>
|
||||
field (DTYP, "stream")<br>
|
||||
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>
|
||||
field (INPA, "$(DEVICE):readcurrent.SEVR CP")<br>
|
||||
field (CALC, "A!=2")<br>
|
||||
field (OOPT, "When Non-zero")<br>
|
||||
field (OUT, "$(DEVICE):clean_2.PROC")<br>
|
||||
}<br>
|
||||
record (stringout, "$(DEVICE):clean_2") {<br>
|
||||
field (VAL, "OK")<br>
|
||||
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
@ -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
155
src/BCDConverter.cc
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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 = ®istrar_##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
224
src/StreamCore.h
Normal 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
103
src/StreamError.cc
Normal 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
@ -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
@ -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
|
435
src/StreamFormatConverter.cc
Normal 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
@ -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 = ®istrar_##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
164
src/StreamProtocol.h
Normal 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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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
@ -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;
|
||||
}
|
||||
|