re-run @init in iocRun after iocPause and streamReload
This commit is contained in:
@ -55,21 +55,22 @@ its <code>SEVR</code> field set to <code>INVALID</code> and its
|
||||
<dt><code>TIMEOUT</code></dt>
|
||||
<dd>
|
||||
The device could not be locked (<code>LockTimeout</code>) or the
|
||||
device did not reply (<code>ReplyTimeout</code>).
|
||||
device did not reply in time
|
||||
(<a href="protocol.html#sysvar"><code>ReplyTimeout</code></a>).
|
||||
</dd>
|
||||
<dt><code>WRITE</code></dt>
|
||||
<dd>
|
||||
Output could not be written to the device (<code>WriteTimeout</code>).
|
||||
Output could not be written to the device in time
|
||||
(<a href="protocol.html#sysvar"><code>WriteTimeout</code></a>).
|
||||
</dd>
|
||||
<dt><code>READ</code></dt>
|
||||
<dd>
|
||||
Input from the device started but stopped unexpectedly
|
||||
(<code>ReadTimeout</code>).
|
||||
(<a href="protocol.html#sysvar"><code>ReadTimeout</code></a>).
|
||||
</dd>
|
||||
<dt><code>COMM</code></dt>
|
||||
<dd>
|
||||
The device driver reported some other communication error (e.g.
|
||||
unplugged cable).
|
||||
The device driver reported that the device is disconnected.
|
||||
</dd>
|
||||
<dt><code>CALC</code></dt>
|
||||
<dd>
|
||||
@ -146,6 +147,19 @@ read from a constant <code>INP</code> or <code>DOL</code> field,
|
||||
or restored from a bump-less reboot system
|
||||
(e.g. <em>autosave</em> from the <em>synApps</em> package).
|
||||
</p>
|
||||
<p class="new">
|
||||
The <code>@init</code> handler is called again in the following situations:
|
||||
</p>
|
||||
<ul class="new">
|
||||
<li>The device driver informs <em>StreamDevice</em> that the device has
|
||||
reconnected after it had been disconnected. This is only detectable for
|
||||
some types of busses.
|
||||
<li>The IOC is resumed with <code>iocRun</code> after it had
|
||||
been paused with <code>iocPause</code>. This requires EPICS 3.14.11 or higher.
|
||||
<li>The record protocol has been <a href="setup.html#reload">reloaded</a>
|
||||
using the shell function <code>streamReload</code> or the subroutine
|
||||
record funtion <code>streamReloadSub</code>.
|
||||
</ul>
|
||||
|
||||
<a name="iointr"></a>
|
||||
<h2>3. I/O Intr</h2>
|
||||
|
@ -557,8 +557,10 @@ There is a fixed set of exception handler names starting with
|
||||
<dd>
|
||||
Not really an exception but formally specified in the same syntax.
|
||||
This handler is called from <code>iocInit</code> during record
|
||||
initialization <span class="new">and again whenever it is detected
|
||||
that the device was disconnected and has reconnected</span>.
|
||||
initialization <span class="new">and again whenever <em>StreamDevice</em>
|
||||
detects that the device has reconnected after a disconnect or when the
|
||||
ioc is resumed with <code>iocRun</code> after beeing paused or
|
||||
when the protocol has been <a href="setup.html#reload">reloaded</a></span>.
|
||||
It can be used to initialize an output record with a value read from
|
||||
the device.
|
||||
Also see chapter <a href="processing.html#init">Record Processing</a>.
|
||||
|
@ -322,8 +322,13 @@ During development, the protocol files might change frequently.
|
||||
To prevent restarting the IOC all the time, it is possible to reload
|
||||
the protocol file of one or all records with the shell function
|
||||
<code>streamReload("<var>record</var>")</code>.
|
||||
If <code>"<var>record</var>"</code> is not given, all records using
|
||||
If <code>"<var>record</var>"</code> is not given
|
||||
<span class="new">or empty</span>, all records using
|
||||
<em>StreamDevice</em> reload their protocols.
|
||||
<span class="new">In EPICS 3.14 or higher,
|
||||
<code><var>record</var></code> can be a glob pattern.</span>
|
||||
</p>
|
||||
<p>
|
||||
Furthermore, the <code>streamReloadSub</code> function can be used
|
||||
with a subroutine record to reload all protocols.
|
||||
</p>
|
||||
@ -336,6 +341,8 @@ protocol is loaded.
|
||||
</p>
|
||||
|
||||
<p>
|
||||
<span class="new">Reloading triggers an <code>@init</code>
|
||||
<a href="protocol.html#except">handler</a>.</span>
|
||||
See the <a href="protocol.html">next chapter</a> for protocol files in depth.
|
||||
</p>
|
||||
|
||||
|
@ -66,7 +66,12 @@ extern DBBASE *pdbbase;
|
||||
#include "epicsStdioRedirect.h"
|
||||
#include "iocsh.h"
|
||||
|
||||
#if (!defined VERSION_INT && EPICS_MODIFICATION<9)
|
||||
#if defined(VERSION_INT) || EPICS_MODIFICATION >= 11
|
||||
#include "initHooks.h"
|
||||
#define WITH_IOC_RUN
|
||||
#endif
|
||||
|
||||
#if !defined(VERSION_INT) && EPICS_MODIFICATION < 9
|
||||
// iocshCmd() is missing in iocsh.h (up to R3.14.8.2)
|
||||
// To build with win32-x86, you MUST fix iocsh.h.
|
||||
// Move the declaration below to iocsh.h and rebuild base.
|
||||
@ -168,6 +173,10 @@ class Stream : protected StreamCore
|
||||
ssize_t scan(format_t *format, void* pvalue, size_t maxStringSize);
|
||||
bool process();
|
||||
|
||||
#ifdef WITH_IOC_RUN
|
||||
static void initHook(initHookState);
|
||||
#endif
|
||||
|
||||
// device support functions
|
||||
friend long streamInitRecord(dbCommon *record, const struct link *ioLink,
|
||||
streamIoFunction readData, streamIoFunction writeData);
|
||||
@ -201,10 +210,8 @@ long streamReloadSub()
|
||||
|
||||
long streamReload(const char* recordname)
|
||||
{
|
||||
DBENTRY dbentry;
|
||||
dbCommon* record;
|
||||
Stream* stream;
|
||||
long status;
|
||||
|
||||
int oldStreamError = streamError;
|
||||
streamError = 1;
|
||||
|
||||
@ -214,38 +221,24 @@ long streamReload(const char* recordname)
|
||||
return ERROR;
|
||||
}
|
||||
debug("streamReload(%s)\n", recordname);
|
||||
dbInitEntry(pdbbase,&dbentry);
|
||||
for (status = dbFirstRecordType(&dbentry); status == OK;
|
||||
status = dbNextRecordType(&dbentry))
|
||||
for (stream = static_cast<Stream*>(Stream::first); stream;
|
||||
stream = static_cast<Stream*>(stream->next))
|
||||
{
|
||||
for (status = dbFirstRecord(&dbentry); status == OK;
|
||||
status = dbNextRecord(&dbentry))
|
||||
{
|
||||
char* value;
|
||||
if (dbFindField(&dbentry, "DTYP") != OK)
|
||||
continue;
|
||||
if ((value = dbGetString(&dbentry)) == NULL)
|
||||
continue;
|
||||
if (strcmp(value, "stream") != 0)
|
||||
continue;
|
||||
record=(dbCommon*)dbentry.precnode->precord;
|
||||
if (recordname && strcmp(recordname, record->name) != 0)
|
||||
continue;
|
||||
|
||||
// This cancels any running protocol and reloads
|
||||
// the protocol file
|
||||
status = record->dset->init_record(record);
|
||||
if (status == OK || status == DO_NOT_CONVERT)
|
||||
{
|
||||
printf("%s: Protocol reloaded\n", record->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
error("%s: Protocol reload failed\n", record->name);
|
||||
}
|
||||
}
|
||||
if (recordname && recordname[0] &&
|
||||
#ifdef EPICS_3_13
|
||||
strcmp(stream->name(), recordname) == 0)
|
||||
#else
|
||||
!epicsStrGlobMatch(stream->name(), recordname))
|
||||
#endif
|
||||
continue;
|
||||
// This cancels any running protocol and reloads
|
||||
// the protocol file
|
||||
status = stream->record->dset->init_record(stream->record);
|
||||
if (status == OK || status == DO_NOT_CONVERT)
|
||||
printf("%s: Protocol reloaded\n", stream->name());
|
||||
else
|
||||
error("%s: Protocol reload failed\n", stream->name());
|
||||
}
|
||||
dbFinishEntry(&dbentry);
|
||||
StreamProtocolParser::free();
|
||||
streamError = oldStreamError;
|
||||
return OK;
|
||||
@ -388,25 +381,25 @@ report(int interest)
|
||||
}
|
||||
}
|
||||
|
||||
Stream* pstream;
|
||||
Stream* stream;
|
||||
printf(" connected records:\n");
|
||||
for (pstream = static_cast<Stream*>(first); pstream;
|
||||
pstream = static_cast<Stream*>(pstream->next))
|
||||
for (stream = static_cast<Stream*>(first); stream;
|
||||
stream = static_cast<Stream*>(stream->next))
|
||||
{
|
||||
if (interest == 2)
|
||||
{
|
||||
printf("\n%s: %s\n", pstream->name(),
|
||||
pstream->ioLink->value.instio.string);
|
||||
pstream->printProtocol();
|
||||
printf("\n%s: %s\n", stream->name(),
|
||||
stream->ioLink->value.instio.string);
|
||||
stream->printProtocol();
|
||||
}
|
||||
else
|
||||
{
|
||||
printf(" %s: %s\n", pstream->name(),
|
||||
pstream->ioLink->value.instio.string);
|
||||
printf(" %s: %s\n", stream->name(),
|
||||
stream->ioLink->value.instio.string);
|
||||
if (interest == 3)
|
||||
{
|
||||
StreamBuffer buffer;
|
||||
pstream->printStatus(buffer);
|
||||
stream->printStatus(buffer);
|
||||
printf(" %s\n", buffer());
|
||||
}
|
||||
}
|
||||
@ -416,23 +409,23 @@ report(int interest)
|
||||
|
||||
long streamReportRecord(const char* recordname)
|
||||
{
|
||||
Stream* pstream;
|
||||
for (pstream = static_cast<Stream*>(Stream::first); pstream;
|
||||
pstream = static_cast<Stream*>(pstream->next))
|
||||
Stream* stream;
|
||||
for (stream = static_cast<Stream*>(Stream::first); stream;
|
||||
stream = static_cast<Stream*>(stream->next))
|
||||
{
|
||||
if (!recordname ||
|
||||
#ifdef EPICS_3_13
|
||||
strcmp(pstream->name(), recordname) == 0)
|
||||
strcmp(stream->name(), recordname) == 0)
|
||||
#else
|
||||
epicsStrGlobMatch(pstream->name(), recordname))
|
||||
epicsStrGlobMatch(stream->name(), recordname))
|
||||
#endif
|
||||
{
|
||||
printf("%s: %s\n", pstream->name(),
|
||||
pstream->ioLink->value.instio.string);
|
||||
printf("%s: %s\n", stream->name(),
|
||||
stream->ioLink->value.instio.string);
|
||||
StreamBuffer buffer;
|
||||
pstream->printStatus(buffer);
|
||||
stream->printStatus(buffer);
|
||||
printf("%s\n", buffer());
|
||||
pstream->printProtocol();
|
||||
stream->printProtocol();
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
@ -453,33 +446,64 @@ drvInit()
|
||||
SYM_TYPE type;
|
||||
if (symFindByName(sysSymTbl,
|
||||
"STREAM_PROTOCOL_PATH", &symbol, &type) == OK)
|
||||
{
|
||||
path = *(char**)symbol;
|
||||
}
|
||||
else
|
||||
if (symFindByName(sysSymTbl,
|
||||
"STREAM_PROTOCOL_DIR", &symbol, &type) == OK)
|
||||
{
|
||||
path = *(char**)symbol;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
if (!path)
|
||||
{
|
||||
fprintf(stderr,
|
||||
"drvStreamInit: Warning! STREAM_PROTOCOL_PATH not set. "
|
||||
"Defaults to \"%s\"\n", StreamProtocolParser::path);
|
||||
}
|
||||
else
|
||||
{
|
||||
StreamProtocolParser::path = path;
|
||||
}
|
||||
debug("StreamProtocolParser::path = %s\n",
|
||||
StreamProtocolParser::path);
|
||||
StreamPrintTimestampFunction = streamEpicsPrintTimestamp;
|
||||
|
||||
#ifdef WITH_IOC_RUN
|
||||
initHookRegister(initHook);
|
||||
#endif
|
||||
|
||||
return OK;
|
||||
}
|
||||
|
||||
#ifdef WITH_IOC_RUN
|
||||
void Stream::
|
||||
initHook(initHookState state)
|
||||
{
|
||||
Stream* stream;
|
||||
|
||||
if (state == initHookAtIocRun)
|
||||
{
|
||||
debug("Stream::initHook(initHookAtIocRun) interruptAccept=%d\n", interruptAccept);
|
||||
|
||||
static int inIocInit = 1;
|
||||
if (inIocInit)
|
||||
{
|
||||
// We don't want to run @init twice in iocInit
|
||||
inIocInit = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
for (stream = static_cast<Stream*>(first); stream;
|
||||
stream = static_cast<Stream*>(stream->next))
|
||||
{
|
||||
if (!stream->onInit) continue;
|
||||
debug("Stream::initHook(initHookAtIocRun) Re-inititializing %s\n", stream->name());
|
||||
if (!stream->startProtocol(StartInit))
|
||||
{
|
||||
error("%s: Re-initialization failed.\n",
|
||||
stream->name());
|
||||
}
|
||||
stream->initDone.wait();
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// device support (C interface) //////////////////////////////////////////
|
||||
|
||||
long streamInit(int after)
|
||||
@ -490,6 +514,7 @@ long streamInit(int after)
|
||||
static int first = 1;
|
||||
if (first)
|
||||
{
|
||||
// restore error filtering to previous setting
|
||||
streamError = oldStreamError;
|
||||
StreamProtocolParser::free();
|
||||
first = 0;
|
||||
@ -500,6 +525,7 @@ long streamInit(int after)
|
||||
static int first = 1;
|
||||
if (first)
|
||||
{
|
||||
// enable errors while reading protocol files
|
||||
oldStreamError = streamError;
|
||||
streamError = 1;
|
||||
first = 0;
|
||||
@ -520,38 +546,38 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
|
||||
memset(busparam, 0 ,sizeof(busparam));
|
||||
|
||||
debug("streamInitRecord(%s): SEVR=%d\n", record->name, record->sevr);
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream)
|
||||
Stream* stream = static_cast<Stream*>(record->dpvt);
|
||||
if (!stream)
|
||||
{
|
||||
// initialize the first time
|
||||
debug("streamInitRecord(%s): create new Stream object\n",
|
||||
record->name);
|
||||
pstream = new Stream(record, ioLink, readData, writeData);
|
||||
record->dpvt = pstream;
|
||||
stream = new Stream(record, ioLink, readData, writeData);
|
||||
record->dpvt = stream;
|
||||
} else {
|
||||
// stop any running protocol
|
||||
debug("streamInitRecord(%s): stop running protocol\n",
|
||||
record->name);
|
||||
pstream->finishProtocol(Stream::Abort);
|
||||
stream->finishProtocol(Stream::Abort);
|
||||
}
|
||||
// scan the i/o link
|
||||
debug("streamInitRecord(%s): parse link \"%s\"\n",
|
||||
record->name, ioLink->value.instio.string);
|
||||
status = pstream->parseLink(ioLink, filename, protocol,
|
||||
status = stream->parseLink(ioLink, filename, protocol,
|
||||
busname, &addr, busparam);
|
||||
// (re)initialize bus and protocol
|
||||
debug("streamInitRecord(%s): calling initRecord\n",
|
||||
record->name);
|
||||
if (status == 0)
|
||||
status = pstream->initRecord(filename, protocol,
|
||||
status = stream->initRecord(filename, protocol,
|
||||
busname, addr, busparam);
|
||||
if (status != OK && status != DO_NOT_CONVERT)
|
||||
{
|
||||
error("%s: Record initialization failed\n", record->name);
|
||||
}
|
||||
else if (!pstream->ioscanpvt)
|
||||
else if (!stream->ioscanpvt)
|
||||
{
|
||||
scanIoInit(&pstream->ioscanpvt);
|
||||
scanIoInit(&stream->ioscanpvt);
|
||||
}
|
||||
debug("streamInitRecord(%s) done status=%#lx\n", record->name, status);
|
||||
return status;
|
||||
@ -559,33 +585,33 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
|
||||
|
||||
long streamReadWrite(dbCommon *record)
|
||||
{
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream || pstream->status == ERROR)
|
||||
Stream* stream = static_cast<Stream*>(record->dpvt);
|
||||
if (!stream || stream->status == ERROR)
|
||||
{
|
||||
(void) recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
|
||||
error("%s: Record not initialised correctly\n", record->name);
|
||||
return ERROR;
|
||||
}
|
||||
return pstream->process() ? pstream->convert : ERROR;
|
||||
return stream->process() ? stream->convert : ERROR;
|
||||
}
|
||||
|
||||
long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
|
||||
{
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
debug("streamGetIointInfo(%s,cmd=%d): pstream=%p, ioscanpvt=%p\n",
|
||||
Stream* stream = static_cast<Stream*>(record->dpvt);
|
||||
debug("streamGetIointInfo(%s,cmd=%d): stream=%p, ioscanpvt=%p\n",
|
||||
record->name, cmd,
|
||||
(void*)pstream, pstream ? pstream->ioscanpvt : NULL);
|
||||
if (!pstream)
|
||||
(void*)stream, stream ? stream->ioscanpvt : NULL);
|
||||
if (!stream)
|
||||
{
|
||||
error("streamGetIointInfo called without stream instance\n");
|
||||
return ERROR;
|
||||
}
|
||||
*ppvt = pstream->ioscanpvt;
|
||||
*ppvt = stream->ioscanpvt;
|
||||
if (cmd == 0)
|
||||
{
|
||||
debug("streamGetIointInfo: starting protocol\n");
|
||||
// SCAN has been set to "I/O Intr"
|
||||
if (!pstream->startProtocol(Stream::StartAsync))
|
||||
if (!stream->startProtocol(Stream::StartAsync))
|
||||
{
|
||||
error("%s: Can't start \"I/O Intr\" protocol\n",
|
||||
record->name);
|
||||
@ -594,7 +620,7 @@ long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
|
||||
else
|
||||
{
|
||||
// SCAN is no longer "I/O Intr"
|
||||
pstream->finishProtocol(Stream::Abort);
|
||||
stream->finishProtocol(Stream::Abort);
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
@ -603,11 +629,11 @@ long streamPrintf(dbCommon *record, format_t *format, ...)
|
||||
{
|
||||
debug("streamPrintf(%s,format=%%%c)\n",
|
||||
record->name, format->priv->conv);
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream) return ERROR;
|
||||
Stream* stream = static_cast<Stream*>(record->dpvt);
|
||||
if (!stream) return ERROR;
|
||||
va_list ap;
|
||||
va_start(ap, format);
|
||||
bool success = pstream->print(format, ap);
|
||||
bool success = stream->print(format, ap);
|
||||
va_end(ap);
|
||||
return success ? OK : ERROR;
|
||||
}
|
||||
@ -616,9 +642,9 @@ ssize_t streamScanfN(dbCommon* record, format_t *format,
|
||||
void* value, size_t maxStringSize)
|
||||
{
|
||||
ssize_t size;
|
||||
Stream* pstream = (Stream*)record->dpvt;
|
||||
if (!pstream) return ERROR;
|
||||
size = pstream->scan(format, value, maxStringSize);
|
||||
Stream* stream = static_cast<Stream*>(record->dpvt);
|
||||
if (!stream) return ERROR;
|
||||
size = stream->scan(format, value, maxStringSize);
|
||||
return size;
|
||||
}
|
||||
|
||||
@ -753,11 +779,12 @@ initRecord(const char* filename, const char* protocol,
|
||||
name());
|
||||
}
|
||||
}
|
||||
return OK;
|
||||
}
|
||||
|
||||
debug("Stream::initRecord %s: initialize the first time\n",
|
||||
name());
|
||||
else
|
||||
{
|
||||
debug("Stream::initRecord %s: initialize the first time\n",
|
||||
name());
|
||||
}
|
||||
|
||||
if (!onInit) return DO_NOT_CONVERT; // no @init handler, keep DOL
|
||||
|
||||
@ -899,8 +926,7 @@ scan(format_t *format, void* value, size_t maxStringSize)
|
||||
void Stream::
|
||||
expire(CALLBACK *pcallback)
|
||||
{
|
||||
Stream* pstream = static_cast<Stream*>(pcallback->user);
|
||||
pstream->timerCallback();
|
||||
static_cast<Stream*>(pcallback->user)->timerCallback();
|
||||
}
|
||||
#else
|
||||
epicsTimerNotify::expireStatus Stream::
|
||||
|
Reference in New Issue
Block a user