Compare commits
38 Commits
stream_2_3
...
stream_2_4
Author | SHA1 | Date | |
---|---|---|---|
2cd969d643 | |||
0c9d4dc4c0 | |||
ef3b2ec9fd | |||
1fb402e838 | |||
eae987e7f1 | |||
93906dd022 | |||
7e717c564d | |||
14b60c210a | |||
8dae0afa30 | |||
95bf2c78c2 | |||
8c10e5395c | |||
69de0b9f31 | |||
22ae510dca | |||
5fdf3912c8 | |||
b50aec424f | |||
4a1033b6dc | |||
68e178d41b | |||
e83ceda41c | |||
b307adb005 | |||
0a2a304a2b | |||
80477709aa | |||
706c59d069 | |||
6541c40818 | |||
04f64c0f67 | |||
af669103c2 | |||
40d9641150 | |||
9cf2770440 | |||
616bc9a410 | |||
53abbb750b | |||
ec3cbbf559 | |||
8a3cbc1c6a | |||
8a4cda6647 | |||
2d513b96d3 | |||
6a0d7b4492 | |||
755df7c3ee | |||
ddb602196a | |||
0a5f486a9b | |||
574052dcb8 |
5
MODULE
Normal file
5
MODULE
Normal file
@ -0,0 +1,5 @@
|
||||
# Please change the following email with yours.
|
||||
Email: dirk.zimoch@psi.ch
|
||||
Module-Name: StreamDevice2
|
||||
Description: StreamDevice2
|
||||
Project-Name:
|
14
Makefile
14
Makefile
@ -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)" >> $@
|
||||
|
@ -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><title></code> tag and leaves anything after the
|
||||
<code></title></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>
|
||||
|
@ -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">%<<em>checksum</em>></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">
|
||||
|
@ -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:
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
|
@ -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++;
|
||||
|
@ -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
|
||||
|
@ -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");
|
||||
|
@ -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
136
src/RawFloatConverter.cc
Normal 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");
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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);}
|
||||
|
||||
|
@ -20,7 +20,11 @@
|
||||
#include "StreamBusInterface.h"
|
||||
|
||||
const char* StreamIoStatusStr[] = {
|
||||
"StreamIoSuccess", "ioTimeout", "ioNoReply", "ioEnd", "ioFault"
|
||||
"StreamIoSuccess",
|
||||
"StreamIoTimeout",
|
||||
"StreamIoNoReply",
|
||||
"StreamIoEnd",
|
||||
"StreamIoFault"
|
||||
};
|
||||
|
||||
StreamBusInterfaceRegistrarBase* StreamBusInterfaceRegistrarBase::first;
|
||||
|
@ -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()());
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user