Compare commits

...

38 Commits

Author SHA1 Message Date
2cd969d643 allow assigned enums: %#{one=1|two|neg=-1} 2010-06-09 13:12:00 +00:00
0c9d4dc4c0 %m format renamed from Exponential to MantissaExponent 2010-06-09 08:11:05 +00:00
ef3b2ec9fd allow formats in exec 2010-06-09 08:10:33 +00:00
1fb402e838 Allow optional input %? format 2010-06-09 08:05:05 +00:00
eae987e7f1 Find byte order at run-time because there is no standard how to do this at compile-time. 2010-06-09 08:03:49 +00:00
93906dd022 Bugfix: prec too long had corrupted output 2010-06-09 08:02:22 +00:00
7e717c564d *** empty log message *** 2010-06-09 08:01:03 +00:00
14b60c210a renamed %m from Exponention to MantissaExponent 2010-06-09 08:00:08 +00:00
8dae0afa30 take care of early input (race condition device reply and start of in command) 2010-06-09 07:54:47 +00:00
95bf2c78c2 install headers to allow plugins (formats) in other projects 2010-06-09 07:44:51 +00:00
8c10e5395c Initial Import 2009-05-20 10:12:59 +00:00
69de0b9f31 bugfix: find() did not find 1 char strings 2008-09-16 07:29:26 +00:00
22ae510dca comments fixed 2008-09-16 07:28:47 +00:00
5fdf3912c8 avoid compiler warning on 64 bit machines 2008-09-16 07:27:27 +00:00
b50aec424f increase minor version number 2008-09-16 07:26:41 +00:00
4a1033b6dc allow for 64 bit machines 2008-09-16 07:26:02 +00:00
68e178d41b Use long constant 1L to allow for 64 bit machines 2008-09-16 07:25:03 +00:00
e83ceda41c get rid of writeRaw for asyn 4.10 2008-09-16 07:24:24 +00:00
b307adb005 on vxworks, return value of sscanf with %* formats is buggy 2008-08-11 08:55:42 +00:00
0a2a304a2b Build calcout only for 3.14 2008-08-07 14:44:18 +00:00
80477709aa do record processing in callback tread instead of port thread 2008-08-04 15:46:39 +00:00
706c59d069 error msg improved 2008-08-04 15:45:12 +00:00
6541c40818 result strings fixed 2008-08-04 15:44:46 +00:00
04f64c0f67 ShowAsyncErrors hack removed 2008-08-04 15:44:17 +00:00
af669103c2 RawFloat added 2008-08-04 15:43:44 +00:00
40d9641150 comment fixed 2008-07-15 08:59:55 +00:00
9cf2770440 raw float converter added 2008-07-11 14:55:10 +00:00
616bc9a410 When not connected, call lock request with priority asynQueuePiorityConnect to avoid deadlock. 2008-07-11 14:53:28 +00:00
53abbb750b raw float added 2008-07-11 14:50:07 +00:00
ec3cbbf559 allow append of n equal bytes 2008-07-11 14:49:23 +00:00
8a3cbc1c6a output format for exponential converter defined 2008-07-11 14:48:17 +00:00
8a4cda6647 fix debug output of format string 2008-07-11 14:46:08 +00:00
2d513b96d3 %m added 2008-02-22 09:13:11 +00:00
6a0d7b4492 comments fixed 2008-02-21 17:09:31 +00:00
755df7c3ee bugfix: 1st byte was lost
feature: little endian support
2008-02-21 17:07:26 +00:00
ddb602196a comment added 2008-02-21 16:18:30 +00:00
0a5f486a9b improved error message 2008-02-21 16:08:09 +00:00
574052dcb8 alignment bug (on machines where it matters) 2008-02-21 16:05:56 +00:00
26 changed files with 676 additions and 251 deletions

5
MODULE Normal file
View File

@ -0,0 +1,5 @@
# Please change the following email with yours.
Email: dirk.zimoch@psi.ch
Module-Name: StreamDevice2
Description: StreamDevice2
Project-Name:

View File

@ -3,7 +3,7 @@ EXCLUDE_VERSIONS = 3.13.2
PROJECT=stream2
BUILDCLASSES += Linux
DOCUDIR = doc
#DOCUDIR = doc
DBDS = stream.dbd
@ -11,10 +11,11 @@ BUSSES += AsynDriver
FORMATS += Enum
FORMATS += BCD
FORMATS += Raw
FORMATS += RawFloat
FORMATS += Binary
FORMATS += Checksum
FORMATS += Regexp
FORMATS += Exponential
FORMATS += MantissaExponent
RECORDTYPES += aai aao
RECORDTYPES += ao ai
RECORDTYPES += bo bi
@ -23,13 +24,18 @@ RECORDTYPES += mbboDirect mbbiDirect
RECORDTYPES += longout longin
RECORDTYPES += stringout stringin
RECORDTYPES += waveform
RECORDTYPES += calcout
SOURCES += $(RECORDTYPES:%=src/dev%Stream.c)
SOURCES += $(FORMATS:%=src/%Converter.cc)
SOURCES += $(BUSSES:%=src/%Interface.cc)
SOURCES += $(wildcard src/Stream*.cc)
SOURCES += src/StreamVersion.c
SOURCES_3.14 += src/devcalcoutStream.c
HEADERS += StreamFormat.h
HEADERS += StreamFormatConverter.h
HEADERS += StreamBuffer.h
HEADERS += StreamError.h
ifeq (${EPICS_BASETYPE},3.13)
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
@ -55,7 +61,7 @@ stream.dbd:
@echo "driver(stream)" >> $@
else
stream.dbd:
@for r in $(RECORDTYPES); \
@for r in $(RECORDTYPES) calcout; \
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
done > $@
@echo "driver(stream)" >> $@

View File

@ -240,6 +240,8 @@ The <code>%b</code> format uses the characters <code>0</code> and
<code>1</code>.
With the <code>%B</code> format, you can choose two other characters
to represent zero and one.
With the <code>#</code> flag, the bit order is changed to <em>little
endian</em>, i.e. least significant bit first.
</p>
<p>
Examples: <code>%B.!</code> or <code>%B\x00\xff</code>.
@ -264,23 +266,28 @@ one character.
<h2>9. Raw LONG Converter (<code>%r</code>)</h2>
<p>
The raw converter does not really "convert".
A signed integer value is written or read in the internal
A signed or unsigned integer value is written or read in the internal
(usually two's complement) representation of the computer.
The normal byte order is <em>big endian</em>, i.e. most significant byte
first.
With the <code>#</code> flag, the byte order is changed to <em>little
endian</em>, i.e. least significant byte first.
With the <code>0</code> flag, the value is unsigned, otherwise signed.
</p>
<p>
In output, the <em>width</em> least significant bytes of the value
are written.
If <em>width</em> is larger than the size of a <code>long</code>,
the value is sign extended.
the value is sign extended or zero extended, depending on the
<code>0</code> flag.
</p>
<p>
In input, <em>width</em> bytes are read and put into the value.
If <em>width</em> is longer than the size of a <code>long</code>, only
If <em>width</em> is larger than the size of a <code>long</code>, only
the least significant bytes are used.
If <em>width</em> is smaller than the size of a <code>long</code>,
the value is sign extended or zero extended, depending on the
<code>0</code> flag.
</p>
<a name="bcd"></a>
@ -451,6 +458,20 @@ the title of an HTML page, skipps anything before the
<code>&lt;title&gt</code> tag and leaves anything after the
<code>&lt;/title&gt;</code> tag in the input buffer.
</p>
<a name="exp"></a>
<h2>13. Exponential DOUBLE converter (<code>%m</code>)</h2>
<p>
This experimental input-only format matches numbers in the format
<i>[sign] mantissa sign exponent<i>, e.g <code>+123-4</code> meaning
123e-4 or 0.0123. Mantissa and exponent are integers. The sign of the
mantissa is optional. Compared to the standard <code>%e</code> format,
this format does not contain the characters <code>.</code> and <code>e</code>.
</p>
<p>
Output formatting is ambigous (e.g. <code>123-4</code> versus
<code>1230-5</code>). Until it has been specified how to handle this,
output is not supported.
</p>
<hr>
<p align="right"><a href="processing.html">Next: Record Processing</a></p>
<p><small>Dirk Zimoch, 2007</small></p>

View File

@ -121,6 +121,7 @@ h1 {font-size:120%;
<li><a target="text" href="formats.html#bcd">%D</a></li>
<li><a target="text" href="formats.html#chksum">%&lt;<em>checksum</em>&gt;</a></li>
<li><a target="text" href="formats.html#regex">%/<em>regex</em>/</a></li>
<li><a target="text" href="formats.html#exp">%m</a></li>
</ul>
</div>
<div class="top">

View File

@ -59,12 +59,12 @@ writeRequest()
pasynManager->queueRequest()
when request is handled
pasynOctet->flush()
pasynOctet->writeRaw()
if writeRaw() times out
pasynOctet->write()
if write() times out
writeCallback(StreamIoTimeout)
if writeRaw fails otherwise
if write fails otherwise
writeCallback(StreamIoFault)
if writeRaw succeeds and all bytes have been written
if write succeeds and all bytes have been written
writeCallback(StreamIoSuccess)
if not all bytes can be written
pasynManager->queueRequest() to write next part
@ -458,12 +458,22 @@ connectToBus(const char* busname, int addr)
bool AsynDriverInterface::
lockRequest(unsigned long lockTimeout_ms)
{
int connected;
asynStatus status;
debug("AsynDriverInterface::lockRequest(%s, %ld msec)\n",
clientName(), lockTimeout_ms);
asynStatus status;
lockTimeout = lockTimeout_ms ? lockTimeout_ms*0.001 : -1.0;
ioAction = Lock;
status = pasynManager->queueRequest(pasynUser, priority(),
status = pasynManager->isConnected(pasynUser, &connected);
if (status != asynSuccess)
{
error("%s: pasynManager->isConnected() failed: %s\n",
clientName(), pasynUser->errorMessage);
return false;
}
status = pasynManager->queueRequest(pasynUser,
connected ? priority() : asynQueuePriorityConnect,
lockTimeout);
if (status != asynSuccess)
{
@ -595,15 +605,27 @@ writeHandler()
size_t streameoslen;
const char* streameos = getOutTerminator(streameoslen);
if (streameos) // stream has added eos
int oldeoslen = -1;
char oldeos[16];
if (streameos) // stream has already added eos, don't do it again in asyn
{
status = pasynOctet->writeRaw(pvtOctet, pasynUser,
outputBuffer, outputSize, &written);
// clear terminator for asyn
status = pasynOctet->getOutputEos(pvtOctet,
pasynUser, oldeos, sizeof(oldeos)-1, &oldeoslen);
if (status != asynSuccess)
{
oldeoslen = -1;
// No EOS support?
}
pasynOctet->setOutputEos(pvtOctet, pasynUser,
NULL, 0);
}
else // asyn should add eos
status = pasynOctet->write(pvtOctet, pasynUser,
outputBuffer, outputSize, &written);
if (oldeoslen >= 0) // restore asyn terminator
{
status = pasynOctet->write(pvtOctet, pasynUser,
outputBuffer, outputSize, &written);
pasynOctet->setOutputEos(pvtOctet, pasynUser,
oldeos, oldeoslen);
}
switch (status)
{
@ -755,7 +777,7 @@ readHandler()
{
// In AsyncRead mode just poll
// and read as much as possible
pasynUser->timeout = readTimeout;
pasynUser->timeout = 0.0;
bytesToRead = buffersize;
}
else
@ -763,7 +785,7 @@ readHandler()
pasynUser->timeout = replyTimeout;
}
bool waitForReply = true;
int received;
size_t received;
int eomReason;
asynStatus status;
long readMore;
@ -775,8 +797,8 @@ readHandler()
eomReason = 0;
status = pasynOctet->read(pvtOctet, pasynUser,
buffer, bytesToRead, (size_t*)&received, &eomReason);
if (ioAction != AsyncRead || status != asynTimeout)
buffer, bytesToRead, &received, &eomReason);
if (ioAction == Read || status != asynTimeout)
{
debug("AsynDriverInterface::readHandler(%s): "
"read(..., bytesToRead=%d, ...) [timeout=%f seconds] = %s\n",
@ -788,14 +810,14 @@ readHandler()
switch (status)
{
case asynSuccess:
if (ioAction == AsyncRead)
if (ioAction != Read)
{
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::readHandler(%s): "
"AsyncRead poll: received %d of %d bytes \"%s\" "
"eomReason=%s [data ignored]\n",
clientName(), received, bytesToRead,
StreamBuffer(buffer, received).expand()(),
clientName(), (int)received, bytesToRead,
StreamBuffer(buffer, (int)received).expand()(),
eomReasonStr[eomReason&0x7]);
#endif
// ignore what we got from here.
@ -808,8 +830,8 @@ readHandler()
debug("AsynDriverInterface::readHandler(%s): "
"received %d of %d bytes \"%s\" "
"eomReason=%s\n",
clientName(), received, bytesToRead,
StreamBuffer(buffer, received).expand()(),
clientName(), (int)received, bytesToRead,
StreamBuffer(buffer, (int)received).expand()(),
eomReasonStr[eomReason&0x7]);
#endif
// asynOctet->read() cuts off terminator, but:
@ -829,7 +851,7 @@ readHandler()
size_t i;
for (i = 0; i < deveoslen; i++, received++)
{
if (received >= 0) buffer[received] = deveos[i];
if ((int)received >= 0) buffer[received] = deveos[i];
// It is safe to add to buffer here, because
// the terminator was already there before
// asynOctet->read() had cut it.
@ -867,7 +889,7 @@ readHandler()
debug("AsynDriverInterface::readHandler(%s): "
"ioAction=%s, timeout after %d of %d bytes \"%s\"\n",
clientName(), ioActionStr[ioAction],
received, bytesToRead,
(int)received, bytesToRead,
StreamBuffer(buffer, received).expand()());
#endif
if (ioAction == AsyncRead || ioAction == AsyncReadMore)
@ -934,26 +956,11 @@ void intrCallbackOctet(void* /*pvt*/, asynUser *pasynUser,
// Problems here:
// 1. We get this message too when we are the poller.
// Thus we have to ignore what we got from polling.
// 2. We get this message multiple times when original reader
// reads in chunks.
// 3. eomReason=ASYN_EOM_CNT when message was too long for
// 2. eomReason=ASYN_EOM_CNT when message was too long for
// internal buffer of asynDriver.
if (!interruptAccept) return; // too early to process records
if (interface->ioAction == AsyncRead ||
interface->ioAction == AsyncReadMore)
{
interface->asynReadHandler(data, numchars, eomReason);
}
else
{
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::intrCallbackOctet(%s, buffer=\"%s\", "
"received=%d eomReason=%s) ioAction=%s\n",
interface->clientName(), StreamBuffer(data, numchars).expand()(),
numchars, eomReasonStr[eomReason&0x7], ioActionStr[interface->ioAction]);
#endif
}
interface->asynReadHandler(data, numchars, eomReason);
}
// get asynchronous input
@ -1277,6 +1284,8 @@ void handleRequest(asynUser* pasynUser)
{
AsynDriverInterface* interface =
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
debug("AsynDriverInterface::handleRequest(%s) %s\n",
interface->clientName(), ioActionStr[interface->ioAction]);
switch (interface->ioAction)
{
case None:
@ -1311,6 +1320,8 @@ void handleTimeout(asynUser* pasynUser)
{
AsynDriverInterface* interface =
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
debug("AsynDriverInterface::handleTimeout(%s)\n",
interface->clientName());
switch (interface->ioAction)
{
case Lock:

View File

@ -65,32 +65,60 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
{
// find number of significant bits
prec = sizeof (value) * 8;
while (prec && (value & (1 << (prec - 1))) == 0) prec--;
while (prec && (value & (1L << (prec - 1))) == 0) prec--;
}
if (prec == 0) prec++; // print at least one bit
int width = prec;
if (format.width > width) width = format.width;
char zero = format.info[0];
char one = format.info[1];
if (!(format.flags & left_flag))
char fill = (format.flags & zero_flag) ? zero : ' ';
if (format.flags & alt_flag)
{
// pad left
char fill = (format.flags & zero_flag) ? zero : ' ';
while (width > prec)
// little endian (least significant bit first)
if (!(format.flags & left_flag))
{
output.append(fill);
// pad left
while (width > prec)
{
output.append(' ');
width--;
}
}
while (prec--)
{
output.append((value & 1) ? one : zero);
value >>= 1;
width--;
}
while (width--)
{
// pad right
output.append(fill);
}
}
while (prec--)
else
{
output.append((value & (1 << prec)) ? one : zero);
width--;
}
while (width--)
{
// pad right
output.append(' ');
// big endian (most significant bit first)
if (!(format.flags & left_flag))
{
// pad left
while (width > prec)
{
output.append(fill);
width--;
}
}
while (prec--)
{
output.append((value & (1L << prec)) ? one : zero);
width--;
}
while (width--)
{
// pad right
output.append(' ');
}
}
return true;
}
@ -102,14 +130,28 @@ scanLong(const StreamFormat& format, const char* input, long& value)
int width = format.width;
if (width == 0) width = -1;
int length = 0;
while (isspace(input[++length])); // skip whitespaces
while (isspace(input[length])) length++; // skip whitespaces
char zero = format.info[0];
char one = format.info[1];
if (input[length] != zero && input[length] != one) return -1;
while (width-- && (input[length] == zero || input[length] == one))
if (format.flags & alt_flag)
{
val <<= 1;
if (input[length++] == one) val |= 1;
// little endian (least significan bit first)
long mask = 1;
while (width-- && (input[length] == zero || input[length] == one))
{
if (input[length++] == one) val |= mask;
mask <<= 1;
}
}
else
{
// big endian (most significan bit first)
while (width-- && (input[length] == zero || input[length] == one))
{
val <<= 1;
if (input[length++] == one) val |= 1;
}
}
value = val;
return length;

View File

@ -38,9 +38,10 @@ SYNAPPS_RECORDS += scalcout
FORMATS += Enum
FORMATS += BCD
FORMATS += Raw
FORMATS += RawFloat
FORMATS += Binary
FORMATS += Checksum
FORMATS += Exponential
FORMATS += MantissaExponent
# Want Perl regular expression matching?
# If PCRE is installed at the same location for all

View File

@ -21,6 +21,7 @@
#include "StreamFormatConverter.h"
#include "StreamError.h"
#include "StreamProtocol.h"
#include <stdlib.h>
// Enum %{string0|string1|...}
@ -31,44 +32,62 @@ class EnumConverter : public StreamFormatConverter
int scanLong(const StreamFormat&, const char*, long&);
};
// info format: <numEnums><index><string>0<index><string>0...
int EnumConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool)
{
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag|alt_flag))
if (fmt.flags & (left_flag|sign_flag|space_flag|zero_flag))
{
error("Use of modifiers '-', '+', ' ', '0', '#'"
error("Use of modifiers '-', '+', ' ', '0' "
"not allowed with %%{ conversion\n");
return false;
}
int i = info.length(); // put maxValue here later
info.append('\0');
int maxValue = 0;
long numEnums = 0;
int n = info.length(); // put numEnums here later
info.append(&numEnums, sizeof(numEnums));
long index = 0;
int i = 0;
i = info.length(); // put index here later
info.append(&index, sizeof(index));
while (*source)
{
switch (*source)
if (*source == '=' && (fmt.flags & alt_flag))
{
case '|':
info.append('\0');
if (++maxValue > 255)
{
error("Too many enums (max 256)\n");
return false;
}
break;
case '}':
source++;
info.append('\0');
info[i] = maxValue;
debug("EnumConverter::parse %d choices: %s\n",
maxValue+1, info.expand(i+1)());
return enum_format;
case esc:
info.append(*source++);
default:
info.append(*source);
char* p;
index = strtol(++source, &p, 0);
if (p == source || (*p != '|' && *p != '}'))
{
error("Integer expected after '=' "
"in %%{ format conversion\n");
return false;
}
memcpy(info(i), &index, sizeof(index));
source = p;
}
if (*source == '|' || *source == '}')
{
numEnums++;
info.append('\0');
if (*source++ == '}')
{
memcpy(info(n), &numEnums, sizeof(numEnums));
debug("EnumConverter::parse %ld choices: %s\n",
numEnums, info.expand()());
return enum_format;
}
index ++;
i = info.length();
info.append(&index, sizeof(index));
}
else
{
if (*source == esc)
info.append(*source++);
info.append(*source++);
}
source++;
}
error("Missing '}' after %%{ format conversion\n");
return false;
@ -77,14 +96,10 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
bool EnumConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{
long maxValue = fmt.info[0]; // number of enums
const char* s = fmt.info+1; // first enum string
if (value < 0 || value > maxValue)
{
error("Value %li out of range [0...%li]\n", value, maxValue);
return false;
}
while (value--)
const char* s = fmt.info;
long numEnums = extract<long>(s);
long index = extract<long>(s);
while (numEnums-- && (value != index))
{
while(*s)
{
@ -92,6 +107,12 @@ printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
s++;
}
s++;
index = extract<long>(s);
}
if (numEnums == -1)
{
error("Value %li not found in enum set\n", value);
return false;
}
while(*s)
{
@ -106,14 +127,16 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
{
debug("EnumConverter::scanLong(%%%c, \"%s\")\n",
fmt.conv, input);
long maxValue = fmt.info[0]; // number of enums
const char* s = fmt.info+1; // first enum string
const char* s = fmt.info;
long numEnums = extract<long>(s);
long index;
int length;
long val;
bool match;
for (val = 0; val <= maxValue; val++)
while (numEnums--)
{
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", val, s);
index = extract<long>(s);
debug("EnumConverter::scanLong: check #%ld \"%s\"\n", index, s);
length = 0;
match = true;
while(*s)
@ -129,8 +152,8 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
}
if (match)
{
debug("EnumConverter::scanLong: value %ld matches\n", val);
value = val;
debug("EnumConverter::scanLong: value %ld matches\n", index);
value = index;
return length;
}
s++;

View File

@ -70,7 +70,7 @@ SHRLIB_DEPLIB_DIRS += $(PCRE_LIB)
endif
endif
LIB_LIBS += Com dbIoc dbStaticIoc registryIoc iocsh
LIB_LIBS += $(EPICS_BASE_IOC_LIBS)
ifeq ($(USE_MEMGUARD),YES)
# memguard looks for memory leaks (gcc only)
@ -108,7 +108,6 @@ $(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
done > $@
@echo "driver(stream)" >> $@
@echo "variable(streamDebug, int)" >> $@
@echo "variable(showAsyncErrors, int)" >> $@
@echo "registrar(streamRegistrar)" >> $@
endif

View File

@ -1,8 +1,7 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2007 Dirk Zimoch (dirk.zimoch@psi.ch) *
* (C) 2008 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is a custom exponential format converter for *
* StreamDevice. *
@ -25,27 +24,33 @@
#include "StreamError.h"
#include <math.h>
// Exponential Converter %m: format +00351-02 means +351e-2
// Exponential Converter %m
// Eric Berryman requested a double format that reads
// +00011-01 as 11e-01
// I.e integer mantissa and exponent without 'e' or '.'
// But why not +11000-04 ?
// For writing, I chose the following convention:
// Format precision defines number of digits in mantissa
// No leading '0' in mantissa (except for 0.0 of course)
// Number of digits in exponent is at least 2
// Format flags +, -, and space are supported in the usual way
// Flags #, 0 are not supported
class ExponentialConverter : public StreamFormatConverter
class MantissaExponentConverter : public StreamFormatConverter
{
virtual int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
virtual int scanDouble(const StreamFormat&, const char*, double&);
virtual bool printDouble(const StreamFormat&, StreamBuffer&, double);
};
int ExponentialConverter::
int MantissaExponentConverter::
parse(const StreamFormat& fmt, StreamBuffer& info,
const char*& source, bool scanFormat)
{
if (!scanFormat)
{
error("At the moment for %%m format only input is implemented\n");
return false;
}
return double_format;
}
int ExponentialConverter::
int MantissaExponentConverter::
scanDouble(const StreamFormat& fmt, const char* input, double& value)
{
int mantissa;
@ -55,9 +60,41 @@ scanDouble(const StreamFormat& fmt, const char* input, double& value)
sscanf(input, "%d%d%n", &mantissa, &exponent, &length);
if (fmt.flags & skip_flag) return length;
if (length == -1) return -1;
value = (double)(mantissa) * pow(10, exponent);
value = (double)(mantissa) * pow(10.0, exponent);
return length;
}
RegisterConverter (ExponentialConverter, "m");
bool MantissaExponentConverter::
printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
{
// Have to divide value into mantissa and exponent
// precision field is number of characters in mantissa
// number of characters in exponent is at least 2
int spaces;
StreamBuffer buf;
int prec = fmt.prec;
if (prec < 1) prec = 6;
buf.printf("%.*e", prec-1, fabs(value)/pow(10.0, prec-1));
buf.remove(1,1);
buf.remove(buf.find('e'),1);
spaces = fmt.width-buf.length();
if (fmt.flags & (space_flag|sign_flag) || value < 0.0) spaces--;
if (spaces < 0) spaces = 0;
if (!(fmt.flags & left_flag))
output.append(' ', spaces);
if ((fmt.flags & (space_flag|sign_flag)) == space_flag && value >= 0.0)
output.append(' ');
if (fmt.flags & sign_flag && value >= 0.0)
output.append('+');
if (value < 0.0)
output.append('-');
output.append(buf);
if (fmt.flags & left_flag)
output.append(' ', spaces);
return true;
}
RegisterConverter (MantissaExponentConverter, "m");

View File

@ -40,12 +40,14 @@ parse(const StreamFormat&, StreamBuffer&,
bool RawConverter::
printLong(const StreamFormat& format, StreamBuffer& output, long value)
{
int prec = format.prec; // number of bytes from value
if (prec == -1) prec = 1; // default: 1 byte
int width = prec; // number of bytes in output
int prec = format.prec; // number of bytes from value
if (prec == -1) prec = 1; // default: 1 byte
int width = prec; // number of bytes in output
if (prec > (int)sizeof(long)) prec=sizeof(long);
if (format.width > width) width = format.width;
char byte = 0;
if (format.flags & alt_flag) // lsb first (little endian)
if (format.flags & alt_flag) // little endian (lsb first)
{
while (prec--)
{
@ -69,7 +71,7 @@ printLong(const StreamFormat& format, StreamBuffer& output, long value)
output.append(byte);
}
}
else // msb first (big endian)
else // big endian (msb first)
{
if (format.flags & zero_flag)
{
@ -107,7 +109,7 @@ scanLong(const StreamFormat& format, const char* input, long& value)
}
if (format.flags & alt_flag)
{
// little endian (sign extended)*/
// little endian (lsb first)
unsigned int shift = 0;
while (--width && shift < sizeof(long)*8)
{
@ -131,7 +133,7 @@ scanLong(const StreamFormat& format, const char* input, long& value)
}
else
{
// big endian */
// big endian (msb first)
if (format.flags & zero_flag)
{
// fill with zero

136
src/RawFloatConverter.cc Normal file
View File

@ -0,0 +1,136 @@
/***************************************************************
* StreamDevice Support *
* *
* (C) 1999 Dirk Zimoch (zimoch@delta.uni-dortmund.de) *
* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) *
* *
* This is the raw format converter of StreamDevice. *
* Please refer to the HTML files in ../doc/ for a detailed *
* documentation. *
* *
* If you do any changes in this file, you are not allowed to *
* redistribute it any more. If there is a bug or a missing *
* feature, send me an email and/or your patch. If I accept *
* your changes, they will go to the next release. *
* *
* DISCLAIMER: If this software breaks something or harms *
* someone, it's your problem. *
* *
***************************************************************/
#include "StreamFormatConverter.h"
#include "StreamError.h"
static int endian = 0;
// Raw Float Converter %R
class RawFloatConverter : public StreamFormatConverter
{
int parse(const StreamFormat&, StreamBuffer&, const char*&, bool);
bool printDouble(const StreamFormat&, StreamBuffer&, double);
int scanDouble(const StreamFormat&, const char*, double&);
};
int RawFloatConverter::
parse(const StreamFormat& format, StreamBuffer&,
const char*&, bool)
{
// Find out byte order
if (!endian) {
union {long l; char c [sizeof(long)];} u;
u.l=1;
if (u.c[0]) { endian = 1234;} // little endian
else if (u.c[sizeof(long)-1]) { endian = 4321;} // big endian
else {
error ("Cannot find out byte order for %%R format.\n");
return false;
}
}
// Assume IEEE formats with 4 or 8 bytes (default: 4)
if (format.width==0 || format.width==4 || format.width==8)
return double_format;
error ("Only width 4 or 8 allowed for %%R format.\n");
return false;
}
bool RawFloatConverter::
printDouble(const StreamFormat& format, StreamBuffer& output, double value)
{
int nbOfBytes;
int n;
union {
double dval;
float fval;
char bytes[8];
} buffer;
nbOfBytes = format.width;
if (nbOfBytes == 0)
nbOfBytes = 4;
if (nbOfBytes == 4)
buffer.fval = value;
else
buffer.dval = value;
if (!(format.flags & alt_flag) ^ (endian == 4321))
{
// swap if byte orders differ
for (n = nbOfBytes-1; n >= 0; n--)
{
output.append(buffer.bytes[n]);
}
} else {
for (n = 0; n < nbOfBytes; n++)
{
output.append(buffer.bytes[n]);
}
}
return true;
}
int RawFloatConverter::
scanDouble(const StreamFormat& format, const char* input, double& value)
{
int nbOfBytes;
int i, n;
union {
double dval;
float fval;
char bytes[8];
} buffer;
nbOfBytes = format.width;
if (nbOfBytes == 0)
nbOfBytes = 4;
if (format.flags & skip_flag)
{
return(nbOfBytes); // just skip input
}
if (!(format.flags & alt_flag) ^ (endian == 4321))
{
// swap if byte orders differ
for (n = nbOfBytes-1, i = 0; n >= 0; n--, i++)
{
buffer.bytes[n] = input[i];
}
} else {
for (n = 0; n < nbOfBytes; n++)
{
buffer.bytes[n] = input[n];
}
}
if (nbOfBytes == 4)
value = buffer.fval;
else
value = buffer.dval;
return nbOfBytes;
}
RegisterConverter (RawFloatConverter, "R");

View File

@ -112,7 +112,7 @@ scanString(const StreamFormat& fmt, const char* input,
if (!(fmt.flags & sign_flag)) {
debug("Matching string \"%s\" too long (%d>%d bytes)\n",
StreamBuffer(input+ovector[subexpr*2], len).expand()(),
len, maxlen-1);
(int)len, (int)maxlen-1);
return -1;
}
len = maxlen-1;

View File

@ -135,7 +135,7 @@ find(const void* m, long size, long start) const
start += len;
if (start < 0) start = 0;
}
if (start >= len-size+1) return -1; // find nothing after end
if (start+size > len) return -1; // find nothing after end
if (!m || size <= 0) return start; // find empty string at start
const char* s = static_cast<const char*>(m);
char* b = buffer+offs;
@ -143,12 +143,12 @@ find(const void* m, long size, long start) const
long i;
while ((p = static_cast<char*>(memchr(p, s[0], b-p+len-size+1))))
{
i = 1;
while (p[i] == s[i])
for (i = 1; i < size; i++)
{
if (++i >= size) return p-b;
if (p[i] != s[i]) goto next;
}
p++;
return p-b;
next: p++;
}
return -1;
}

View File

@ -29,11 +29,11 @@
class StreamBuffer
{
char local[64];
long len;
long cap;
long offs;
char* buffer;
char local[64];
void grow(long);
void init(const void*, long);
@ -103,11 +103,11 @@ public:
// reserve: reserve size bytes of memory and return
// pointer to that memory (for copying something to it)
char* reserve(long size)
{check(size); char* p=buffer+offs+len; len+=size; return p; }
{grow(size); char* p=buffer+len; len+=size; return p;}
// append: append data at the end of the buffer
StreamBuffer& append(char c)
{check(1); buffer[offs+len++]=c; return *this;}
StreamBuffer& append(char c, long count=1)
{check(count); while(count-->0) buffer[offs+len++]=c; return *this;}
StreamBuffer& append(const void* s, long size);
@ -144,7 +144,7 @@ public:
StreamBuffer& replace(long pos, long length, const StreamBuffer& s)
{return replace(pos, length, s.buffer+s.offs, s.len);}
// replace: delete part of buffer
// remove: delete from start/pos
StreamBuffer& remove(long pos, long length)
{return replace(pos, length, NULL, 0);}
@ -152,7 +152,7 @@ public:
{if (length>len) length=len;
offs+=length; len-=length; return *this;}
// replace: delete end of buffer
// truncate: delete end of buffer
StreamBuffer& truncate(long pos)
{return replace(pos, len, NULL, 0);}

View File

@ -20,7 +20,11 @@
#include "StreamBusInterface.h"
const char* StreamIoStatusStr[] = {
"StreamIoSuccess", "ioTimeout", "ioNoReply", "ioEnd", "ioFault"
"StreamIoSuccess",
"StreamIoTimeout",
"StreamIoNoReply",
"StreamIoEnd",
"StreamIoFault"
};
StreamBusInterfaceRegistrarBase* StreamBusInterfaceRegistrarBase::first;

View File

@ -23,12 +23,6 @@
#include <ctype.h>
#include <stdlib.h>
#include <epicsExport.h>
int showAsyncErrors = 0;
extern "C" {
epicsExportAddress(int, showAsyncErrors);
}
enum Commands { end_cmd, in_cmd, out_cmd, wait_cmd, event_cmd, exec_cmd,
connect_cmd, disconnect_cmd };
const char* commandStr[] = { "end", "in", "out", "wait", "event", "exec",
@ -366,7 +360,7 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
{
buffer.append(exec_cmd);
if (!protocol->compileString(buffer, args,
NoFormat, this))
PrintFormat, this))
{
return false;
}
@ -396,6 +390,22 @@ compileCommand(StreamProtocolParser::Protocol* protocol,
// Run the protocol
// Input and events may come asynchonously from the bus driver
// Especially in the sequence 'out "request"; in "reply";' the
// reply can come before the 'in' command has actually started.
// For asyncronous protocols, input can come at any time.
// Thus, we must always accept input and event while the protocol
// is running or when asyncronous mode is active.
// Early input and event must be buffered until 'in' or 'event'
// start. An 'out' command must discard any early input to avoid
// problems with late input from aborted protocols.
// Async mode ends on Abort or Error or if another command comes
// after the asynchronous 'in' command.
// Input can be discarded when it is not accepted any more, i.e.
// at the end of syncronous protocols and when an asynchronous
// mode ends (but not when 'in'-only protocol finishes normally).
bool StreamCore::
startProtocol(StartMode startMode)
{
@ -449,7 +459,7 @@ finishProtocol(ProtocolResult status)
flags & WaitPending ? "timerCallback()" : "");
status = Fault;
}
flags &= ~(AcceptInput|AcceptEvent);
//// flags &= ~(AcceptInput|AcceptEvent);
if (runningHandler)
{
// get original error status
@ -524,6 +534,7 @@ finishProtocol(ProtocolResult status)
flags &= ~BusOwner;
}
busFinish();
flags &= ~(AcceptInput|AcceptEvent);
protocolFinishHook(status);
}
@ -545,16 +556,16 @@ evalCommand()
switch (*commandIndex++)
{
case out_cmd:
flags &= ~(AcceptInput|AcceptEvent);
//// flags &= ~(AcceptInput|AcceptEvent);
return evalOut();
case in_cmd:
flags &= ~AcceptEvent;
//// flags &= ~AcceptEvent;
return evalIn();
case wait_cmd:
flags &= ~(AcceptInput|AcceptEvent);
//// flags &= ~(AcceptInput|AcceptEvent);
return evalWait();
case event_cmd:
flags &= ~AcceptInput;
//// flags &= ~AcceptInput;
return evalEvent();
case exec_cmd:
return evalExec();
@ -622,6 +633,7 @@ formatOutput()
char command;
const char* fieldName = NULL;
const char* formatstring;
int formatstringlen;
while ((command = *commandIndex++) != StreamProtocolParser::eos)
{
switch (command)
@ -631,7 +643,7 @@ formatOutput()
debug("StreamCore::formatOutput(%s): StreamProtocolParser::format_field\n",
name());
// code layout:
// field <StreamProtocolParser::eos> addrlen AddressStructure formatstring <StreamProtocolParser::eos> StreamFormat [info]
// field <eos> addrlen AddressStructure formatstring <eos> StreamFormat [info]
fieldName = commandIndex;
commandIndex += strlen(commandIndex)+1;
unsigned short addrlen = extract<unsigned short>(commandIndex);
@ -641,14 +653,23 @@ formatOutput()
case StreamProtocolParser::format:
{
// code layout:
// formatstring <StreamProtocolParser::eos> StreamFormat [info]
// formatstring <eos> StreamFormat [info]
formatstring = commandIndex;
while (*commandIndex++); // jump after <StreamProtocolParser::eos>
// jump after <eos>
while (*commandIndex)
{
if (*commandIndex == esc) commandIndex++;
commandIndex++;
}
formatstringlen = commandIndex-formatstring;
commandIndex++;
StreamFormat fmt = extract<StreamFormat>(commandIndex);
fmt.info = commandIndex; // point to info string
commandIndex += fmt.infolen;
#ifndef NO_TEMPORARY
debug("StreamCore::formatOutput(%s): format = %%%s\n",
name(), formatstring);
name(), StreamBuffer(formatstring, formatstringlen).expand()());
#endif
if (fmt.type == pseudo_format)
{
if (!StreamFormatConverter::find(fmt.conv)->
@ -663,12 +684,13 @@ formatOutput()
flags &= ~Separator;
if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL))
{
StreamBuffer formatstr(formatstring, formatstringlen);
if (fieldName)
error("%s: Cannot format field '%s' with '%%%s'\n",
name(), fieldName, formatstring);
name(), fieldName, formatstr.expand()());
else
error("%s: Cannot format value with '%%%s'\n",
name(), formatstring);
name(), formatstr.expand()());
return false;
}
fieldAddress.clear();
@ -782,6 +804,8 @@ lockCallback(StreamIoStatus status)
flags |= BusOwner;
if (status != StreamIoSuccess)
{
error("%s: Lock timeout\n",
name());
finishProtocol(LockTimeout);
return;
}
@ -807,8 +831,6 @@ writeCallback(StreamIoStatus status)
flags &= ~WritePending;
if (status != StreamIoSuccess)
{
error("%s: write failed: %s\n",
name(), StreamIoStatusStr[status]);
finishProtocol(WriteTimeout);
return;
}
@ -894,11 +916,17 @@ readCallback(StreamIoStatus status,
if (!(flags & AcceptInput))
{
error("StreamCore::readCallback(%s) called unexpectedly\n",
name());
#ifdef NO_TEMPORARY
error("StreamCore::readCallback(%s, %s) called unexpectedly\n",
name(), StreamIoStatusStr[status]);
#else
error("StreamCore::readCallback(%s, %s, \"%s\") called unexpectedly\n",
name(), StreamIoStatusStr[status],
StreamBuffer(input, size).expand()());
#endif
return 0;
}
flags &= ~AcceptInput;
//// flags &= ~AcceptInput;
unparsedInput = false;
switch (status)
{
@ -957,9 +985,15 @@ readCallback(StreamIoStatus status,
{
// look for terminator
end = inputBuffer.find(inTerminator);
if (end >= 0) termlen = inTerminator.length();
debug("StreamCore::readCallback(%s) inTerminator %sfound\n",
name(), end >= 0 ? "" : "not ");
if (end >= 0)
{
termlen = inTerminator.length();
debug("StreamCore::readCallback(%s) inTerminator %s at position %ld\n",
name(), inTerminator.expand()(), end);
} else {
debug("StreamCore::readCallback(%s) inTerminator %s not found\n",
name(), inTerminator.expand()());
}
}
if (status == StreamIoEnd && end < 0)
{
@ -981,6 +1015,12 @@ readCallback(StreamIoStatus status,
end = maxInput;
termlen = 0;
}
if (end >= 0)
{
// be forgiving with timeout because end is found
if (status == StreamIoTimeout)
status = StreamIoEnd;
}
if (end < 0)
{
// no end found
@ -997,23 +1037,22 @@ readCallback(StreamIoStatus status,
}
// try to parse what we got
end = inputBuffer.length();
if (!(flags & AsyncMode)||showAsyncErrors)
if (flags & AsyncMode)
{
debug("StreamCore::readCallback(%s) async timeout: just restart\n",
name());
inputBuffer.clear();
commandIndex = commandStart;
evalIn();
return 0;
}
else
{
error("%s: Timeout after reading %ld byte%s \"%s%s\"\n",
name(), end, end==1 ? "" : "s", end > 20 ? "..." : "",
inputBuffer.expand(-20)());
}
}
if (status == StreamIoTimeout && (flags & AsyncMode))
{
debug("StreamCore::readCallback(%s) async timeout: just restart\n",
name());
inputBuffer.clear();
commandIndex = commandStart;
evalIn();
return 0;
}
inputLine.set(inputBuffer(), end);
debug("StreamCore::readCallback(%s) input line: \"%s\"\n",
@ -1054,7 +1093,7 @@ readCallback(StreamIoStatus status,
return 0;
}
// end input mode and do next command
flags &= ~(AsyncMode|AcceptInput);
//// flags &= ~(AsyncMode|AcceptInput);
// -- should we tell someone that input has finished? --
evalCommand();
return 0;
@ -1127,14 +1166,21 @@ matchInput()
}
if (consumed < 0)
{
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
if (fmt.flags & default_flag)
{
error("%s: Input \"%s%s\" does not match format %%%s\n",
name(), inputLine.expand(consumedInput, 20)(),
inputLine.length()-consumedInput > 20 ? "..." : "",
formatstring);
consumed = 0;
}
else
{
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{
error("%s: Input \"%s%s\" does not match format %%%s\n",
name(), inputLine.expand(consumedInput, 20)(),
inputLine.length()-consumedInput > 20 ? "..." : "",
formatstring);
}
return false;
}
return false;
}
consumedInput += consumed;
break;
@ -1142,7 +1188,7 @@ matchInput()
flags &= ~Separator;
if (!matchValue(fmt, fieldAddress ? fieldAddress() : NULL))
{
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{
if (flags & ScanTried)
error("%s: Input \"%s%s\" does not match format %%%s\n",
@ -1172,7 +1218,7 @@ matchInput()
{
int i = 0;
while (commandIndex[i] >= ' ') i++;
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{
error("%s: Input \"%s%s\" too short."
" No match for \"%s\"\n",
@ -1185,7 +1231,7 @@ matchInput()
}
if (command != inputLine[consumedInput])
{
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{
int i = 0;
while (commandIndex[i] >= ' ') i++;
@ -1210,7 +1256,7 @@ matchInput()
long surplus = inputLine.length()-consumedInput;
if (surplus > 0 && !(flags & IgnoreExtraInput))
{
if ((!(flags & AsyncMode)||showAsyncErrors) && onMismatch[0] != in_cmd)
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{
error("%s: %ld byte%s surplus input \"%s%s\"\n",
name(), surplus, surplus==1 ? "" : "s",
@ -1284,8 +1330,16 @@ scanValue(const StreamFormat& fmt, long& value)
scanLong(fmt, inputLine(consumedInput), value);
debug("StreamCore::scanValue(%s, format=%%%c, long) input=\"%s\"\n",
name(), fmt.conv, inputLine.expand(consumedInput)());
if (consumed < 0 ||
consumed > inputLine.length()-consumedInput) return -1;
if (consumed < 0)
{
if (fmt.flags & default_flag)
{
value = 0;
consumed = 0;
}
else return -1;
}
if (consumed > inputLine.length()-consumedInput) return -1;
debug("StreamCore::scanValue(%s) scanned %li\n",
name(), value);
flags |= GotValue;
@ -1307,8 +1361,16 @@ scanValue(const StreamFormat& fmt, double& value)
scanDouble(fmt, inputLine(consumedInput), value);
debug("StreamCore::scanValue(%s, format=%%%c, double) input=\"%s\"\n",
name(), fmt.conv, inputLine.expand(consumedInput)());
if (consumed < 0 ||
consumed > inputLine.length()-consumedInput) return -1;
if (consumed < 0)
{
if (fmt.flags & default_flag)
{
value = 0.0;
consumed = 0;
}
else return -1;
}
if (consumed > inputLine.length()-consumedInput) return -1;
debug("StreamCore::scanValue(%s) scanned %#g\n",
name(), value);
flags |= GotValue;
@ -1331,8 +1393,16 @@ scanValue(const StreamFormat& fmt, char* value, long maxlen)
scanString(fmt, inputLine(consumedInput), value, maxlen);
debug("StreamCore::scanValue(%s, format=%%%c, char*, maxlen=%ld) input=\"%s\"\n",
name(), fmt.conv, maxlen, inputLine.expand(consumedInput)());
if (consumed < 0 ||
consumed > inputLine.length()-consumedInput) return -1;
if (consumed < 0)
{
if (fmt.flags & default_flag)
{
value[0] = 0;
consumed = 0;
}
else return -1;
}
if (consumed > inputLine.length()-consumedInput) return -1;
#ifndef NO_TEMPORARY
debug("StreamCore::scanValue(%s) scanned \"%s\"\n",
name(), StreamBuffer(value, maxlen).expand()());

View File

@ -41,6 +41,7 @@ extern "C" {
#include <semLib.h>
#include <wdLib.h>
#include <taskLib.h>
extern DBBASE *pdbbase;
@ -52,6 +53,7 @@ extern DBBASE *pdbbase;
#include <epicsMutex.h>
#include <epicsEvent.h>
#include <epicsTime.h>
#include <epicsThread.h>
#include <registryFunction.h>
#include <iocsh.h>
@ -76,10 +78,12 @@ epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
enum MoreFlags {
// 0x00FFFFFF used by StreamCore
InDestructor = 0x0100000,
ValueReceived = 0x0200000
ValueReceived = 0x0200000,
Aborted = 0x0400000
};
extern "C" void streamExecuteCommand(CALLBACK *pcallback);
extern "C" void streamRecordProcessCallback(CALLBACK *pcallback);
extern "C" long streamReload(char* recordname);
class Stream : protected StreamCore
@ -108,6 +112,7 @@ class Stream : protected StreamCore
long currentValueLength;
IOSCANPVT ioscanpvt;
CALLBACK commandCallback;
CALLBACK processCallback;
#ifdef EPICS_3_14
@ -118,7 +123,7 @@ class Stream : protected StreamCore
#endif
// StreamCore methods
// void protocolStartHook(); // Nothing to do here?
void protocolStartHook();
void protocolFinishHook(ProtocolResult);
void startTimer(unsigned long timeout);
bool getFieldAddress(const char* fieldname,
@ -131,6 +136,7 @@ class Stream : protected StreamCore
void releaseMutex();
bool execute();
friend void streamExecuteCommand(CALLBACK *pcallback);
friend void streamRecordProcessCallback(CALLBACK *pcallback);
// Stream Epics methods
long initRecord();
@ -280,12 +286,15 @@ epicsExportAddress(drvet, stream);
void streamEpicsPrintTimestamp(char* buffer, int size)
{
int tlen;
epicsTime tm = epicsTime::getCurrent();
tm.strftime(buffer, size, "%Y/%m/%d %H:%M:%S.%03f");
tlen = tm.strftime(buffer, size, "%Y/%m/%d %H:%M:%S.%03f");
sprintf(buffer+tlen, " %.*s", size-tlen-2, epicsThreadGetNameSelf());
}
#else
void streamEpicsPrintTimestamp(char* buffer, int size)
{
int tlen;
char* c;
TS_STAMP tm;
tsLocalTime (&tm);
@ -294,6 +303,8 @@ void streamEpicsPrintTimestamp(char* buffer, int size)
if (c) {
c[4] = 0;
}
tlen = strlen(buffer);
sprintf(buffer+tlen, " %.*s", size-tlen-2, taskName(0));
}
#endif
@ -310,7 +321,7 @@ report(int interest)
printf(" %s\n", interface.name());
++interface;
}
if (interest < 1) return OK;
printf(" registered converters:\n");
StreamFormatConverter* converter;
@ -323,7 +334,7 @@ report(int interest)
printf(" %%%c %s\n", c, converter->name());
}
}
Stream* pstream;
printf(" connected records:\n");
for (pstream = static_cast<Stream*>(first); pstream;
@ -490,8 +501,8 @@ long streamScanSep(dbCommon* record)
long streamScanfN(dbCommon* record, format_t *format,
void* value, size_t maxStringSize)
{
debug("streamScanfN(%s,format=%%%c,maxStringSize=%d)\n",
record->name, format->priv->conv, maxStringSize);
debug("streamScanfN(%s,format=%%%c,maxStringSize=%ld)\n",
record->name, format->priv->conv, (long)maxStringSize);
Stream* pstream = (Stream*)record->dpvt;
if (!pstream) return ERROR;
if (!pstream->scan(format, value, maxStringSize))
@ -525,6 +536,8 @@ Stream(dbCommon* _record, struct link *ioLink,
#endif
callbackSetCallback(streamExecuteCommand, &commandCallback);
callbackSetUser(this, &commandCallback);
callbackSetCallback(streamRecordProcessCallback, &processCallback);
callbackSetUser(this, &processCallback);
status = ERROR;
convert = DO_NOT_CONVERT;
ioscanpvt = NULL;
@ -561,14 +574,14 @@ initRecord()
// scan link parameters: filename protocol busname addr busparam
// It is safe to call this function again with different
// link text or different protocol file.
char filename[80];
char protocol[80];
char busname[80];
int addr = -1;
char busparam[80];
int n;
if (ioLink->type != INST_IO)
{
error("%s: Wrong link type %s\n", name(),
@ -790,6 +803,12 @@ expire(CALLBACK *pcallback)
// StreamCore virtual methods ////////////////////////////////////////////
void Stream::
protocolStartHook()
{
flags &= ~Aborted;
}
void Stream::
protocolFinishHook(ProtocolResult result)
{
@ -824,6 +843,7 @@ protocolFinishHook(ProtocolResult result)
status = CALC_ALARM;
break;
case Abort:
flags |= Aborted;
case Fault:
status = UDF_ALARM;
if (record->pact || record->scan == SCAN_IO_EVENT)
@ -845,28 +865,46 @@ protocolFinishHook(ProtocolResult result)
#endif
return;
}
if (record->pact || record->scan == SCAN_IO_EVENT)
{
debug("Stream::protocolFinishHook(stream=%s,result=%d) "
"processing record\n", name(), result);
// process record
// This will call streamReadWrite.
dbScanLock(record);
((DEVSUPFUN)record->rset->process)(record);
dbScanUnlock(record);
debug("Stream::protocolFinishHook(stream=%s,result=%d) done\n",
name(), result);
}
if (result != Abort && record->scan == SCAN_IO_EVENT)
{
// re-enable early input
flags |= AcceptInput;
}
if (record->pact || record->scan == SCAN_IO_EVENT)
{
// process record in callback thread to break possible recursion
callbackSetPriority(priority(), &processCallback);
callbackRequest(&processCallback);
}
}
void streamRecordProcessCallback(CALLBACK *pcallback)
{
Stream* pstream = static_cast<Stream*>(pcallback->user);
dbCommon* record = pstream->record;
// process record
// This will call streamReadWrite.
debug("streamRecordProcessCallback(%s) processing record\n",
pstream->name());
dbScanLock(record);
((DEVSUPFUN)record->rset->process)(record);
dbScanUnlock(record);
debug("streamRecordProcessCallback(%s) processing record done\n",
pstream->name());
if (record->scan == SCAN_IO_EVENT && !(pstream->flags & Aborted))
{
// restart protocol for next turn
debug("Stream::process(%s) restart async protocol\n",
name());
if (!startProtocol(StartAsync))
debug("streamRecordProcessCallback(%s) restart async protocol\n",
pstream->name());
if (!pstream->startProtocol(Stream::StartAsync))
{
error("%s: Can't restart \"I/O Intr\" protocol\n",
name());
pstream->name());
}
}
}
@ -1003,7 +1041,7 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
char* buffer;
int status;
const char* putfunc;
if (fieldaddress)
{
// Format like "%([record.]field)..." has requested to put value
@ -1157,11 +1195,11 @@ noMoreElements:
}
#ifdef EPICS_3_14
// Pass command to iocsh
void streamExecuteCommand(CALLBACK *pcallback)
{
Stream* pstream = static_cast<Stream*>(pcallback->user);
if (iocshCmd(pstream->outputLine()) != OK)
{
pstream->execCallback(StreamIoFault);
@ -1172,12 +1210,13 @@ void streamExecuteCommand(CALLBACK *pcallback)
}
}
#else
extern "C" int execute (const char *cmd);
// Pass command to vxWorks shell
extern "C" int execute(const char *cmd);
void streamExecuteCommand(CALLBACK *pcallback)
{
Stream* pstream = static_cast<Stream*>(pcallback->user);
if (execute(pstream->outputLine()) != OK)
{
pstream->execCallback(StreamIoFault);

View File

@ -23,12 +23,13 @@
#define StreamFormat_h
typedef enum {
left_flag = 0x01,
sign_flag = 0x02,
space_flag = 0x04,
alt_flag = 0x08,
zero_flag = 0x10,
skip_flag = 0x20
left_flag = 0x01,
sign_flag = 0x02,
space_flag = 0x04,
alt_flag = 0x08,
zero_flag = 0x10,
skip_flag = 0x20,
default_flag = 0x40
} StreamFormatFlag;
typedef enum {

View File

@ -112,7 +112,7 @@ static void copyFormatString(StreamBuffer& info, const char* source)
const char* p = source - 1;
while (*p != '%' && *p != ')') p--;
info.append('%');
while (++p != source-1) info.append(*p);
while (++p != source-1) if (*p != '?') info.append(*p);
}
// Standard Long Converter for 'diouxX'
@ -160,7 +160,9 @@ scanLong(const StreamFormat& fmt, const char* input, long& value)
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
/* can't use return value on vxWorks: sscanf with %* format
returns -1 at end of string whether value is found or not */
sscanf(input, fmt.info, &length);
}
else
{
@ -216,7 +218,9 @@ scanDouble(const StreamFormat& fmt, const char* input, double& value)
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
/* can't use return value on vxWorks: sscanf with %* format
returns -1 at end of string whether value is found or not */
sscanf(input, fmt.info, &length);
}
else
{
@ -274,12 +278,14 @@ scanString(const StreamFormat& fmt, const char* input,
if (*input == '\0')
{
// match empty string
value[0] = '\0';
if (value) value[0] = '\0';
return 0;
}
if (fmt.flags & skip_flag)
{
if (sscanf(input, fmt.info, &length) < 0) return -1;
/* can't use return value on vxWorks: sscanf with %* format
returns -1 at end of string whether value is found or not */
sscanf(input, fmt.info, &length);
}
else
{
@ -288,7 +294,7 @@ scanString(const StreamFormat& fmt, const char* input,
if (maxlen <= fmt.width || fmt.width == 0)
{
// assure not to read too much
sprintf(tmpformat, "%%%d%c%%n", maxlen-1, fmt.conv);
sprintf(tmpformat, "%%%ld%c%%n", (long)maxlen-1, fmt.conv);
f = tmpformat;
}
else
@ -406,7 +412,9 @@ scanString(const StreamFormat& fmt, const char* input,
int length = -1;
if (fmt.flags & skip_flag)
{
if (sscanf (input, fmt.info, &length) < 0) return -1;
/* can't use return value on vxWorks: sscanf with %* format
returns -1 at end of string whether value is found or not */
sscanf(input, fmt.info, &length);
}
else
{
@ -416,7 +424,7 @@ scanString(const StreamFormat& fmt, const char* input,
{
const char *p = strchr (fmt.info, '[');
// assure not to read too much
sprintf(tmpformat, "%%%d%s", maxlen-1, p);
sprintf(tmpformat, "%%%ld%s", (long)maxlen-1, p);
f = tmpformat;
}
else

View File

@ -1255,7 +1255,7 @@ compileString(StreamBuffer& buffer, const char*& source,
// so that extra information is ready for format converter
if (numFormats+1 == sizeof(formatPos))
{
errorMsg(line, "Too many formats in line");
errorMsg(line, "Max 20 formats allowed in one protocol line");
return false;
}
formatPos[numFormats++]=buffer.length();
@ -1504,6 +1504,16 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
}
streamFormat.flags |= skip_flag;
break;
case '?':
if (formatType != ScanFormat)
{
errorMsg(line,
"Use of default modifier '?' "
"only allowed in input formats\n");
return false;
}
streamFormat.flags |= default_flag;
break;
default:
loop = false;
}

View File

@ -22,7 +22,7 @@
#define devStream_h
#define STREAM_MAJOR 2
#define STREAM_MINOR 3
#define STREAM_MINOR 4
#if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h>

View File

@ -25,12 +25,12 @@
static long readData (dbCommon *record, format_t *format)
{
aiRecord *ai = (aiRecord *) record;
double val;
switch (format->type)
{
case DBF_DOUBLE:
{
double val;
if (streamScanf (record, format, &val)) return ERROR;
if (ai->aslo != 0.0) val *= ai->aslo;
val += ai->aoff;
@ -43,7 +43,10 @@ static long readData (dbCommon *record, format_t *format)
}
case DBF_LONG:
{
return streamScanf (record, format, &ai->rval);
long rval;
if (streamScanf (record, format, &rval)) return ERROR;
ai->rval = rval;
return OK;
}
}
return ERROR;
@ -64,7 +67,7 @@ static long writeData (dbCommon *record, format_t *format)
}
case DBF_LONG:
{
return streamPrintf (record, format, ai->rval);
return streamPrintf (record, format, (long) ai->rval);
}
}
return ERROR;

View File

@ -51,12 +51,12 @@ static long readData (dbCommon *record, format_t *format)
static long writeData (dbCommon *record, format_t *format)
{
aoRecord *ao = (aoRecord *) record;
double val;
switch (format->type)
{
case DBF_DOUBLE:
{
double val;
if (INIT_RUN) val = ao->val;
else val = ao->oval;
val -= ao->aoff;
@ -65,7 +65,7 @@ static long writeData (dbCommon *record, format_t *format)
}
case DBF_LONG:
{
return streamPrintf (record, format, ao->rval);
return streamPrintf (record, format, (long) ao->rval);
}
}
return ERROR;

View File

@ -28,7 +28,10 @@ static long readData (dbCommon *record, format_t *format)
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamScanf (record, format, &li->val);
long val;
if (streamScanf (record, format, &val)) return ERROR;
li->val = val;
return OK;
}
return ERROR;
}
@ -39,7 +42,7 @@ static long writeData (dbCommon *record, format_t *format)
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamPrintf (record, format, li->val);
return streamPrintf (record, format, (long) li->val);
}
return ERROR;
}

View File

@ -29,7 +29,10 @@ static long readData (dbCommon *record, format_t *format)
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamScanf (record, format, &lo->val);
long val;
if (streamScanf (record, format, &val)) return ERROR;
lo->val = val;
return OK;
}
return ERROR;
}
@ -40,7 +43,7 @@ static long writeData (dbCommon *record, format_t *format)
if (format->type == DBF_LONG || format->type == DBF_ENUM)
{
return streamPrintf (record, format, lo->val);
return streamPrintf (record, format, (long) lo->val);
}
return ERROR;
}