Compare commits
19 Commits
Author | SHA1 | Date | |
---|---|---|---|
668d1d5255 | |||
ae5ca0c45b | |||
b00099973f | |||
5bf5cb9a67 | |||
f6848f0503 | |||
1496089bc8 | |||
a090cd4d8f | |||
211f689cdf | |||
793675bb12 | |||
922294bf6a | |||
942c4779c9 | |||
8ceee295ae | |||
c30e2a4e31 | |||
fc67fb8721 | |||
c123c5c8f7 | |||
8746dea7cf | |||
2915830b02 | |||
fdfa4d4695 | |||
4d717288da |
1
.ci
Submodule
1
.ci
Submodule
Submodule .ci added at dead44c3cb
12
.ci-local/defaults.set
Normal file
12
.ci-local/defaults.set
Normal file
@ -0,0 +1,12 @@
|
||||
MODULES=calc asyn
|
||||
|
||||
# EPICS Base
|
||||
BASE_DIRNAME=base
|
||||
BASE_REPONAME=epics-base
|
||||
BASE_REPOOWNER=epics-base
|
||||
BASE_VARNAME=EPICS_BASE
|
||||
BASE_RECURSIVE=no
|
||||
|
||||
ASYN_REPOOWNER=epics-modules
|
||||
|
||||
CALC_REPOOWNER=epics-modules
|
164
.github/workflows/ci-scripts-build.yml
vendored
Normal file
164
.github/workflows/ci-scripts-build.yml
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
# .github/workflows/ci-scripts-build.yml for use with EPICS Base ci-scripts
|
||||
# (see: https://github.com/epics-base/ci-scripts)
|
||||
|
||||
# This is YAML - indentation levels are crucial
|
||||
|
||||
# Workflow name, shared by all branches
|
||||
|
||||
name: StreamDevice
|
||||
|
||||
# Trigger on pushes and PRs to any branch
|
||||
on:
|
||||
push:
|
||||
paths-ignore:
|
||||
- 'docs/*'
|
||||
- '.gitattributes'
|
||||
- '.gitignore'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- 'docs/*'
|
||||
- '.gitattributes'
|
||||
- '.gitignore'
|
||||
- '**/*.html'
|
||||
- '**/*.md'
|
||||
|
||||
env:
|
||||
SETUP_PATH: .ci-local:.ci
|
||||
EPICS_TEST_IMPRECISE_TIMING: YES
|
||||
|
||||
jobs:
|
||||
native:
|
||||
name: ${{ matrix.name }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
# Set environment variables from matrix parameters
|
||||
env:
|
||||
CMP: ${{ matrix.cmp }}
|
||||
BCFG: ${{ matrix.configuration }}
|
||||
BASE: ${{ matrix.base }}
|
||||
WINE: ${{ matrix.wine }}
|
||||
RTEMS: ${{ matrix.rtems }}
|
||||
RTEMS_TARGET: ${{ matrix.rtems_target }}
|
||||
TEST: ${{ matrix.test }}
|
||||
EXTRA: ${{ matrix.extra }}
|
||||
VV: "1"
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- name: Native Linux (WError)
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
extra: "CMD_CPPFLAGS=-Werror"
|
||||
pcre: apt
|
||||
|
||||
- name: Cross mingw64 DLL
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
wine: "64"
|
||||
pcre: no
|
||||
|
||||
- name: Cross mingw64 static
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: static
|
||||
base: "7.0"
|
||||
wine: "64"
|
||||
pcre: no
|
||||
|
||||
- name: RTEMS 4.10
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
rtems: "4.10"
|
||||
rtems_target: RTEMS-pc386-qemu
|
||||
pcre: no
|
||||
|
||||
- name: Native Linux with clang
|
||||
os: ubuntu-20.04
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
pcre: apt
|
||||
|
||||
- name: Native Linux with 3.15
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "3.15"
|
||||
pcre: apt
|
||||
|
||||
- name: Native Linux with 3.14
|
||||
os: ubuntu-20.04
|
||||
cmp: gcc
|
||||
configuration: default
|
||||
base: "3.14"
|
||||
pcre: apt
|
||||
|
||||
- name: OSX
|
||||
os: macos-latest
|
||||
cmp: clang
|
||||
configuration: default
|
||||
base: "7.0"
|
||||
pcre: no
|
||||
|
||||
- name: vs2019 DLL
|
||||
os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: debug
|
||||
base: "7.0"
|
||||
pcre: no
|
||||
extra: "CMD_CFLAGS=-analysis CMD_CXXFLAGS=-analysis"
|
||||
|
||||
- name: vs2019 static
|
||||
os: windows-2019
|
||||
cmp: vs2019
|
||||
configuration: static-debug
|
||||
base: "7.0"
|
||||
pcre: no
|
||||
extra: "CMD_CFLAGS=-analysis CMD_CXXFLAGS=-analysis"
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Automatic core dumper analysis
|
||||
uses: mdavidsaver/ci-core-dumper@master
|
||||
- name: "apt-get install"
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get -y install qemu-system-x86 g++-mingw-w64-x86-64 gdb
|
||||
if: runner.os == 'Linux'
|
||||
|
||||
- name: Reset RELEASE
|
||||
shell: bash
|
||||
# 'make' on Mac doesn't understand "undefine PCRE"
|
||||
# so replace the whole file
|
||||
run: |
|
||||
cat <<EOF > configure/RELEASE
|
||||
-include \$(TOP)/../RELEASE.local
|
||||
-include \$(TOP)/../RELEASE.\$(EPICS_HOST_ARCH).local
|
||||
-include \$(TOP)/configure/RELEASE.local
|
||||
EOF
|
||||
|
||||
- name: Prepare and compile dependencies
|
||||
run: python .ci/cue.py prepare
|
||||
|
||||
- name: "apt-get install pcre"
|
||||
if: matrix.pcre == 'apt'
|
||||
shell: bash
|
||||
run: |
|
||||
sudo apt-get -y install libpcre3-dev
|
||||
cat <<EOF >> configure/CONFIG_SITE.local
|
||||
PCRE_INCLUDE=/usr/include
|
||||
PCRE_LIB=/usr/lib
|
||||
EOF
|
||||
|
||||
- name: Build main module
|
||||
run: python .ci/cue.py build
|
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
[submodule ".ci"]
|
||||
path = .ci
|
||||
url = https://github.com/epics-base/ci-scripts.git
|
@ -24,11 +24,11 @@ HEADERS += src/StreamFormat.h
|
||||
HEADERS += src/StreamFormatConverter.h
|
||||
HEADERS += src/StreamBuffer.h
|
||||
HEADERS += src/StreamError.h
|
||||
HEADERS += src/StreamVersion.h
|
||||
HEADERS += src/StreamProtocol.h
|
||||
HEADERS += src/StreamBusInterface.h
|
||||
HEADERS += src/StreamCore.h
|
||||
HEADERS += src/MacroMagic.h
|
||||
HEADERS += $(COMMON_DIR)/StreamVersion.h
|
||||
|
||||
CPPFLAGS += -DSTREAM_INTERNAL -I$(COMMON_DIR)
|
||||
|
||||
|
@ -4,10 +4,6 @@ TOP=..
|
||||
|
||||
include $(TOP)/configure/CONFIG
|
||||
|
||||
# Set the following to NO to disable consistency checking of
|
||||
# the support applications defined in $(TOP)/configure/RELEASE
|
||||
CHECK_RELEASE = YES
|
||||
|
||||
TARGETS = $(CONFIG_TARGETS)
|
||||
CONFIGS += $(subst ../,,$(wildcard $(CONFIG_INSTALLS)))
|
||||
|
||||
|
@ -594,6 +594,18 @@ In input, the next byte or bytes must match the checksum.
|
||||
href="https://en.wikipedia.org/wiki/Longitudinal_redundancy_check">Wikipedia</a>.</dd>
|
||||
<dt><code>%<hexlrc></code></dt>
|
||||
<dd>One byte. The LRC for the hex digits. (Other characters are ignored.)</dd>
|
||||
<dt><code>%<leybold></code></dt>
|
||||
<dd>One byte. Used by some Leybold products. 255-bytesum%255 (+32 if result would be <32)</dd>
|
||||
<dt><code>%<brksCryo></code></dt>
|
||||
<dd>One byte. Used by Brooks Cryopumps.</dd>
|
||||
<dt><code>%<CPI></code></dt>
|
||||
<dd>One byte. Used by TRIUMF CPI RF amplifier.</dd>
|
||||
<dt><code>%<bitsum></code> or <code>%<bitsum8></code></dt>
|
||||
<dd>One byte. Number of 1 bits in all characters.</dd>
|
||||
<dt><code>%<bitsum16></code></dt>
|
||||
<dd>Two bytes. Number of 1 bits in all characters.</dd>
|
||||
<dt><code>%<bitsum32></code></dt>
|
||||
<dd>Four bytes. Number of 1 bits in all characters.</dd>
|
||||
</dl>
|
||||
|
||||
<a name="regex"></a>
|
||||
|
22
docs/makepdf
22
docs/makepdf
@ -1,10 +1,22 @@
|
||||
#/bin/sh
|
||||
if ! wkhtmltopdf -V >/dev/null 2>&1
|
||||
then
|
||||
wkhtmltopdf --enable-local-file-access -V >/dev/null 2>&1
|
||||
case $? in
|
||||
127)
|
||||
echo "wkhtmltopdf not installed." >&2
|
||||
echo "See https://wkhtmltopdf.org" >&2
|
||||
exit 1
|
||||
fi
|
||||
;;
|
||||
0)
|
||||
# have (and need) --enable-local-file-access
|
||||
ENABLE_FILE_ACCESS=--enable-local-file-access
|
||||
;;
|
||||
1)
|
||||
# have no (and need no) --enable-local-file-access
|
||||
;;
|
||||
*)
|
||||
# Some error but I don't know what it means. Try anyway.
|
||||
;;
|
||||
esac
|
||||
|
||||
PAGES="
|
||||
index.html
|
||||
@ -43,5 +55,5 @@ osinterface.html
|
||||
"
|
||||
|
||||
rm -f stream.pdf
|
||||
wkhtmltopdf --print-media-type --dpi 1200 --zoom 0.85 --page-size Letter \
|
||||
$PAGES stream.pdf
|
||||
wkhtmltopdf --print-media-type --page-size Letter \
|
||||
$ENABLE_FILE_ACCESS $PAGES stream.pdf
|
||||
|
@ -385,35 +385,45 @@ See the <a href="protocol.html">next chapter</a> for protocol files in depth.
|
||||
Generation of debug and error messages is controlled with two shell variables,
|
||||
<code>streamDebug</code> and <code>streamError</code>.
|
||||
Setting those variables to 1 (actually to any number but 0) enables the
|
||||
messages.
|
||||
messages. A few noisy and rarely useful debug messages are only enabled when
|
||||
setting <code>streamDebug</code> to 2.
|
||||
Per default debug messages are switched off and error messages are switched on.
|
||||
Errors occuring while loading protocol files are always shown.
|
||||
</p>
|
||||
<p>
|
||||
Warning: Enabling debug messages can create a lot of output!
|
||||
At the moment, there is no way to set filters on debug or error messages.
|
||||
Warning: Enabling debug messages this way can create a lot of output!
|
||||
Therefore, some limited debugging can be enabled per record, independent of
|
||||
the <code>streamDebug</code> variable using the <code>.TPRO</code> field of
|
||||
the record. Currently, setting <code>.TPRO</code> to 1 or 2 enables some
|
||||
basic information about the processing of a record and its i/o.
|
||||
</p>
|
||||
<p>
|
||||
Debug output can be redirected to a file with the command
|
||||
<code>streamSetLogfile("<var>filename</var>")</code>.
|
||||
When called without a filename, debug output is directed back
|
||||
to the console.
|
||||
If the file already exists, it will be overwritten, not appended to.
|
||||
While debug messages are only written to the defined log file, error messages
|
||||
are still printed to <var>stderr</var> too.
|
||||
Calling <code>streamSetLogfile</code> without a filename directs debug output
|
||||
back to <var>stderr</var> and closes the log file.
|
||||
</p>
|
||||
<p>
|
||||
By default the debug/error output is set to be colored if the terminal allows
|
||||
it but this can be set to always colored or never colored by setting
|
||||
<code>streamDebugColored</code> to 1 or 0 respectively.
|
||||
By default, error messages to the console are printed in red color if
|
||||
<var>stderr</var> is a tty at startup time, using ANSI color codes. Some
|
||||
terminals may not support this properly.
|
||||
The variable <code>streamDebugColored</code> can be set to 0 or 1 to
|
||||
disable or enable colored error messages explicitly.
|
||||
Error messages written to a log file do not use colors.
|
||||
</p>
|
||||
<p>
|
||||
Error and debug messages are prefixed with a time stamp unless the variable
|
||||
<code>streamMsgTimeStamped</code> is set to 0.
|
||||
</p>
|
||||
<p>
|
||||
When a device is disconnected StreamDevice can produce many repeated timeout
|
||||
messages. To reduce this logging you can set <code>streamErrorDeadTime</code>
|
||||
to an integer number of seconds. When this is set repeated timeout messages
|
||||
will not be printed in the specified dead time after the last message. The
|
||||
default dead time is 0, resulting in every message being printed.
|
||||
when a device is unresponsive, StreamDevice may produce many repeated timeout
|
||||
messages. To reduce this, you can set <code>streamErrorDeadTime</code>
|
||||
to an integer number of seconds. In this case, repeated timeout messages
|
||||
will not be printed during the specified dead time after the last printed
|
||||
message. The default dead time is 0, resulting in every message being printed.
|
||||
</p>
|
||||
|
||||
<h3>Example (vxWorks):</h3>
|
||||
|
179
docs/stream.css
179
docs/stream.css
@ -1,90 +1,104 @@
|
||||
a:link {color: #0000D0;}
|
||||
a:visited {color: #0000D0;}
|
||||
a:hover {color: #FF0000;}
|
||||
a:link { color: #0000D0; }
|
||||
a:visited { color: #0000D0; }
|
||||
a:hover { color: #FF0000; }
|
||||
|
||||
body {
|
||||
margin-right:1em;
|
||||
margin-left:15em;
|
||||
margin-top:75px;
|
||||
padding-top:1px;
|
||||
margin-right: 1em;
|
||||
margin-left: 15em;
|
||||
margin-top: 75px;
|
||||
padding-top: 1px;
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 100%;
|
||||
background-color:#ffffff;
|
||||
background-color: #ffffff;
|
||||
}
|
||||
|
||||
a[name] { position:relative; top:-11ex;}
|
||||
a[name] {
|
||||
position: relative;
|
||||
top: -11ex;
|
||||
}
|
||||
|
||||
pre, tt, kbd, code {
|
||||
font-size: 95%;
|
||||
font-family: Mono, "Lucida Console", Courier, monospace;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color:#f4f4f4;
|
||||
padding:1ex;
|
||||
border:1px solid #000000;
|
||||
white-space:pre;
|
||||
margin:2ex;
|
||||
page-break-inside:avoid;
|
||||
background-color: #f4f4f4;
|
||||
padding: 1ex;
|
||||
border: 1px solid #000000;
|
||||
white-space: pre;
|
||||
margin: 2ex;
|
||||
page-break-inside: avoid;
|
||||
font-size: 85%;
|
||||
white-space: pre-wrap;
|
||||
}
|
||||
|
||||
kbd {
|
||||
font-weight:bold;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
code {
|
||||
color: #008000;
|
||||
}
|
||||
|
||||
dt {
|
||||
margin-top:0.5ex;
|
||||
margin-top: 0.5ex;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:250%;
|
||||
margin-top:0;
|
||||
font-style:italic;
|
||||
font-weight:bold;
|
||||
font-family:"Times New Roman", serif;
|
||||
text-align:center;
|
||||
position:fixed;
|
||||
top:0;
|
||||
left:0;
|
||||
width:100%;
|
||||
line-height:190%;
|
||||
background-color:white;
|
||||
border-width:0;
|
||||
border-bottom:3px solid #1b4486;
|
||||
white-space:nowrap;
|
||||
background-image:url(PSI.png);
|
||||
background-repeat:no-repeat;
|
||||
background-position:10px 5px;
|
||||
text-shadow:.1em .1em .1em darkgray;
|
||||
box-shadow:0 .3em .1em -.2em darkgray;
|
||||
font-size: 250%;
|
||||
margin-top: 0;
|
||||
font-style: italic;
|
||||
font-weight: bold;
|
||||
font-family: "Times New Roman", Times, serif;
|
||||
text-align: center;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
line-height: 190%;
|
||||
background-color: white;
|
||||
border-width: 0;
|
||||
border-bottom: 3px solid #1b4486;
|
||||
white-space: nowrap;
|
||||
background-image: url(PSI.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 10px 5px;
|
||||
text-shadow: .1em .1em .1em lightgray;
|
||||
box-shadow: 0 .3em .1em -.2em darkgray;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size:150%;
|
||||
margin-bottom:0.5ex;
|
||||
font-size: 140%;
|
||||
margin-bottom: 0.5ex;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size:120%;
|
||||
margin-bottom:0.25ex;
|
||||
font-size: 120%;
|
||||
margin-bottom: 0.25ex;
|
||||
}
|
||||
|
||||
h4 {
|
||||
font-size:100%;
|
||||
margin-bottom:0.25ex;
|
||||
font-size: 100%;
|
||||
margin-bottom: 0.25ex;
|
||||
}
|
||||
|
||||
h1, h2, h3, h4 {
|
||||
page-break-after:avoid;
|
||||
page-break-after: avoid;
|
||||
}
|
||||
|
||||
p {
|
||||
margin-top:0.75ex;
|
||||
margin-bottom:0.75ex;
|
||||
margin-top: 0.75ex;
|
||||
margin-bottom: 0.75ex;
|
||||
}
|
||||
|
||||
body h1 + p {
|
||||
margin-top:1.5ex;
|
||||
margin-bottom:0.75ex;
|
||||
margin-top: 1.5ex;
|
||||
margin-bottom: 0.75ex;
|
||||
}
|
||||
|
||||
footer {
|
||||
font-size:75%;
|
||||
font-size: 75%;
|
||||
margin-top: 1em;
|
||||
border-top: 1px solid darkgray;
|
||||
padding-top: 1em;
|
||||
@ -97,44 +111,40 @@ footer a:only-of-type {
|
||||
}
|
||||
|
||||
small {
|
||||
font-size:75%;
|
||||
}
|
||||
|
||||
code {
|
||||
font-size: 125%;
|
||||
color: #008000;
|
||||
font-size: 75%;
|
||||
}
|
||||
|
||||
.indent {
|
||||
text-indent:-4ex;
|
||||
margin-left:4ex;
|
||||
margin-top:0.5ex;
|
||||
text-align:left;
|
||||
text-indent: -4ex;
|
||||
margin-left: 4ex;
|
||||
margin-top: 0.5ex;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.box {
|
||||
margin-left:1ex;
|
||||
margin-right:1ex;
|
||||
margin-top:0.5ex;
|
||||
margin-left: 1ex;
|
||||
margin-right: 1ex;
|
||||
margin-top: 0.5ex;
|
||||
padding: 0 1ex;
|
||||
border: 1px solid black;
|
||||
text-align:left;
|
||||
background-color:#f0f0f0;
|
||||
border: thin solid black;
|
||||
text-align: left;
|
||||
background-color: #f0f0f0;
|
||||
page-break-inside: avoid;
|
||||
}
|
||||
|
||||
#navleft {
|
||||
position:fixed;
|
||||
left:0;
|
||||
top:0;
|
||||
padding-top:70px;
|
||||
width:14em;
|
||||
height:100%;
|
||||
border-style:solid;
|
||||
border-color:black;
|
||||
border-width:0 1px 0 0;
|
||||
background-color:#e3eaf6;
|
||||
overflow:hidden;
|
||||
z-index:0;
|
||||
position: fixed;
|
||||
left: 0;
|
||||
top: 0;
|
||||
padding-top: 70px;
|
||||
width: 14em;
|
||||
height: 100%;
|
||||
border-style: solid;
|
||||
border-color: black;
|
||||
border-width: 0 1px 0 0;
|
||||
background-color: #e3eaf6;
|
||||
overflow: hidden;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.new {
|
||||
@ -142,17 +152,18 @@ code {
|
||||
}
|
||||
|
||||
a[target=ex]:after {
|
||||
content:" " url(ex.png);
|
||||
content: " " url(ex.png);
|
||||
}
|
||||
|
||||
a[target=ex]:hover:after {
|
||||
content: " " url(exr.png);
|
||||
content: " " url(exr.png);
|
||||
}
|
||||
|
||||
@media print {
|
||||
a:link {text-decoration:none;}
|
||||
a[target=ex]:after {content:" [" attr(href) "]";}
|
||||
body {margin:0 4em;}
|
||||
h1 {position:relative; background-position:0 0;}
|
||||
#navleft {display:none;}
|
||||
a:link { text-decoration: none; }
|
||||
a[target=ex]:after { content:" [" attr(href) "]"; font-size: 75%; }
|
||||
body { margin: 0 4em; }
|
||||
h1 { position: relative; background-position: 0 0; }
|
||||
#navleft { display: none; }
|
||||
footer { display: none; }
|
||||
}
|
||||
|
@ -293,9 +293,10 @@ record (stringout, "$(DEVICE):clean_2") {<br>
|
||||
<a name="web"></a>
|
||||
<h2>I need to read a web page</h2>
|
||||
<p>
|
||||
First you have to send a correctly formatted HTML request.
|
||||
Note that this request must contain the full URL like
|
||||
"http://server/page" and must be terminated with <u>two</u> newlines.
|
||||
First you have to send a correctly formatted HTML header for a GET request.
|
||||
Note that this header must contain the full URL like
|
||||
"http://server/page" and must be terminated with <u>two</u>
|
||||
CR LF sequences (<code>"\r\n\r\n"</code> or <code>CR LF CR LF</code>).
|
||||
The server should be the same as in the
|
||||
<a href="setup.html#sta"><code>drvAsynIPPortConfigure</code></a>
|
||||
command (if not using a http proxy).
|
||||
@ -313,17 +314,18 @@ Read the title of a web page.
|
||||
get_title {<br>
|
||||
extrainput = ignore;<br>
|
||||
replyTimeout = 1000;<br>
|
||||
out "GET http://\$1\n\n";<br>
|
||||
out "GET http://\$1\r\n\r\n";<br>
|
||||
in "%+.1/(?im)<title>(.*)<\/title>/";<br>
|
||||
}
|
||||
</code>
|
||||
</p>
|
||||
<p>
|
||||
Terminate the request with two newlines, either explicit like here
|
||||
<u>or</u> using an
|
||||
Terminate the request with two carriage return + newlines, either explicit
|
||||
like here <u>or</u> using an
|
||||
<a href="protocol.html#sysvar"><code>outTerminator</code></a>.
|
||||
The URI (without http:// but including the web server host name)
|
||||
is passed as <a href="protocol.html#argvar">argument</a> 1 to <code>\$1</code>.
|
||||
is passed as <a href="protocol.html#argvar">argument</a> 1 to <code>\$1</code>
|
||||
in this example.
|
||||
Note that web servers may be slow, so allow some
|
||||
<a href="protocol.html#argvar"><code>replyTimeout</code></a>.
|
||||
</p>
|
||||
@ -390,7 +392,7 @@ Then we read the number.
|
||||
get_title {<br>
|
||||
extrainput = ignore;<br>
|
||||
replyTimeout = 1000;<br>
|
||||
out "GET http://\$1\n\n";<br>
|
||||
out "GET http://\$1\r\n\r\n";<br>
|
||||
in "%*/Interesting value:/%f more text";<br>
|
||||
}
|
||||
</code>
|
||||
|
@ -20,43 +20,57 @@
|
||||
* along with StreamDevice. If not, see https://www.gnu.org/licenses/.
|
||||
*************************************************************************/
|
||||
|
||||
#if defined(vxWorks)
|
||||
#include <version.h>
|
||||
#if defined(_WRS_VXWORKS_MAJOR) && _WRS_VXWORKS_MAJOR > 6 || (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR > 8)
|
||||
#include <stdint.h>
|
||||
#define PRIX32 "X"
|
||||
#define PRIu32 "u"
|
||||
#ifdef vxWorks
|
||||
#include <version.h>
|
||||
/* VxWorks has strncasecmp since version 6
|
||||
but availability depends on configuration.
|
||||
We cannot know.
|
||||
*/
|
||||
#define NEED_strncasecmp
|
||||
/* VxWorks does not have inttypes.h and uint32_t differs between versions */
|
||||
#if defined(_WRS_VXWORKS_MAJOR) && (_WRS_VXWORKS_MAJOR > 6 || (_WRS_VXWORKS_MAJOR == 6 && _WRS_VXWORKS_MINOR >= 9))
|
||||
#define PRIX32 "X"
|
||||
#define PRIu32 "u"
|
||||
#else
|
||||
#define PRIX32 "lX"
|
||||
#define PRIu32 "lu"
|
||||
#endif
|
||||
#define PRIX8 "X"
|
||||
#elif defined(_MSC_VER) && _MSC_VER < 1700
|
||||
/* Visual Studio 2010 does not have inttypes.h */
|
||||
#define PRIX32 "X"
|
||||
#define PRIu32 "u"
|
||||
#define PRIX8 "X"
|
||||
#else
|
||||
#define PRIX32 "lX"
|
||||
#define PRIu32 "lu"
|
||||
#endif
|
||||
#define PRIX8 "X"
|
||||
#define uint_fast8_t uint8_t
|
||||
#define int_fast8_t int8_t
|
||||
#elif defined(_MSC_VER) && _MSC_VER < 1700 /* Visual Studio 2010 does not have inttypes.h */
|
||||
#include <stdint.h>
|
||||
#define PRIX32 "X"
|
||||
#define PRIu32 "u"
|
||||
#define PRIX8 "X"
|
||||
#else
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <stdint.h>
|
||||
#include <inttypes.h>
|
||||
#define __STDC_FORMAT_MACROS
|
||||
#include <inttypes.h>
|
||||
#endif
|
||||
|
||||
#include <ctype.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#if defined(__rtems__)
|
||||
#include <rtems.h>
|
||||
#if __RTEMS_MAJOR__ < 5
|
||||
/* RTEMS has strncasecmp since version 5 */
|
||||
#define NEED_strncasecmp
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
#define strncasecmp _strnicmp
|
||||
/* Windows strncasecmp has a different name. */
|
||||
#define strncasecmp _strnicmp
|
||||
#endif
|
||||
|
||||
#if defined(vxWorks) || defined(__rtems__)
|
||||
// These systems have no strncasecmp at the moment
|
||||
// But avoid compiler errors in case strncasecmp exists in future versions
|
||||
static int mystrncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int r=0;
|
||||
while (n && (r = toupper(*s1)-toupper(*s2)) == 0) { n--; s1++; s2++; };
|
||||
return r;
|
||||
#ifdef NEED_strncasecmp
|
||||
// Have no strncasecmp but avoid compiler errors in case it exists in future versions
|
||||
extern "C" {
|
||||
static int mystrncasecmp(const char *s1, const char *s2, size_t n)
|
||||
{
|
||||
int r=0;
|
||||
while (n && (r = toupper(*s1)-toupper(*s2)) == 0) { n--; s1++; s2++; };
|
||||
return r;
|
||||
}
|
||||
}
|
||||
#define strncasecmp mystrncasecmp
|
||||
#endif
|
||||
@ -534,6 +548,7 @@ static uint32_t leybold(const uint8_t* data, size_t len, uint32_t sum)
|
||||
sum += *data++;
|
||||
}
|
||||
sum = ~sum;
|
||||
sum &= 0xff;
|
||||
if (sum < 32) sum+=32;
|
||||
return sum;
|
||||
}
|
||||
@ -731,7 +746,7 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
|
||||
const char* info = format.info;
|
||||
uint32_t init = extract<uint32_t>(info);
|
||||
uint32_t xorout = extract<uint32_t>(info);
|
||||
uint_fast8_t fnum = extract<uint8_t>(info);
|
||||
uint8_t fnum = extract<uint8_t>(info);
|
||||
|
||||
size_t start = format.width;
|
||||
size_t length = output.length();
|
||||
@ -752,8 +767,8 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
|
||||
debug("ChecksumConverter %s: output checksum is 0x%" PRIX32 "\n",
|
||||
checksumMap[fnum].name, sum);
|
||||
|
||||
uint_fast8_t i;
|
||||
uint_fast8_t outchar;
|
||||
uint8_t i;
|
||||
uint8_t outchar;
|
||||
|
||||
if (format.flags & sign_flag) // decimal
|
||||
{
|
||||
@ -812,7 +827,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
|
||||
uint32_t init = extract<uint32_t>(info);
|
||||
uint32_t xorout = extract<uint32_t>(info);
|
||||
size_t start = format.width;
|
||||
uint_fast8_t fnum = extract<uint8_t>(info);
|
||||
uint8_t fnum = extract<uint8_t>(info);
|
||||
size_t length = cursor;
|
||||
if (length >= start) length -= start;
|
||||
else length = 0;
|
||||
@ -824,7 +839,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
|
||||
debug("ChecksumConverter %s: input to check: \"%s\n",
|
||||
checksumMap[fnum].name, input.expand(start,length)());
|
||||
|
||||
uint_fast8_t nDigits =
|
||||
uint8_t nDigits =
|
||||
// get number of decimal digits from number of bytes: ceil(bytes*2.5)
|
||||
format.flags & sign_flag ? (checksumMap[fnum].bytes + 1) * 25 / 10 - 2 :
|
||||
format.flags & (zero_flag|left_flag) ? 2 * checksumMap[fnum].bytes :
|
||||
@ -867,7 +882,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
|
||||
else
|
||||
if (format.flags & alt_flag) // lsb first (little endian)
|
||||
{
|
||||
uint_fast8_t i;
|
||||
uint8_t i;
|
||||
for (i = 0; i < checksumMap[fnum].bytes; i++)
|
||||
{
|
||||
if (format.flags & zero_flag) // ASCII
|
||||
@ -910,8 +925,8 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
|
||||
}
|
||||
else // msb first (big endian)
|
||||
{
|
||||
int_fast8_t i;
|
||||
uint_fast8_t j;
|
||||
int8_t i;
|
||||
uint8_t j;
|
||||
for (i = checksumMap[fnum].bytes-1, j = 0; i >= 0; i--, j++)
|
||||
{
|
||||
if (format.flags & zero_flag) // ASCII
|
||||
|
@ -102,7 +102,7 @@ init(const void* s, ssize_t minsize)
|
||||
}
|
||||
|
||||
// How the buffer looks like:
|
||||
// |----free-----|####used####|--------00--------|
|
||||
// |----junk-----|####used####|--------00--------|
|
||||
///|<--- offs -->|<-- len --->|<- cap-offs-len ->|
|
||||
// 0 offs offs+len cap
|
||||
// |<-------------- minsize --------------->
|
||||
@ -254,39 +254,71 @@ replace(ssize_t remstart, ssize_t remlen, const void* ins, ssize_t inslen)
|
||||
if (inslen < 0) inslen = 0;
|
||||
size_t remend = remstart+remlen;
|
||||
size_t newlen = len+inslen-remlen;
|
||||
|
||||
// How the buffer looks like before and after:
|
||||
// |---junk---|##content_start##|/////////remove_this////////|##content_end##|0000|
|
||||
// |<- offs ->|<-- remstart --->|<--------- remlen --------->| | |
|
||||
// | |<--------------------- len ---------------------------------->| |
|
||||
// 0 offs offs+remstart offs+remend offs+len cap
|
||||
//
|
||||
// If content size stays the same, no need to move old content:
|
||||
// |---junk---|##content_start##|+++++++inserted_text++++++++|##content_end##|0000|
|
||||
// |<----- inslen==remlen ----->| newlen==len
|
||||
//
|
||||
// If content shrinks (need to clear end of buffer): |< clear this >|
|
||||
// |---junk---|##content_start##|inserted_text|##content_end##|00000000000000|0000|
|
||||
// 0 offs |<- inslen -->| |< len-newlen >|
|
||||
// offs+newlen offs+len
|
||||
//
|
||||
// If content grows but still fits (make sure to keep at least one 0 byte at end):
|
||||
// |---junk---|##content_start##|++++++++inserted_text++++++++++|##content_end##|0|
|
||||
// |<- offs ->|<--------------------- newlen ---------------------------------->| |
|
||||
// 0 offs offs+newlen<cap
|
||||
//
|
||||
// If content would overflow, moving to offs 0 may help:
|
||||
// May need to clear end if newlen < offs+len: |<clear>|
|
||||
// |##content_start##|++++++++inserted_text++++++++++|##content_end##|0000000|0000|
|
||||
// |<--------------------- newlen ---------------------------------->| |
|
||||
// newlen offs+len
|
||||
//
|
||||
// Otherwise we need to copy to a new buffer.
|
||||
|
||||
|
||||
if (cap <= newlen)
|
||||
{
|
||||
// buffer too short
|
||||
// buffer too short, copy to new buffer
|
||||
size_t 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);
|
||||
memcpy(newbuffer, buffer+offs, remstart); // copy content start
|
||||
memcpy(newbuffer+remstart, ins, inslen); // insert
|
||||
memcpy(newbuffer+remstart+inslen, buffer+offs+remend, len-remend); // copy content end
|
||||
memset(newbuffer+newlen, 0, newcap-newlen); // clear buffer end
|
||||
if (buffer != local)
|
||||
{
|
||||
delete [] buffer;
|
||||
}
|
||||
delete[] buffer;
|
||||
buffer = newbuffer;
|
||||
cap = newcap;
|
||||
offs = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (newlen+offs<=cap)
|
||||
if (offs+newlen < 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);
|
||||
// modified content still fits with current offs, just move content end
|
||||
if (newlen != len)
|
||||
memmove(buffer+offs+remstart+inslen, buffer+offs+remend, len-remend); // move old content end if necessary
|
||||
memcpy(buffer+offs+remstart, ins, inslen); // insert before
|
||||
if (newlen < len)
|
||||
memset(buffer+offs+newlen, 0, len-newlen); // clear buffer end if content shrunk
|
||||
}
|
||||
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);
|
||||
// move content to start of buffer
|
||||
memmove(buffer, buffer+offs, remstart); // move content start to 0 offs
|
||||
memmove(buffer+remstart+inslen, buffer+offs+remend, len-remend); // move content end
|
||||
memcpy(buffer+remstart, ins, inslen); // insert in between
|
||||
if (newlen < offs+len)
|
||||
memset(buffer+newlen, 0, offs+len-newlen); // clear buffer end if necessary
|
||||
offs = 0;
|
||||
}
|
||||
}
|
||||
@ -358,7 +390,7 @@ dump() const
|
||||
StreamBuffer result;
|
||||
size_t i;
|
||||
result.print("%" P "d,%" P "d,%" P "d:", offs, len, cap);
|
||||
if (offs) result.print(ansiEscape(ANSI_BG_WHITE));
|
||||
if (offs) result.print("%s", ansiEscape(ANSI_BG_WHITE));
|
||||
char c;
|
||||
for (i = 0; i < cap; i++)
|
||||
{
|
||||
|
@ -603,6 +603,7 @@ evalOut()
|
||||
// flush all unread input
|
||||
unparsedInput = false;
|
||||
inputBuffer.clear();
|
||||
inputLine.clear();
|
||||
if (!formatOutput())
|
||||
{
|
||||
finishProtocol(FormatError);
|
||||
@ -1011,6 +1012,7 @@ readCallback(StreamIoStatus status,
|
||||
finishProtocol(Fault);
|
||||
return 0;
|
||||
}
|
||||
inputHook(input, size);
|
||||
inputBuffer.append(input, size);
|
||||
debug("StreamCore::readCallback(%s) inputBuffer=\"%s\", size %" Z "u\n",
|
||||
name(), inputBuffer.expand()(), inputBuffer.length());
|
||||
|
@ -219,6 +219,7 @@ protected:
|
||||
|
||||
// virtual methods
|
||||
virtual void protocolStartHook() {}
|
||||
virtual void inputHook(const void* input, size_t size) {};
|
||||
virtual void protocolFinishHook(ProtocolResult) {}
|
||||
virtual void startTimer(unsigned long timeout) = 0;
|
||||
virtual bool formatValue(const StreamFormat&, const void* fieldaddress) = 0;
|
||||
|
@ -25,6 +25,7 @@
|
||||
#include <stdarg.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#if defined(vxWorks)
|
||||
#include <symLib.h>
|
||||
@ -136,6 +137,7 @@ class Stream : protected StreamCore
|
||||
#endif
|
||||
|
||||
// StreamCore methods
|
||||
void inputHook(const void* input, size_t size);
|
||||
void protocolFinishHook(ProtocolResult);
|
||||
void startTimer(unsigned long timeout);
|
||||
bool getFieldAddress(const char* fieldname,
|
||||
@ -933,6 +935,10 @@ process()
|
||||
debug("Stream::process(%s) start\n", name());
|
||||
status = NO_ALARM;
|
||||
convert = OK;
|
||||
if (record->tpro)
|
||||
{
|
||||
StreamDebugClass(record->name).print("start protocol '%s'\n", protocolname());
|
||||
}
|
||||
if (!startProtocol(record->proc == 2 ? StreamCore::StartInit : StreamCore::StartNormal))
|
||||
{
|
||||
debug("Stream::process(%s): could not start %sprotocol, status=%s (%d)\n",
|
||||
@ -1027,11 +1033,26 @@ expire(const epicsTime&)
|
||||
|
||||
// StreamCore virtual methods ////////////////////////////////////////////
|
||||
|
||||
void Stream::
|
||||
inputHook(const void* input, size_t size)
|
||||
{
|
||||
if (record->tpro > 1)
|
||||
{
|
||||
StreamDebugClass(record->name).print("received \"%s\"\n",
|
||||
StreamBuffer(input, size).expand()());
|
||||
}
|
||||
}
|
||||
|
||||
void Stream::
|
||||
protocolFinishHook(ProtocolResult result)
|
||||
{
|
||||
debug("Stream::protocolFinishHook(%s, %s)\n",
|
||||
name(), toStr(result));
|
||||
if (record->tpro)
|
||||
{
|
||||
StreamDebugClass(record->name).print("%s. out=\"%s\", in=\"%s\"\n",
|
||||
toStr(result), outputLine.expand()(), inputLine.expand()());
|
||||
}
|
||||
switch (result)
|
||||
{
|
||||
case Success:
|
||||
|
@ -171,8 +171,6 @@ print(const char* fmt, ...)
|
||||
{
|
||||
va_list args;
|
||||
va_start(args, fmt);
|
||||
const char* f = strrchr(file, '/');
|
||||
if (f) f++; else f = file;
|
||||
FILE* fp = StreamDebugFile ? StreamDebugFile : stderr;
|
||||
if (streamMsgTimeStamped)
|
||||
{
|
||||
@ -184,7 +182,13 @@ print(const char* fmt, ...)
|
||||
{
|
||||
fprintf(fp, "%s ", StreamGetThreadNameFunction());
|
||||
}
|
||||
fprintf(fp, "%s:%d: ", f, line);
|
||||
if (file) {
|
||||
const char* f = strrchr(file, '/');
|
||||
if (f) f++; else f = file;
|
||||
fprintf(fp, "%s:", f);
|
||||
if (line) fprintf(fp, "%d:", line);
|
||||
fprintf(fp, " ");
|
||||
}
|
||||
vfprintf(fp, fmt, args);
|
||||
fflush(fp);
|
||||
va_end(args);
|
||||
|
@ -56,19 +56,15 @@ class StreamDebugClass
|
||||
const char* file;
|
||||
int line;
|
||||
public:
|
||||
StreamDebugClass(const char* file, int line) :
|
||||
StreamDebugClass(const char* file = NULL, int line = 0) :
|
||||
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
|
||||
#define debug2 (streamDebug<2)?0:StreamDebugObject(__FILE__,__LINE__).print
|
||||
#define debug (!streamDebug)?0:StreamDebugClass(__FILE__,__LINE__).print
|
||||
#define debug2 (streamDebug<2)?0:StreamDebugClass(__FILE__,__LINE__).print
|
||||
|
||||
/*
|
||||
* ANSI escape sequences for terminal output
|
||||
|
@ -19,7 +19,7 @@
|
||||
# along with StreamDevice. If not, see https://www.gnu.org/licenses/.
|
||||
#########################################################################/
|
||||
|
||||
terminator = CR LF;
|
||||
terminator = LF;
|
||||
|
||||
cmd {
|
||||
out "%s";
|
||||
|
Reference in New Issue
Block a user