Compare commits

...

13 Commits

8 changed files with 284 additions and 200 deletions

View File

@ -88,11 +88,11 @@ many process variables distributed over many records.
<h2>Recommended Readings</h2> <h2>Recommended Readings</h2>
<p> <p>
<a href="http://www.aps.anl.gov/epics/base/R3-14/8-docs/AppDevGuide.pdf" <a href="http://www.aps.anl.gov/epics/base/R3-14/12-docs/AppDevGuide"
target="ex">IOC Application Developer's Guide (PDF)</a> target="ex">IOC Application Developer's Guide</a>
</p> </p>
<p> <p>
<a href="http://www.aps.anl.gov/asd/controls/epics/EpicsDocumentation/AppDevManuals/RecordRef/Recordref-1.html" <a href="https://wiki-ext.aps.anl.gov/epics/index.php/RRM_3-14"
target="ex">EPICS Record Reference Manual</a> target="ex">EPICS Record Reference Manual</a>
</p> </p>

View File

@ -23,18 +23,18 @@
#include "StreamBuffer.h" #include "StreamBuffer.h"
#include <epicsVersion.h> #include <epicsVersion.h>
#if (EPICS_VERSION == 3 && EPICS_REVISION == 14) #ifdef BASE_VERSION
#define EPICS_3_14 #define EPICS_3_13
#endif #endif
#ifdef EPICS_3_14 #ifdef EPICS_3_13
#include <epicsAssert.h>
#include <epicsTime.h>
#include <epicsTimer.h>
#else
#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>
} }
@ -139,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
{ {
@ -169,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);
@ -194,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
@ -216,22 +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
+epicsThreadSleepQuantum()*0.5
);
#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
} }
@ -270,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
} }
@ -313,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);
@ -617,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?
@ -739,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");
@ -978,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:
@ -1255,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;
@ -1304,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)
{ {
@ -1319,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::

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

@ -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

@ -23,11 +23,11 @@
#include "StreamError.h" #include "StreamError.h"
#include <epicsVersion.h> #include <epicsVersion.h>
#if (EPICS_VERSION == 3 && EPICS_REVISION == 14) #ifdef BASE_VERSION
#define EPICS_3_14 #define EPICS_3_13
#endif #endif
#ifndef EPICS_3_14 #ifdef EPICS_3_13
extern "C" { extern "C" {
#endif #endif
@ -39,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>
@ -92,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;
@ -120,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
@ -144,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,
@ -171,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);
} }
@ -230,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[] =
@ -256,7 +260,7 @@ static void streamRegistrar ()
extern "C" { extern "C" {
epicsExportRegistrar(streamRegistrar); epicsExportRegistrar(streamRegistrar);
} }
#endif // EPICS_3_14 #endif
// driver support //////////////////////////////////////////////////////// // driver support ////////////////////////////////////////////////////////
@ -270,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;
@ -297,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::
@ -398,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)
@ -412,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);
@ -441,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");
@ -500,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);
@ -537,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))
@ -615,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))
{ {
@ -640,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());
@ -669,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;
} }
@ -749,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());
@ -767,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 ////////////////////////////////////////////
@ -840,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;
} }
@ -896,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
} }
@ -962,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)
@ -970,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);
} }
@ -1009,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",
@ -1059,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:
@ -1073,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;
@ -1081,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]);
@ -1095,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:
@ -1103,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;
} }
@ -1112,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
@ -1126,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
} }
@ -1171,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)
{ {
@ -1234,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);
} }
@ -1250,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);
} }
@ -1279,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

@ -22,8 +22,8 @@
#define devStream_h #define devStream_h
#define STREAM_MAJOR 2 #define STREAM_MAJOR 2
#define STREAM_MINOR 5 #define STREAM_MINOR 6
#define STREAM_PATCHLEVEL 11 #define STREAM_PATCHLEVEL 0
#if defined(__vxworks) || defined(vxWorks) #if defined(__vxworks) || defined(vxWorks)
#include <vxWorks.h> #include <vxWorks.h>
@ -41,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
@ -56,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
@ -77,11 +77,13 @@ extern const char StreamVersion [];
typedef long (*streamIoFunction) (dbCommon*, format_t*); typedef long (*streamIoFunction) (dbCommon*, format_t*);
epicsShareExtern long streamInit(int after); epicsShareExtern long streamInit(int after);
epicsShareExtern long streamInitRecord(dbCommon *record, struct link *ioLink, epicsShareExtern long streamInitRecord(dbCommon *record,
const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData); streamIoFunction readData, streamIoFunction writeData);
epicsShareExtern long streamReport(int interest); epicsShareExtern long streamReport(int interest);
epicsShareExtern long streamReadWrite(dbCommon *record); epicsShareExtern long streamReadWrite(dbCommon *record);
epicsShareExtern long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt); epicsShareExtern long streamGetIointInfo(int cmd,
dbCommon *record, IOSCANPVT *ppvt);
epicsShareExtern long streamPrintf(dbCommon *record, format_t *format, ...); epicsShareExtern long streamPrintf(dbCommon *record, format_t *format, ...);
epicsShareExtern long streamScanfN(dbCommon *record, format_t *format, epicsShareExtern long streamScanfN(dbCommon *record, format_t *format,
void*, size_t maxStringSize); void*, size_t maxStringSize);

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)