Compare commits

...

15 Commits

10 changed files with 326 additions and 221 deletions

View File

@ -35,6 +35,7 @@ SOURCES += $(BUSSES:%=src/%Interface.cc)
SOURCES += $(wildcard src/Stream*.cc)
SOURCES += src/StreamVersion.c
HEADERS += devStream.h
HEADERS += StreamFormat.h
HEADERS += StreamFormatConverter.h
HEADERS += StreamBuffer.h

View File

@ -22,14 +22,19 @@
#include "StreamError.h"
#include "StreamBuffer.h"
#ifdef EPICS_3_14
#include <epicsAssert.h>
#include <epicsTime.h>
#include <epicsTimer.h>
#else
#include <epicsVersion.h>
#ifdef BASE_VERSION
#define EPICS_3_13
#endif
#ifdef EPICS_3_13
#include <assert.h>
#include <wdLib.h>
#include <sysLib.h>
#else
#include <epicsAssert.h>
#include <epicsTime.h>
#include <epicsTimer.h>
extern "C" {
#include <callback.h>
}
@ -126,7 +131,7 @@ static const char* ioActionStr[] = {
};
static const char* asynStatusStr[] = {
"asynSuccess", "asynTimeout", "asynOverflow", "asynError"
"asynSuccess", "asynTimeout", "asynOverflow", "asynError", "asynDisconnected", "asynDisabled"
};
static const char* eomReasonStr[] = {
@ -134,7 +139,7 @@ static const char* eomReasonStr[] = {
};
class AsynDriverInterface : StreamBusInterface
#ifdef EPICS_3_14
#ifndef EPICS_3_13
, epicsTimerNotify
#endif
{
@ -164,12 +169,12 @@ class AsynDriverInterface : StreamBusInterface
const char* outputBuffer;
size_t outputSize;
int peeksize;
#ifdef EPICS_3_14
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
#else
#ifdef EPICS_3_13
WDOG_ID timer;
CALLBACK timeoutCallback;
#else
epicsTimerQueueActive* timerQueue;
epicsTimer* timer;
#endif
AsynDriverInterface(Client* client);
@ -189,11 +194,11 @@ class AsynDriverInterface : StreamBusInterface
bool disconnectRequest();
void finish();
#ifdef EPICS_3_14
#ifdef EPICS_3_13
static void expire(CALLBACK *pcallback);
#else
// epicsTimerNotify methods
epicsTimerNotify::expireStatus expire(const epicsTime &);
#else
static void expire(CALLBACK *pcallback);
#endif
// local methods
@ -211,20 +216,22 @@ class AsynDriverInterface : StreamBusInterface
(StreamBusInterface::priority());
}
void startTimer(double timeout) {
#ifdef EPICS_3_14
timer->start(*this, timeout);
#else
#ifdef EPICS_3_13
callbackSetPriority(priority(), &timeoutCallback);
wdStart(timer, (int)((timeout+1)*sysClkRateGet())-1,
reinterpret_cast<FUNCPTR>(callbackRequest),
reinterpret_cast<int>(&timeoutCallback));
#else
timer->start(*this, timeout
+epicsThreadSleepQuantum()*0.5
);
#endif
}
void cancelTimer() {
#ifdef EPICS_3_14
timer->cancel();
#else
#ifdef EPICS_3_13
wdCancel(timer);
#else
timer->cancel();
#endif
}
@ -263,15 +270,15 @@ AsynDriverInterface(Client* client) : StreamBusInterface(client)
handleTimeout);
assert(pasynUser);
pasynUser->userPvt = this;
#ifdef EPICS_3_14
#ifdef EPICS_3_13
timer = wdCreate();
callbackSetCallback(expire, &timeoutCallback);
callbackSetUser(this, &timeoutCallback);
#else
timerQueue = &epicsTimerQueueActive::allocate(true);
assert(timerQueue);
timer = &timerQueue->createTimer();
assert(timer);
#else
timer = wdCreate();
callbackSetCallback(expire, &timeoutCallback);
callbackSetUser(this, &timeoutCallback);
#endif
}
@ -306,11 +313,11 @@ AsynDriverInterface::
}
// Now, no handler is running any more and none will start.
#ifdef EPICS_3_14
#ifdef EPICS_3_13
wdDelete(timer);
#else
timer->destroy();
timerQueue->release();
#else
wdDelete(timer);
#endif
pasynManager->disconnect(pasynUser);
pasynManager->freeAsynUser(pasynUser);
@ -516,7 +523,7 @@ connectToAsynPort()
status = pasynOctet->read(pvtOctet, pasynUser,
buffer, 0, &received, &eomReason);
debug("AsynDriverInterface::connectToAsynPort(%s): "
"read(..., 0, ...) [timeout=%f seconds] = %s\n",
"read(..., 0, ...) [timeout=%g sec] = %s\n",
clientName(), pasynUser->timeout,
asynStatusStr[status]);
pasynManager->isConnected(pasynUser, &connected);
@ -610,19 +617,26 @@ writeHandler()
clientName());
asynStatus status;
size_t written = 0;
pasynUser->timeout = writeTimeout;
// discard any early input or early events
status = pasynOctet->flush(pvtOctet, pasynUser);
// discard any early input, but forward it to potential async records
// 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;
if (status != asynSuccess)
{
error("%s: pasynOctet->flush() failed: %s\n",
clientName(), pasynUser->errorMessage);
writeCallback(StreamIoFault);
return;
}
pasynUser->timeout = writeTimeout;
// has stream already added a terminator or should
// asyn do so?
@ -648,7 +662,7 @@ writeHandler()
outputBuffer, outputSize, &written);
debug("AsynDriverInterface::writeHandler(%s): "
"write(..., outputSize=%ld, written=%ld) "
"[timeout=%f seconds] = %s\n",
"[timeout=%g sec] = %s\n",
clientName(), (long)outputSize, (long)written,
pasynUser->timeout, asynStatusStr[status]);
@ -732,7 +746,7 @@ readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
long _expectedLength, bool async)
{
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,
_expectedLength, async?"yes":"no");
@ -754,12 +768,22 @@ readRequest(unsigned long replyTimeout_ms, unsigned long readTimeout_ms,
ioAction = Read;
queueTimeout = replyTimeout;
}
debug("AsynDriverInterface::readRequest %s: queueRequest(..., priority=%d, queueTimeout=%f)\n",
clientName(), priority(), queueTimeout);
status = pasynManager->queueRequest(pasynUser,
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",
clientName(), pasynUser->errorMessage);
return false;
@ -867,7 +891,7 @@ readHandler()
debug("AsynDriverInterface::readHandler(%s): ioAction=%s "
"read(..., bytesToRead=%ld, ...) "
"[timeout=%f seconds]\n",
"[timeout=%g sec]\n",
clientName(), ioActionStr[ioAction],
bytesToRead, pasynUser->timeout);
status = pasynOctet->read(pvtOctet, pasynUser,
@ -961,6 +985,9 @@ readHandler()
// reply timeout
if (ioAction == AsyncRead)
{
debug("AsynDriverInterface::readHandler(%s): "
"no async input, retry in in %g seconds\n",
clientName(), replyTimeout);
// start next poll after timer expires
if (replyTimeout != 0.0) startTimer(replyTimeout);
// continues with:
@ -977,7 +1004,7 @@ readHandler()
// read timeout
#ifndef NO_TEMPORARY
debug("AsynDriverInterface::readHandler(%s): "
"ioAction=%s, timeout [%f seconds] "
"ioAction=%s, timeout [%g sec] "
"after %ld of %ld bytes \"%s\"\n",
clientName(), ioActionStr[ioAction], pasynUser->timeout,
(long)received, bytesToRead,
@ -1238,6 +1265,9 @@ timerExpired()
int autoconnect, connected;
switch (ioAction)
{
case None:
// Timeout of async poll crossed with parasitic input
return;
case ReceiveEvent:
// timeout while waiting for event
ioAction = None;
@ -1256,6 +1286,8 @@ timerExpired()
// queueRequest might fail if another request was just queued
pasynManager->isAutoConnect(pasynUser, &autoconnect);
pasynManager->isConnected(pasynUser, &connected);
debug("%s: polling for I/O Intr: autoconnected: %d, connect: %d\n",
clientName(), autoconnect, connected);
if (autoconnect && !connected)
{
// has explicitely been disconnected
@ -1266,8 +1298,14 @@ timerExpired()
else
{
// queue for read poll (no timeout)
pasynManager->queueRequest(pasynUser,
asynStatus status = pasynManager->queueRequest(pasynUser,
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:
// handleRequest() -> readHandler() -> readCallback()
}
@ -1279,14 +1317,7 @@ timerExpired()
}
}
#ifdef EPICS_3_14
epicsTimerNotify::expireStatus AsynDriverInterface::
expire(const epicsTime &)
{
timerExpired();
return noRestart;
}
#else
#ifdef EPICS_3_13
void AsynDriverInterface::
expire(CALLBACK *pcallback)
{
@ -1294,6 +1325,13 @@ expire(CALLBACK *pcallback)
static_cast<AsynDriverInterface*>(pcallback->user);
interface->timerExpired();
}
#else
epicsTimerNotify::expireStatus AsynDriverInterface::
expire(const epicsTime &)
{
timerExpired();
return noRestart;
}
#endif
bool AsynDriverInterface::
@ -1392,6 +1430,7 @@ void handleRequest(asynUser* pasynUser)
{
AsynDriverInterface* interface =
static_cast<AsynDriverInterface*>(pasynUser->userPvt);
interface->cancelTimer();
debug("AsynDriverInterface::handleRequest(%s) %s\n",
interface->clientName(), ioActionStr[interface->ioAction]);
switch (interface->ioAction)

View File

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

View File

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

View File

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

View File

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

View File

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

@ -18,7 +18,7 @@ proc createTerm {sock} {
}
proc connect {sock addr port} {
fconfigure $sock -blocking 0 -buffering none
fconfigure $sock -blocking 0 -buffering none -translation binary
createTerm $sock
fileevent $sock readable "receiveHandler $sock"
}
@ -136,14 +136,6 @@ proc sendAsync {wait message} {
after $wait sendAsync $wait [list $message]
}
proc sendAsyncX {wait} {
if {$::counter < 0} return
foreach term [array names ::socket] {
sendReply $::socket($term) "\u00101\u0004~\u0005~\u00100\u0002|0062|2|1|0|1216|0|0.1087E+0 \u0003\u0012"
}
after $wait sendAsyncX $wait
}
if {[info proc tkTextInsert] != ""} {
set insert tkTextInsert
set paste tkTextPaste
@ -196,7 +188,7 @@ for {set ascii 0x61} {$ascii <= 0x7a} {incr ascii} {
bind Text <Control-[format %c $ascii]> ""
}
#remove bindings on symbolic tags
foreach tag {Clear Paste Copy Cut } {
foreach tag {Clear Paste Copy Cut} {
bind Text <<$tag>> ""
}

View File

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