Compare commits

...

19 Commits

Author SHA1 Message Date
668d1d5255 add some per-record debugging 2024-06-06 13:49:15 +02:00
ae5ca0c45b allow to use the StreamDebugClass without file name or line number 2024-06-06 11:29:09 +02:00
b00099973f don't need the wrapper function 2024-06-06 11:29:09 +02:00
5bf5cb9a67 example terminal.tcl uses LF terminator 2024-06-06 11:29:09 +02:00
f6848f0503 Update .ci 2024-03-20 10:17:54 +01:00
1496089bc8 Bugfix: make sure not to lose teminating 0 byte in StreamBuffer::replace() 2024-03-19 09:38:11 +01:00
a090cd4d8f fix header file path 2024-03-18 17:43:45 +01:00
211f689cdf decuments recently added checksums 2022-11-21 17:34:20 +01:00
793675bb12 fix leybold sum 2022-11-21 17:11:42 +01:00
922294bf6a strncasecmp is still not reliably available in Vxworks 6 2022-10-07 12:20:23 +02:00
942c4779c9 add GHA config 2022-10-05 14:25:08 +02:00
8ceee295ae Removed hardcode CHECKRELEASE in configure/Makefile 2022-10-05 14:25:08 +02:00
c30e2a4e31 vxWorks 6.9 has stdint.h and strncasecmp, RTEMS 5 has strncasecmp 2022-10-05 14:24:34 +02:00
fc67fb8721 Fix cplusplus linkage error with extern C 2022-07-11 15:44:16 +02:00
c123c5c8f7 Quiet warning with -Wformat-security
Best practice is to only use constant format strings,
which ansiEscape() almost is.
2022-07-11 15:43:26 +02:00
8746dea7cf fix HTML example to use correct line terminator CR LF 2022-07-11 15:43:26 +02:00
2915830b02 fix some rendering problems when printing 2022-07-11 15:43:26 +02:00
fdfa4d4695 fix makepdf for newer wkhtmltopdf version 2022-07-11 15:43:26 +02:00
4d717288da fix compile problem with EPICS 3.13 on vxWorks 6 2022-07-11 15:43:26 +02:00
19 changed files with 477 additions and 183 deletions

1
.ci Submodule

Submodule .ci added at dead44c3cb

12
.ci-local/defaults.set Normal file
View 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
View 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
View File

@ -0,0 +1,3 @@
[submodule ".ci"]
path = .ci
url = https://github.com/epics-base/ci-scripts.git

View File

@ -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)

View File

@ -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)))

View File

@ -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>%&lt;hexlrc&gt;</code></dt>
<dd>One byte. The LRC for the hex digits. (Other characters are ignored.)</dd>
<dt><code>%&lt;leybold&gt;</code></dt>
<dd>One byte. Used by some Leybold products. 255-bytesum%255 (+32 if result would be <32)</dd>
<dt><code>%&lt;brksCryo&gt;</code></dt>
<dd>One byte. Used by Brooks Cryopumps.</dd>
<dt><code>%&lt;CPI&gt;</code></dt>
<dd>One byte. Used by TRIUMF CPI RF amplifier.</dd>
<dt><code>%&lt;bitsum&gt;</code> or <code>%&lt;bitsum8&gt;</code></dt>
<dd>One byte. Number of 1 bits in all characters.</dd>
<dt><code>%&lt;bitsum16&gt;</code></dt>
<dd>Two bytes. Number of 1 bits in all characters.</dd>
<dt><code>%&lt;bitsum32&gt;</code></dt>
<dd>Four bytes. Number of 1 bits in all characters.</dd>
</dl>
<a name="regex"></a>

View File

@ -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

View File

@ -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>

View File

@ -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; }
}

View File

@ -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>
&nbsp;&nbsp;extrainput = ignore;<br>
&nbsp;&nbsp;replyTimeout = 1000;<br>
&nbsp;&nbsp;out "GET http://\$1\n\n";<br>
&nbsp;&nbsp;out "GET http://\$1\r\n\r\n";<br>
&nbsp;&nbsp;in "%+.1/(?im)&lt;title&gt(.*)&lt\/title&gt;/";<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>
&nbsp;&nbsp;extrainput = ignore;<br>
&nbsp;&nbsp;replyTimeout = 1000;<br>
&nbsp;&nbsp;out "GET http://\$1\n\n";<br>
&nbsp;&nbsp;out "GET http://\$1\r\n\r\n";<br>
&nbsp;&nbsp;in "%*/Interesting value:/%f more text";<br>
}
</code>

View File

@ -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

View File

@ -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++)
{

View File

@ -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());

View File

@ -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;

View File

@ -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:

View File

@ -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);

View File

@ -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

View File

@ -19,7 +19,7 @@
# along with StreamDevice. If not, see https://www.gnu.org/licenses/.
#########################################################################/
terminator = CR LF;
terminator = LF;
cmd {
out "%s";