Compare commits

...

39 Commits

Author SHA1 Message Date
e5dfe7b816 version update 2012-08-30 13:41:24 +00:00
6efb17be64 remove warning 2012-08-30 13:39:05 +00:00
0310b30446 Fixes for 3.15 2012-08-30 13:37:25 +00:00
863cdf3d7f avoid multiple inclusion of asyn.dbd 2012-08-22 12:33:20 +00:00
e51bb2555e Adjustments for 3.15: test for 3.13 instead of 3.14 2012-08-22 11:49:43 +00:00
9e0157d679 link pointer is const 2012-08-09 12:22:50 +00:00
3e91eb4fc1 Link parsing in a separate function 2012-08-09 12:22:32 +00:00
caf811c23a Improved debug message, more digits in debug timestamp. 2012-08-09 08:38:05 +00:00
de116c9e5a Dummy-read stale input instead of flushing it in order to make it available for I/O Intr records. 2012-08-09 08:37:08 +00:00
eef38b0557 check if businterface exists before calling its methods 2012-08-08 09:44:18 +00:00
0057899476 missing header 2012-08-07 14:19:51 +00:00
0148e6e27e version update 2012-08-07 14:17:46 +00:00
c6af859f9d no eol translation 2012-07-06 11:19:06 +00:00
01f0761ca8 Fixes for Windows: dll import and export 2012-07-02 16:25:55 +00:00
d7770846aa fixes for asyn 4.11+ (queueRequest fails on disconnected ports) 2012-06-19 13:45:38 +00:00
0e76ba6464 reading web pages 2012-05-16 15:53:06 +00:00
fcb44ec845 anchor for redirections 2012-05-16 15:52:43 +00:00
cec8195e5e always escape special chars and bytes 2012-05-16 14:24:21 +00:00
6c0eb32d75 Print helpful message when matching string is too long 2012-05-16 14:23:43 +00:00
9ae5f1d583 Use disconnect by server as a regular terminator (e.g. web pages) 2012-05-16 14:23:15 +00:00
3b60bb9420 put patchlevel into version string 2012-05-16 09:59:48 +00:00
db50c35ad6 get correct version of stream library 2012-05-15 15:52:29 +00:00
4f05d4e254 added constant "%" test 2012-05-15 15:51:47 +00:00
c5a17b217d high PRIO for command records 2012-05-15 15:50:59 +00:00
fdccb0c5a9 add "binary" command, update help, GUI with exit button. 2012-05-15 15:49:41 +00:00
ccbafef55a rework number parsing. make sure '%' in any numeric format gets escaped. 2012-05-15 15:37:21 +00:00
c594134317 more debug 2012-05-15 13:58:03 +00:00
11cfaf44ee use perl script instead of bash commands to create dbd file 2012-05-15 13:37:19 +00:00
a34678cbcc for 3.13 don't create variable and registrar entries 2012-05-15 13:36:14 +00:00
26fd99823c use a StreamBuffer to hold format string for later error and debug messages 2012-05-15 11:44:41 +00:00
42c305c79b Skip leading whitespaces only if neither 0 nor 1 are whitespaces. 2012-05-15 09:20:22 +00:00
9585506644 debug messages improved 2012-05-15 09:14:34 +00:00
4317ae0f75 forgot to update minot version 2012-03-21 08:45:31 +00:00
6e6c79f5ea implement decimal checksum output 2012-02-17 10:28:02 +00:00
8da0014091 need to rename StreamBuffer method printf to print because on cris architecture printf is a macro. 2012-01-04 15:49:53 +00:00
60d292b3ea calcout support was missing 2011-10-18 14:54:35 +00:00
5ce9b737cf remove compiler warning 2011-10-13 15:42:00 +00:00
7bcb494044 allow checksums in decimal ascii 2011-10-13 15:39:41 +00:00
13758eb08e bugfix: too high array index 2011-10-13 15:37:28 +00:00
31 changed files with 646 additions and 384 deletions

View File

@ -132,6 +132,7 @@ Thus, data type modifiers like <code>l</code> or <code>h</code> do not
exist in <em>StreamDevice</em> formats. exist in <em>StreamDevice</em> formats.
</p> </p>
<a name="redirection"></a>
<h3>Redirection</h3> <h3>Redirection</h3>
<p> <p>
To use other fields of the record or even fields of other records on the To use other fields of the record or even fields of other records on the

View File

@ -178,6 +178,7 @@ div div div a {list-style-type:circle;}
<a target="_parent" href="tipsandtricks.html#writemany">Write more than one value in one message</a> <a target="_parent" href="tipsandtricks.html#writemany">Write more than one value in one message</a>
<a target="_parent" href="tipsandtricks.html#readmany">Read more than one value from one message</a> <a target="_parent" href="tipsandtricks.html#readmany">Read more than one value from one message</a>
<a target="_parent" href="tipsandtricks.html#mixed">Read values of mixed data type</a> <a target="_parent" href="tipsandtricks.html#mixed">Read values of mixed data type</a>
<a target="_parent" href="tipsandtricks.html#web">Read a web page</a>
</div> </div>
</div> </div>

View File

@ -103,7 +103,7 @@ an array: (3.14, 17.30, -12.34)
<h3>B) We have up to 12 numeric values</h3> <h3>B) We have up to 12 numeric values</h3>
<p> <p>
Use a <a href="calcout.html">calcout</a> record and Use a <a href="calcout.html">calcout</a> record and
<a href="formats.html#types">field references</a> in the format. <a href="formats.html#redirection">redirection to fields</a>.
</p> </p>
<p> <p>
<code> <code>
@ -128,7 +128,7 @@ record (calcout, "$(RECORD)") {<br>
</p> </p>
<h3>C) Values are in other records on the same IOC</h3> <h3>C) Values are in other records on the same IOC</h3>
<p> <p>
Use <a href="formats.html#types">record references</a> in the format. Use <a href="formats.html#redirection">redirection to records</a>.
</p> </p>
<p> <p>
<code> <code>
@ -216,7 +216,7 @@ Any non-matching input is ignored by record B.
</p> </p>
<h3>C) Values should be stored in other records on the same IOC</h3> <h3>C) Values should be stored in other records on the same IOC</h3>
<p> <p>
Use <a href="formats.html#types">record references</a> in the format. Use <a href="formats.html#redirection">redirection to records</a>.
To avoid record names in protocol files, use To avoid record names in protocol files, use
<a href="protocol.html#argvar">protocol arguments</a>. <a href="protocol.html#argvar">protocol arguments</a>.
</p> </p>
@ -244,11 +244,11 @@ processes record B.
</p> </p>
<a name="mixed"></a> <a name="mixed"></a>
<h2>I have a device that sends mixed data types: numbers and strings</h2> <h2>I have a device that sends mixed data types: numbers or strings</h2>
<p> <p>
Use a <code>@mismatch</code> Use a <code>@mismatch</code>
<a href="protocol.html#except">exception handler</a> and <a href="protocol.html#except">exception handler</a> and
<a href="formats.html#types">record references</a> in the format. <a href="formats.html#redirection">redirection to records</a>.
To avoid record names in protocol files, use To avoid record names in protocol files, use
<a href="protocol.html#argvar">protocol arguments</a>. <a href="protocol.html#argvar">protocol arguments</a>.
</p> </p>
@ -289,9 +289,124 @@ record (stringout, "$(DEVICE):clean_2") {<br>
&nbsp;&nbsp;field (VAL, "OK")<br> &nbsp;&nbsp;field (VAL, "OK")<br>
&nbsp;&nbsp;field (OUT, "$(DEVICE):message PP")<br> &nbsp;&nbsp;field (OUT, "$(DEVICE):message PP")<br>
}<br> }<br>
</code> </code>
<a name="web"></a>
<h2>I need to read a web page</h2>
<p>
First you have to send a correctly formatted HTML request.
Note that this request must contain the full URL like
"http://server/page" and must be terminated with <u>two</u> newlines.
The server should be the same as in the
<a href="setup.html#sta"><code>drvAsynIPPortConfigure</code></a>
command (if not using a http proxy).
The web page you get often contains much more information than you need.
<a href="formats.html#regex">Regular expressions</a> are great
to find what you are looking for.
</p>
<h3>Example 1</h3>
<p>
Read the title of a web page.
</p>
<p>
<code>
get_title {<br>
&nbsp;&nbsp;extrainput = ignore;<br>
&nbsp;&nbsp;replyTimeout = 1000;<br>
&nbsp;&nbsp;out "GET http://\$1\n\n";<br>
&nbsp;&nbsp;in "%+.1/(?im)&lt;title&gt(.*)&lt\/title&gt;/";<br>
}
</code>
</p>
<p>
Terminate the request with two newlines, either explicit like here
<u>or</u> using an
<a href="protocol.html#sysvar"><code>outTerminator</code></a>.
The URI (without http:// but including the web server host name)
is passed as <a href="protocol.html#argvar">argument</a> 1 to <code>\$1</code>.
Note that web servers may be slow, so allow some
<a href="protocol.html#argvar"><code>replyTimeout</code></a>.
</p>
<p>
If you don't use an <code>inTerminator</code> then the whole page is
read as one "line" to the <code>in</code> command and can be parsed easily
with a regular expression.
We want to see the string between <code>&lt;title&gt</code> and
<code>&lt;/title&gt;</code>, so we put it into a subexpression in
<code>()</code> and request the first subexpression with <code>.1</code>.
Note that the <code>/</code> in the closing tag has be be escaped
to avoid a misinterpretation as the closing <code>/</code> of the regular
expression.
</p>
<p>
The tags may be upper or lower case like <code>&lt;TITLE&gt;</code> or
<code>&lt;Title&gt;</code>, so we ask for case insensitive matching with
<code>(?i)</code>.
</p>
<p>
The string should be terminated with the first closing
<code>&lt;/title&gt;</code>, not the last one in the file.
(There should not be more than one title but you never know.)
Thus we ask not to be greedy with <code>(?m)</code>.
<code>(?i)</code> and <code>(?m)</code> can be combined to <code>(?im)</code>.
See the PCRE documentation for more regexp syntax.
</p>
<p>
The regular expression matcher ignores and discards any content before the
matching section.
Content after the match is discarded with <code>extrainput = ignore</code>
so that it does not trigger errors reporting "surplus input".
</p>
<p>
Finally, the title may be too long for the record.
The <code>+</code> tells the format matcher not to fail in this case
but to truncate the string instead.
You can read the string with a stringin record or for longer strings with
a waveform record with data type CHAR.
</p>
<p>
<code>
record (stringin, "$(DEVICE):title") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@$(DEVICETYPE).proto get_title($(PAGE)) $(BUS)")<br>
}<br>
record (waveform, "$(DEVICE):longtitle") {<br>
&nbsp;&nbsp;field (DTYP, "stream")<br>
&nbsp;&nbsp;field (INP, "@$(DEVICETYPE).proto get_title($(PAGE)) $(BUS)")<br>
&nbsp;&nbsp;field (FTVL, "CHAR")<br>
&nbsp;&nbsp;field (NELM, "100")<br>
}<br>
</code>
</p>
<h3>Example 2</h3>
<p>
Read a number from a web page. First we have to locate the number.
For that we match against any known string right before the number
(and <a href="formats.html#syntax">discard the match</a> with <code>*</code>).
Then we read the number.
</p>
<code>
get_title {<br>
&nbsp;&nbsp;extrainput = ignore;<br>
&nbsp;&nbsp;replyTimeout = 1000;<br>
&nbsp;&nbsp;out "GET http://\$1\n\n";<br>
&nbsp;&nbsp;in "%*/Interesting value:/%f more text";<br>
}
</code>
<p>
When using <code>extrainput = ignore;</code>, it is always a good idea to
match a few bytes after the value, too.
This catches errors where loading of the page is interrupted in the middle
of the number. (You don't want to miss the exponent from something like 1.23E-14).
</p>
<p>
You can read more than one value from a file with successive regular expressions
and <a href="formats.html#redirection">redirections</a>.
But this only works if the order of the values is predictible.
<i>StreamDevice</i> is not an XML parser! It always reads sequentially.
</p>
<hr> <hr>
<p><small>Dirk Zimoch, 2007</small></p> <p><small>Dirk Zimoch, 2012</small></p>
</body> </body>
</html> </html>

View File

@ -34,8 +34,8 @@ SOURCES += $(FORMATS:%=src/%Converter.cc)
SOURCES += $(BUSSES:%=src/%Interface.cc) SOURCES += $(BUSSES:%=src/%Interface.cc)
SOURCES += $(wildcard src/Stream*.cc) SOURCES += $(wildcard src/Stream*.cc)
SOURCES += src/StreamVersion.c SOURCES += src/StreamVersion.c
SOURCES_3.14 += src/devcalcoutStream.c
HEADERS += devStream.h
HEADERS += StreamFormat.h HEADERS += StreamFormat.h
HEADERS += StreamFormatConverter.h HEADERS += StreamFormatConverter.h
HEADERS += StreamBuffer.h HEADERS += StreamBuffer.h
@ -44,6 +44,9 @@ HEADERS += StreamError.h
ifeq (${EPICS_BASETYPE},3.13) ifeq (${EPICS_BASETYPE},3.13)
USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h USR_INCLUDES += -include $(INSTALL_INCLUDE)/compat3_13.h
endif endif
ifeq (${EPICS_BASETYPE},3.14)
RECORDTYPES += calcout
endif
StreamCore.o: streamReferences StreamCore.o: streamReferences

View File

@ -22,14 +22,19 @@
#include "StreamError.h" #include "StreamError.h"
#include "StreamBuffer.h" #include "StreamBuffer.h"
#ifdef EPICS_3_14 #include <epicsVersion.h>
#include <epicsAssert.h> #ifdef BASE_VERSION
#include <epicsTime.h> #define EPICS_3_13
#include <epicsTimer.h> #endif
#else
#ifdef EPICS_3_13
#include <assert.h> #include <assert.h>
#include <wdLib.h> #include <wdLib.h>
#include <sysLib.h> #include <sysLib.h>
#else
#include <epicsAssert.h>
#include <epicsTime.h>
#include <epicsTimer.h>
extern "C" { extern "C" {
#include <callback.h> #include <callback.h>
} }
@ -126,7 +131,7 @@ static const char* ioActionStr[] = {
}; };
static const char* asynStatusStr[] = { static const char* asynStatusStr[] = {
"asynSuccess", "asynTimeout", "asynOverflow", "asynError" "asynSuccess", "asynTimeout", "asynOverflow", "asynError", "asynDisconnected", "asynDisabled"
}; };
static const char* eomReasonStr[] = { static const char* eomReasonStr[] = {
@ -134,7 +139,7 @@ static const char* eomReasonStr[] = {
}; };
class AsynDriverInterface : StreamBusInterface class AsynDriverInterface : StreamBusInterface
#ifdef EPICS_3_14 #ifndef EPICS_3_13
, epicsTimerNotify , epicsTimerNotify
#endif #endif
{ {
@ -164,12 +169,12 @@ class AsynDriverInterface : StreamBusInterface
const char* outputBuffer; const char* outputBuffer;
size_t outputSize; size_t outputSize;
int peeksize; int peeksize;
#ifdef EPICS_3_14 #ifdef EPICS_3_13
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
#else
WDOG_ID timer; WDOG_ID timer;
CALLBACK timeoutCallback; CALLBACK timeoutCallback;
#else
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
#endif #endif
AsynDriverInterface(Client* client); AsynDriverInterface(Client* client);
@ -189,11 +194,11 @@ class AsynDriverInterface : StreamBusInterface
bool disconnectRequest(); bool disconnectRequest();
void finish(); void finish();
#ifdef EPICS_3_14 #ifdef EPICS_3_13
static void expire(CALLBACK *pcallback);
#else
// epicsTimerNotify methods // epicsTimerNotify methods
epicsTimerNotify::expireStatus expire(const epicsTime &); epicsTimerNotify::expireStatus expire(const epicsTime &);
#else
static void expire(CALLBACK *pcallback);
#endif #endif
// local methods // local methods
@ -211,20 +216,22 @@ class AsynDriverInterface : StreamBusInterface
(StreamBusInterface::priority()); (StreamBusInterface::priority());
} }
void startTimer(double timeout) { void startTimer(double timeout) {
#ifdef EPICS_3_14 #ifdef EPICS_3_13
timer->start(*this, timeout);
#else
callbackSetPriority(priority(), &timeoutCallback); callbackSetPriority(priority(), &timeoutCallback);
wdStart(timer, (int)((timeout+1)*sysClkRateGet())-1, wdStart(timer, (int)((timeout+1)*sysClkRateGet())-1,
reinterpret_cast<FUNCPTR>(callbackRequest), reinterpret_cast<FUNCPTR>(callbackRequest),
reinterpret_cast<int>(&timeoutCallback)); reinterpret_cast<int>(&timeoutCallback));
#else
timer->start(*this, timeout
+epicsThreadSleepQuantum()*0.5
);
#endif #endif
} }
void cancelTimer() { void cancelTimer() {
#ifdef EPICS_3_14 #ifdef EPICS_3_13
timer->cancel();
#else
wdCancel(timer); wdCancel(timer);
#else
timer->cancel();
#endif #endif
} }
@ -263,15 +270,15 @@ AsynDriverInterface(Client* client) : StreamBusInterface(client)
handleTimeout); handleTimeout);
assert(pasynUser); assert(pasynUser);
pasynUser->userPvt = this; pasynUser->userPvt = this;
#ifdef EPICS_3_14 #ifdef EPICS_3_13
timer = wdCreate();
callbackSetCallback(expire, &timeoutCallback);
callbackSetUser(this, &timeoutCallback);
#else
timerQueue = &epicsTimerQueueActive::allocate(true); timerQueue = &epicsTimerQueueActive::allocate(true);
assert(timerQueue); assert(timerQueue);
timer = &timerQueue->createTimer(); timer = &timerQueue->createTimer();
assert(timer); assert(timer);
#else
timer = wdCreate();
callbackSetCallback(expire, &timeoutCallback);
callbackSetUser(this, &timeoutCallback);
#endif #endif
} }
@ -306,11 +313,11 @@ AsynDriverInterface::
} }
// Now, no handler is running any more and none will start. // Now, no handler is running any more and none will start.
#ifdef EPICS_3_14 #ifdef EPICS_3_13
wdDelete(timer);
#else
timer->destroy(); timer->destroy();
timerQueue->release(); timerQueue->release();
#else
wdDelete(timer);
#endif #endif
pasynManager->disconnect(pasynUser); pasynManager->disconnect(pasynUser);
pasynManager->freeAsynUser(pasynUser); pasynManager->freeAsynUser(pasynUser);
@ -516,7 +523,7 @@ connectToAsynPort()
status = pasynOctet->read(pvtOctet, pasynUser, status = pasynOctet->read(pvtOctet, pasynUser,
buffer, 0, &received, &eomReason); buffer, 0, &received, &eomReason);
debug("AsynDriverInterface::connectToAsynPort(%s): " debug("AsynDriverInterface::connectToAsynPort(%s): "
"read(..., 0, ...) [timeout=%f seconds] = %s\n", "read(..., 0, ...) [timeout=%g sec] = %s\n",
clientName(), pasynUser->timeout, clientName(), pasynUser->timeout,
asynStatusStr[status]); asynStatusStr[status]);
pasynManager->isConnected(pasynUser, &connected); pasynManager->isConnected(pasynUser, &connected);
@ -610,19 +617,26 @@ writeHandler()
clientName()); clientName());
asynStatus status; asynStatus status;
size_t written = 0; size_t written = 0;
pasynUser->timeout = writeTimeout;
// discard any early input or early events // discard any early input, but forward it to potential async records
status = pasynOctet->flush(pvtOctet, pasynUser); // thus do not use pasynOctet->flush()
pasynUser->timeout = 0;
do {
char buffer [256];
size_t received = sizeof(buffer);
int eomReason = 0;
status = pasynOctet->read(pvtOctet, pasynUser,
buffer, received, &received, &eomReason);
#ifndef NO_TEMPORARY
if (received) debug("AsynDriverInterface::writeHandler(%s): flushing %ld bytes: \"%s\"\n",
clientName(), (long)received, StreamBuffer(buffer, received).expand()());
#endif
} while (status != asynTimeout);
// discard any early events
receivedEvent = 0; receivedEvent = 0;
if (status != asynSuccess) pasynUser->timeout = writeTimeout;
{
error("%s: pasynOctet->flush() failed: %s\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
}
// has stream already added a terminator or should // has stream already added a terminator or should
// asyn do so? // asyn do so?
@ -648,7 +662,7 @@ writeHandler()
outputBuffer, outputSize, &written); outputBuffer, outputSize, &written);
debug("AsynDriverInterface::writeHandler(%s): " debug("AsynDriverInterface::writeHandler(%s): "
"write(..., outputSize=%ld, written=%ld) " "write(..., outputSize=%ld, written=%ld) "
"[timeout=%f seconds] = %s\n", "[timeout=%g sec] = %s\n",
clientName(), (long)outputSize, (long)written, clientName(), (long)outputSize, (long)written,
pasynUser->timeout, asynStatusStr[status]); pasynUser->timeout, asynStatusStr[status]);
@ -732,7 +746,7 @@ readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
long _expectedLength, bool async) long _expectedLength, bool async)
{ {
debug("AsynDriverInterface::readRequest(%s, %ld msec reply, " debug("AsynDriverInterface::readRequest(%s, %ld msec reply, "
"%ld msec read, expect %ld bytes, asyn=%s)\n", "%ld msec read, expect %ld bytes, async=%s)\n",
clientName(), replyTimeout_ms, readTimeout_ms, clientName(), replyTimeout_ms, readTimeout_ms,
_expectedLength, async?"yes":"no"); _expectedLength, async?"yes":"no");
@ -756,8 +770,20 @@ readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
} }
status = pasynManager->queueRequest(pasynUser, status = pasynManager->queueRequest(pasynUser,
priority(), queueTimeout); priority(), queueTimeout);
if (status != asynSuccess && !async) debug("AsynDriverInterface::readRequest %s: "
"queueRequest(..., priority=%d, queueTimeout=%g sec) = %s [async=%s] %s\n",
clientName(), priority(), queueTimeout,
asynStatusStr[status], async? "true" : "false",
status!=asynSuccess ? pasynUser->errorMessage : "");
if (status != asynSuccess)
{ {
// Not queued for some reason (e.g. disconnected / already queued)
if (async)
{
// silently try again later
startTimer(replyTimeout);
return true;
}
error("%s readRequest: pasynManager->queueRequest() failed: %s\n", error("%s readRequest: pasynManager->queueRequest() failed: %s\n",
clientName(), pasynUser->errorMessage); clientName(), pasynUser->errorMessage);
return false; return false;
@ -863,27 +889,26 @@ readHandler()
received = 0; received = 0;
eomReason = 0; eomReason = 0;
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
"read(..., bytesToRead=%ld, ...) "
"[timeout=%g sec]\n",
clientName(), ioActionStr[ioAction],
bytesToRead, pasynUser->timeout);
status = pasynOctet->read(pvtOctet, pasynUser, status = pasynOctet->read(pvtOctet, pasynUser,
buffer, bytesToRead, &received, &eomReason); buffer, bytesToRead, &received, &eomReason);
if (ioAction == Read || status != asynTimeout) debug("AsynDriverInterface::readHandler(%s): "
{ "read returned %s: ioAction=%s received=%ld, eomReason=%s, buffer=\"%s\"\n",
debug("AsynDriverInterface::readHandler(%s): " clientName(), asynStatusStr[status], ioActionStr[ioAction],
"read(..., bytesToRead=%ld, received=%ld...) " (long)received,eomReasonStr[eomReason&0x7],
"[timeout=%f seconds] = %s\n", StreamBuffer(buffer, received).expand()());
clientName(), bytesToRead, (long)received,
pasynUser->timeout, asynStatusStr[status]);
}
pasynManager->isConnected(pasynUser, &connected); pasynManager->isConnected(pasynUser, &connected);
debug("AsynDriverInterface::readHandler(%s): " debug("AsynDriverInterface::readHandler(%s): "
"device is %sconnected\n", "device is now %sconnected\n",
clientName(),connected?"":"dis"); clientName(),connected?"":"dis");
if (!connected) { // asyn 4.16 sets reason to ASYN_EOM_END when device disconnects.
error("%s: connection closed in read\n", // What about earlier versions?
clientName()); if (!connected) eomReason |= ASYN_EOM_END;
readCallback(StreamIoFault);
return;
}
// pasynOctet->read() has already cut off terminator.
if (status == asynTimeout && if (status == asynTimeout &&
pasynUser->timeout == 0.0 && pasynUser->timeout == 0.0 &&
@ -910,6 +935,7 @@ readHandler()
#endif #endif
// ignore what we got from here. // ignore what we got from here.
// input was already handeled by asynReadHandler() // input was already handeled by asynReadHandler()
// read until no more input is available // read until no more input is available
readMore = -1; readMore = -1;
break; break;
@ -959,6 +985,9 @@ readHandler()
// reply timeout // reply timeout
if (ioAction == AsyncRead) if (ioAction == AsyncRead)
{ {
debug("AsynDriverInterface::readHandler(%s): "
"no async input, retry in in %g seconds\n",
clientName(), replyTimeout);
// start next poll after timer expires // start next poll after timer expires
if (replyTimeout != 0.0) startTimer(replyTimeout); if (replyTimeout != 0.0) startTimer(replyTimeout);
// continues with: // continues with:
@ -975,7 +1004,7 @@ readHandler()
// read timeout // read timeout
#ifndef NO_TEMPORARY #ifndef NO_TEMPORARY
debug("AsynDriverInterface::readHandler(%s): " debug("AsynDriverInterface::readHandler(%s): "
"ioAction=%s, timeout [%f seconds] " "ioAction=%s, timeout [%g sec] "
"after %ld of %ld bytes \"%s\"\n", "after %ld of %ld bytes \"%s\"\n",
clientName(), ioActionStr[ioAction], pasynUser->timeout, clientName(), ioActionStr[ioAction], pasynUser->timeout,
(long)received, bytesToRead, (long)received, bytesToRead,
@ -1236,6 +1265,9 @@ timerExpired()
int autoconnect, connected; int autoconnect, connected;
switch (ioAction) switch (ioAction)
{ {
case None:
// Timeout of async poll crossed with parasitic input
return;
case ReceiveEvent: case ReceiveEvent:
// timeout while waiting for event // timeout while waiting for event
ioAction = None; ioAction = None;
@ -1254,6 +1286,8 @@ timerExpired()
// queueRequest might fail if another request was just queued // queueRequest might fail if another request was just queued
pasynManager->isAutoConnect(pasynUser, &autoconnect); pasynManager->isAutoConnect(pasynUser, &autoconnect);
pasynManager->isConnected(pasynUser, &connected); pasynManager->isConnected(pasynUser, &connected);
debug("%s: polling for I/O Intr: autoconnected: %d, connect: %d\n",
clientName(), autoconnect, connected);
if (autoconnect && !connected) if (autoconnect && !connected)
{ {
// has explicitely been disconnected // has explicitely been disconnected
@ -1264,8 +1298,14 @@ timerExpired()
else else
{ {
// queue for read poll (no timeout) // queue for read poll (no timeout)
pasynManager->queueRequest(pasynUser, asynStatus status = pasynManager->queueRequest(pasynUser,
asynQueuePriorityLow, -1.0); asynQueuePriorityLow, -1.0);
// if this fails, we are already queued by another thread
debug("AsynDriverInterface::timerExpired %s: "
"queueRequest(..., priority=Low, queueTimeout=-1) = %s %s\n",
clientName(), asynStatusStr[status],
status!=asynSuccess ? pasynUser->errorMessage : "");
if (status != asynSuccess) startTimer(replyTimeout);
// continues with: // continues with:
// handleRequest() -> readHandler() -> readCallback() // handleRequest() -> readHandler() -> readCallback()
} }
@ -1277,14 +1317,7 @@ timerExpired()
} }
} }
#ifdef EPICS_3_14 #ifdef EPICS_3_13
epicsTimerNotify::expireStatus AsynDriverInterface::
expire(const epicsTime &)
{
timerExpired();
return noRestart;
}
#else
void AsynDriverInterface:: void AsynDriverInterface::
expire(CALLBACK *pcallback) expire(CALLBACK *pcallback)
{ {
@ -1292,6 +1325,13 @@ expire(CALLBACK *pcallback)
static_cast<AsynDriverInterface*>(pcallback->user); static_cast<AsynDriverInterface*>(pcallback->user);
interface->timerExpired(); interface->timerExpired();
} }
#else
epicsTimerNotify::expireStatus AsynDriverInterface::
expire(const epicsTime &)
{
timerExpired();
return noRestart;
}
#endif #endif
bool AsynDriverInterface:: bool AsynDriverInterface::
@ -1390,6 +1430,7 @@ void handleRequest(asynUser* pasynUser)
{ {
AsynDriverInterface* interface = AsynDriverInterface* interface =
static_cast<AsynDriverInterface*>(pasynUser->userPvt); static_cast<AsynDriverInterface*>(pasynUser->userPvt);
interface->cancelTimer();
debug("AsynDriverInterface::handleRequest(%s) %s\n", debug("AsynDriverInterface::handleRequest(%s) %s\n",
interface->clientName(), ioActionStr[interface->ioAction]); interface->clientName(), ioActionStr[interface->ioAction]);
switch (interface->ioAction) switch (interface->ioAction)

View File

@ -130,9 +130,10 @@ scanLong(const StreamFormat& format, const char* input, long& value)
int width = format.width; int width = format.width;
if (width == 0) width = -1; if (width == 0) width = -1;
int length = 0; int length = 0;
while (isspace(input[length])) length++; // skip whitespaces
char zero = format.info[0]; char zero = format.info[0];
char one = format.info[1]; char one = format.info[1];
if (!isspace(zero) && !isspace(one))
while (isspace(input[length])) length++; // skip whitespaces
if (input[length] != zero && input[length] != one) return -1; if (input[length] != zero && input[length] != one) return -1;
if (format.flags & alt_flag) if (format.flags & alt_flag)
{ {

View File

@ -265,7 +265,7 @@ static ulong crc_0x04C11DB7(const uchar* data, ulong len, ulong crc)
{ {
// x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + // x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 +
// x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7) // x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7)
const static ulong table[] = { const static unsigned int table[] = {
0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9,
0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005, 0x130476dc, 0x17c56b6b, 0x1a864db2, 0x1e475005,
0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
@ -340,7 +340,7 @@ static ulong crc_0x04C11DB7_r(const uchar* data, ulong len, ulong crc)
// x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + // x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 +
// x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7) // x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + x^0 (0x04C11DB7)
// reflected // reflected
const static ulong table[] = { const static unsigned int table[] = {
0x00000000, 0x77073096, 0xee0e612c, 0x990951ba, 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3, 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988, 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
@ -545,9 +545,9 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
debug("ChecksumConverter %s: output to check: \"%s\"\n", debug("ChecksumConverter %s: output to check: \"%s\"\n",
checksumMap[fnum].name, output.expand(start,length)()); checksumMap[fnum].name, output.expand(start,length)());
sum = checksumMap[fnum].xorout ^ checksumMap[fnum].func( sum = (checksumMap[fnum].xorout ^ checksumMap[fnum].func(
reinterpret_cast<uchar*>(output(start)), length, reinterpret_cast<uchar*>(output(start)), length,
checksumMap[fnum].init) & mask[checksumMap[fnum].bytes]; checksumMap[fnum].init)) & mask[checksumMap[fnum].bytes];
debug("ChecksumConverter %s: output checksum is 0x%lX\n", debug("ChecksumConverter %s: output checksum is 0x%lX\n",
checksumMap[fnum].name, sum); checksumMap[fnum].name, sum);
@ -555,6 +555,17 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
int i; int i;
unsigned outchar; unsigned outchar;
if (format.flags & sign_flag) // decimal
{
// get number of decimal digits from number of bytes: ceil(xbytes*2.5)
i = (checksumMap[fnum].bytes+1)*25/10-2;
output.print("%0*ld", i, sum);
debug("ChecksumConverter %s: decimal appending %0*ld\n",
checksumMap[fnum].name, i, sum);
return true;
}
if (format.flags & alt_flag) // lsb first (little endian) if (format.flags & alt_flag) // lsb first (little endian)
{ {
for (i = 0; i < checksumMap[fnum].bytes; i++) for (i = 0; i < checksumMap[fnum].bytes; i++)
@ -563,7 +574,7 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
debug("ChecksumConverter %s: little endian appending 0x%X\n", debug("ChecksumConverter %s: little endian appending 0x%X\n",
checksumMap[fnum].name, outchar); checksumMap[fnum].name, outchar);
if (format.flags & zero_flag) // ASCII if (format.flags & zero_flag) // ASCII
output.printf("%02X", outchar); output.print("%02X", outchar);
else // binary else // binary
output.append(outchar); output.append(outchar);
sum >>= 8; sum >>= 8;
@ -578,7 +589,7 @@ printPseudo(const StreamFormat& format, StreamBuffer& output)
debug("ChecksumConverter %s: big endian appending 0x%X\n", debug("ChecksumConverter %s: big endian appending 0x%X\n",
checksumMap[fnum].name, outchar); checksumMap[fnum].name, outchar);
if (format.flags & zero_flag) // ASCII if (format.flags & zero_flag) // ASCII
output.printf("%02X", outchar); output.print("%02X", outchar);
else // binary else // binary
output.append(outchar); output.append(outchar);
sum <<= 8; sum <<= 8;
@ -606,9 +617,9 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, long& cursor)
return -1; return -1;
} }
sum = checksumMap[fnum].xorout ^ checksumMap[fnum].func( sum = (checksumMap[fnum].xorout ^ checksumMap[fnum].func(
reinterpret_cast<uchar*>(input(start)), length, reinterpret_cast<uchar*>(input(start)), length,
checksumMap[fnum].init) & mask[checksumMap[fnum].bytes]; checksumMap[fnum].init)) & mask[checksumMap[fnum].bytes];
debug("ChecksumConverter %s: input checksum is 0x%0*lX\n", debug("ChecksumConverter %s: input checksum is 0x%0*lX\n",
checksumMap[fnum].name, 2*checksumMap[fnum].bytes, sum); checksumMap[fnum].name, 2*checksumMap[fnum].bytes, sum);
@ -616,6 +627,24 @@ scanPseudo(const StreamFormat& format, StreamBuffer& input, long& cursor)
int i,j; int i,j;
unsigned inchar; unsigned inchar;
if (format.flags & sign_flag) // decimal
{
ulong sumin = 0;
// get number of decimal digits from number of bytes: ceil(xbytes*2.5)
j = (checksumMap[fnum].bytes+1)*25/10-2;
for (i = 0; i < j; i++)
{
inchar = input[cursor+i];
if (isdigit(inchar)) sumin = sumin*10+inchar-'0';
else break;
}
if (sumin==sum) return i;
error("Input %0*lu does not match checksum %0*lu\n",
i, sumin, j, sum);
return -1;
}
if (format.flags & alt_flag) // lsb first (little endian) if (format.flags & alt_flag) // lsb first (little endian)
{ {
for (i = 0; i < checksumMap[fnum].bytes; i++) for (i = 0; i < checksumMap[fnum].bytes; i++)

View File

@ -79,12 +79,11 @@ include $(TOP)/configure/RULES
# Update version string (contains __DATE__ and __TIME__) # Update version string (contains __DATE__ and __TIME__)
# each time make runs. # each time make runs.
StreamVersion$(OBJ): FORCE StreamVersion$(OBJ): ../*.c ../*.h ../*.cc ../CONFIG_STREAM ../Makefile
FORCE:
# Add references to all registrars to main file to avoid # Add references to all registrars to main file to avoid
# missing initialization. # missing initialization.
StreamCore$(OBJ): streamReferences StreamCore$(OBJ) StreamCore$(DEP): streamReferences
streamReferences: ../CONFIG_STREAM streamReferences: ../CONFIG_STREAM
$(PERL) ../makeref.pl Interface $(BUSSES) > $@ $(PERL) ../makeref.pl Interface $(BUSSES) > $@
@ -94,4 +93,7 @@ streamReferences: ../CONFIG_STREAM
$(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM $(COMMON_DIR)/$(LIBRARY_DEFAULT).dbd: ../CONFIG_STREAM
$(PERL) ../makedbd.pl $(RECORDS) > $@ $(PERL) ../makedbd.pl $(RECORDS) > $@
$(LIBRARY_DEFAULT).dbd$(DEP): ../CONFIG_STREAM
echo $(LIBRARY_DEFAULT).dbd: $< > $@
endif endif

View File

@ -39,7 +39,4 @@ include $(TOP)/config/RULES.Host
# create stream.dbd from all RECORDS # create stream.dbd from all RECORDS
stream.dbd: ../CONFIG_STREAM stream.dbd: ../CONFIG_STREAM
@for r in $(RECORDS_3_13); \ $(PERL) ../makedbd.pl -3.13 $(RECORDS_3_13) > $@
do echo "device($$r,INST_IO,dev$${r}Stream,\"stream\")"; \
done > $@
@echo "driver(stream)" >> $@

View File

@ -75,7 +75,7 @@ printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
int prec = fmt.prec; int prec = fmt.prec;
if (prec < 1) prec = 6; if (prec < 1) prec = 6;
buf.printf("%.*e", prec-1, fabs(value)/pow(10.0, prec-1)); buf.print("%.*e", prec-1, fabs(value)/pow(10.0, prec-1));
buf.remove(1,1); buf.remove(1,1);
buf.remove(buf.find('e'),1); buf.remove(buf.find('e'),1);

View File

@ -70,7 +70,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
} }
if (*source == esc) { if (*source == esc) {
source++; source++;
pattern.printf("\\x%02x", *source++ & 0xFF); pattern.print("\\x%02x", *source++ & 0xFF);
continue; continue;
} }
pattern.append(*source++); pattern.append(*source++);
@ -110,7 +110,7 @@ scanString(const StreamFormat& fmt, const char* input,
len = ovector[subexpr*2+1] - ovector[subexpr*2]; len = ovector[subexpr*2+1] - ovector[subexpr*2];
if (len >= maxlen) { if (len >= maxlen) {
if (!(fmt.flags & sign_flag)) { if (!(fmt.flags & sign_flag)) {
debug("Matching string \"%s\" too long (%d>%d bytes)\n", error("Regexp: Matching string \"%s\" too long (%d>%d bytes). You may want to try the + flag: \"%%+/.../\"\n",
StreamBuffer(input+ovector[subexpr*2], len).expand()(), StreamBuffer(input+ovector[subexpr*2], len).expand()(),
(int)len, (int)maxlen-1); (int)len, (int)maxlen-1);
return -1; return -1;

View File

@ -248,7 +248,7 @@ replace(long remstart, long remlen, const void* ins, long inslen)
} }
StreamBuffer& StreamBuffer:: StreamBuffer& StreamBuffer::
printf(const char* fmt, ...) print(const char* fmt, ...)
{ {
va_list va; va_list va;
int printed; int printed;
@ -296,7 +296,7 @@ StreamBuffer StreamBuffer::expand(long start, long length) const
c = buffer[i]; c = buffer[i];
if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f) if ((c & 0x7f) < 0x20 || (c & 0x7f) == 0x7f)
{ {
result.printf("<%02x>", c & 0xff); result.print("<%02x>", c & 0xff);
} }
else else
{ {
@ -312,17 +312,17 @@ dump() const
StreamBuffer result(256+cap*5); StreamBuffer result(256+cap*5);
result.append("\033[0m"); result.append("\033[0m");
long i; long i;
result.printf("%ld,%ld,%ld:\033[37m", offs, len, cap); result.print("%ld,%ld,%ld:\033[37m", offs, len, cap);
for (i = 0; i < cap; i++) for (i = 0; i < cap; i++)
{ {
if (i == offs) result.append("\033[34m[\033[0m"); if (i == offs) result.append("\033[34m[\033[0m");
if ((buffer[i] & 0x7f) < 0x20 || (buffer[i] & 0x7f) == 0x7f) if ((buffer[i] & 0x7f) < 0x20 || (buffer[i] & 0x7f) == 0x7f)
{ {
if (i < offs || i >= offs+len) if (i < offs || i >= offs+len)
result.printf( result.print(
"<%02x>", buffer[i] & 0xff); "<%02x>", buffer[i] & 0xff);
else else
result.printf( result.print(
"\033[34m<%02x>\033[37m", buffer[i] & 0xff); "\033[34m<%02x>\033[37m", buffer[i] & 0xff);
} }
else else

View File

@ -21,7 +21,6 @@
#define StreamBuffer_h #define StreamBuffer_h
#include <string.h> #include <string.h>
#include <stdio.h>
#ifndef __GNUC__ #ifndef __GNUC__
#define __attribute__(x) #define __attribute__(x)
@ -189,7 +188,7 @@ public:
StreamBuffer& insert(long pos, char c) StreamBuffer& insert(long pos, char c)
{return replace(pos, 0, &c, 1);} {return replace(pos, 0, &c, 1);}
StreamBuffer& printf(const char* fmt, ...) StreamBuffer& print(const char* fmt, ...)
__attribute__ ((format(printf,2,3))); __attribute__ ((format(printf,2,3)));
// find: get index of data in buffer or -1 // find: get index of data in buffer or -1

View File

@ -52,42 +52,62 @@ public:
protected: protected:
StreamBusInterface* businterface; StreamBusInterface* businterface;
bool busSupportsEvent() { bool busSupportsEvent() {
return businterface->supportsEvent(); if (businterface)
return businterface->supportsEvent();
else return false;
} }
bool busSupportsAsyncRead() { bool busSupportsAsyncRead() {
return businterface->supportsAsyncRead(); if (businterface)
return businterface->supportsAsyncRead();
else return false;
} }
bool busAcceptEvent(unsigned long mask, bool busAcceptEvent(unsigned long mask,
unsigned long replytimeout_ms) { unsigned long replytimeout_ms) {
return businterface->acceptEvent(mask, replytimeout_ms); if (businterface)
return businterface->acceptEvent(mask, replytimeout_ms);
else return false;
} }
void busRelease() { void busRelease() {
businterface->release(); if (businterface)
businterface->release();
} }
bool busLockRequest(unsigned long timeout_ms) { bool busLockRequest(unsigned long timeout_ms) {
return businterface->lockRequest(timeout_ms); if (businterface)
return businterface->lockRequest(timeout_ms);
else return false;
} }
bool busUnlock() { bool busUnlock() {
return businterface->unlock(); if (businterface)
return businterface->unlock();
else return false;
} }
bool busWriteRequest(const void* output, size_t size, bool busWriteRequest(const void* output, size_t size,
unsigned long timeout_ms) { unsigned long timeout_ms) {
return businterface->writeRequest(output, size, timeout_ms); if (businterface)
return businterface->writeRequest(output, size, timeout_ms);
else return false;
} }
bool busReadRequest(unsigned long replytimeout_ms, bool busReadRequest(unsigned long replytimeout_ms,
unsigned long readtimeout_ms, long expectedLength, unsigned long readtimeout_ms, long expectedLength,
bool async) { bool async) {
return businterface->readRequest(replytimeout_ms, if (businterface)
readtimeout_ms, expectedLength, async); return businterface->readRequest(replytimeout_ms,
readtimeout_ms, expectedLength, async);
else return false;
} }
void busFinish() { void busFinish() {
businterface->finish(); if (businterface)
businterface->finish();
} }
bool busConnectRequest(unsigned long timeout_ms) { bool busConnectRequest(unsigned long timeout_ms) {
return businterface->connectRequest(timeout_ms); if (businterface)
return businterface->connectRequest(timeout_ms);
else return false;
} }
bool busDisconnect() { bool busDisconnect() {
return businterface->disconnectRequest(); if (businterface)
return businterface->disconnectRequest();
else return false;
} }
}; };

View File

@ -57,12 +57,12 @@ static char* printCommands(StreamBuffer& buffer, const char* c)
break; break;
case wait_cmd: case wait_cmd:
timeout = extract<unsigned long>(c); timeout = extract<unsigned long>(c);
buffer.printf(" wait %ld;\n # ms", timeout); buffer.print(" wait %ld;\n # ms", timeout);
break; break;
case event_cmd: case event_cmd:
eventnumber = extract<unsigned long>(c); eventnumber = extract<unsigned long>(c);
timeout = extract<unsigned long>(c); timeout = extract<unsigned long>(c);
buffer.printf(" event(%ld) %ld; # ms\n", eventnumber, timeout); buffer.print(" event(%ld) %ld; # ms\n", eventnumber, timeout);
break; break;
case exec_cmd: case exec_cmd:
buffer.append(" exec \""); buffer.append(" exec \"");
@ -71,7 +71,7 @@ static char* printCommands(StreamBuffer& buffer, const char* c)
break; break;
case connect_cmd: case connect_cmd:
timeout = extract<unsigned long>(c); timeout = extract<unsigned long>(c);
buffer.printf(" connect %ld; # ms\n", timeout); buffer.print(" connect %ld; # ms\n", timeout);
break; break;
case disconnect_cmd: case disconnect_cmd:
buffer.append(" disconnect;\n"); buffer.append(" disconnect;\n");
@ -118,7 +118,6 @@ printProtocol()
if (onMismatch) if (onMismatch)
printf(" @Mismatch {\n%s }\n", printf(" @Mismatch {\n%s }\n",
printCommands(buffer.clear(), onMismatch())); printCommands(buffer.clear(), onMismatch()));
debug("StreamCore::printProtocol: commands=%s\n", commands.expand()());
printf("\n%s}\n", printf("\n%s}\n",
printCommands(buffer.clear(), commands())); printCommands(buffer.clear(), commands()));
} }
@ -1159,8 +1158,7 @@ matchInput()
*/ */
char command; char command;
const char* fieldName = NULL; const char* fieldName = NULL;
const char* formatstring; StreamBuffer formatstring;
int formatstringlen;
consumedInput = 0; consumedInput = 0;
@ -1186,22 +1184,14 @@ normal_format:
int consumed; int consumed;
// code layout: // code layout:
// formatstring <eos> StreamFormat [info] // formatstring <eos> StreamFormat [info]
formatstring = commandIndex; commandIndex = StreamProtocolParser::printString(formatstring, commandIndex);
// jump after <eos>
while (*commandIndex)
{
if (*commandIndex == esc) commandIndex++;
commandIndex++;
}
formatstringlen = commandIndex-formatstring;
commandIndex++;
StreamFormat fmt = extract<StreamFormat>(commandIndex); StreamFormat fmt = extract<StreamFormat>(commandIndex);
fmt.info = commandIndex; // point to info string fmt.info = commandIndex; // point to info string
commandIndex += fmt.infolen; commandIndex += fmt.infolen;
#ifndef NO_TEMPORARY #ifndef NO_TEMPORARY
debug("StreamCore::matchInput(%s): format = %%%s\n", debug("StreamCore::matchInput(%s): format = \"%%%s\"\n",
name(), StreamBuffer(formatstring, formatstringlen).expand()()); name(), formatstring());
#endif #endif
if (fmt.flags & skip_flag || fmt.type == pseudo_format) if (fmt.flags & skip_flag || fmt.type == pseudo_format)
@ -1243,10 +1233,10 @@ normal_format:
{ {
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd) if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{ {
error("%s: Input \"%s%s\" does not match format %%%s\n", error("%s: Input \"%s%s\" does not match format \"%%%s\"\n",
name(), inputLine.expand(consumedInput, 20)(), name(), inputLine.expand(consumedInput, 20)(),
inputLine.length()-consumedInput > 20 ? "..." : "", inputLine.length()-consumedInput > 20 ? "..." : "",
formatstring); formatstring());
} }
return false; return false;
} }
@ -1260,13 +1250,12 @@ normal_format:
flags &= ~Separator; flags &= ~Separator;
if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL)) if (!formatValue(fmt, fieldAddress ? fieldAddress() : NULL))
{ {
StreamBuffer formatstr(formatstring, formatstringlen);
if (fieldAddress) if (fieldAddress)
error("%s: Cannot format field '%s' with '%%%s'\n", error("%s: Cannot format variable \"%s\" with \"%%%s\"\n",
name(), fieldName, formatstr.expand()()); name(), fieldName, formatstring());
else else
error("%s: Cannot format value with '%%%s'\n", error("%s: Cannot format value with \"%%%s\"\n",
name(), formatstr.expand()()); name(), formatstring());
return false; return false;
} }
#ifndef NO_TEMPORARY #ifndef NO_TEMPORARY
@ -1279,11 +1268,11 @@ normal_format:
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd) if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{ {
error("%s: Input \"%s%s\" too short." error("%s: Input \"%s%s\" too short."
" No match for format %%%s (\"%s\")\n", " No match for format \"%%%s\" (\"%s\")\n",
name(), name(),
inputLine.length() > 20 ? "..." : "", inputLine.length() > 20 ? "..." : "",
inputLine.expand(-20)(), inputLine.expand(-20)(),
formatstring, formatstring(),
outputLine.expand()()); outputLine.expand()());
} }
return false; return false;
@ -1292,11 +1281,10 @@ normal_format:
{ {
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd) if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{ {
error("%s: Input \"%s%s\" does not match" error("%s: Input \"%s%s\" does not match format \"%%%s\" (\"%s\")\n",
" format %%%s (\"%s\")\n",
name(), inputLine.expand(consumedInput, 20)(), name(), inputLine.expand(consumedInput, 20)(),
inputLine.length()-consumedInput > 20 ? "..." : "", inputLine.length()-consumedInput > 20 ? "..." : "",
formatstring, formatstring(),
outputLine.expand()()); outputLine.expand()());
} }
return false; return false;
@ -1310,13 +1298,13 @@ normal_format:
if (!(flags & AsyncMode) && onMismatch[0] != in_cmd) if (!(flags & AsyncMode) && onMismatch[0] != in_cmd)
{ {
if (flags & ScanTried) if (flags & ScanTried)
error("%s: Input \"%s%s\" does not match format %%%s\n", error("%s: Input \"%s%s\" does not match format \"%%%s\"\n",
name(), inputLine.expand(consumedInput, 20)(), name(), inputLine.expand(consumedInput, 20)(),
inputLine.length()-consumedInput > 20 ? "..." : "", inputLine.length()-consumedInput > 20 ? "..." : "",
formatstring); formatstring());
else else
error("%s: Format %%%s has data type %s which does not match this variable.\n", error("%s: Format \"%%%s\" has data type %s which does not match variable \"%s\".\n",
name(), formatstring, StreamFormatTypeStr[fmt.type] ); name(), formatstring(), StreamFormatTypeStr[fmt.type], fieldName);
} }
return false; return false;
} }

View File

@ -18,11 +18,16 @@
* * * *
***************************************************************/ ***************************************************************/
#include "devStream.h"
#include "StreamCore.h" #include "StreamCore.h"
#include "StreamError.h" #include "StreamError.h"
#include "devStream.h"
#ifndef EPICS_3_14 #include <epicsVersion.h>
#ifdef BASE_VERSION
#define EPICS_3_13
#endif
#ifdef EPICS_3_13
extern "C" { extern "C" {
#endif #endif
@ -34,10 +39,11 @@ extern "C" {
#include <recSup.h> #include <recSup.h>
#include <recGbl.h> #include <recGbl.h>
#include <devLib.h> #include <devLib.h>
#define epicsAlarmGLOBAL
#include <alarm.h> #include <alarm.h>
#include <callback.h> #include <callback.h>
#ifndef EPICS_3_14 #ifdef EPICS_3_13
#include <semLib.h> #include <semLib.h>
#include <wdLib.h> #include <wdLib.h>
@ -87,24 +93,24 @@ extern "C" void streamRecordProcessCallback(CALLBACK *pcallback);
extern "C" long streamReload(char* recordname); extern "C" long streamReload(char* recordname);
class Stream : protected StreamCore class Stream : protected StreamCore
#ifdef EPICS_3_14 #ifndef EPICS_3_13
, epicsTimerNotify , epicsTimerNotify
#endif #endif
{ {
dbCommon* record; dbCommon* record;
struct link *ioLink; const struct link *ioLink;
streamIoFunction readData; streamIoFunction readData;
streamIoFunction writeData; streamIoFunction writeData;
#ifdef EPICS_3_14 #ifdef EPICS_3_13
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
epicsMutex mutex;
epicsEvent initDone;
#else
WDOG_ID timer; WDOG_ID timer;
CALLBACK timeoutCallback; CALLBACK timeoutCallback;
SEM_ID mutex; SEM_ID mutex;
SEM_ID initDone; SEM_ID initDone;
#else
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
epicsMutex mutex;
epicsEvent initDone;
#endif #endif
StreamBuffer fieldBuffer; StreamBuffer fieldBuffer;
int status; int status;
@ -115,11 +121,11 @@ class Stream : protected StreamCore
CALLBACK processCallback; CALLBACK processCallback;
#ifdef EPICS_3_14 #ifdef EPICS_3_13
static void expire(CALLBACK *pcallback);
#else
// epicsTimerNotify method // epicsTimerNotify method
expireStatus expire(const epicsTime&); expireStatus expire(const epicsTime&);
#else
static void expire(CALLBACK *pcallback);
#endif #endif
// StreamCore methods // StreamCore methods
@ -139,16 +145,19 @@ class Stream : protected StreamCore
friend void streamRecordProcessCallback(CALLBACK *pcallback); friend void streamRecordProcessCallback(CALLBACK *pcallback);
// Stream Epics methods // Stream Epics methods
long initRecord(); Stream(dbCommon* record, const struct link *ioLink,
Stream(dbCommon* record, struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData); streamIoFunction readData, streamIoFunction writeData);
~Stream(); ~Stream();
long parseLink(const struct link *ioLink, char* filename, char* protocol,
char* busname, int* addr, char* busparam);
long initRecord(const char* filename, const char* protocol,
const char* busname, int addr, const char* busparam);
bool print(format_t *format, va_list ap); bool print(format_t *format, va_list ap);
bool scan(format_t *format, void* pvalue, size_t maxStringSize); bool scan(format_t *format, void* pvalue, size_t maxStringSize);
bool process(); bool process();
// device support functions // device support functions
friend long streamInitRecord(dbCommon *record, struct link *ioLink, friend long streamInitRecord(dbCommon *record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData); streamIoFunction readData, streamIoFunction writeData);
friend long streamReadWrite(dbCommon *record); friend long streamReadWrite(dbCommon *record);
friend long streamGetIointInfo(int cmd, dbCommon *record, friend long streamGetIointInfo(int cmd, dbCommon *record,
@ -166,7 +175,7 @@ public:
// shell functions /////////////////////////////////////////////////////// // shell functions ///////////////////////////////////////////////////////
#ifdef EPICS_3_14 #ifndef EPICS_3_13
extern "C" { extern "C" {
epicsExportAddress(int, streamDebug); epicsExportAddress(int, streamDebug);
} }
@ -225,7 +234,7 @@ extern "C" long streamReload(char* recordname)
return OK; return OK;
} }
#ifdef EPICS_3_14 #ifndef EPICS_3_13
static const iocshArg streamReloadArg0 = static const iocshArg streamReloadArg0 =
{ "recordname", iocshArgString }; { "recordname", iocshArgString };
static const iocshArg * const streamReloadArgs[] = static const iocshArg * const streamReloadArgs[] =
@ -251,7 +260,7 @@ static void streamRegistrar ()
extern "C" { extern "C" {
epicsExportRegistrar(streamRegistrar); epicsExportRegistrar(streamRegistrar);
} }
#endif // EPICS_3_14 #endif
// driver support //////////////////////////////////////////////////////// // driver support ////////////////////////////////////////////////////////
@ -265,19 +274,13 @@ struct stream_drvsup {
Stream::drvInit Stream::drvInit
}; };
#ifdef EPICS_3_14 #ifndef EPICS_3_13
extern "C" { extern "C" {
epicsExportAddress(drvet, stream); epicsExportAddress(drvet, stream);
} }
#endif
void streamEpicsPrintTimestamp(char* buffer, int size) #ifdef EPICS_3_13
{
int tlen;
epicsTime tm = epicsTime::getCurrent();
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) void streamEpicsPrintTimestamp(char* buffer, int size)
{ {
int tlen; int tlen;
@ -292,6 +295,14 @@ void streamEpicsPrintTimestamp(char* buffer, int size)
tlen = strlen(buffer); tlen = strlen(buffer);
sprintf(buffer+tlen, " %.*s", size-tlen-2, taskName(0)); sprintf(buffer+tlen, " %.*s", size-tlen-2, taskName(0));
} }
#else
void streamEpicsPrintTimestamp(char* buffer, int size)
{
int tlen;
epicsTime tm = epicsTime::getCurrent();
tlen = tm.strftime(buffer, size, "%Y/%m/%d %H:%M:%S.%06f");
sprintf(buffer+tlen, " %.*s", size-tlen-2, epicsThreadGetNameSelf());
}
#endif #endif
long Stream:: long Stream::
@ -393,9 +404,16 @@ long streamInit(int after)
return OK; return OK;
} }
long streamInitRecord(dbCommon* record, struct link *ioLink, long streamInitRecord(dbCommon* record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData) streamIoFunction readData, streamIoFunction writeData)
{ {
char filename[80];
char protocol[80];
char busname[80];
int addr = -1;
char busparam[80];
memset(busparam, 0 ,sizeof(busparam));
debug("streamInitRecord(%s): SEVR=%d\n", record->name, record->sevr); debug("streamInitRecord(%s): SEVR=%d\n", record->name, record->sevr);
Stream* pstream = (Stream*)record->dpvt; Stream* pstream = (Stream*)record->dpvt;
if (!pstream) if (!pstream)
@ -407,8 +425,12 @@ long streamInitRecord(dbCommon* record, struct link *ioLink,
// stop any running protocol // stop any running protocol
pstream->finishProtocol(Stream::Abort); pstream->finishProtocol(Stream::Abort);
} }
// scan the i/o link
pstream->parseLink(ioLink, filename, protocol,
busname, &addr, busparam);
// (re)initialize bus and protocol // (re)initialize bus and protocol
long status = pstream->initRecord(); long status = pstream->initRecord(filename, protocol,
busname, addr, busparam);
if (status != OK && status != DO_NOT_CONVERT) if (status != OK && status != DO_NOT_CONVERT)
{ {
error("%s: Record initialization failed\n", record->name); error("%s: Record initialization failed\n", record->name);
@ -436,7 +458,8 @@ long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
{ {
Stream* pstream = (Stream*)record->dpvt; Stream* pstream = (Stream*)record->dpvt;
debug("streamGetIointInfo(%s,cmd=%d): pstream=%p, ioscanpvt=%p\n", debug("streamGetIointInfo(%s,cmd=%d): pstream=%p, ioscanpvt=%p\n",
record->name, cmd, (void*)pstream, pstream ? pstream->ioscanpvt : NULL); record->name, cmd,
(void*)pstream, pstream ? pstream->ioscanpvt : NULL);
if (!pstream) if (!pstream)
{ {
error("streamGetIointInfo called without stream instance\n"); error("streamGetIointInfo called without stream instance\n");
@ -495,20 +518,20 @@ long streamScanfN(dbCommon* record, format_t *format,
// Stream methods //////////////////////////////////////////////////////// // Stream methods ////////////////////////////////////////////////////////
Stream:: Stream::
Stream(dbCommon* _record, struct link *ioLink, Stream(dbCommon* _record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData) streamIoFunction readData, streamIoFunction writeData)
:record(_record), ioLink(ioLink), readData(readData), writeData(writeData) :record(_record), ioLink(ioLink), readData(readData), writeData(writeData)
{ {
streamname = record->name; streamname = record->name;
#ifdef EPICS_3_14 #ifdef EPICS_3_13
timerQueue = &epicsTimerQueueActive::allocate(true);
timer = &timerQueue->createTimer();
#else
timer = wdCreate(); timer = wdCreate();
mutex = semMCreate(SEM_INVERSION_SAFE | SEM_Q_PRIORITY); mutex = semMCreate(SEM_INVERSION_SAFE | SEM_Q_PRIORITY);
initDone = semBCreate(SEM_Q_FIFO, SEM_EMPTY); initDone = semBCreate(SEM_Q_FIFO, SEM_EMPTY);
callbackSetCallback(expire, &timeoutCallback); callbackSetCallback(expire, &timeoutCallback);
callbackSetUser(this, &timeoutCallback); callbackSetUser(this, &timeoutCallback);
#else
timerQueue = &epicsTimerQueueActive::allocate(true);
timer = &timerQueue->createTimer();
#endif #endif
callbackSetCallback(streamExecuteCommand, &commandCallback); callbackSetCallback(streamExecuteCommand, &commandCallback);
callbackSetUser(this, &commandCallback); callbackSetUser(this, &commandCallback);
@ -532,57 +555,57 @@ Stream::
record->dpvt = NULL; record->dpvt = NULL;
debug("~Stream(%s): dpvt cleared\n", name()); debug("~Stream(%s): dpvt cleared\n", name());
} }
#ifdef EPICS_3_14 #ifdef EPICS_3_13
wdDelete(timer);
debug("~Stream(%s): watchdog destroyed\n", name());
#else
timer->destroy(); timer->destroy();
debug("~Stream(%s): timer destroyed\n", name()); debug("~Stream(%s): timer destroyed\n", name());
timerQueue->release(); timerQueue->release();
debug("~Stream(%s): timer queue released\n", name()); debug("~Stream(%s): timer queue released\n", name());
#else
wdDelete(timer);
debug("~Stream(%s): watchdog destroyed\n", name());
#endif #endif
releaseMutex(); releaseMutex();
} }
long Stream:: long Stream::
initRecord() parseLink(const struct link *ioLink, char* filename,
char* protocol, char* busname, int* addr, char* busparam)
{ {
// scan link parameters: filename protocol busname addr busparam // parse 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; int n;
if (ioLink->type != INST_IO) if (ioLink->type != INST_IO)
{ {
error("%s: Wrong link type %s\n", name(), error("%s: Wrong I/O link type %s\n", name(),
pamaplinkType[ioLink->type].strvalue); pamaplinkType[ioLink->type].strvalue);
return S_dev_badInitRet; return S_dev_badInitRet;
} }
int items = sscanf(ioLink->value.instio.string, "%79s%79s%79s%n%i%n", int items = sscanf(ioLink->value.instio.string, "%s%s%s%n%i%n",
filename, protocol, busname, &n, &addr, &n); filename, protocol, busname, &n, addr, &n);
if (items <= 0) if (items <= 0)
{ {
error("%s: Empty link. Forgot the leading '@' or confused INP with OUT ?\n", error("%s: Empty I/O link. "
"Forgot the leading '@' or confused INP with OUT ?\n",
name()); name());
return S_dev_badInitRet; return S_dev_badInitRet;
} }
if (items < 3) if (items < 3)
{ {
error("%s: Wrong link format\n" error("%s: Wrong I/O link format\n"
" expect \"@file protocol bus addr params\"\n" " expect \"@file protocol bus addr params\"\n"
" in \"@%s\"\n", name(), " in \"@%s\"\n", name(),
ioLink->value.instio.string); ioLink->value.instio.string);
return S_dev_badInitRet; return S_dev_badInitRet;
} }
memset(busparam, 0 ,80); while (isspace((unsigned char)ioLink->value.instio.string[n])) n++;
for (n = 0; isspace((unsigned char)ioLink->value.instio.string[n]); n++); strcpy (busparam, ioLink->value.constantStr+n);
strncpy (busparam, ioLink->value.constantStr+n, 79); return OK;
}
long Stream::
initRecord(const char* filename, const char* protocol,
const char* busname, int addr, const char* busparam)
{
// It is safe to call this function again with different arguments
// attach to bus interface // attach to bus interface
if (!attachBus(busname, addr, busparam)) if (!attachBus(busname, addr, busparam))
@ -610,7 +633,8 @@ initRecord()
name()); name());
if (record->scan == SCAN_IO_EVENT) if (record->scan == SCAN_IO_EVENT)
{ {
debug("Stream::initRecord %s: restarting \"I/O Intr\" after reload\n", debug("Stream::initRecord %s: "
"restarting \"I/O Intr\" after reload\n",
name()); name());
if (!startProtocol(StartAsync)) if (!startProtocol(StartAsync))
{ {
@ -635,10 +659,10 @@ initRecord()
} }
debug("Stream::initRecord %s: waiting for initDone\n", debug("Stream::initRecord %s: waiting for initDone\n",
name()); name());
#ifdef EPICS_3_14 #ifdef EPICS_3_13
initDone.wait();
#else
semTake(initDone, WAIT_FOREVER); semTake(initDone, WAIT_FOREVER);
#else
initDone.wait();
#endif #endif
debug("Stream::initRecord %s: initDone\n", debug("Stream::initRecord %s: initDone\n",
name()); name());
@ -664,8 +688,11 @@ process()
{ {
if (status != NO_ALARM) if (status != NO_ALARM)
{ {
debug("Stream::process(%s) error status=%d\n", debug("Stream::process(%s) error status=%s (%d)\n",
name(), status); name(),
status >= 0 && status < ALARM_NSTATUS ?
epicsAlarmConditionStrings[status] : "ERROR",
status);
(void) recGblSetSevr(record, status, INVALID_ALARM); (void) recGblSetSevr(record, status, INVALID_ALARM);
return false; return false;
} }
@ -744,7 +771,8 @@ scan(format_t *format, void* value, size_t maxStringSize)
break; break;
case DBF_STRING: case DBF_STRING:
sptr = (char*)value; sptr = (char*)value;
currentValueLength = scanValue(*format->priv, sptr, maxStringSize); currentValueLength = scanValue(*format->priv, sptr,
maxStringSize);
break; break;
default: default:
error("INTERNAL ERROR (%s): Illegal format type\n", name()); error("INTERNAL ERROR (%s): Illegal format type\n", name());
@ -762,20 +790,20 @@ scan(format_t *format, void* value, size_t maxStringSize)
// epicsTimerNotify virtual method /////////////////////////////////////// // epicsTimerNotify virtual method ///////////////////////////////////////
#ifdef EPICS_3_14 #ifdef EPICS_3_13
epicsTimerNotify::expireStatus Stream::
expire(const epicsTime&)
{
timerCallback();
return noRestart;
}
#else
void Stream:: void Stream::
expire(CALLBACK *pcallback) expire(CALLBACK *pcallback)
{ {
Stream* pstream = static_cast<Stream*>(pcallback->user); Stream* pstream = static_cast<Stream*>(pcallback->user);
pstream->timerCallback(); pstream->timerCallback();
} }
#else
epicsTimerNotify::expireStatus Stream::
expire(const epicsTime&)
{
timerCallback();
return noRestart;
}
#endif #endif
// StreamCore virtual methods //////////////////////////////////////////// // StreamCore virtual methods ////////////////////////////////////////////
@ -835,10 +863,10 @@ protocolFinishHook(ProtocolResult result)
} }
if (flags & InitRun) if (flags & InitRun)
{ {
#ifdef EPICS_3_14 #ifdef EPICS_3_13
initDone.signal();
#else
semGive(initDone); semGive(initDone);
#else
initDone.signal();
#endif #endif
return; return;
} }
@ -891,13 +919,13 @@ startTimer(unsigned long timeout)
{ {
debug("Stream::startTimer(stream=%s, timeout=%lu) = %f seconds\n", debug("Stream::startTimer(stream=%s, timeout=%lu) = %f seconds\n",
name(), timeout, timeout * 0.001); name(), timeout, timeout * 0.001);
#ifdef EPICS_3_14 #ifdef EPICS_3_13
timer->start(*this, timeout * 0.001);
#else
callbackSetPriority(priority(), &timeoutCallback); callbackSetPriority(priority(), &timeoutCallback);
wdStart(timer, (timeout+1)*sysClkRateGet()/1000-1, wdStart(timer, (timeout+1)*sysClkRateGet()/1000-1,
reinterpret_cast<FUNCPTR>(callbackRequest), reinterpret_cast<FUNCPTR>(callbackRequest),
reinterpret_cast<int>(&timeoutCallback)); reinterpret_cast<int>(&timeoutCallback));
#else
timer->start(*this, timeout * 0.001);
#endif #endif
} }
@ -957,7 +985,8 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
if (format.type != double_format) if (format.type != double_format)
{ {
error ("%s: can only read double values from TIME field\n", name()); error ("%s: can only read double values from TIME field\n",
name());
return false; return false;
} }
if (pdbaddr->precord == record) if (pdbaddr->precord == record)
@ -965,8 +994,10 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
/* if getting time from own record, update timestamp first */ /* if getting time from own record, update timestamp first */
recGblGetTimeStamp(record); recGblGetTimeStamp(record);
} }
time = pdbaddr->precord->time.secPastEpoch + 631152000u + pdbaddr->precord->time.nsec * 1e-9; time = pdbaddr->precord->time.secPastEpoch +
debug("Stream::formatValue(%s): read %f from TIME field\n", name(), time); 631152000u + pdbaddr->precord->time.nsec * 1e-9;
debug("Stream::formatValue(%s): read %f from TIME field\n",
name(), time);
return printValue(format, time); return printValue(format, time);
} }
@ -1004,7 +1035,8 @@ formatValue(const StreamFormat& format, const void* fieldaddress)
return false; return false;
break; break;
case pseudo_format: case pseudo_format:
error("%s: %%(FIELD) syntax not allowed with pseudo formats\n", error("%s: %%(FIELD) syntax not allowed "
"with pseudo formats\n",
name()); name());
default: default:
error("INTERNAL ERROR %s: Illegal format.type=%d\n", error("INTERNAL ERROR %s: Illegal format.type=%d\n",
@ -1054,7 +1086,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
buffer = fieldBuffer.clear().reserve(size); buffer = fieldBuffer.clear().reserve(size);
for (nord = 0; nord < nelem; nord++) for (nord = 0; nord < nelem; nord++)
{ {
debug("Stream::matchValue(%s): buffer before: %s\n", name(), fieldBuffer.expand()()); debug("Stream::matchValue(%s): buffer before: %s\n",
name(), fieldBuffer.expand()());
switch (format.type) switch (format.type)
{ {
case long_format: case long_format:
@ -1068,7 +1101,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
case enum_format: case enum_format:
{ {
consumed = scanValue(format, lval); consumed = scanValue(format, lval);
if (consumed >= 0) ((epicsUInt16*)buffer)[nord] = (epicsUInt16)lval; if (consumed >= 0)
((epicsUInt16*)buffer)[nord] = (epicsUInt16)lval;
debug("Stream::matchValue(%s): %s[%li] = %li\n", debug("Stream::matchValue(%s): %s[%li] = %li\n",
name(), pdbaddr->precord->name, nord, lval); name(), pdbaddr->precord->name, nord, lval);
break; break;
@ -1076,10 +1110,13 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
case double_format: case double_format:
{ {
consumed = scanValue(format, dval); consumed = scanValue(format, dval);
// Direct assignment to buffer fails fith gcc 3.4.3 for xscale_be // Direct assignment to buffer fails with
// gcc 3.4.3 for xscale_be
// Optimization bug? // Optimization bug?
epicsFloat64 f64=dval; epicsFloat64 f64=dval;
if (consumed >= 0) memcpy(((epicsFloat64*)buffer)+nord, &f64, sizeof(f64)); if (consumed >= 0)
memcpy(((epicsFloat64*)buffer)+nord,
&f64, sizeof(f64));
debug("Stream::matchValue(%s): %s[%li] = %#g %#g\n", debug("Stream::matchValue(%s): %s[%li] = %#g %#g\n",
name(), pdbaddr->precord->name, nord, dval, name(), pdbaddr->precord->name, nord, dval,
((epicsFloat64*)buffer)[nord]); ((epicsFloat64*)buffer)[nord]);
@ -1090,7 +1127,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
consumed = scanValue(format, consumed = scanValue(format,
buffer+MAX_STRING_SIZE*nord, MAX_STRING_SIZE); buffer+MAX_STRING_SIZE*nord, MAX_STRING_SIZE);
debug("Stream::matchValue(%s): %s[%li] = \"%.*s\"\n", debug("Stream::matchValue(%s): %s[%li] = \"%.*s\"\n",
name(), pdbaddr->precord->name, nord, MAX_STRING_SIZE, buffer+MAX_STRING_SIZE*nord); name(), pdbaddr->precord->name, nord,
MAX_STRING_SIZE, buffer+MAX_STRING_SIZE*nord);
break; break;
} }
default: default:
@ -1098,7 +1136,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
"Illegal format type\n", name()); "Illegal format type\n", name());
return false; return false;
} }
debug("Stream::matchValue(%s): buffer after: %s\n", name(), fieldBuffer.expand()()); debug("Stream::matchValue(%s): buffer after: %s\n",
name(), fieldBuffer.expand()());
if (consumed < 0) break; if (consumed < 0) break;
consumedInput += consumed; consumedInput += consumed;
} }
@ -1107,7 +1146,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
// scan error: set other record to alarm status // scan error: set other record to alarm status
if (pdbaddr->precord != record) if (pdbaddr->precord != record)
{ {
(void) recGblSetSevr(pdbaddr->precord, CALC_ALARM, INVALID_ALARM); (void) recGblSetSevr(pdbaddr->precord,
CALC_ALARM, INVALID_ALARM);
if (!INIT_RUN) if (!INIT_RUN)
{ {
// process other record to send alarm monitor // process other record to send alarm monitor
@ -1121,19 +1161,24 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
#ifdef epicsTimeEventDeviceTime #ifdef epicsTimeEventDeviceTime
if (format.type != double_format) if (format.type != double_format)
{ {
error ("%s: can only write double values to TIME field\n", name()); error ("%s: can only write double values to TIME field\n",
name());
return false; return false;
} }
dval = dval-631152000u; dval = dval-631152000u;
pdbaddr->precord->time.secPastEpoch = (long)dval; pdbaddr->precord->time.secPastEpoch = (long)dval;
/* rouding: we don't have 9 digits precision in a double of today's number of seconds */ // rouding: we don't have 9 digits precision
// in a double of today's number of seconds
pdbaddr->precord->time.nsec = (long)((dval-(long)dval)*1e6)*1000; pdbaddr->precord->time.nsec = (long)((dval-(long)dval)*1e6)*1000;
debug("Stream::matchValue(%s): writing %i.%i to TIME field\n", name(), debug("Stream::matchValue(%s): writing %i.%i to TIME field\n",
pdbaddr->precord->time.secPastEpoch, pdbaddr->precord->time.nsec); name(),
pdbaddr->precord->time.secPastEpoch,
pdbaddr->precord->time.nsec);
pdbaddr->precord->tse = epicsTimeEventDeviceTime; pdbaddr->precord->tse = epicsTimeEventDeviceTime;
return true; return true;
#else #else
error ("%s: writing TIME field is not supported in this EPICS version\n", name()); error ("%s: writing TIME field is not supported "
"in this EPICS version\n", name());
return false; return false;
#endif #endif
} }
@ -1166,7 +1211,8 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
((dbFldDes*)pdbaddr->pfldDes)->name, ((dbFldDes*)pdbaddr->pfldDes)->name,
fieldBuffer.expand()()); fieldBuffer.expand()());
putfunc = "dbPutField"; putfunc = "dbPutField";
status = dbPutField(pdbaddr, dbfMapping[format.type], buffer, nord); status = dbPutField(pdbaddr, dbfMapping[format.type],
buffer, nord);
} }
if (status != 0) if (status != 0)
{ {
@ -1229,13 +1275,15 @@ matchValue(const StreamFormat& format, const void* fieldaddress)
return true; return true;
} }
#ifdef EPICS_3_14 #ifdef EPICS_3_13
// Pass command to iocsh // Pass command to vxWorks shell
extern "C" int execute(const char *cmd);
void streamExecuteCommand(CALLBACK *pcallback) void streamExecuteCommand(CALLBACK *pcallback)
{ {
Stream* pstream = static_cast<Stream*>(pcallback->user); Stream* pstream = static_cast<Stream*>(pcallback->user);
if (iocshCmd(pstream->outputLine()) != OK) if (execute(pstream->outputLine()) != OK)
{ {
pstream->execCallback(StreamIoFault); pstream->execCallback(StreamIoFault);
} }
@ -1245,14 +1293,12 @@ void streamExecuteCommand(CALLBACK *pcallback)
} }
} }
#else #else
// Pass command to vxWorks shell // Pass command to iocsh
extern "C" int execute(const char *cmd);
void streamExecuteCommand(CALLBACK *pcallback) void streamExecuteCommand(CALLBACK *pcallback)
{ {
Stream* pstream = static_cast<Stream*>(pcallback->user); Stream* pstream = static_cast<Stream*>(pcallback->user);
if (execute(pstream->outputLine()) != OK) if (iocshCmd(pstream->outputLine()) != OK)
{ {
pstream->execCallback(StreamIoFault); pstream->execCallback(StreamIoFault);
} }
@ -1274,19 +1320,19 @@ execute()
void Stream:: void Stream::
lockMutex() lockMutex()
{ {
#ifdef EPICS_3_14 #ifdef EPICS_3_13
mutex.lock();
#else
semTake(mutex, WAIT_FOREVER); semTake(mutex, WAIT_FOREVER);
#else
mutex.lock();
#endif #endif
} }
void Stream:: void Stream::
releaseMutex() releaseMutex()
{ {
#ifdef EPICS_3_14 #ifdef EPICS_3_13
mutex.unlock();
#else
semGive(mutex); semGive(mutex);
#else
mutex.unlock();
#endif #endif
} }

View File

@ -20,6 +20,7 @@
#include "StreamError.h" #include "StreamError.h"
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <stdio.h>
int streamDebug = 0; int streamDebug = 0;
extern "C" { extern "C" {

View File

@ -20,8 +20,8 @@
#ifndef StreamError_h #ifndef StreamError_h
#define StreamError_h #define StreamError_h
#include <stdio.h>
#include <stdarg.h> #include <stdarg.h>
#include <stddef.h>
#ifndef __GNUC__ #ifndef __GNUC__
#define __attribute__(x) #define __attribute__(x)

View File

@ -316,7 +316,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
bool StdLongConverter:: bool StdLongConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value) printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{ {
output.printf(fmt.info, value); output.print(fmt.info, value);
return true; return true;
} }
@ -395,7 +395,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
bool StdDoubleConverter:: bool StdDoubleConverter::
printDouble(const StreamFormat& fmt, StreamBuffer& output, double value) printDouble(const StreamFormat& fmt, StreamBuffer& output, double value)
{ {
output.printf(fmt.info, value); output.print(fmt.info, value);
return true; return true;
} }
@ -451,7 +451,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
bool StdStringConverter:: bool StdStringConverter::
printString(const StreamFormat& fmt, StreamBuffer& output, const char* value) printString(const StreamFormat& fmt, StreamBuffer& output, const char* value)
{ {
output.printf(fmt.info, value); output.print(fmt.info, value);
return true; return true;
} }
@ -549,7 +549,7 @@ parse(const StreamFormat& fmt, StreamBuffer& info,
bool StdCharsConverter:: bool StdCharsConverter::
printLong(const StreamFormat& fmt, StreamBuffer& output, long value) printLong(const StreamFormat& fmt, StreamBuffer& output, long value)
{ {
output.printf(fmt.info, value); output.print(fmt.info, value);
return true; return true;
} }

View File

@ -634,7 +634,7 @@ printString(StreamBuffer& buffer, const char* s)
switch (*s) switch (*s)
{ {
case esc: case esc:
buffer.printf("\\x%02x", (*++s) & 0xff); buffer.print("\\x%02x", (*++s) & 0xff);
break; break;
case '\r': case '\r':
buffer.append("\\r"); buffer.append("\\r");
@ -655,7 +655,7 @@ printString(StreamBuffer& buffer, const char* s)
buffer.append("\\\\"); buffer.append("\\\\");
break; break;
case format_field: case format_field:
buffer.printf("%%(%s)", ++s); buffer.print("%%(%s)", ++s);
while (*s++); while (*s++);
s += extract<unsigned short>(s); // skip fieldaddress s += extract<unsigned short>(s); // skip fieldaddress
goto format; goto format;
@ -670,7 +670,7 @@ format: {
continue; continue;
default: default:
if ((*s & 0x7f) < 0x20 || (*s & 0x7f) == 0x7f) if ((*s & 0x7f) < 0x20 || (*s & 0x7f) == 0x7f)
buffer.printf("\\x%02x", *s & 0xff); buffer.print("\\x%02x", *s & 0xff);
else else
buffer.append(*s); buffer.append(*s);
} }
@ -742,7 +742,7 @@ Protocol(const Protocol& p, StreamBuffer& name, int _line)
int i; int i;
const char* nextparameter; const char* nextparameter;
parameter[0] = protocolname(); parameter[0] = protocolname();
for (i = 0; i < 10; i++) for (i = 0; i < 9; i++)
{ {
debug("StreamProtocolParser::Protocol::Protocol $%d=\"%s\"\n", debug("StreamProtocolParser::Protocol::Protocol $%d=\"%s\"\n",
i, parameter[i]); i, parameter[i]);
@ -1059,7 +1059,6 @@ compileString(StreamBuffer& buffer, const char*& source,
FormatType formatType, Client* client, int quoted) FormatType formatType, Client* client, int quoted)
{ {
bool escaped = false; bool escaped = false;
int n;
int newline = 0; int newline = 0;
StreamBuffer formatbuffer; StreamBuffer formatbuffer;
int formatpos = buffer.length(); int formatpos = buffer.length();
@ -1136,7 +1135,8 @@ compileString(StreamBuffer& buffer, const char*& source,
if (escaped) // char after \ in quoted text if (escaped) // char after \ in quoted text
{ {
escaped = false; escaped = false;
unsigned int temp; unsigned int c;
int n=1;
switch (*source) switch (*source)
{ {
case '$': // can't be: readToken would have made a token from this case '$': // can't be: readToken would have made a token from this
@ -1145,61 +1145,50 @@ compileString(StreamBuffer& buffer, const char*& source,
return false; return false;
case '?': case '?':
buffer.append(skip); buffer.append(skip);
break; source++;
continue;
case '_': case '_':
buffer.append(whitespace); buffer.append(whitespace);
break; source++;
continue;
case 'a': case 'a':
buffer.append(7); c=7;
break; break;
case 'b': case 'b':
buffer.append(8); c=8;
break; break;
case 't': case 't':
buffer.append(9); c=9;
break; break;
case 'n': case 'n':
buffer.append('\n'); c='\n';
break; break;
case 'r': case 'r':
buffer.append('\r'); c='\r';
break; break;
case 'e': case 'e':
buffer.append(esc).append(esc); c=esc;
break; break;
case '0': // octal numbers (max 3 digits after 0) case '0': // octal numbers (max 4 digits)
sscanf (source, "%3o%n", &temp, &n); sscanf (source, "%4o%n", &c, &n); //cannot fail
if (temp > 0xFF) if (c > 0xFF)
{ {
error(line, filename(), error(line, filename(),
"Octal source %#o does not fit in byte: %s\n", "Octal number %#o does not fit in byte: \"%s\"\n",
temp, source-1); c, source-1);
return false; return false;
} }
if (formatType != NoFormat && break;
(temp < last_function_code || temp == esc)) case 'x': // hex numbers (max 2 digits after x)
{ if (sscanf (source+1, "%2x%n", &c, &n) < 1)
buffer.append(esc);
}
buffer.append(temp);
source += n;
continue;
case 'x': // hex numbers (max 2 digits after 0)
if (sscanf (source+1, "%2x%n", &temp, &n) < 1)
{ {
error(line, filename(), error(line, filename(),
"Hex digit expected after \\x: %s\n", "Hex digit expected after \\x: \"%s\"\n",
source-1); source-1);
return false; return false;
} }
if (formatType != NoFormat && n++;
(temp < last_function_code || temp == esc)) break;
{
buffer.append(esc);
}
buffer.append(temp);
source += n+1;
continue;
case '1': // decimal numbers (max 3 digits) case '1': // decimal numbers (max 3 digits)
case '2': case '2':
case '3': case '3':
@ -1209,26 +1198,24 @@ compileString(StreamBuffer& buffer, const char*& source,
case '7': case '7':
case '8': case '8':
case '9': case '9':
sscanf (source, "%3u%n", &temp, &n); sscanf (source, "%3u%n", &c, &n); //cannot fail
if (temp > 0xFF) if (c > 0xFF)
{ {
error(line, filename(), error(line, filename(),
"Decimal source %d does not fit in byte: %s\n", "Decimal number %d does not fit in byte: \"%s\"\n",
temp, source-1); c, source-1);
return false; return false;
} }
if (formatType != NoFormat && break;
(temp < last_function_code || temp == esc))
{
buffer.append(esc);
}
buffer.append(temp);
source += n;
continue;
default: // escaped literals default: // escaped literals
buffer.append(esc).append(*source); c=*source;
} }
source++; source+=n;
if (formatType != NoFormat)
{
buffer.append(esc);
}
buffer.append(c);
continue; continue;
} }
if (quoted) // look for ending quotes and escapes if (quoted) // look for ending quotes and escapes
@ -1281,7 +1268,7 @@ compileString(StreamBuffer& buffer, const char*& source,
} }
// try numeric byte value // try numeric byte value
char* p; char* p;
int temp = strtol(source, &p, 0); int c = strtol(source, &p, 0);
if (p != source) if (p != source)
{ {
if (*p != 0) if (*p != 0)
@ -1290,18 +1277,17 @@ compileString(StreamBuffer& buffer, const char*& source,
"Garbage after numeric source: %s", source); "Garbage after numeric source: %s", source);
return false; return false;
} }
if (temp > 0xFF || temp < -0x80) if (c > 0xFF || c < -0x80)
{ {
error(line, filename(), error(line, filename(),
"Value %s does not fit in byte\n", source); "Value %s does not fit in byte\n", source);
return false; return false;
} }
if (formatType != NoFormat && if (formatType != NoFormat)
(temp < last_function_code || temp == esc))
{ {
buffer.append(esc); buffer.append(esc);
} }
buffer.append(temp); buffer.append(c);
source = p+1+sizeof(int); source = p+1+sizeof(int);
continue; continue;
} }
@ -1348,7 +1334,7 @@ compileString(StreamBuffer& buffer, const char*& source,
{"del", 0x7f} {"del", 0x7f}
}; };
size_t i; size_t i;
char c = 0; c=-1;
for (i = 0; i < sizeof(codes)/sizeof(*codes); i++) for (i = 0; i < sizeof(codes)/sizeof(*codes); i++)
{ {
if (strcmp(source, codes[i].name) == 0) if (strcmp(source, codes[i].name) == 0)
@ -1361,8 +1347,7 @@ compileString(StreamBuffer& buffer, const char*& source,
source); source);
return false; return false;
} }
if (formatType != NoFormat && if (formatType != NoFormat)
(temp < last_function_code || temp == esc))
{ {
buffer.append(esc); buffer.append(esc);
} }
@ -1371,10 +1356,10 @@ compileString(StreamBuffer& buffer, const char*& source,
break; break;
} }
} }
if (c) continue; if (c >= 0) continue;
// source may contain a function name // source may contain a function name
error(line, filename(), error(line, filename(),
"Unexpected word: %s\n", source); "Unexpected word: \"%s\"\n", source);
return false; return false;
} }
debug("StreamProtocolParser::Protocol::compileString buffer=%s\n", buffer.expand()()); debug("StreamProtocolParser::Protocol::compileString buffer=%s\n", buffer.expand()());
@ -1493,8 +1478,10 @@ compileFormat(StreamBuffer& buffer, const char*& formatstr,
buffer.append(&streamFormat, sizeof(streamFormat)); buffer.append(&streamFormat, sizeof(streamFormat));
buffer.append(infoString); buffer.append(infoString);
debug("StreamProtocolParser::Protocol::compileFormat: format.type=%s, infolen=%d\n", debug("StreamProtocolParser::Protocol::compileFormat: format.type=%s, "
StreamFormatTypeStr[streamFormat.type], streamFormat.infolen); "infolen=%d infostring=\"%s\"\n",
StreamFormatTypeStr[streamFormat.type],
streamFormat.infolen, infoString.expand()());
formatstr = source; // move pointer after parsed format formatstr = source; // move pointer after parsed format
return true; return true;
} }

View File

@ -23,6 +23,7 @@
#define STR2(x) #x #define STR2(x) #x
#define STR(x) STR2(x) #define STR(x) STR2(x)
const char StreamVersion [] = const char StreamVersion [] =
"StreamDevice " STR(STREAM_MAJOR) "." STR(STREAM_MINOR) "StreamDevice " STR(STREAM_MAJOR)
"." STR(STREAM_MINOR)
"." STR(STREAM_PATCHLEVEL)
" built " __DATE__ " " __TIME__; " built " __DATE__ " " __TIME__;

View File

@ -82,14 +82,14 @@ parse(const StreamFormat&, StreamBuffer& info,
if (*c == 'f') if (*c == 'f')
{ {
source = c; source = c;
info.printf("%%0%uf", n); info.print("%%0%uf", n);
break; break;
} }
} }
/* look for nanoseconds %N of %f */ /* look for nanoseconds %N of %f */
if (*source == 'N' || *source == 'f') if (*source == 'N' || *source == 'f')
{ {
info.printf("%%09f"); info.print("%%09f");
break; break;
} }
/* look for seconds with fractions like %.3S */ /* look for seconds with fractions like %.3S */
@ -100,7 +100,7 @@ parse(const StreamFormat&, StreamBuffer& info,
if (toupper(*c) == 'S') if (toupper(*c) == 'S')
{ {
source = c; source = c;
info.printf("%%%c.%%0%uf", *c, n); info.print("%%%c.%%0%uf", *c, n);
break; break;
} }
} }

View File

@ -22,7 +22,8 @@
#define devStream_h #define devStream_h
#define STREAM_MAJOR 2 #define STREAM_MAJOR 2
#define STREAM_MINOR 4 #define STREAM_MINOR 5
#define STREAM_PATCHLEVEL 12
#if defined(__vxworks) || defined(vxWorks) #if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h> #include <vxWorks.h>
@ -40,11 +41,11 @@
#define INIT_RUN (!interruptAccept) #define INIT_RUN (!interruptAccept)
#include <epicsVersion.h> #include <epicsVersion.h>
#if (EPICS_VERSION == 3 && EPICS_REVISION == 14) #ifdef BASE_RELEASE
#define EPICS_3_14 #define EPICS_3_13
#endif #endif
#if defined(__cplusplus) && !defined(EPICS_3_14) #if defined(__cplusplus) && defined(EPICS_3_13)
extern "C" { extern "C" {
#endif #endif
@ -55,7 +56,7 @@ extern "C" {
/* #include <dbFldTypes.h> */ /* #include <dbFldTypes.h> */
#include <dbAccess.h> #include <dbAccess.h>
#if defined(__cplusplus) && !defined(EPICS_3_14) #if defined(__cplusplus) && defined(EPICS_3_13)
} }
#endif #endif
@ -69,23 +70,22 @@ typedef const struct format_s {
extern "C" { extern "C" {
#endif #endif
#ifdef _WIN32 epicsShareExtern FILE* StreamDebugFile;
__declspec(dllimport)
#endif
extern FILE* StreamDebugFile;
extern const char StreamVersion []; extern const char StreamVersion [];
typedef long (*streamIoFunction) (dbCommon*, format_t*); typedef long (*streamIoFunction) (dbCommon*, format_t*);
long streamInit(int after); epicsShareExtern long streamInit(int after);
long streamInitRecord(dbCommon *record, struct link *ioLink, epicsShareExtern long streamInitRecord(dbCommon *record,
const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData); streamIoFunction readData, streamIoFunction writeData);
long streamReport(int interest); epicsShareExtern long streamReport(int interest);
long streamReadWrite(dbCommon *record); epicsShareExtern long streamReadWrite(dbCommon *record);
long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt); epicsShareExtern long streamGetIointInfo(int cmd,
long streamPrintf(dbCommon *record, format_t *format, ...); dbCommon *record, IOSCANPVT *ppvt);
long streamScanfN(dbCommon *record, format_t *format, epicsShareExtern long streamPrintf(dbCommon *record, format_t *format, ...);
epicsShareExtern long streamScanfN(dbCommon *record, format_t *format,
void*, size_t maxStringSize); void*, size_t maxStringSize);
/* backward compatibility stuff */ /* backward compatibility stuff */

View File

@ -1,6 +1,10 @@
if (@ARGV[0] == "-3.13") {
shift;
} else {
print "variable(streamDebug, int)\n";
print "registrar(streamRegistrar)\n";
}
print "driver(stream)\n";
for (@ARGV) { for (@ARGV) {
print "device($_,INST_IO,dev${_}Stream,\"stream\")\n"; print "device($_,INST_IO,dev${_}Stream,\"stream\")\n";
} }
print "driver(stream)\n";
print "variable(streamDebug, int)\n";
print "registrar(streamRegistrar)\n";

View File

@ -29,17 +29,10 @@ PROD_SRCS_vxWorks = -nil-
PROD_LIBS = stream PROD_LIBS = stream
ifdef ASYN ifdef ASYN
# Which types of asyn busses do you have? # edit asynRegistrars.dbd if necessary
streamApp_DBD += asynRegistrars.dbd
# asynDriver up to version 4-16 does not support serial port for Windows! # add asyn.dbd if you want to have asyn Record and asyn device supports
#ifneq ($(OS_CLASS), WIN32) # streamApp_DBD += asyn.dbd
streamApp_DBD += drvAsynSerialPort.dbd
#endif
streamApp_DBD += drvAsynIPPort.dbd
#streamApp_DBD += drvGsIP488.dbd
#streamApp_DBD += drvNi1014.dbd
streamApp_DBD += drvVxi11.dbd
PROD_LIBS += asyn PROD_LIBS += asyn
endif endif
@ -73,7 +66,11 @@ CPPFLAGS += -DDEBUGFILE=StreamDebug.log
include $(TOP)/configure/RULES include $(TOP)/configure/RULES
ifeq ($(EPICS_REVISION),14)
clean:: myclean clean:: myclean
else
clean: myclean
endif
myclean: myclean:
$(RM) core* StreamDebug.log $(RM) core* StreamDebug.log

View File

@ -0,0 +1,9 @@
registrar(asynRegister)
registrar(asynInterposeFlushRegister)
registrar(asynInterposeEosRegister)
# asynDriver up to version 4-16 does not support serial port for Windows!
registrar(drvAsynSerialPortRegisterCommands)
registrar(drvAsynIPPortRegisterCommands)
registrar(drvAsynIPServerPortRegisterCommands)
registrar(vxi11RegisterCommands)

View File

@ -1,7 +1,5 @@
#!/usr/bin/env wish #!/usr/bin/env wish
wm iconify .
proc createTerm {sock} { proc createTerm {sock} {
global socket port global socket port
toplevel .$sock toplevel .$sock
@ -19,16 +17,8 @@ proc createTerm {sock} {
wm title .$sock "port $port <-> [fconfigure $sock -peername]" wm title .$sock "port $port <-> [fconfigure $sock -peername]"
} }
set port [lindex $argv 0]
if {$port == ""} { set port 40000 }
if [catch {
socket -server connect $port
} msg ] {
return -code error "$msg (port $port)"
}
proc connect {sock addr port} { proc connect {sock addr port} {
fconfigure $sock -blocking 0 -buffering none fconfigure $sock -blocking 0 -buffering none -translation binary
createTerm $sock createTerm $sock
fileevent $sock readable "receiveHandler $sock" fileevent $sock readable "receiveHandler $sock"
} }
@ -84,6 +74,10 @@ proc receiveHandler {sock} {
"echo" { "echo" {
sendReply $sock [string range $a 5 end] sendReply $sock [string range $a 5 end]
} }
"binary" {
set x [checkNum [lindex $l 1]]
sendReply $sock [format %c $x]
}
"longmsg" { "longmsg" {
set length [checkNum [lindex $l 1]] set length [checkNum [lindex $l 1]]
sendReply $sock "[string range x[string repeat 0123456789abcdefghijklmnopqrstuvwxyz [expr $length / 36 + 1]] 1 $length]\n" sendReply $sock "[string range x[string repeat 0123456789abcdefghijklmnopqrstuvwxyz [expr $length / 36 + 1]] 1 $length]\n"
@ -95,7 +89,7 @@ proc receiveHandler {sock} {
"start" { "start" {
set wait [checkNum [lindex $l 1]] set wait [checkNum [lindex $l 1]]
set ::counter 0 set ::counter 0
after $wait sendAsync $wait [list [lindex $l 2]] after $wait sendAsync $wait [list [lrange $l 2 end-1]]
sendReply $sock "Started\n" sendReply $sock "Started\n"
} }
"stop" { "stop" {
@ -116,12 +110,16 @@ proc receiveHandler {sock} {
"help" { "help" {
sendReply $sock "help this text\n" sendReply $sock "help this text\n"
sendReply $sock "echo string reply string\n" sendReply $sock "echo string reply string\n"
sendReply $sock "wait msec reply Done after some time\n" sendReply $sock "binary number reply byte with value number\n"
sendReply $sock "longmsg length reply string with length characters\n"
sendReply $sock "wait msec reply \"Done\" after some time\n"
sendReply $sock "start msec start sending messages priodically\n" sendReply $sock "start msec start sending messages priodically\n"
sendReply $sock "stop stop sending messages\n" sendReply $sock "stop stop sending messages\n"
sendReply $sock "set key value set a value\n" sendReply $sock "set key value store a value into variable key\n"
sendReply $sock "get key reply value\n" sendReply $sock "get key reply previously stored value from key\n"
sendReply $sock "disconnect close connection\n" sendReply $sock "disconnect close connection\n"
sendReply $sock "exit kill terminal server\n"
} }
} }
} msg] { } msg] {
@ -153,7 +151,6 @@ rename $paste tkTextPaste_org
rename $pastesel tkTextPasteSel_org rename $pastesel tkTextPasteSel_org
proc $insert {w s} { proc $insert {w s} {
puts [list insert $w $s]
global socket global socket
if {[string equal $s ""] || [string equal [$w cget -state] "disabled"]} { if {[string equal $s ""] || [string equal [$w cget -state] "disabled"]} {
return return
@ -191,8 +188,20 @@ for {set ascii 0x61} {$ascii <= 0x7a} {incr ascii} {
bind Text <Control-[format %c $ascii]> "" bind Text <Control-[format %c $ascii]> ""
} }
#remove bindings on symbolic tags #remove bindings on symbolic tags
foreach tag {Clear Paste Copy Cut } { foreach tag {Clear Paste Copy Cut} {
bind Text <<$tag>> "" bind Text <<$tag>> ""
} }
bind Text <Control-Key> [list $insert %W %A] bind Text <Control-Key> [list $insert %W %A]
set port [lindex $argv 0]
if {$port == ""} { set port 40000 }
if [catch {
socket -server connect $port
} msg ] {
return -code error "$msg (port $port)"
}
label .info -text "Accepting connections on port $port"
button .exit -text "Exit" -command exit
pack .info .exit -expand yes -fill x

View File

@ -2,17 +2,20 @@ record (stringout, "$(P):cmd")
{ {
field (DTYP, "stream") field (DTYP, "stream")
field (OUT, "@test.proto command terminal") field (OUT, "@test.proto command terminal")
field (PRIO, "HIGH")
field (VAL, "") field (VAL, "")
} }
record (stringout, "$(P):info") record (stringout, "$(P):info")
{ {
field (DTYP, "stream") field (DTYP, "stream")
field (OUT, "@test.proto info terminal") field (OUT, "@test.proto info terminal")
field (PRIO, "HIGH")
} }
record (stringout, "$(P):request") record (stringout, "$(P):request")
{ {
field (DTYP, "stream") field (DTYP, "stream")
field (OUT, "@test.proto request($(P):reply.VAL) terminal") field (OUT, "@test.proto request($(P):reply.VAL) terminal")
field (PRIO, "HIGH")
} }
record (stringin, "$(P):reply") record (stringin, "$(P):reply")
{ {

View File

@ -1,5 +1,5 @@
terminator = CR LF; terminator = LF;
readtimeout = 1000; readtimeout = 10;
pollperiod = 10; pollperiod = 10;
replytimeout = 1000; replytimeout = 1000;
command { command {

View File

@ -52,7 +52,7 @@ proc startioc {} {
if [info exists streamversion] { if [info exists streamversion] {
puts $fd "#!/usr/local/bin/iocsh" puts $fd "#!/usr/local/bin/iocsh"
puts $fd "require stream,$streamversion" puts $fd "require stream2,$streamversion"
} else { } else {
puts $fd "#!../O.$env(EPICS_HOST_ARCH)/streamApp" puts $fd "#!../O.$env(EPICS_HOST_ARCH)/streamApp"
puts $fd "dbLoadDatabase ../O.Common/streamApp.dbd" puts $fd "dbLoadDatabase ../O.Common/streamApp.dbd"

View File

@ -7,6 +7,11 @@ source streamtestlib.tcl
# Send commands to the ioc shell with ioccmd # Send commands to the ioc shell with ioccmd
set records { set records {
record (bo, "DZ:percent")
{
field (DTYP, "stream")
field (OUT, "@test.proto percent device")
}
record (ao, "DZ:ao") record (ao, "DZ:ao")
{ {
field (DTYP, "stream") field (DTYP, "stream")
@ -34,6 +39,7 @@ set records {
set protocol { set protocol {
Terminator = LF; Terminator = LF;
percent {out "\%\x25\37\045" 0x25 37 045;}
ao {out "%.2f %.2e %.2E %.2g %.2G %i %d %u %o %04x %#.2f %#.2e %#.2E %#.2g %#.2G %#i %#d %#u %#o %#06x";} ao {out "%.2f %.2e %.2E %.2g %.2G %i %d %u %o %04x %#.2f %#.2e %#.2E %#.2g %#.2G %#i %#d %#u %#o %#06x";}
lo {out "%d %(VAL)d %06d %x %06X %b %06b %.6b %B.! %06B.!";} lo {out "%d %(VAL)d %06d %x %06X %b %06b %.6b %B.! %06B.!";}
bcd {out "%D %6D %.2D %.4D %.6D %.8D %#D %#6D %#.2D %#.4D %#.6D";} bcd {out "%D %6D %.2D %.4D %.6D %.8D %#D %#6D %#.2D %#.4D %#.6D";}
@ -58,6 +64,8 @@ startioc
# This is normal. E.g. -1 HAS a different number of 1 bits. # This is normal. E.g. -1 HAS a different number of 1 bits.
# Specify the width field in the format if this is a problem. # Specify the width field in the format if this is a problem.
ioccmd {dbpf DZ:percent 1}
assure "%%%%%%%\n"
ioccmd {dbpf DZ:ao 0} ioccmd {dbpf DZ:ao 0}
assure "0.00 0.00e+00 0.00E+00 0 0 32767 32767 32767 77777 7fff 0.00 0.00e+00 0.00E+00 0.0 0.0 32767 32767 32767 077777 0x7fff\n" assure "0.00 0.00e+00 0.00E+00 0 0 32767 32767 32767 77777 7fff 0.00 0.00e+00 0.00E+00 0.0 0.0 32767 32767 32767 077777 0x7fff\n"
ioccmd {dbpf DZ:ao 10} ioccmd {dbpf DZ:ao 10}