re-run @init in iocRun after iocPause and streamReload

This commit is contained in:
2018-07-18 17:56:49 +02:00
parent 04d0eb6361
commit cad3c25079
4 changed files with 148 additions and 99 deletions

View File

@ -55,21 +55,22 @@ its <code>SEVR</code> field set to <code>INVALID</code> and its
<dt><code>TIMEOUT</code></dt> <dt><code>TIMEOUT</code></dt>
<dd> <dd>
The device could not be locked (<code>LockTimeout</code>) or the 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> </dd>
<dt><code>WRITE</code></dt> <dt><code>WRITE</code></dt>
<dd> <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> </dd>
<dt><code>READ</code></dt> <dt><code>READ</code></dt>
<dd> <dd>
Input from the device started but stopped unexpectedly Input from the device started but stopped unexpectedly
(<code>ReadTimeout</code>). (<a href="protocol.html#sysvar"><code>ReadTimeout</code></a>).
</dd> </dd>
<dt><code>COMM</code></dt> <dt><code>COMM</code></dt>
<dd> <dd>
The device driver reported some other communication error (e.g. The device driver reported that the device is disconnected.
unplugged cable).
</dd> </dd>
<dt><code>CALC</code></dt> <dt><code>CALC</code></dt>
<dd> <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 or restored from a bump-less reboot system
(e.g. <em>autosave</em> from the <em>synApps</em> package). (e.g. <em>autosave</em> from the <em>synApps</em> package).
</p> </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> <a name="iointr"></a>
<h2>3. I/O Intr</h2> <h2>3. I/O Intr</h2>

View File

@ -557,8 +557,10 @@ There is a fixed set of exception handler names starting with
<dd> <dd>
Not really an exception but formally specified in the same syntax. Not really an exception but formally specified in the same syntax.
This handler is called from <code>iocInit</code> during record This handler is called from <code>iocInit</code> during record
initialization <span class="new">and again whenever it is detected initialization <span class="new">and again whenever <em>StreamDevice</em>
that the device was disconnected and has reconnected</span>. 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 It can be used to initialize an output record with a value read from
the device. the device.
Also see chapter <a href="processing.html#init">Record Processing</a>. Also see chapter <a href="processing.html#init">Record Processing</a>.

View File

@ -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 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 the protocol file of one or all records with the shell function
<code>streamReload("<var>record</var>")</code>. <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. <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 Furthermore, the <code>streamReloadSub</code> function can be used
with a subroutine record to reload all protocols. with a subroutine record to reload all protocols.
</p> </p>
@ -336,6 +341,8 @@ protocol is loaded.
</p> </p>
<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. See the <a href="protocol.html">next chapter</a> for protocol files in depth.
</p> </p>

View File

@ -66,7 +66,12 @@ extern DBBASE *pdbbase;
#include "epicsStdioRedirect.h" #include "epicsStdioRedirect.h"
#include "iocsh.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) // iocshCmd() is missing in iocsh.h (up to R3.14.8.2)
// To build with win32-x86, you MUST fix iocsh.h. // To build with win32-x86, you MUST fix iocsh.h.
// Move the declaration below to iocsh.h and rebuild base. // 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); ssize_t scan(format_t *format, void* pvalue, size_t maxStringSize);
bool process(); bool process();
#ifdef WITH_IOC_RUN
static void initHook(initHookState);
#endif
// device support functions // device support functions
friend long streamInitRecord(dbCommon *record, const struct link *ioLink, friend long streamInitRecord(dbCommon *record, const struct link *ioLink,
streamIoFunction readData, streamIoFunction writeData); streamIoFunction readData, streamIoFunction writeData);
@ -201,10 +210,8 @@ long streamReloadSub()
long streamReload(const char* recordname) long streamReload(const char* recordname)
{ {
DBENTRY dbentry; Stream* stream;
dbCommon* record;
long status; long status;
int oldStreamError = streamError; int oldStreamError = streamError;
streamError = 1; streamError = 1;
@ -214,38 +221,24 @@ long streamReload(const char* recordname)
return ERROR; return ERROR;
} }
debug("streamReload(%s)\n", recordname); debug("streamReload(%s)\n", recordname);
dbInitEntry(pdbbase,&dbentry); for (stream = static_cast<Stream*>(Stream::first); stream;
for (status = dbFirstRecordType(&dbentry); status == OK; stream = static_cast<Stream*>(stream->next))
status = dbNextRecordType(&dbentry))
{ {
for (status = dbFirstRecord(&dbentry); status == OK; if (recordname && recordname[0] &&
status = dbNextRecord(&dbentry)) #ifdef EPICS_3_13
{ strcmp(stream->name(), recordname) == 0)
char* value; #else
if (dbFindField(&dbentry, "DTYP") != OK) !epicsStrGlobMatch(stream->name(), recordname))
continue; #endif
if ((value = dbGetString(&dbentry)) == NULL) continue;
continue; // This cancels any running protocol and reloads
if (strcmp(value, "stream") != 0) // the protocol file
continue; status = stream->record->dset->init_record(stream->record);
record=(dbCommon*)dbentry.precnode->precord; if (status == OK || status == DO_NOT_CONVERT)
if (recordname && strcmp(recordname, record->name) != 0) printf("%s: Protocol reloaded\n", stream->name());
continue; else
error("%s: Protocol reload failed\n", stream->name());
// 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);
}
}
} }
dbFinishEntry(&dbentry);
StreamProtocolParser::free(); StreamProtocolParser::free();
streamError = oldStreamError; streamError = oldStreamError;
return OK; return OK;
@ -388,25 +381,25 @@ report(int interest)
} }
} }
Stream* pstream; Stream* stream;
printf(" connected records:\n"); printf(" connected records:\n");
for (pstream = static_cast<Stream*>(first); pstream; for (stream = static_cast<Stream*>(first); stream;
pstream = static_cast<Stream*>(pstream->next)) stream = static_cast<Stream*>(stream->next))
{ {
if (interest == 2) if (interest == 2)
{ {
printf("\n%s: %s\n", pstream->name(), printf("\n%s: %s\n", stream->name(),
pstream->ioLink->value.instio.string); stream->ioLink->value.instio.string);
pstream->printProtocol(); stream->printProtocol();
} }
else else
{ {
printf(" %s: %s\n", pstream->name(), printf(" %s: %s\n", stream->name(),
pstream->ioLink->value.instio.string); stream->ioLink->value.instio.string);
if (interest == 3) if (interest == 3)
{ {
StreamBuffer buffer; StreamBuffer buffer;
pstream->printStatus(buffer); stream->printStatus(buffer);
printf(" %s\n", buffer()); printf(" %s\n", buffer());
} }
} }
@ -416,23 +409,23 @@ report(int interest)
long streamReportRecord(const char* recordname) long streamReportRecord(const char* recordname)
{ {
Stream* pstream; Stream* stream;
for (pstream = static_cast<Stream*>(Stream::first); pstream; for (stream = static_cast<Stream*>(Stream::first); stream;
pstream = static_cast<Stream*>(pstream->next)) stream = static_cast<Stream*>(stream->next))
{ {
if (!recordname || if (!recordname ||
#ifdef EPICS_3_13 #ifdef EPICS_3_13
strcmp(pstream->name(), recordname) == 0) strcmp(stream->name(), recordname) == 0)
#else #else
epicsStrGlobMatch(pstream->name(), recordname)) epicsStrGlobMatch(stream->name(), recordname))
#endif #endif
{ {
printf("%s: %s\n", pstream->name(), printf("%s: %s\n", stream->name(),
pstream->ioLink->value.instio.string); stream->ioLink->value.instio.string);
StreamBuffer buffer; StreamBuffer buffer;
pstream->printStatus(buffer); stream->printStatus(buffer);
printf("%s\n", buffer()); printf("%s\n", buffer());
pstream->printProtocol(); stream->printProtocol();
printf("\n"); printf("\n");
} }
} }
@ -453,33 +446,64 @@ drvInit()
SYM_TYPE type; SYM_TYPE type;
if (symFindByName(sysSymTbl, if (symFindByName(sysSymTbl,
"STREAM_PROTOCOL_PATH", &symbol, &type) == OK) "STREAM_PROTOCOL_PATH", &symbol, &type) == OK)
{
path = *(char**)symbol; path = *(char**)symbol;
}
else else
if (symFindByName(sysSymTbl, if (symFindByName(sysSymTbl,
"STREAM_PROTOCOL_DIR", &symbol, &type) == OK) "STREAM_PROTOCOL_DIR", &symbol, &type) == OK)
{
path = *(char**)symbol; path = *(char**)symbol;
}
} }
#endif #endif
if (!path) if (!path)
{
fprintf(stderr, fprintf(stderr,
"drvStreamInit: Warning! STREAM_PROTOCOL_PATH not set. " "drvStreamInit: Warning! STREAM_PROTOCOL_PATH not set. "
"Defaults to \"%s\"\n", StreamProtocolParser::path); "Defaults to \"%s\"\n", StreamProtocolParser::path);
}
else else
{
StreamProtocolParser::path = path; StreamProtocolParser::path = path;
}
debug("StreamProtocolParser::path = %s\n", debug("StreamProtocolParser::path = %s\n",
StreamProtocolParser::path); StreamProtocolParser::path);
StreamPrintTimestampFunction = streamEpicsPrintTimestamp; StreamPrintTimestampFunction = streamEpicsPrintTimestamp;
#ifdef WITH_IOC_RUN
initHookRegister(initHook);
#endif
return OK; 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) ////////////////////////////////////////// // device support (C interface) //////////////////////////////////////////
long streamInit(int after) long streamInit(int after)
@ -490,6 +514,7 @@ long streamInit(int after)
static int first = 1; static int first = 1;
if (first) if (first)
{ {
// restore error filtering to previous setting
streamError = oldStreamError; streamError = oldStreamError;
StreamProtocolParser::free(); StreamProtocolParser::free();
first = 0; first = 0;
@ -500,6 +525,7 @@ long streamInit(int after)
static int first = 1; static int first = 1;
if (first) if (first)
{ {
// enable errors while reading protocol files
oldStreamError = streamError; oldStreamError = streamError;
streamError = 1; streamError = 1;
first = 0; first = 0;
@ -520,38 +546,38 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
memset(busparam, 0 ,sizeof(busparam)); 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* stream = static_cast<Stream*>(record->dpvt);
if (!pstream) if (!stream)
{ {
// initialize the first time // initialize the first time
debug("streamInitRecord(%s): create new Stream object\n", debug("streamInitRecord(%s): create new Stream object\n",
record->name); record->name);
pstream = new Stream(record, ioLink, readData, writeData); stream = new Stream(record, ioLink, readData, writeData);
record->dpvt = pstream; record->dpvt = stream;
} else { } else {
// stop any running protocol // stop any running protocol
debug("streamInitRecord(%s): stop running protocol\n", debug("streamInitRecord(%s): stop running protocol\n",
record->name); record->name);
pstream->finishProtocol(Stream::Abort); stream->finishProtocol(Stream::Abort);
} }
// scan the i/o link // scan the i/o link
debug("streamInitRecord(%s): parse link \"%s\"\n", debug("streamInitRecord(%s): parse link \"%s\"\n",
record->name, ioLink->value.instio.string); record->name, ioLink->value.instio.string);
status = pstream->parseLink(ioLink, filename, protocol, status = stream->parseLink(ioLink, filename, protocol,
busname, &addr, busparam); busname, &addr, busparam);
// (re)initialize bus and protocol // (re)initialize bus and protocol
debug("streamInitRecord(%s): calling initRecord\n", debug("streamInitRecord(%s): calling initRecord\n",
record->name); record->name);
if (status == 0) if (status == 0)
status = pstream->initRecord(filename, protocol, status = stream->initRecord(filename, protocol,
busname, addr, busparam); 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);
} }
else if (!pstream->ioscanpvt) else if (!stream->ioscanpvt)
{ {
scanIoInit(&pstream->ioscanpvt); scanIoInit(&stream->ioscanpvt);
} }
debug("streamInitRecord(%s) done status=%#lx\n", record->name, status); debug("streamInitRecord(%s) done status=%#lx\n", record->name, status);
return status; return status;
@ -559,33 +585,33 @@ long streamInitRecord(dbCommon* record, const struct link *ioLink,
long streamReadWrite(dbCommon *record) long streamReadWrite(dbCommon *record)
{ {
Stream* pstream = (Stream*)record->dpvt; Stream* stream = static_cast<Stream*>(record->dpvt);
if (!pstream || pstream->status == ERROR) if (!stream || stream->status == ERROR)
{ {
(void) recGblSetSevr(record, UDF_ALARM, INVALID_ALARM); (void) recGblSetSevr(record, UDF_ALARM, INVALID_ALARM);
error("%s: Record not initialised correctly\n", record->name); error("%s: Record not initialised correctly\n", record->name);
return ERROR; return ERROR;
} }
return pstream->process() ? pstream->convert : ERROR; return stream->process() ? stream->convert : ERROR;
} }
long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt) long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
{ {
Stream* pstream = (Stream*)record->dpvt; Stream* stream = static_cast<Stream*>(record->dpvt);
debug("streamGetIointInfo(%s,cmd=%d): pstream=%p, ioscanpvt=%p\n", debug("streamGetIointInfo(%s,cmd=%d): stream=%p, ioscanpvt=%p\n",
record->name, cmd, record->name, cmd,
(void*)pstream, pstream ? pstream->ioscanpvt : NULL); (void*)stream, stream ? stream->ioscanpvt : NULL);
if (!pstream) if (!stream)
{ {
error("streamGetIointInfo called without stream instance\n"); error("streamGetIointInfo called without stream instance\n");
return ERROR; return ERROR;
} }
*ppvt = pstream->ioscanpvt; *ppvt = stream->ioscanpvt;
if (cmd == 0) if (cmd == 0)
{ {
debug("streamGetIointInfo: starting protocol\n"); debug("streamGetIointInfo: starting protocol\n");
// SCAN has been set to "I/O Intr" // 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", error("%s: Can't start \"I/O Intr\" protocol\n",
record->name); record->name);
@ -594,7 +620,7 @@ long streamGetIointInfo(int cmd, dbCommon *record, IOSCANPVT *ppvt)
else else
{ {
// SCAN is no longer "I/O Intr" // SCAN is no longer "I/O Intr"
pstream->finishProtocol(Stream::Abort); stream->finishProtocol(Stream::Abort);
} }
return OK; return OK;
} }
@ -603,11 +629,11 @@ long streamPrintf(dbCommon *record, format_t *format, ...)
{ {
debug("streamPrintf(%s,format=%%%c)\n", debug("streamPrintf(%s,format=%%%c)\n",
record->name, format->priv->conv); record->name, format->priv->conv);
Stream* pstream = (Stream*)record->dpvt; Stream* stream = static_cast<Stream*>(record->dpvt);
if (!pstream) return ERROR; if (!stream) return ERROR;
va_list ap; va_list ap;
va_start(ap, format); va_start(ap, format);
bool success = pstream->print(format, ap); bool success = stream->print(format, ap);
va_end(ap); va_end(ap);
return success ? OK : ERROR; return success ? OK : ERROR;
} }
@ -616,9 +642,9 @@ ssize_t streamScanfN(dbCommon* record, format_t *format,
void* value, size_t maxStringSize) void* value, size_t maxStringSize)
{ {
ssize_t size; ssize_t size;
Stream* pstream = (Stream*)record->dpvt; Stream* stream = static_cast<Stream*>(record->dpvt);
if (!pstream) return ERROR; if (!stream) return ERROR;
size = pstream->scan(format, value, maxStringSize); size = stream->scan(format, value, maxStringSize);
return size; return size;
} }
@ -753,11 +779,12 @@ initRecord(const char* filename, const char* protocol,
name()); name());
} }
} }
return OK;
} }
else
debug("Stream::initRecord %s: initialize the first time\n", {
name()); debug("Stream::initRecord %s: initialize the first time\n",
name());
}
if (!onInit) return DO_NOT_CONVERT; // no @init handler, keep DOL 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:: void Stream::
expire(CALLBACK *pcallback) expire(CALLBACK *pcallback)
{ {
Stream* pstream = static_cast<Stream*>(pcallback->user); static_cast<Stream*>(pcallback->user)->timerCallback();
pstream->timerCallback();
} }
#else #else
epicsTimerNotify::expireStatus Stream:: epicsTimerNotify::expireStatus Stream::