Compare commits

..

20 Commits

Author SHA1 Message Date
75aa267875 error message on reply timeout 2020-03-16 16:58:07 +01:00
7dda6ca773 bugix for systems where vsnprintf returns -1 on buffer overrun: double buffer capacity 2020-03-02 13:56:42 +01:00
1378a9fcbb Merge branch 'windows_build_fix' of https://github.com/FreddieAkeroyd/StreamDevice 2020-03-02 09:13:38 +01:00
4d3672994d bugfix: when passing negative start index to find(), don't start earlier than index 0 2020-03-02 09:11:50 +01:00
967539c4e1 allow commas inside matching pairs of parentheses in protocol parameters 2020-03-02 09:10:28 +01:00
f072c217f3 allow () inside protocol parameters 2020-02-27 13:45:54 +01:00
a60ffd685f tests updated 2020-02-27 13:44:32 +01:00
0803c94c79 cleanup spaces 2020-02-26 15:53:02 +01:00
b03726903d Fix linker error on WIN32 2019-12-16 19:11:27 +00:00
afe8cc9a3e Merge branch 'master' of github.com:paulscherrerinstitute/StreamDevice 2019-11-27 16:50:36 +01:00
277635c65f fix linker problem on windows 2019-11-27 16:49:57 +01:00
4180bbbdd9 Merge pull request #45 from jgnrendra3/master
New Checksums including support for Brooks Cryopump
2019-11-12 15:30:14 +01:00
d683969556 New Checksums including support for Brooks Cryopump 2019-11-12 07:13:02 -05:00
a62d4d1a40 update documentation according to previous API change 2019-11-11 13:35:56 +01:00
aadecf1853 Merge pull request #44 from SLACer-garth/buffer-overflow-fix
Buffer overflow fix
2019-11-11 13:24:39 +01:00
f172f3d0a0 Merge pull request #43 from bfrk/master
remove error message in streamReadWrite
2019-11-11 12:47:41 +01:00
08ac900eda Make expectedLength ssize_t everywhere.
Also fix other type related warnings.
2019-11-07 17:20:08 -08:00
53caf69f3a Cast expectedLength to ssize_t where it is used as a signed number. Fixes buffer overrun memory corruption segfault. 2019-11-07 17:19:43 -08:00
3eac2b804d Bugfix in 64 bit data type handling. (Thanks to Andrew Johnson) 2019-11-07 20:23:35 +01:00
9b9e7bf722 remove error message in streamReadWrite
If the record hasn't been initialized correctly, we don't want the console
to be spammed every time the record gets processed.
2019-10-10 14:51:56 +02:00
26 changed files with 328 additions and 183 deletions

View File

@ -69,7 +69,7 @@ class MyInterface : StreamBusInterface
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, size_t expectedLength, bool async);
bool <a href="#read">readRequest</a>(unsigned long replyTimeout_ms, unsigned long readTimeout_ms, ssize_t 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 timeout_ms);
@ -118,7 +118,7 @@ bool <a href="#write">writeRequest</a>(const&nbsp;void*&nbsp;output,
<div class="indent"><code>
bool <a href="#read">readRequest</a>(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
size_t&nbsp;expectedLength, bool&nbsp;async);
ssize_t&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
bool <a href="#read">supportsAsyncRead</a>();
@ -460,7 +460,7 @@ The client may request more I/O or call <code>unlock()</code> after
<div class="indent"><code>
bool readRequest(unsigned&nbsp;long&nbsp;replyTimeout_ms,
unsigned&nbsp;long&nbsp;readTimeout_ms,
size_t&nbsp;expectedLength, bool&nbsp;async);
ssize_t&nbsp;expectedLength, bool&nbsp;async);
</code></div>
<div class="indent"><code>
ssize_t readCallback(IoStatus&nbsp;status,

View File

@ -482,12 +482,63 @@ To make this easier, <em>protocol arguments</em> can be used:
move { out "\$1 GOTO %d"; }
</pre>
<p>
Now, the protocol can be references in the <code>OUT</code> link
Now the same protocol can be used 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>
<p>
Up to 9 parameters can be specified in parentheses, separated by comma.
In the protocol, they are referenced as <code>$1</code> ...
<code>$9</code> outside quotes or <code>\$1</code> ... <code>\$9</code>
within quotes. The parameter <code>$0</code> resolves to the protocol name.
</p>
<div class="new">
<p>
To make links more readable, one space is allowed before and after each comma
and the enclosing parentheses. This space is not part of the parameter string.
Any additional space is part of the parameter.
</p>
<p>
If a parameter contains matching pairs of parentheses, these and all commas
inside are part of the parameter.
This allows to pass parameter strings like <code>(1,2)</code> easily without
much escaping.
</p>
<p>
Unmatched parentheses must be escaped with double backslash <code>\\</code>
as well as must be commas outside pairs of parentheses.
Double backslash is necessary because one backslash is already consumed by
the db file parser.
To pass a literal backslash in a parameter string use 4 backslashes
<code>\\\\</code>.
</p>
</div>
<p>
Note that macros can be used in parameters. That makes it possible to
pass part of the record name to the protocol to be used in
<a href="formats.html#redirection">redirections</a>.
</p>
<h4>Example:</h3>
<pre>
record(ai, "$(PREFIX)recX5") {
field(DTYP, "stream")
field(INP, "@$(PROTOCOLFILE) read(5, X\\,Y $(PREFIX)) $(PORT)")
}
record(ai, "$(PREFIX)recY5") {}
read { out 0x8$1 "READ \$2"; in "%f,%(\$3recY\$1)f" }
</pre>
<p>
The protocol resolves to:
</p>
<pre>
read { out 0x85 "READ X,Y"; in "%f,%($(PREFIX)recY5)f" }
</pre>
<p>
Here <code>$(PREFIX)</code> is replaced with its macro value.
But be aware that the macro is actually replaced before the link is parsed so
that macro values containing comma or parentheses may have unintended effects.
</p>
<a name="usrvar"></a>
@ -565,7 +616,7 @@ There is a fixed set of exception handler names starting with
</dd>
</dl>
<h3>Example:</h3>
<h4>Example:</h3>
<pre>
setPosition {
out "POS %f";

View File

@ -17,18 +17,6 @@
* *
***************************************************************/
#include "StreamBusInterface.h"
#include "StreamError.h"
#include "StreamBuffer.h"
#include "asynDriver.h"
#include "asynOctet.h"
#include "asynInt32.h"
#include "asynUInt32Digital.h"
#include "asynGpibDriver.h"
#include "devStream.h"
#ifdef EPICS_3_13
#include <assert.h>
#include <wdLib.h>
@ -43,6 +31,18 @@ extern "C" {
#include "iocsh.h"
#endif
#include "StreamBusInterface.h"
#include "StreamError.h"
#include "StreamBuffer.h"
#include "asynDriver.h"
#include "asynOctet.h"
#include "asynInt32.h"
#include "asynUInt32Digital.h"
#include "asynGpibDriver.h"
#include "devStream.h"
#include "MacroMagic.h"
#define Z PRINTF_SIZE_T_PREFIX
@ -162,7 +162,7 @@ class AsynDriverInterface : StreamBusInterface
double writeTimeout;
double readTimeout;
double replyTimeout;
size_t expectedLength;
ssize_t expectedLength;
unsigned long eventMask;
unsigned long receivedEvent;
StreamBuffer inputBuffer;
@ -187,7 +187,7 @@ class AsynDriverInterface : StreamBusInterface
bool writeRequest(const void* output, size_t size,
unsigned long writeTimeout_ms);
bool readRequest(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms, size_t expectedLength, bool async);
unsigned long readTimeout_ms, ssize_t expectedLength, bool async);
bool acceptEvent(unsigned long mask, unsigned long replytimeout_ms);
bool supportsEvent();
bool supportsAsyncRead();
@ -800,7 +800,7 @@ writeHandler()
// interface function: we want to read something
bool AsynDriverInterface::
readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
size_t _expectedLength, bool async)
ssize_t _expectedLength, bool async)
{
debug("AsynDriverInterface::readRequest(%s, %ld msec reply, "
"%ld msec read, expect %" Z "u bytes, async=%s)\n",

29
src/ChecksumConverter.cc Normal file → Executable file
View File

@ -504,6 +504,18 @@ static uint32_t leybold(const uint8_t* data, size_t len, uint32_t sum)
return sum;
}
// Checksum used by Brooks Cryopumps
static uint32_t brksCryo(const uint8_t* data, size_t len, uint32_t sum)
{
uint32_t xsum;
while (len--) {
sum += (*data++) & 0x7F;
}
xsum = (((sum >> 6) ^ sum) & 0x3F) + 0x30;
return xsum;
}
struct checksum
{
const char* name;
@ -521,8 +533,13 @@ static checksum checksumMap[] =
{"sum8", sum, 0x00, 0x00, 1}, // 0xDD
{"sum16", sum, 0x0000, 0x0000, 2}, // 0x01DD
{"sum32", sum, 0x00000000, 0x00000000, 4}, // 0x000001DD
{"nsum8", sum, 0xFF, 0xFF, 1}, // 0x23
{"nsum16", sum, 0xFFFF, 0xFFFF, 2}, // 0xFE23
{"nsum32", sum, 0xFFFFFFFF, 0xFFFFFFFF, 4}, // 0xFFFFFE23
{"notsum", sum, 0x00, 0xFF, 1}, // 0x22
{"xor", xor8, 0x00, 0x00, 1}, // 0x31
{"xor8", xor8, 0x00, 0x00, 1}, // 0x31
{"xor8ff", xor8, 0x00, 0xFF, 1}, // 0xCE
{"xor7", xor7, 0x00, 0x00, 1}, // 0x31
{"crc8", crc_0x07, 0x00, 0x00, 1}, // 0xF4
{"ccitt8", crc_0x31, 0x00, 0x00, 1}, // 0xA1
@ -541,6 +558,7 @@ static checksum checksumMap[] =
{"hexsum8", hexsum, 0x00, 0x00, 1}, // 0x2D
{"cpi", CPI, 0x00, 0x00, 1}, // 0x7E
{"leybold", leybold, 0x00, 0x00, 1}, // 0x22
{"brksCryo",brksCryo, 0x00, 0x00, 1} // 0x4A
};
static uint32_t mask[5] = {0, 0xFF, 0xFFFF, 0xFFFFFF, 0xFFFFFFFF};
@ -706,13 +724,14 @@ 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 expectedLength =
uint_fast8_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 :
checksumMap[fnum].bytes;
ssize_t expectedLength = nDigits;
if (input.length() - cursor < expectedLength)
if ((ssize_t)( input.length() - cursor ) < expectedLength)
{
debug("ChecksumConverter %s: Input '%s' too short for checksum\n",
checksumMap[fnum].name, input.expand(cursor)());
@ -731,7 +750,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
if (format.flags & sign_flag) // decimal
{
uint32_t sumin = 0;
size_t i;
ssize_t i;
for (i = 0; i < expectedLength; i++)
{
inchar = input[cursor+i];
@ -753,7 +772,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
{
if (format.flags & zero_flag) // ASCII
{
if (sscanf(input(cursor+2*i), "%2" SCNx8, &inchar) != 1)
if (sscanf(input(cursor+2*i), "%2" SCNx8, (int8_t *) &inchar) != 1)
{
debug("ChecksumConverter %s: Input byte '%s' is not a hex byte\n",
checksumMap[fnum].name, input.expand(cursor+2*i,2)());
@ -797,7 +816,7 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, size_t& cursor)
{
if (format.flags & zero_flag) // ASCII
{
sscanf(input(cursor+2*i), "%2" SCNx8, &inchar);
sscanf(input(cursor+2*i), "%2" SCNx8, (int8_t *) &inchar);
}
else
if (format.flags & left_flag) // poor man's hex: 0x30 - 0x3F

View File

@ -37,7 +37,7 @@ class DebugInterface : StreamBusInterface
bool writeRequest(const void* output, size_t size,
unsigned long writeTimeout_ms);
bool readRequest(unsigned long replyTimeout_ms,
unsigned long readTimeout_ms, size_t expectedLength, bool async);
unsigned long readTimeout_ms, ssize_t expectedLength, bool async);
protected:
~DebugInterface();
@ -169,9 +169,9 @@ writeRequest(const void* output, size_t size, unsigned long writeTimeout_ms)
// Return false if the read request cannot be accepted.
bool DebugInterface::
readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
size_t expectedLength, bool async)
ssize_t expectedLength, bool async)
{
debug("DebugInterface::readRequest(%s, %ld msec reply, %ld msec read, expect %" Z "u bytes, asyn=%s)\n",
debug("DebugInterface::readRequest(%s, %ld msec reply, %ld msec read, expect %" Z "d bytes, asyn=%s)\n",
clientName(), replyTimeout_ms, readTimeout_ms, expectedLength, async?"yes":"no");
// Debug interface does not support async mode.

View File

@ -127,7 +127,7 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
if (numEnums < 0) numEnums=-numEnums-1;
while (numEnums-- && (value != index))
{
while(*s)
while (*s)
{
if (*s == esc) s++;
s++;
@ -140,7 +140,7 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
error("Value %li not found in enum set\n", value);
return false;
}
while(*s)
while (*s)
{
if (*s == esc) s++;
output.append(*s++);
@ -165,7 +165,7 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", index, s);
consumed = 0;
match = true;
while(*s)
while (*s)
{
if (*s == StreamProtocolParser::skip)
{

View File

@ -28,7 +28,7 @@
#define ENUM(type, args...) \
enum type { args }; \
static inline const char* type##ToStr(int x) {switch(x){MACRO_FOR_EACH(_CASE_LINE,args)default: return "invalid";}}\
static inline const char* type##ToStr(int x) {switch(x) {MACRO_FOR_EACH(_CASE_LINE,args)default: return "invalid";}}\
_ENUM_CAST(type)
#else
@ -65,7 +65,7 @@ _ENUM_CAST(type)
#define ENUM(type,...) \
enum type { __VA_ARGS__ }; \
static inline const char* type##ToStr(int x) {switch(x){_EXPAND(MACRO_FOR_EACH(_CASE_LINE,__VA_ARGS__)) default: return "invalid";}} \
static inline const char* type##ToStr(int x) {switch(x) {_EXPAND(MACRO_FOR_EACH(_CASE_LINE,__VA_ARGS__)) default: return "invalid";}} \
_ENUM_CAST(type)
#endif

View File

@ -306,7 +306,7 @@ print(const char* fmt, ...)
return *this;
}
if (printed > -1) grow(len+printed);
else grow(len);
else grow(cap*2-1);
}
}

View File

@ -199,11 +199,10 @@ public:
// find: get index of data in buffer or -1
ssize_t find(char c, ssize_t start=0) const
{char* p;
{if (start < 0 && (start -= len) < 0) start = 0;
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;}
memchr(buffer+offs+start, c, len-start)))? p-(buffer+offs) : -1;}
ssize_t find(const void* s, size_t size, ssize_t start=0) const;

View File

@ -118,7 +118,7 @@ writeRequest(const void*, size_t, unsigned long)
}
bool StreamBusInterface::
readRequest(unsigned long, unsigned long, size_t, bool)
readRequest(unsigned long, unsigned long, ssize_t, bool)
{
return false;
}

View File

@ -76,7 +76,7 @@ public:
return businterface && businterface->writeRequest(output, size, timeout_ms);
}
bool busReadRequest(unsigned long replytimeout_ms,
unsigned long readtimeout_ms, size_t expectedLength,
unsigned long readtimeout_ms, ssize_t expectedLength,
bool async) {
return businterface && businterface->readRequest(replytimeout_ms,
readtimeout_ms, expectedLength, async);
@ -133,7 +133,7 @@ protected:
virtual bool writeRequest(const void* output, size_t size,
unsigned long timeout_ms);
virtual bool readRequest(unsigned long replytimeout_ms,
unsigned long readtimeout_ms, size_t expectedLength,
unsigned long readtimeout_ms, ssize_t expectedLength,
bool async);
virtual bool supportsEvent(); // defaults to false
virtual bool supportsAsyncRead(); // defaults to false

View File

@ -34,7 +34,7 @@ printCommands(StreamBuffer& buffer, const char* c)
unsigned long eventnumber;
while (1)
{
switch(*c++)
switch (*c++)
{
case end:
return buffer();
@ -189,14 +189,26 @@ parse(const char* filename, const char* _protocolname)
ssize_t i = protocolname.find('(');
if (i >= 0)
{
while (i >= 0)
while (i < (ssize_t)protocolname.length())
{
if (protocolname[i-1] == ' ')
protocolname.remove(--i, 1); // remove trailing space
protocolname[i] = '\0'; // replace '(' and ',' with '\0'
protocolname[i] = '\0'; // replace initial '(' and separating ',' with '\0'
if (protocolname[i+1] == ' ')
protocolname.remove(i+1, 1); // remove leading space
i = protocolname.find(',', i+1);
int brackets = 0;
do {
i++;
i += strcspn(protocolname(i), ",()\\");
char c = protocolname[i];
if (c == '(') brackets++;
else if (c == ')') brackets--;
else if (c == ',' && brackets <= 0) break;
else if (c == '\\') {
if (protocolname[i+1] == '\\') i++; // keep '\\'
else protocolname.remove(i, 1); // else skip over next char
}
} while (i < (ssize_t)protocolname.length());
}
// should have closing parentheses
if (protocolname[-1] != ')')
@ -206,9 +218,8 @@ parse(const char* filename, const char* _protocolname)
}
protocolname.truncate(-1); // remove ')'
if (protocolname[-1] == ' ')
{
protocolname.truncate(-1); // remove trailing space
}
debug("StreamCore::parse \"%s\" -> \"%s\"\n", _protocolname, protocolname.expand()());
}
StreamProtocolParser::Protocol* protocol;
protocol = StreamProtocolParser::getProtocol(filename, protocolname);
@ -975,8 +986,8 @@ readCallback(StreamIoStatus status,
evalIn();
return 0;
}
debug("StreamCore::readCallback(%s): No reply from device within %ld ms\n",
name(), replyTimeout);
error("%s: No reply within %ld ms to \"%s\"\n",
name(), replyTimeout, outputLine.expand()());
inputBuffer.clear();
finishProtocol(ReplyTimeout);
return 0;
@ -1343,7 +1354,7 @@ normal_format:
{
int i = 0;
while (commandIndex[i] >= ' ') i++;
error("%s: Input \"%s%s%s\"\n",
error("%s: Input \"%s%s%s\"\n",
name(),
consumedInput > 20 ? "..." : "",
inputLine.expand(consumedInput > 20 ? consumedInput-20 : 0, 40)(),
@ -1356,7 +1367,7 @@ normal_format:
consumedInput > 10 ? "..." : "",
inputLine.expand(consumedInput > 10 ? consumedInput-10 : 0,
consumedInput > 10 ? 10 : consumedInput)());
error("%s: got \"%s%s\" where \"%s\" was expected\n",
name(),
inputLine.expand(consumedInput, 10)(),

View File

@ -224,7 +224,7 @@ public:
void printProtocol(FILE* = stdout);
const char* name() { return streamname; }
void printStatus(StreamBuffer& buffer);
private:
char* printCommands(StreamBuffer& buffer, const char* c);
};

View File

@ -29,6 +29,9 @@
#ifdef EPICS_3_13
extern "C" {
static char* epicsStrDup(const char *s) { char* c = (char*)malloc(strlen(s)+1); strcpy(c, s); return c; }
#endif
#define epicsAlarmGLOBAL
@ -161,10 +164,7 @@ class Stream : protected StreamCore
Stream(dbCommon* record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData);
~Stream();
long parseLink(const struct link *ioLink, char* filename, char* protocol,
char* busname, int* addr, char* busparam);
long initRecord(const char* filename, const char* protocol,
const char* busname, int addr, const char* busparam);
long initRecord(char* linkstring);
bool print(format_t *format, va_list ap);
ssize_t scan(format_t *format, void* pvalue, size_t maxStringSize);
bool process();
@ -211,7 +211,7 @@ long streamReload(const char* recordname)
int oldStreamError = streamError;
streamError = 1;
if(!pdbbase) {
if (!pdbbase) {
error("No database has been loaded\n");
streamError = oldStreamError;
return ERROR;
@ -223,7 +223,7 @@ long streamReload(const char* recordname)
if (recordname && recordname[0] &&
#ifdef EPICS_3_13
strcmp(stream->name(), recordname) == 0)
#else
#else
!epicsStrGlobMatch(stream->name(), recordname))
#endif
continue;
@ -534,12 +534,7 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData)
{
long status;
char filename[256];
char protocol[256];
char busname[256];
int addr = -1;
char busparam[256];
memset(busparam, 0 ,sizeof(busparam));
char* linkstring;
debug("streamInitRecord(%s): SEVR=%d\n", record->name, record->sevr);
Stream* stream = static_cast<Stream*>(record->dpvt);
@ -556,19 +551,30 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
record->name);
stream->finishProtocol(Stream::Abort);
}
// scan the i/o link
debug("streamInitRecord(%s): parse link \"%s\"\n",
record->name, ioLink->value.instio.string);
status = stream->parseLink(ioLink, filename, protocol,
busname, &addr, busparam);
// (re)initialize bus and protocol
if (status == 0)
if (ioLink->type != INST_IO)
{
debug("streamInitRecord(%s): calling initRecord\n",
record->name);
status = stream->initRecord(filename, protocol,
busname, addr, busparam);
error("%s: Wrong I/O link type %s\n", record->name,
pamaplinkType[ioLink->type].strvalue);
return S_dev_badInitRet;
}
if (!ioLink->value.instio.string[0])
{
error("%s: Empty I/O link. "
"Forgot the leading '@' or confused INP with OUT or link is too long ?\n",
record->name);
return S_dev_badInitRet;
}
// (re)initialize bus and protocol
linkstring = epicsStrDup(ioLink->value.instio.string);
if (!linkstring)
{
error("%s: Out of memory", record->name);
return S_dev_noMemory;
}
debug("streamInitRecord(%s): calling initRecord\n",
record->name);
status = stream->initRecord(linkstring);
free(linkstring);
if (status != OK && status != DO_NOT_CONVERT)
{
error("%s: Record initialization failed\n", record->name);
@ -587,7 +593,6 @@ long streamReadWrite(dbCommon *record)
if (!stream || stream->status == ERROR)
{
(void) recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
error("%s: Record not initialised correctly\n", record->name);
return ERROR;
}
return stream->process() ? stream->convert : ERROR;
@ -699,77 +704,77 @@ Stream::
}
long Stream::
parseLink(const struct link *ioLink, char* filename,
char* protocol, char* busname, int* addr, char* busparam)
initRecord(char* linkstring /* modifiable copy */)
{
// parse link parameters: filename protocol busname addr busparam
int n1, n2;
if (ioLink->type != INST_IO)
{
error("%s: Wrong I/O link type %s\n", name(),
pamaplinkType[ioLink->type].strvalue);
return S_dev_badInitRet;
}
if (0 >= sscanf(ioLink->value.instio.string, "%s%n", filename, &n1))
{
error("%s: Empty I/O link. "
"Forgot the leading '@' or confused INP with OUT or link is too long ?\n",
name());
return S_dev_badInitRet;
}
if (0 >= sscanf(ioLink->value.instio.string+n1, " %[^ \t(] %n", protocol, &n2))
{
error("%s: Missing protocol name\n"
" expect \"@file protocol[(arg1,...)] bus [addr] [params]\"\n"
" in \"@%s\"\n", name(),
ioLink->value.instio.string);
return S_dev_badInitRet;
}
n1+=n2;
if (ioLink->value.instio.string[n1] == '(')
{
n2 = 0;
sscanf(ioLink->value.instio.string+n1, " %[^)] %n", protocol+strlen(protocol), &n2);
n1+=n2;
if (ioLink->value.instio.string[n1++] != ')')
{
error("%s: Missing ')' after protocol arguments '%s'\n"
" expect \"@file protocol(arg1,...) bus [addr] [params]\"\n"
" in \"@%s\"\n", name(), protocol,
ioLink->value.instio.string);
return S_dev_badInitRet;
}
strcat(protocol, ")");
}
if (0 >= sscanf(ioLink->value.instio.string+n1, "%s %i %99c", busname, addr, busparam))
{
error("%s: Missing bus name\n"
" expect \"@file protocol[(arg1,...)] bus [addr] [params]\"\n"
" in \"@%s\"\n", name(),
ioLink->value.instio.string);
return S_dev_badInitRet;
}
return OK;
}
char *filename;
char *protocol;
char *busname;
char *busparam;
long addr = -1;
long Stream::
initRecord(const char* filename, const char* protocol,
const char* busname, int addr, const char* busparam)
{
// It is safe to call this function again with different arguments
debug("Stream::initRecord %s: parse link string \"%s\"\n", name(), linkstring);
while (isspace(*linkstring)) linkstring++;
filename = linkstring;
while (*linkstring && !isspace(*linkstring)) linkstring++;
if (*linkstring) *linkstring++ = 0;
while (isspace(*linkstring)) linkstring++;
protocol = linkstring;
while (*linkstring && !isspace(*linkstring) && *linkstring != '(') linkstring++;
while (isspace(*linkstring)) linkstring++;
if (*linkstring == '(') {
int brackets = 0;
while(*++linkstring) {
if (*linkstring == '(') brackets++;
else if (*linkstring == ')') brackets--;
else if (*linkstring == '\\' && !*++linkstring) break;
else if (isspace(*linkstring) && brackets < 0) break;
}
}
else if (*linkstring) linkstring--;
if (*linkstring) *linkstring++ = 0;
while (isspace(*linkstring)) linkstring++;
busname = linkstring;
while (*linkstring && !isspace(*linkstring)) linkstring++;
if (*linkstring) *linkstring++ = 0;
if (linkstring) addr = strtol(linkstring, &linkstring, 0);
while (isspace(*linkstring)) linkstring++;
busparam = linkstring;
debug("Stream::initRecord %s: filename=\"%s\" protocol=\"%s\" bus=\"%s\" addr=%ld params=\"%s\"\n",
name(), filename, protocol, busname, addr, busparam);
if (!*filename)
{
error("%s: Missing protocol file name\n", name());
return S_dev_badInitRet;
}
if (!*protocol)
{
error("%s: Missing protocol name\n", name());
return S_dev_badInitRet;
}
if (!*busname)
{
error("%s: Missing bus name\n", name());
return S_dev_badInitRet;
}
// attach to bus interface
debug("Stream::initRecord %s: attachBus(%s, %d, \"%s\")\n",
debug("Stream::initRecord %s: attachBus(\"%s\", %ld, \"%s\")\n",
name(), busname, addr, busparam);
if (!attachBus(busname, addr, busparam))
{
error("%s: Can't attach to bus %s %d\n",
error("%s: Can't attach to bus %s %ld\n",
name(), busname, addr);
return S_dev_noDevice;
}
// parse protocol file
debug("Stream::initRecord %s: parse(%s, %s)\n",
debug("Stream::initRecord %s: parse(\"%s\", \"%s\")\n",
name(), filename, protocol);
if (!parse(filename, protocol))
{
@ -872,7 +877,7 @@ process()
if (!startProtocol(record->proc == 2 ? StreamCore::StartInit : StreamCore::StartNormal))
{
debug("Stream::process(%s): could not start %sprotocol, status=%s (%d)\n",
name(), record->proc == 2 ? "@init " : "",
name(), record->proc == 2 ? "@init " : "",
status >= 0 && status < ALARM_NSTATUS ?
epicsAlarmConditionStrings[status] : "ERROR",
status);
@ -1100,7 +1105,7 @@ getFieldAddress(const char* fieldname, StreamBuffer& address)
}
static const unsigned char dbfMapping[] =
#ifdef DBF_INT64
#ifdef DBR_INT64
{0, DBF_UINT64, DBF_INT64, DBF_ENUM, DBF_DOUBLE, DBF_STRING};
#else
{0, DBF_ULONG, DBF_LONG, DBF_ENUM, DBF_DOUBLE, DBF_STRING};

View File

@ -27,12 +27,7 @@
int streamDebug = 0;
int streamError = 1;
extern "C" {
#ifdef _WIN32
__declspec(dllexport)
#endif
FILE *StreamDebugFile = NULL;
}
#ifndef va_copy
#ifdef __va_copy

View File

@ -286,7 +286,7 @@ parseProtocol(Protocol& protocol, StreamBuffer* commands)
}
if (token[0] == '{')
{
error(line, filename(), "Expect %s name before '%c'\n",
error(line, filename(), "Expect %s name before '%c'\n",
isGlobalContext(commands) ? "protocol" : "handler",
token[0]);
return false;
@ -1039,7 +1039,7 @@ compileNumber(unsigned long& number, const char*& source, unsigned long max)
*source, source);
if (*source == '$')
{
if(!replaceVariable(buffer, source)) return false;
if (!replaceVariable(buffer, source)) return false;
debug("buffer=%s\n", buffer.expand()());
buffer.truncate(-1-(int)sizeof(int));
}

View File

@ -40,6 +40,12 @@
#define DO_NOT_CONVERT 2
#define INIT_RUN (!interruptAccept)
#ifdef epicsExportSharedSymbols
# define devStream_epicsExportSharedSymbols
# undef epicsExportSharedSymbols
# include "shareLib.h"
#endif
#include "epicsVersion.h"
#ifdef BASE_VERSION
#define EPICS_3_13
@ -49,12 +55,6 @@ extern "C" {
#endif
#endif
#ifdef epicsExportSharedSymbols
# define devStream_epicsExportSharedSymbols
# undef epicsExportSharedSymbols
# include <shareLib.h>
#endif
#include "dbCommon.h"
#include "dbScan.h"
#include "devSup.h"
@ -65,12 +65,6 @@ extern "C" {
#include "dbEvent.h"
#include "epicsMath.h"
#ifdef devStream_epicsExportSharedSymbols
# undef devStream_epicsExportSharedSymbols
# define epicsExportSharedSymbols
# include <shareLib.h>
#endif
#ifdef EPICS_3_13
#ifdef __cplusplus
}
@ -79,6 +73,12 @@ extern "C" {
#include "epicsStdioRedirect.h"
#endif
#ifdef devStream_epicsExportSharedSymbols
# undef devStream_epicsExportSharedSymbols
# define epicsExportSharedSymbols
# include "shareLib.h"
#endif
#ifdef _WIN32
typedef ptrdiff_t ssize_t;
#endif
@ -88,8 +88,7 @@ typedef const struct format_s {
const struct StreamFormat* priv;
} format_t;
epicsShareExtern FILE* StreamDebugFile;
extern const char StreamVersion [];
extern FILE* StreamDebugFile;
typedef long (*streamIoFunction) (dbCommon*, format_t*);
@ -97,6 +96,8 @@ typedef long (*streamIoFunction) (dbCommon*, format_t*);
extern "C" {
#endif
extern const char StreamVersion [];
long streamInit(int after);
long streamInitRecord(dbCommon *record,
const struct link *ioLink,

View File

@ -69,7 +69,7 @@ static long readData(dbCommon *record, format_t *format)
case DBF_FLOAT:
((epicsFloat32 *)aai->bptr)[aai->nord] = (epicsFloat32)lval;
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
case DBF_UINT64:
((epicsInt64 *)aai->bptr)[aai->nord] = (epicsInt64)lval;
@ -167,7 +167,7 @@ static long writeData(dbCommon *record, format_t *format)
case DBF_FLOAT:
dval = ((epicsFloat32 *)aai->bptr)[nowd];
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
dval = ((epicsInt64 *)aai->bptr)[nowd];
break;
@ -210,12 +210,12 @@ static long writeData(dbCommon *record, format_t *format)
{
switch (aai->ftvl)
{
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
lval = ((epicsInt64 *)aao->bptr)[nowd];
lval = ((epicsInt64 *)aai->bptr)[nowd];
break;
case DBF_UINT64:
lval = ((epicsUInt64 *)aao->bptr)[nowd];
lval = ((epicsUInt64 *)aai->bptr)[nowd];
break;
#endif
case DBF_LONG:

View File

@ -68,7 +68,7 @@ static long readData(dbCommon *record, format_t *format)
case DBF_FLOAT:
((epicsFloat32 *)aao->bptr)[aao->nord] = (epicsFloat32)lval;
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
case DBF_UINT64:
((epicsInt64 *)aao->bptr)[aao->nord] = (epicsInt64)lval;
@ -171,7 +171,7 @@ end_no_check:
#endif
if (monitor_mask)
db_post_events(aao, aao->bptr, monitor_mask);
return OK;
}
@ -196,7 +196,7 @@ static long writeData(dbCommon *record, format_t *format)
case DBF_FLOAT:
dval = ((epicsFloat32 *)aao->bptr)[nowd];
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
dval = ((epicsInt64 *)aao->bptr)[nowd];
break;
@ -239,7 +239,7 @@ static long writeData(dbCommon *record, format_t *format)
{
switch (aao->ftvl)
{
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
lval = ((epicsInt64 *)aao->bptr)[nowd];
break;

View File

@ -46,7 +46,7 @@ static long readData(dbCommon *record, format_t *format)
break;
}
default:
return ERROR;
return ERROR;
}
if (record->pact) return OK;
/* In @init handler, no processing, enforce monitor updates. */
@ -60,10 +60,10 @@ static long readData(dbCommon *record, format_t *format)
monitor_mask |= DBE_LOG;
co->alst = co->val;
}
if (monitor_mask){
if (monitor_mask) {
db_post_events(record, &co->val, monitor_mask);
}
return OK;
}

View File

@ -25,7 +25,7 @@
of the device support.
Fix: sCalcoutRecord.c, end of init_record() add
if(pscalcoutDSET->init_record ) {
if (pscalcoutDSET->init_record ) {
return (*pscalcoutDSET->init_record)(pcalc);
}
*/

View File

@ -70,10 +70,10 @@ static long readData(dbCommon *record, format_t *format)
case DBF_FLOAT:
((epicsFloat32 *)wf->bptr)[wf->nord] = (epicsFloat32)lval;
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
case DBF_UINT64:
((epicsInt64 *)wf->bptr)[aao->nord] = (epicsInt64)lval;
((epicsInt64 *)wf->bptr)[wf->nord] = (epicsInt64)lval;
break;
#endif
case DBF_LONG:
@ -168,7 +168,7 @@ static long writeData(dbCommon *record, format_t *format)
case DBF_FLOAT:
dval = ((epicsFloat32 *)wf->bptr)[nowd];
break;
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
dval = ((epicsInt64 *)wf->bptr)[nowd];
break;
@ -211,7 +211,7 @@ static long writeData(dbCommon *record, format_t *format)
{
switch (wf->ftvl)
{
#ifdef DBF_INT64
#ifdef DBR_INT64
case DBF_INT64:
lval = ((epicsInt64 *)wf->bptr)[nowd];
break;

View File

@ -62,14 +62,17 @@ proc startioc {} {
puts $fd "streamApp_registerRecordDeviceDriver"
}
puts $fd "streamSetLogfile StreamDebug.log"
puts $fd "var streamDebug 1"
puts $fd "var streamError 1"
puts $fd "epicsEnvSet STREAM_PROTOCOL_PATH ."
puts $fd "drvAsynIPPortConfigure device localhost:$port"
if [info exists startup] {
puts $fd $startup
}
puts $fd "dbLoadRecords test.db"
puts $fd $startup
puts $fd "iocInit"
puts $fd "dbl"
puts $fd "dbior stream 2"
puts $fd "var streamDebug 1"
close $fd
if [info exists streamversion] {
set ioc [open "|iocsh test.cmd >& $testname.ioclog 2>@stderr" w]

View File

@ -0,0 +1,61 @@
#!/usr/bin/env tclsh
source streamtestlib.tcl
# Define records, protocol and startup (text goes to files)
# The asynPort "device" is connected to a network TCP socket
# Talk to the socket with send/receive/assure
# Send commands to the ioc shell with ioccmd
set records {
record(ao, "DZ:test1")
{
field (DTYP, "stream")
field (OUT, "@test.proto test1(ARG(10),20,30) device")
}
record(ao, "DZ:test2")
{
field (DTYP, "stream")
field (OUT, "@test.proto test1 (ARG(10,20), 30) device")
}
record(ao, "DZ:test3")
{
field (DTYP, "stream")
field (OUT, "@test.proto test1 ( ARG ( 10 , 20 ) , 30 ) device")
}
record(ao, "DZ:test4")
{
field (DTYP, "stream")
field (OUT, "@test.proto test1( ARG \\( 10 , 20 , 30 ) device")
}
record(ao, "DZ:test5")
{
field (DTYP, "stream")
field (OUT, "@test.proto test1(\\ ARG\\,\\\\(10,20)\\,30) device")
}
}
set protocol {
Terminator = LF;
test1 { out "VAL:\$1:%d"}
}
set startup {
}
set debug 0
startioc
put DZ:test1 "1"
assure "VAL:ARG(10):1\n"
put DZ:test2 "1"
assure "VAL:ARG(10,20):1\n"
put DZ:test3 "1"
assure "VAL:ARG ( 10 , 20 ):1\n"
put DZ:test4 "1"
assure "VAL: ARG ( 10 :1\n"
put DZ:test5 "1"
assure "VAL: ARG,\\(10,20),30:1\n"
#finish

View File

@ -16,7 +16,7 @@ set records {
set protocol {
Terminator = LF;
test1 {out "\%\e%d\e\e\%";}
test1 {out "\%\e%d%%\e\e\%";}
}
set startup {
@ -28,6 +28,6 @@ set debug 0
startioc
put DZ:test1 1
assure "%\0331\033\033%\n"
assure "%\0331%\033\033%\n"
finish

View File

@ -63,7 +63,7 @@ fi
for o in $O
do
g++ -I ../../src $o test.cc -o test.exe
test.exe
./test.exe
if [ $? != 0 ]
then
echo -e "\033[31;7mTest failed.\033[0m"