StreamDevice is an
asynchronous device support.
Whenever the record is processed, the protocol
is scheduled to start and the record is left active (PACT=1).
The protocol itself runs in another thread.
That means that any waiting in the protocol does not delay any other
part of the IOC.
After the protocol has finished, the record is processed again, leaving
PACT=0 this time, triggering monitors and processing
the forward link FLNK.
Note that input links with PP flag pointing to a StreamDevice
record will read the old value first and start the protocol
afterward.
This is a problem all asynchronous EPICS device supports have.
The first out command in the protocol locks the device
for exclusive access.
That means that no other record can communicate with that device.
This ensures that replies given by the device reach the record
which has sent the request.
On a bus with many devices on different addresses, this
normally locks only one device.
The device is unlocked when the protocol terminates.
Another record trying to lock the same device has to wait and
might get a LockTimeout.
If any error happens, the protocol is aborted. The record will have
its SEVR field set to INVALID and its
STAT field to something describing the error:
TIMEOUTLockTimeout) because
other records are keeping the device busy or the device did not reply
in time (ReplyTimeout).
WRITEWriteTimeout).
READReadTimeout).
COMMCALCin command
or it contained values the record did not accept.
UDF
If the protocol is aborted, an
exception handler might be executed
if defined.
Even if the exception handler can complete with no further error, the
protocol will not resume and SEVR and STAT
will be set according to the original error.
Often, it is useful to initialize records from the hardware after
booting the IOC, especially output records.
For this purpose, initialization is formally handled as an
exception.
The @init handler is called as part of the
initRecord() function during iocInit
before any scan task starts and may be re-run
later under circumstances listed below.
In contrast to normal processing, the protocol
is handled synchronously.
That means that initRecord() does not return before the
@init handler has finished.
Thus, the records initialize one after the other.
The scan tasks are not started and iocInit does not
return before all @init handlers have finished.
If the handler fails, the record remains uninitialized:
UDF=1, SEVR=INVALID,
STAT=UDF.
The @init handler has nothing to do with the
PINI field.
The handler does not process the record nor does it trigger
forward links or any links with the PP flag.
It runs before PINI is handled.
If the record has PINI=YES, the PINI
processing is a normal processing after the
@init handlers of all records have completed.
Depending on the record type, format converters might work slightly different from normal processing. Refer to the description of supported record types for details.
If the @inithandler has read a value and has completed
without error, the record starts in a defined state.
That means UDF=0, SEVR=NO_ALARM,
STAT=NO_ALARM and the VAL field contains
the value read from the device.
If no @init handler is installed, VAL and
RVAL fields remain untouched.
That means they contain the value defined in the record definition,
read from a constant INP or DOL field,
or restored from a bump-less reboot system
(e.g. autosave from the synApps package).
The @init handler is called in the following situations:
iocInit during record initialization
as described above.
iocRun (after
beeing paused with iocPause) before the scan tasks
restart and before records with PINI=RUN are processed.
streamReload ["recordname"].
streamReinit
"asynPortname"[,addr] is called
(if using an asynDriver port).
2 is written to the
.PROC field of the record.
In this case the record is processed and thus its FLNK
and links with the PP flag are triggered.
StreamDevice supports I/O event scanning. This is a mode where record processing is triggered by the device whenever the device sends input.
In terms of protocol execution this means:
When the SCAN field is set to I/O Intr
(during iocInit or later),
the protocol starts without processing the record.
With the first in command, the protocol is suspended.
If the device has been locked (i.e there was an out
command earlier in the protocol), it is unlocked now.
That means that other records can communicate to the device while
this record is waiting for input.
This in command ignores replyTimeout,
it waits forever.
The protocol now receives any input from the device.
It also gets a copy of all input directed to other records.
Non-matching input does not generate a mismatch
exception.
It just restarts the in command until matching input
is received.
After receiving matching input, the protocol continues normally.
All other in commands are handled normally.
When the protocol has completed, the record is processed.
It then triggers monitors, forward links, etc.
After the record has been processed, the protocol restarts.
This mode is useful in two cases:
First for devices that send data automatically without being asked.
Second to distribute multiple values in one message to different
records.
In this case, one record would send a request to the device and pick
only one value out of the reply.
The other values are read by records in I/O Intr mode.
Device dev1 has a "region of interest" (ROI) defined by
a start value and an end value. When asked "ROI?",
it replies something like "ROI 17.3 58.7",
i.e. a string containing both values.
We need two ai records to store the two values. Whenever record
ROI:start is processed, it requests ROI from the device.
Record ROI:end updates automatically.
record (ai "ROI:start") {
field (DTYP, "stream")
field (INP, "@myDev.proto getROIstart dev1")
}
record (ai "ROI:end") {
field (DTYP, "stream")
field (INP, "@myDev.proto getROIend dev1")
field (SCAN, "I/O Intr")
}
Only one of the two protocols sends a request, but both read their part of the same reply message.
getROIstart {
out "ROI?";
in "ROI %f %*f";
}
getROIend {
in "ROI %*f %f";
}
Note that the other value is also parsed by each protocol, but skipped
because of the %* format.
Even though the getROIend protocol may receive input
from other requests, it silently ignores every message that does not start
with "ROI", followed by two floating point numbers.