commit 90a6fd4af9fd6cb04b46959d4889e3544d620b59
Author: zimoch
+Note: aai record support is disabled per default.
+Enable it in
+With aai records, the format converter is applied to
+each element. Between the elements, a separator is printed
+or expected as specified by the
+During input, a maximum of
+During output, the first
+The format data type must be convertible to or from the type
+specified in the
+ During initialization, the
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+calcout
+scalcout
+ Dirk Zimoch, 2006
+Note: aao record support is disabled per default.
+Enable it in
+With aao records, the format converter is applied to
+each element. Between the elements, a separator is printed
+or expected as specified by the
+During output, the first
+During input, a maximum of
+The format data type must be convertible to or from the type
+specified in the
+ During initialization, the
+aai
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+calcout
+scalcout
+ Dirk Zimoch, 2006
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+StreamDevice already comes with an interface to
+ asynDriver.
+You should first try to implement your bus driver compatible to
+asynDriver.
+Then it can be used by StreamDevice automatically.
+Only if that does not work, write your own bus interface.
+
+A bus interface is a C++ class that inherits from
+StreamBusInterface.
+Its purpose is to provide an interface to StreamDevice for a
+low-level I/O bus driver.
+StreamDevice acts as a client of the interface, calling interface
+methods and receiving replies via callbacks.
+Since the internal details of StreamDevice are not of
+interest to a bus interface, I will reference it simply as
+client in this chapter.
+The interface class must be registered via a call to
+
+Interface methods called by the client must not block for arbitrary
+long times.
+That means the interface is allowed to take mutex semaphores to protect
+its internal data structures but it must not take event semaphores to
+wait for external I/O or similar.
+
+It is assumed that the interface creates a separate thread to handle
+blocking I/O and to call the callback methods in the context of that
+thread when I/O has completed or timed out.
+The callback methods don't block but may in turn call interface methods.
+Much of the actual work will be done in the context of those callbacks,
+i.e. in the interface thread, thus be generous with stack.
+
+The interface class must implement a public static creator method:
+
+And it must implement the following pure virtual methods:
+
+It may implement additional virtual methods if the bus supports it:
+
+It also may override the following virtual method:
+
+The base class StreamBusInterface implements a set of protected
+callback methods which must be called in response to the above request
+methods (most probably from another thread):
+
+During initialization, the macro
+During startup, each client instance searches for its bus interface
+by name.
+It does so by calling the static
+On success,
+On failure, or if this interface class is not responsible for that bus,
+
+When the client does not need the interface any more, it calls
+
+Connection should be handled automatically.
+If the device is disconnected, each attempt to access the
+device should try to (re-)connect.
+Normally, the interface should not try to disconnect unless
+the device does so.
+
+However, sometimes the client wants to connect or
+disconnect explicitely.
+To connect, the client calls
+If a device cannot be connected, for example because there is
+something wrong with the I/O hardware,
+
+To disconnect, the client calls
+Before doing output, the client calls
+If a device cannot be locked, for example because there is
+something wrong with the I/O hardware,
+
+Normally, it is not necessary to lock the complete bus but only one
+device (i.e. one address).
+Other clients should still be able to talk to other devices on the same bus.
+
+The client may perform several read and write operations when it has
+locked the device.
+When the protocol ends and the device is locked, the client calls
+
+The client calls
+To start output, the client calls
+The function should arrange transmission of
+If output blocks for
+If output is impossible, for example because there is
+something wrong with the I/O hardware,
+
+The interface must transmit excactly the
+A call to
+The buffer referenced by
+The client may request more I/O or call
+The client calls
+This function must not block until input is available.
+Instead, it should arrange for
+
+Here,
+It is not necessary to wait until all data has been received.
+The bus interface can call
+With some bus interfaces,
+The
+The
+A call to
+If
+Some busses (e.g. GPIB) support special "end of message" signals.
+If such a signal is received, the bus interface should call
+
+If input is impossible, for example because there is
+something wrong with the I/O hardware,
+
+If the
+If a client wishes to receive asynchonous input, it first calls
+
+For asynchonous requests,
+If the client calls
+An event is a sort of input from a device which is not part of
+the normal byte stream.
+One example is the SRQ line of GPIB.
+Not all bus types have events.
+To support events, the bus interface must overwrite
+
+If
+To avoid race conditions, the bus interface should buffer events and
+also report a matching event which occured before the actual call
+to Dirk Zimoch, 2007
+Note: Device support for calcout records is only available for
+EPICS base R3.14.5 or higher.
+
+Different record fields are used for output and input. The variable
+
+ For calcout records, it is probably more useful to access fields
+
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+scalcout
+ Dirk Zimoch, 2005
+StreamDevice version 2.2 and higher can run on EPICS 3.13.
+However, this requires some preparation, because EPICS 3.13 is missing
+some libraries and header files.
+Also asynDriver needs to be modified to compile with EPICS 3.13.
+Due to the limitations of EPICS 3.13, you can build streamDevice only for
+vxWorks systems.
+
+Of course, you need an installation of EPICS 3.13.
+I guess you already have that, otherwhise you would want to
+install StreamDevice on EPICS 3.14.
+I have tested StreamDevice with EPICS versions 3.13.7 up to 3.13.10
+with vxWorks 5.3.1 and 5.5 on a ppc604 processor.
+
+Download my
+compatibility package,
+asynDriver
+version 4-3 or higher,
+and my
+configure patches.
+
+Unpack compat-1-0.tgz in the <top> directory of
+your application build area.
+(Please refer to the
+EPICS IOC Software Configuration Management document.)
+
+Change to the compat directory and run make.
+This installs many EPICS 3.14-style header files and a small library
+(compatLib).
+
+Unpack the asynDriver package and change to its top directory.
+
+Unpack configure.tgz here.
+This will modify files in the configure directory.
+Change to the configure directory and edit CONFIG_APP.
+Set
+Edit RELEASE and comment out
+Run make in the configure directory.
+
+Change to ../asyn/devGpib and edit
+devGpib.h and devSupportGpib.c.
+Change all occurrences of
+Go one directory up (to asyn) and run make twice!
+(The first run will just create Makefile.Vx.)
+Ignore all compiler warnings.
+
+Do not try to build the test applications. It will not work.
+
+Go to the <top> directory of your application build area.
+
+Edit config/RELEASE and add the variable
+Unpack the StreamDevice package in your <top>
+directory.
+Change to the newly created StreamDevice directory
+and run make.
+
+To use StreamDevice, your application must be built with the
+asyn, stream, and compat libraries and must load
+asyn.dbd and stream.dbd.
+Also, as the stream library contains C++ code, the application
+must be munched.
+Therefore, include $(TOP)/config/RULES.munch.
+(Put your application in the same <top> as the
+StreamDevice installation.)
+
+Include the following lines in your Makefile.Vx:
+
+Include the following lines in your xxxAppInclude.dbd file to use
+stream and asyn (you also need a base.dbd):
+
+You can find an example application in the streamApp
+subdirectory.
+
+StreamDevice is based on protocol files.
+To tell StreamDevice where to search for protocol files,
+set the environment variable
+Also configure the buses (in asynDriver terms: ports) you want
+to use with StreamDevice.
+You can give the buses any name you want, like COM1 or
+socket, but I recommend to use names related to the
+connected device.
+
+A power supply with serial communication (9600 baud, 8N1) is connected to
+/dev/ttyS1.
+The name of the power supply is PS1.
+Protocol files are either in the current working directory or in the
+../protocols directory.
+
+Then the startup script must contain lines like this:
+
+An alternative approach is to skip step 5 (do not build an application)
+and load all components explicitely in the startup script.
+The Dirk Zimoch, 2006 Dirk Zimoch, 2006
+StreamDevice format converters work very similar to the format
+converters of the C functions printf() and scanf().
+But StreamDevice provides more different converters and you can
+also write your own converters.
+Formats are specified in quoted strings
+as arguments of
+A format converter consists of
+
+The
+The
+The '
+The
+The
+Every conversion character corresponds to one of the data types DOUBLE,
+LONG, ENUM, or STRING.
+In opposite to to printf() and scanf(), it is not
+required to specify a variable for the conversion.
+The variable is typically the
+StreamDevice makes no difference between
+To use other fields of the record or even fields of other records on the
+same IOC for the conversion, write the field name in parentheses directly
+after the
+Some formats are not actually converters.
+They format data which is not stored in a record field, such as a
+checksum.
+No data type corresponds to those pseudo-converters and the
+
+In output,
+In input, all these formats are equivalent.
+Leading whitespaces are skipped.
+
+In output,
+In input,
+In output,
+In input,
+This is an input-only format.
+It matches a sequence of characters from charset.
+If charset starts with
+Example:
+This format maps an unsigned integer value on a set of strings.
+The value 0 corresponds to string0 and so on.
+The strings are separated by
+Example:
+In output, depending on the value, one of the strings is printed.
+
+In input, if any of the strings matches the value is set accordingly.
+
+This format prints or scans an unsigned integer represented as a binary
+string (one character per bit).
+The
+Examples:
+If in output width is larger than the number of significant bits,
+then the flag
+In input, leading spaces are skipped.
+A maximum of width characters is read.
+Conversion stops with the first character that is not the zero or the
+one character.
+
+The raw converter does not really "convert".
+A signed integer value is written or read in the internal
+(usually two's complement) representation of the computer.
+The normal byte order is big endian, i.e. most significant byte
+first.
+With the
+In output, the width least significant bytes of the value
+are written.
+If width is larger than the size of a
+In input, width bytes are read and put into the value.
+If width is longer than the size of a
+Packed BCD is a format where each byte contains two binary coded
+decimal digits (
+In output, precision decimal digits are printed in at least
+width output bytes.
+Signed negative values have
+In input, width bytes are read.
+If the value is signed, a one in the most significant bit is interpreted as
+a negative sign.
+Input stops with the first byte (after the sign) that does not represent a
+BCD value, i.e. where either the upper or the lower half byte is larger
+than 9.
+
+This is not a normal "converter", because no user data is converted.
+Instead, a checksum is calculated from the input or output.
+The width field is the byte number from which to start
+calculating the checksum.
+Default is 0, i.e. the first byte of the input or output of the current
+command.
+The last byte is prec bytes before the checksum (default 0).
+For example in
+Normally, multi-byte checksums are in big endian byteorder,
+i.e. most significant byte first.
+With the
+The
+In output, the checksum is appended.
+
+In input, the next byte or bytes must match the checksum.
+ Dirk Zimoch, 2007
+The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+calcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+Depending on the format type, different record fields are used
+for output and input. The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+
+
+ Dirk Zimoch, 2006
+StreamDevice is an asynchronous device support
+(see IOC Application Developer's Guide chapter 12:
+Device Support).
+Whenever the record is processed, the protocol
+is scheduled to start and the record is left active (
+After the protocol has finished, the record is processed again, leaving
+
+The first
+ If any error happens, the protocol is aborted. The record will have
+its
+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
+Often, it is required to initialize records from the hardware after
+booting the IOC, especially output records.
+For this purpose, initialization is formally handled as an
+exception.
+The
+In contrast to normal processing, the protocol
+is handled synchronously.
+That means that
+The
+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
+If no
+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
+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
+After receiving matching input, the protocol continues normally.
+All other
+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
+Device dev1 has a "region of interest" (ROI) defined by
+a start value and an end value. When asked "
+We need two ai records to store the two values. Whenever record
+
+Only one of the two protocols sends a request, but both read
+their part of the same reply message.
+
+Note that the other value is also parsed by each protocol, but skipped
+because of the Dirk Zimoch, 2005
+A protocol file describes the communication with one device type.
+It contains protocols for each function
+of the device type and variables which affect
+how the commands in a protocol work.
+It does not contain information about the individual device or the used
+communication bus.
+
+Each device type should have its own protocol file.
+I suggest to choose a file name that contains the name of the device type.
+Don't use spaces in the file name and keep it short.
+The file will be referenced by its name in the
+The protocol file is a plain text file.
+Everything not enclosed in quotes
+(single
+For each function of the device type, define one protocol.
+A protocol consists of a name followed by a body in braces
+The protocol body contains a sequence of commands and
+optionally variable assignments separated by
+
+To save some typing, a previously defined protocol can be called inside
+another protocol like a command without parameters.
+The protocol name is replaced by the commands in the referenced protocol.
+However, this does not include any
+variable assignments or
+exception handlers from the referenced protocol.
+See the
+The StreamDevice protocol is not a programming language.
+It has neither loops nor conditionals
+(in this version of StreamDevice).
+However, if an error occurs, e.g. a timeout or a mismatch in input
+parsing, an exception handler can be called to
+clean up.
+
+Seven different commands can be used in a protocol:
+
+In a StreamDevice protocol file, strings can be written
+as quoted literals (single quotes or double quotes), as
+a sequence of bytes values, or as a combination of both.
+
+Examples for quoted literals are:
+There is no difference between double quoted and single quoted
+literals, it just makes it easier to use quotes of the other type
+in a string.
+To break long strings into multiple lines of the protocol file,
+close the quotes before the line break and reopen them in the next line.
+Don't use a line break inside quotes.
+
+As arguments of
+StreamDevice uses the backslash character
+For non-printable characters, it is often easier to write sequences of
+byte values instead of escaped quoted string literals.
+A byte is written as an unquoted decimal, hexadecimal, or octal
+number in the range of -128 to 255 (-0x80 to 0xff, -0200 to 0377).
+StreamDevice also defines some symbolic names for frequently
+used byte codes as aliases for the numeric byte value:
+A single string can be built from several quoted literals and byte values
+by writing them separated by whitespaces or comma.
+
+The following lines represent the same string:
+StreamDevice uses three types of variables in a protocol file.
+System variables influence the behavior
+of
+System and user variables can be set in the global context of the
+protocol file or locally inside protocols.
+When set globally, a variable keeps its value until overwritten.
+When set locally, a variable is valid inside the protocol only.
+To set a variable use the syntax:
+Set variables can be referenced outside of
+quoted strings by
+
+This is a list of system variables, their default settings and
+what they influence.
+
+Sometimes, protocols differ only very little.
+In that case it can be convenient to write only one protocol
+and use protocol arguments for the difference.
+For example a motor controller for the 3 axes X, Y, Z requires
+three protocols to set a position.
+
+It also needs three versions of any other protocol.
+That means basically writing everything three times.
+To make this easier, protocol arguments can be used:
+
+Now, the protocol can be references in the
+User defined variables are just a means to save some typing.
+Once set, a user variable can be referenced later in the protocol.
+
+When an error happens, an exception handler may be called.
+Exception handlers are a kind of sub-protocols in a protocol.
+They consist of the same set of commands and are intended to
+reset the device or to finish the protocol cleanly in case of
+communication problems.
+Like variables, exception handlers can be defined globally or
+locally.
+Globally defined handlers are used for all following protocols
+unless overwritten by a local handler.
+There is a fixed set of exception handler names starting with
+
+After executing the exception handler, the protocol terminates.
+If any exception occurs within an exception handler, no other handler
+is called but the protocol terminates immediately.
+An exception handler uses all system variable
+settings from the protocol in which the exception occurred.
+ Dirk Zimoch, 2006 Dirk Zimoch, 2006
+StreamDevice comes with support for all standard record types
+in EPICS base which can have device support.
+
+There is a separate page for each supported record type:
+Each page describes which record fields are used in input and output
+for different format data types
+during normal record processing
+and initialization.
+
+It is also possible to
+write support for other recordtypes.
+ Dirk Zimoch, 2005
+Note: The scalcout record is part of the calc module of
+the synApps package.
+Device support for scalcout records is only available for calc
+module release 2-4 or higher.
+You also need the synApps modules genSub and sscan to
+build calc.
+
+Up to release 2-6 (synApps release 5.1), the scalcout record needs a fix.
+In sCalcout.c at the end of
+Different record fields are used for output and input. The variable
+
+ For scalcout records, it is probably more useful to access fields
+
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+ Dirk Zimoch, 2005
+StreamDevice requires either
+EPICS base R3.14.6 or higher or
+EPICS base R3.13.7 or higher.
+How to use StreamDevice on EPICS R3.13 is described on a
+separate page.
+Because StreamDevice comes with an interface to
+asynDriver version R4-3 or higher as the
+underlying driver layer,
+you should have asynDriver installed first.
+
+StreamDevice has support for the
+scalcout record from the
+calc module of synApps.
+Up to calc release R2-6 (synApps release R5_1),
+the scalcout record needs a fix.
+(See separate scalcout page.)
+
+Up to release R3.14.8.2, a fix in EPICS base is required to build
+StreamDevice on Windows (not cygwin).
+In src/iocsh/iocsh.h, add the following line
+and rebuild base.
+
+Make sure that the asyn library and the calc module of
+synApps can be found, e.g. by
+adding
+For details on <top> directories and RELEASE files,
+please refer to the
+IOC Application Developer's Guide chapter 4:
+EPICS Build Facility.
+
+Unpack the
+StreamDevice package in a <top> directory
+of your application build area.
+(You might probably have done this already.)
+Go to the newly created StreamDevice directory
+and run make (or gmake).
+This will create and install the stream library and the
+stream.dbd file.
+
+To use StreamDevice, your application must be built with the
+asyn and stream libraries and must load
+asyn.dbd and stream.dbd.
+
+Include the following lines in your application Makefile:
+
+Include the following lines in your xxxAppInclude.dbd file to use
+stream and asyn with serial lines and IP sockets:
+
+You can find an example application in the streamApp
+subdirectory.
+
+StreamDevice is based on protocol files.
+To tell StreamDevice where to search for protocol files,
+set the environment variable
+Also configure the buses (in asynDriver terms: ports) you want
+to use with StreamDevice.
+You can give the buses any name you want, like COM1 or
+socket, but I recommend to use names related to the
+connected device.
+
+A power supply with serial communication (9600 baud, 8N1) is connected to
+/dev/ttyS1.
+The name of the power supply is PS1.
+Protocol files are either in the current working directory or in the
+../protocols directory.
+
+Then the startup script must contain lines like this:
+ If the power supply was connected via telnet-style TCP/IP
+at address 192.168.164.10 on port 23,
+the startupscript would contain:
+
+With a VXI11 (GPIB via TCP/IP) connection, e.g. a
+HP E2050A on IP address 192.168.164.10, it would look like this:
+
+For each different type of hardware, create a protocol file
+which defines protocols for all needed functions of the device.
+The file name is arbitrary, but I recommend that it contains
+the device type.
+It must not contain spaces and should be short.
+During
+PS1 is an ExamplePS power supply.
+It communicates via ASCII strings which are terminated by
+<carriage return> <line feed> (ASCII codes 13, 10).
+The output current can be set by sending a string like
+
+Normally, an analog output record should write its value to the device.
+But during startup, the record should be initialized from the the device.
+The protocol file ExamplePS.proto defines the protocol
+
+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
+
+Reloading the protocol file aborts currently running protocols.
+This might set
+See the next chapter for protocol files in depth.
+
+To make a record use StreamDevice, set its
+Here,
+The communication channel is specified with
+Create an output record to set the current of PS1.
+Use protocol setCurrent from file ExamplePS.proto.
+The bus is called PS1 like the device.
+ Dirk Zimoch, 2007
+StreamDevice is a generic
+EPICS
+device support for devices with a "byte stream" based
+communication interface.
+That means devices that can be controlled by sending and
+receiving strings (in the broadest sense, including non-printable
+characters and even null-bytes).
+Examples for this type of communication interface are
+serial line (RS-232, RS-485, ...),
+IEEE-488 (also known as GPIB or HP-IB), and TCP/IP.
+StreamDevice comes with an interface to
+asynDriver
+but can be extended to
+support other bus drivers.
+
+If the device can be controlled with strings like
+"
+Each record with StreamDevice support runs one protocol
+to read or write its value.
+All protocols are defined in
+protocol files in plain ASCII text.
+No compiling is necessary to change a protocol or to support new devices.
+Protocols can be as simple as just one output string or can
+consist of many strings sent to and read from the device.
+However, a protocol is linear.
+That means it runs from start to end each time the record is
+processed.
+It does not provide loops or branches.
+
+StreamDevice supports all
+standard records of EPICS base
+which can have device support.
+It is also possible to
+write support for new record types.
+
+It is not a programming language for a high-level application.
+It is, for example, not possible to write a complete scanning program
+in a protocol.
+Use other tools for that and use StreamDevice only for the
+primitive commands.
+
+It is not a block oriented device support.
+It is not possible to send or receive huge blocks of data that contain
+many process variables distributed over many records.
+
+IOC Application Developer's Guide (PDF)
+
+EPICS Record Reference Manual
+ Dirk Zimoch, 2006
+The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringout
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+The variable
+ During initialization, the
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+waveform
+calcout
+scalcout
+ Dirk Zimoch, 2005
+You can give arguments to a protocol.
+In the
+
+In the protocol, reference arguments as
+
+
+Use
+
+
+Use as many
+
+There is more than one solution to this problem.
+Different approaches have different requirements.
+
+Use array records (e.g. waveform,
+aao).
+
+
+The format StreamDevice: aai Records
+src/CONFIG_STREAM
.
+Normal Operation
+Separator
+variable in the protocol.
+When parsing input, a space as the first character of the
+Separator
matches any number of any whitespace
+characters.
+NELM
elements is
+read and NORD
is updated accordingly.
+Parsing of elements stops when the separator does not match,
+conversion fails, or the end of the input is reached.
+A minimum of one element must be available.
+NORD
elements are
+written.
+FTVL
field.
+The variable x[i]
stands for one element of
+the written or read value.
+
+
+
+%f
):x[i]=double(VAL[i])
+ FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ Input: VAL[i]=FTVL(x[i])
+ FTVL
must be "FLOAT"
or "DOUBLE"
+ %i
or %{
):x[i]=long(VAL[i])
+ FTVL
can be
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ Signed values are sign-extended to long, unsigned values are
+ zero-extended to long before converting them.
+ Input: VAL[i]=FTVL(x[i])
+ FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ The value is truncated to the least significant bytes if
+ FTVL
has a smaller data size than long
.
+ %s
):
+
+ Other values of FTVL=="STRING"
:x[i]=VAL[i]
+ Input: VAL[i]=x[i]
+ Note that this is an array of strings, not an array of characters.
+ FTVL=="CHAR"
or FTVL="UCHAR"
:NORD
.
+ No separators are printed or expected.
+ Output: x=range(VAL,0,NORD)
+ The first NORD
characters are printed,
+ which might be less than NELM
.
+ Input: VAL=x, NORD=length(x)
+ A maximum of NELM-1
characters can be read.
+ NORD
is updated to the index of the first of the
+ trailing zeros.
+ Usually, this is the same as the string length.
+ FTVL
are not allowed for this format.
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: aao Records
+src/CONFIG_STREAM
.
+Normal Operation
+Separator
+variable in the protocol.
+When parsing input, a space as the first character of the
+Separator
matches any number of any whitespace
+characters.
+NORD
elements are
+written.
+NELM
elements is
+read and NORD
is updated accordingly.
+Parsing of elements stops when the separator does not match,
+conversion fails, or the end of the input is reached.
+A minimum of one element must be available.
+FTVL
field.
+The variable x[i]
stands for one element of
+the written or read value.
+
+
+
+%f
):x[i]=double(VAL[i])
+ FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ Input: VAL[i]=FTVL(x[i])
+ FTVL
must be "FLOAT"
or "DOUBLE"
+ %i
or %{
):x[i]=long(VAL[i])
+ FTVL
can be
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ Signed values are sign-extended to long, unsigned values are
+ zero-extended to long before converting them.
+ Input: VAL[i]=FTVL(x[i])
+ FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).
+ The value is truncated to the least significant bytes if
+ FTVL
has a smaller data size than long
.
+ %s
):
+
+ Other values of FTVL=="STRING"
:x[i]=VAL[i]
+ Input: VAL[i]=x[i]
+ Note that this is an array of strings, not an array of characters.
+ FTVL=="CHAR"
or FTVL="UCHAR"
:NORD
.
+ No separators are printed or expected.
+ Output: x=range(VAL,0,NORD)
+ The first NORD
characters are printed,
+ which might be less than NELM
.
+ Input: VAL=x, NORD=length(x)
+ A maximum of NELM-1
characters can be read.
+ NORD
is updated to the index of the first of the
+ trailing zeros.
+ Usually, this is the same as the string length.
+ FTVL
are not allowed for this format.
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: ai Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):x=(VAL-AOFF)/ASLO
+ Input: VAL=(x*ASLO+AOFF)*(1.0-SMOO)+VAL*SMOO
+ In both cases, if ASLO==0.0
, it is treated as 1.0
.
+ Default values are ASLO=1.0
, AOFF=0.0
,
+ SMOO=0.0
.
+ If input is successful, UDF
is cleared.
+ %i
):x=RVAL
+ Input: RVAL=x
+ Note that the record calculates
+ VAL=(((RVAL+ROFF)*ASLO+AOFF)*ESLO+EOFF)*(1.0-SMOO)+VAL*SMOO
+ if LINR=="LINEAR"
.
+ ESLO
and EOFF
might be set in the record
+ definition. StreamDevice does not set it. For example,
+ EOFF=-10
and ESLO=0.000305180437934
+ (=20.0/0xFFFF) maps 0x0000 to -10.0, 0x7FFF to 0.0 and 0xFFFF to 10.0.
+ %{
):%s
):Initialization
+@init
handler is executed, if present.
+ In contrast to normal operation, in DOUBLE input SMOO
is ignored
+ (treated as 0.0
).
+
+StreamDevice: ao Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):x=(OVAL-AOFF)/ASLO
+ Input: VAL=x*ASLO+AOFF
+ In both cases, if ASLO==0.0
, it is treated as 1.0
.
+ Default values are ASLO=1.0
, AOFF=0.0
.
+ Note that OVAL
is not necessarily equal to VAL
+ if OROC!=0.0
.
+ %i
):x=RVAL
+ Input: RBV=x
+ Note that the record calculates
+ RVAL=(((OVAL-EOFF)/ESLO)-AOFF)/ASLO
if
+ LINR=="LINEAR"
. ESLO
and EOFF
+ might be set in the record definition. StreamDevice does not set it.
+ For example, EOFF=-10
and ESLO=0.000305180437934
+ (=20.0/0xFFFF) maps -10.0 to 0x0000, 0.0 to 0x7FFF and 10.0 to 0xFFFF.
+ %{
):%s
):Initialization
+@init
handler is executed, if
+ present. In contrast to normal operation, output in DOUBLE format uses
+ VAL
instead of OVAL
. Note that the record
+ initializes VAL
from DOL
if that is a constant.
+ LONG input is put to RVAL
as well as to RBV
and
+ converted by the record.
+
+StreamDevice: bi Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):x=RVAL
+ Input: RVAL=x&MASK
+ MASK
can be set be set in the record definition. Stream
+ Device does not set it. If MASK==0
, it is ignored
+ (i.e. RVAL=x
). The record sets
+ VAL=(RVAL!=0)
, i.e. 1
if RVAL!=0
+ and 0
if RVAL==0
.
+ %{
):x=VAL
+ Input: VAL=(x!=0)
+ %s
):VAL
, ZNAM
or
+ ONAM
is written, i.e. x=VAL?ONAM:ZNAM
.
+ Input: If input is equal to ZNAM
or ONAM
,
+ VAL
is set accordingly. Other input strings are not accepted.
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: bo Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):x=RVAL
+ Input: RBV=x&MASK
+ MASK
can be set be set in the record definition. Stream
+ Device does not set it. If MASK==0
, it is ignored
+ (i.e. RBV=x
).
+ %{
):x=VAL
+ Input: VAL=(x!=0)
+ %s
):VAL
, ZNAM
or
+ ONAM
is written, i.e. x=VAL?ONAM:ZNAM
.
+ Input: If input is equal to ZNAM
or ONAM
,
+ VAL
is set accordingly. Other input strings are not accepted.
+ Initialization
+@init
handler is executed, if
+ present. In contrast to normal operation, LONG input is put to
+ RVAL
as well as to RBV
and converted by the record.
+
+Bus API
+
+
+Bus Interface Class
+
+RegisterStreamBusInterface()
+in the global context of the C++ file (not in a header file).
+Example bus interface class declaration
+
+#include <StreamBusInterface.h>
+
+class MyInterface : StreamBusInterface
+{
+ // ... (internally used attributes and methods)
+
+ MyInterface(Client* client);
+ ~MyInterface();
+
+ // StreamBusInterface virtual methods
+ bool lockRequest(unsigned long lockTimeout_ms);
+ bool unlock();
+ bool writeRequest(const void* output, size_t size,
+ unsigned long writeTimeout_ms);
+ bool readRequest(unsigned long replyTimeout_ms,
+ unsigned long readTimeout_ms,
+ long expectedLength, bool async);
+ bool supportsAsyncRead();
+ bool supportsEvent();
+ bool acceptEvent(unsigned long mask,
+ unsigned long replytimeout_ms);
+ bool connectRequest(unsigned long connecttimeout_ms);
+ bool disconnect();
+ void finish();
+
+public:
+ // creator method
+ static StreamBusInterface* getBusInterface(
+ Client* client, const char* busname,
+ int addr, const char* param);
+};
+
+RegisterStreamBusInterface(MyInterface);
+
+// ... (implementation)
+
+
+Methods to implement
+
+static StreamBusInterface*
+ getBusInterface(Client* client,
+ const char* busname, int addr,
+ const char* param);
+
+bool lockRequest(unsigned long lockTimeout_ms);
+
+bool unlock();
+
+bool writeRequest(const void* output,
+ size_t size, unsigned long writeTimeout_ms);
+
+bool readRequest(unsigned long replyTimeout_ms,
+ unsigned long readTimeout_ms,
+ long expectedLength, bool async);
+
+bool supportsAsyncRead();
+
+bool supportsEvent();
+
+bool acceptEvent(unsigned long mask,
+ unsigned long replytimeout_ms);
+
+bool connectRequest(unsigned long connecttimeout_ms);
+
+bool disconnect();
+
+void finish();
+
+void release();
+
Callback methods provided
+
+void lockCallback(StreamIoStatus status);
+
+void writeCallback(StreamIoStatus status);
+
+long readCallback(StreamIoStatus status,
+ const void* input = NULL,
+ long size = 0);
+
+void eventCallback(StreamIoStatus status);
+
+void connectCallback(StreamIoStatus status);
+
Other provided methods, attibutes, and types
+
+
+StreamBusInterface(Client* client);
+
+long priority();
+
+const char* clientName();
+
+const char* getOutTerminator(size_t& length);
+
+const char* getInTerminator(size_t& length);
+
+enum StreamIoStatus {StreamIoSuccess, StreamIoTimeout, StreamIoNoReply, StreamIoEnd, StreamIoFault};
+
Theory of Operation
+
+
+Registration
+
+RegisterStreamBusInterface(interfaceClass);
+
RegisterStreamBusInterface()
+registers the bus interface.
+It must be called exactly once for each bus interface class in global
+file context.
+Creation and deletion
+
+static StreamBusInterface* getBusInterface(Client* client,
+ const char* busname, int addr,
+ const char* param);
+
+StreamBusInterface(Client* client);
+
+void release();
+
+const char* clientName();
+
getBusInterface()
method
+of every registered interface class.
+This method should check by busname
if its interface class
+is responsible for that bus.
+If yes, it should check if the address addr
is valid and
+associate a device with busname
/addr
.
+Some busses do not have addresses and allow only one device
+(e.g. RS232).
+Interfaces to such busses can ignore addr
.
+The bus interface may then try to connect to the device, but it should
+allow it to be disconnected or switched off at that time.
+If the bus interface requires additional parameters, parse the
+param
string.
+Your constructor should pass client
to the base class
+constructor StreamBusInterface(Client* client)
.
+getBusInterface
should then return a pointer
+to a bus interface instance.
+Note that many client instances may want to connect to the same device.
+Each needs its own bus interface instance.
+The bus interface can get a string containing the name of the
+client instance from clientName()
.
+This name is for use in error and log messages.
+getBusInterface
should return NULL
.
+The client will then try other bus interface classes.
+release()
.
+The default implementation of release()
assumes that
+getBusInterface()
has allocated a new bus interface
+and just calls delete
.
+You should change release()
if that assumption is not
+correct.
+Connecting and disconnecting
+
+bool connectRequest(unsigned long connecttimeout_ms);
+
+bool disconnect();
+
+void connectCallback(IoStatus status);
+
connectRequest()
.
+This function should return true
immediately
+or false
if the request cannot be accepted.
+The interface should call connectCallback(StreamIoSuccess)
+once the bus could be connected.
+If the bus cannot be connected within connecttimeout_ms
+milliseconds, the bus interface should call
+connectCallback(StreamIoTimeout)
.
+connectCallback(StreamIoFault)
may be called.
+disconnect()
;
+This function should return true
immediately or
+false
if disconnecting is impossible.
+There is no callback for disconnect()
.
+Bus locking
+
+bool lockRequest(unsigned long lockTimeout_ms);
+
+void lockCallback(IoStatus status);
+
+bool unlock();
+
+long priority();
+
+void finish();
+
lockRequest()
to get
+exclusive access to the device.
+This function should return true
immediately
+or false
if the request cannot be accepted.
+If the device is already locked, the bus interface should add itself to
+a queue, sorted by priority()
.
+As soon as the device is available, the bus interface should call
+lockCallback(StreamIoSuccess)
.
+If the bus cannot be locked within lockTimeout_ms
+milliseconds, the bus interface should call
+lockCallback(StreamIoTimeout)
.
+lockCallback(StreamIoFault)
may be called.
+unlock()
.
+If other bus interfaces are in the lock queue, the next one should
+call lockCallback(StreamIoSuccess)
now.
+finish()
when the protocol ends.
+This allows the bus interface to clean up.
+The bus interface should also cancel any outstanding requests of
+this client.
+Writing output
+
+bool writeRequest(const void* output,
+ size_t size, unsigned long writeTimeout_ms);
+
+void writeCallback(IoStatus status);
+
+const char* getOutTerminator(size_t& length);
+
writeRequest()
.
+You can safely assume that the device has already been locked at this
+time.
+That means, no other client will call writeRequest()
+for this device and no other output is currently active for this device
+until it has been unlocked.
+size
bytes of
+output
but return true
immediately
+or false
if the request cannot be accepted.
+It must not block until output has completed.
+After all output has been successfully transmitted, but not earlier, the
+interface should call writeCallback(StreamIoSuccess)
.
+writeTimeout_ms
milliseconds,
+the interface should abort the transmision and call
+writeCallback(StreamIoTimeout)
.
+writeCallback(StreamIoFault)
may be called.
+size
bytes
+from output
.
+It must not change anything and it should not assume that
+any bytes have a special meaning.
+In particular, a null byte does not terminate output
.
+getOutTerminator
tells the interface which
+terminator has already been added to the output.
+If NULL
was returned, the client is not aware of a
+terminator (no outTerminator was defined in the protocol).
+In this case, the interface may add a terminator which it knows from
+other sources.
+An interface is not required to support NULL
results
+and may not add any terminator in this case.
+output
stays valid until
+writeCallback()
is called.
+unlock()
after
+writeCallback()
has been called.
+Reading input
+
+bool readRequest(unsigned long replyTimeout_ms,
+ unsigned long readTimeout_ms,
+ long expectedLength, bool async);
+
+long readCallback(IoStatus status,
+ const void* input = NULL,
+ long size = 0);
+
+const char* getInTerminator(size_t& length);
+
+bool supportsAsyncRead();
+
readRequest()
to tell the bus interface
+that it expects input.
+Depending on the bus, this function might have to set the bus hardware
+into receive mode.
+If expectedLength>0
, the the bus interface should stop
+input after this number of bytes have been received.
+In opposite to writing, the device may be in a non-locked status when
+readRequest()
is called.
+readCallback(StreamIoSuccess, buffer, size)
to be called
+when input has been received and return true
+immediately or false
if the request cannot be accepted.
+buffer
is a pointer to
+size
input bytes.
+The bus interface is responsible for the buffer.
+The client copies its contents. It does not modify or free it.
+n=readCallback()
after
+any amount of input has been received.
+If the client needs more input, readCallback()
+returns a non-zero value.
+A positive n
means, the client needs another
+n
bytes of input.
+A negative n
means, the client needs an unspecified
+amount of additional input.
+readRequest()
might not have to
+do anything because the bus is always receiving.
+It might also be that the bus has no local buffer associated to store
+input before it is fetched with some read()
call.
+In this case, a race condition between device and client can occure.
+To avoid loss of data, readCallback(StreamIoSuccess, buffer, size)
+may be called in this case even before readRequest()
.
+If the client is expecting input in the next future, it will store it.
+Otherwise the input is dropped.
+replyTimeout_ms
parameter defines how many milliseconds
+to wait for the first byte of a reply before the device is considered
+offline.
+If no input has been received after replyTimeout_ms
+milliseconds, the bus interface should call
+readCallback(StreamIoNoReply)
.
+readTimeout_ms
parameter is the maximum time to wait
+for further input.
+If input stops for longer than readTimeout_ms
milliseconds
+the bus interface should call
+readCallback(StreamIoTimeout,buffer,size)
.
+The client decides if this timeout is an error or a legal termination.
+Thus, pass all input received so far.
+getInTerminator(length)
tells the interface which
+terminator is expected for input and length
is set to the
+number of bytes of the terminator. The result is a hint to the bus
+interface to recognize the end of an input.
+Once the terminator string is found, the bus interface should stop
+receiving input and call
+readCallback(StreamIoSuccess, buffer, size)
.
+It is not necessary to remove the terminator string from the received input.
+An empty terminator string (length==0
) means:
+Don't look for terminators.
+NULL
was returned, the client is not aware of a
+terminator (no inTerminator was defined in the protocol).
+In this case, the interface may look for a terminator which it knows from
+other sources, reduce size by the terminator length and call
+readCallback(StreamIoEnd, buffer, size)
.
+A bus interface is not required to support NULL
results and
+may treat them as empty terminator (see above).
+readCallback(StreamIoEnd, buffer, size)
.
+Use it to indicate a special "end of message" signal which is not
+visible in the normal byte data stream.
+If getInTerminator()
has not returned NULL
+it it not necessary to remove a terminator which may come in
+addition to the "end of message" signal.
+readCallback(StreamIoFault)
may be called.
+async
flag is true
, the client
+wants to read input asyncronously without any timeout.
+That means, the bus interface should call readCallback()
+even if the input was requested by another client.
+supportsAsyncRead()
.
+The default implementation of this method always returns
+false
.
+A bus interface may overwrite this method to return true
+and eventually prepare for asynchonous input.
+The client is then allowed to call readRequest()
with
+the async==true
.
+This means that the client is interested in any input.
+It should receive a readCallback()
of all input which came
+in response to a synchonous (async==false
) request from
+another client (which of course should receive the input, too).
+The interface should also receive asynchonous input when no
+synchonous client is active at the moment.
+Many asynchonous readRequest()
calls from different clients
+may be active at the same time.
+All of them should receive the same input.
+replyTimeout_ms
has a different
+meaning: If the bus interface has to poll the bus for input, it may take
+replyTimeout_ms
as a hint for the poll period.
+If many asynchonous requests are active at the same time, it should poll
+with the shortest period of all clients.
+An asynchonous request does not time out.
+It stays active until the next input arrives.
+The client may reissue the asynchronous readRequest()
+from within the readCallback()
if it wants to continue
+receiving asynchonous input.
+finish()
at any time, the bus
+interface should cancel all outstanding requests, including
+asynchonous read requests.
+Handling events
+
+bool supportsEvent();
+
+bool acceptEvent(unsigned long mask, unsigned long replytimeout_ms);
+
+void eventCallback(StreamIoStatus status);
+
supportsEvent()
to return true
.
+The default implementation always returns false
.
+true
is returned, the client is allowed to call
+acceptEvent()
, where mask
defines the
+(bus dependent) type of event or events to wait for.
+If mask
is illegal, acceptEvent()
should
+return false
.
+The call to acceptEvent()
must not block.
+It should arrange to call eventCallback(StreamIoSuccess)
+when the event matching mask
arrives within
+replytimeout_ms
milliseconds.
+If no such event arrives within this time, the bus interface
+should call eventCallback(StreamIoTimeout)
.
+acceptEvent()
but after any previous call of any
+other request method like writeRequest()
.
+
+StreamDevice: calcout Records
+
+Normal Operation
+x
stands for the written or read value.
+
+
+%f
):x=OVAL
+ Input: VAL=x
+ Note that the record calculates OVAL
from CALC
+ or OCAL
depending on DOPT
.
+ %i
):x=int(OVAL)
+ Input: VAL=x
+ %{
):x=int(OVAL)
+ Input: VAL=x
+ %s
):A
to L
directly (e.g. "%(A)f"
).
+ However, even if OVAL
is not used, it is calculated by the
+ record. Thus, CALC
must always contain a valid expression
+ (e.g. "0"
).
+Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: Using EPICS 3.13
+
+
+1. Prerequisites
+2. Build the Compatibility Package
+3. Build the asynDriver Library
+COMPAT=...
to the <top>
+directory where you have installed the compatibility package before.
+(This patch might also allow you to compile other 3.14-style drivers for 3.13.
+It has absolutely no effect if you use EPICS 3.14.)
+IPAC=...
+(unless you have the ipac package and somehow made it
+compatible to EPICS 3.13).
+Set EPICS_BASE
to your EPICS 3.13 installation.
+static gDset
to
+gDset
.
+4. Build the StreamDevice Library
+ASYN
.
+Set it to the location of the asynDriver installation.
+Also set the COMPAT
variable to the location of the
+compatibility package.
+Run make in the config directory.
+5. Build an Application
+
+LDLIBS += $(COMPAT_BIN)/compatLib
+LDLIBS += $(ASYN_BIN)/asynLib
+LDLIBS += $(INSTALL_BIN)/streamLib
+
+include $(TOP)/config/RULES.munch
+
+
+include "base.dbd"
+include "stream.dbd"
+include "asyn.dbd"
+
+6. The Startup Script
+STREAM_PROTOCOL_PATH
to a
+list of directories to search.
+Directories are separated by :
.
+The default value is STREAM_PROTOCOL_PATH=.
,
+i.e. the current directory.
+Example:
+
+ld < iocCore
+ld < streamApp.munch
+dbLoadDatabase ("streamApp.dbd")
+
+putenv ("STREAM_PROTOCOL_PATH=.:../protocols")
+
+drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
+asynSetOption ("PS1", 0, "baud", "9600")
+asynSetOption ("PS1", 0, "bits", "8")
+asynSetOption ("PS1", 0, "parity", "none")
+asynSetOption ("PS1", 0, "stop", "1")
+
+
+STREAM_PROTOCOL_PATH
variable can also be a vxWorks shell
+variable.
+
+ld < iocCore
+ld < compatLib
+ld < asynLib
+ld < streamLib.munch
+dbLoadDatabase ("asyn.dbd")
+dbLoadDatabase ("stream.dbd")
+
+STREAM_PROTOCOL_PATH=".:../protocols"
+
+drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
+asynSetOption ("PS1", 0, "baud", "9600")
+asynSetOption ("PS1", 0, "bits", "8")
+asynSetOption ("PS1", 0, "parity", "none")
+asynSetOption ("PS1", 0, "stop", "1")
+
+
+7. Continue as with EPICS 3.14.
+
+
+StreamDevice: Format Converter API
+
+Sorry, this documentation is still missing.
+
+
+StreamDevice: Format Converters
+
+
+1. Format Syntax
+out
or in
+commands.
+
+
+
+%
character()
*# +0-
.
) followed
+ by an integer precision field (input ony for most formats)*
flag skips data in input formats.
+Input is consumed and parsed, a mismatch is an error, but the read
+data is dropped.
+This is useful if input contains more than one value.
+Example: in "%*f%f";
reads the second floating point
+number.
+#
flag may alter the format, depending on the
+converter (see below).
+
' (space) and +
flags print a space
+or a +
sign before positive numbers, where negative
+numbers would have a -
.
+0
flag says that numbers should be left padded with
+0
if width is larger than required.
+-
flag specifies that output is left justified if
+width is larger than required.
+Examples:
+
+
+
+
+
+
+
+ in "%f";
float
+
+
+
+ out "%(HOPR)7.4f";
the HOPR field as 7 char float with precision 4
+
+
+
+ out "%#010x";
0-padded 10 char alternate hex (with leading 0x)
+
+
+
+ in "%[_a-zA-Z0-9]";
string of chars out of a charset
+
+
+
+ in "%*i";
skipped integer number
+ 2. Data Types and Record Fields
+Default fields
+VAL
or RVAL
field
+of the record, selected automatically depending on the data type.
+Not all data types make sense for all record types.
+Refer to the description of supported record
+types for details.
+float
+and double
nor between short
, int
+and long
values.
+Thus, data type modifiers like l
or h
do not
+exist in StreamDevice formats.
+Accessing record fields directly
+%
.
+For example out "%(EGU)s";
outputs the EGU
+field formatted as a string.
+Use in "%(otherrecord.VAL)f";
to write the floating
+point input value into the VAL
field of
+otherrecord
.
+(You can't skip .VAL
here.)
+This is very useful when one line of input contains many values that should
+be distributed to many records.
+If otherrecord
is passive and the field has the PP
+attribute (see
+Record Reference Manual), the record will be processed.
+It is your responsibility that the data type of the record field is
+compatible to the the data type of the converter.
+Note that using this syntax is by far not as efficient as using the
+default field.
+Pseudo-converters
+%(FIELD)
syntax cannot be used.
+3. Standard DOUBLE Converters (
+%f
, %e
,
+%E
, %g
, %G
)%f
prints fixed point, %e
prints
+exponential notation and %g
prints either fixed point or
+exponential depending on the magnitude of the value.
+%E
and %G
use E
instead of
+e
to separate the exponent.
+With the #
flag, output always contains a period character.
+4. Standard LONG Converters (
+%d
, %i
,
+%u
, %o
, %x
, %X
)%d
and %i
print signed decimal,
+%u
unsigned decimal, %o
unsigned octal, and
+%x
or %X
unsigned hexadecimal.
+%X
uses upper case letters.
+With the #
flag, octal values are prefixed with 0
+and hexadecimal values with 0x
or 0X
.
+%d
matches signed decimal, %u
matches
+unsigned decimal, %o
unsigned octal.
+%x
and %X
both match upper or lower case unsigned
+hexadecimal.
+Octal and hexadecimal values can optionally be prefixed.
+%i
matches any integer in decimal, or prefixed octal or
+hexadecimal notation.
+Leading whitespaces are skipped.
+5. Standard STRING Converters (
+%s
, %c
)%s
prints a string.
+If precision is specified, this is the maximum string length.
+%c
is a LONG format in output, printing one character!
+%s
matches a sequence of non-whitespace characters
+and %c
a sequence of not-null characters.
+The maximum string length is given by width.
+The default width is infinite for %s
and
+1 for %c
.
+Leading whitespaces are skipped with %s
but not with
+%c
.
+The empty string matches.
+6. Standard Charset STRING Converter (
+%[charset]
)^
, the format matches
+all characters not in charset.
+Leading whitespaces are not skipped.
+%[_a-z]
matches a string consisting
+entirely of _
(underscore) or letters from a
+to z
.
+7. ENUM Converter (
+%{string0|string1|...}
)|
.
+If one of the strings contains |
or }
,
+a \
must be used to escape the character.
+%{OFF|STANDBY|ON}
mapps the string OFF
+to the value 0, STANDBY
to 1 and ON
to 2.
+8. Binary LONG Converter (
+%b
, %Bzo
)%b
format uses the characters 0
and
+1
.
+With the %B
format, you can choose two other characters
+to represent zero and one.
+%B.!
or %B\x00\xff
.
+%B01
is equivalent to %b
.
+0
means that the value should be padded with
+with the chosen zero character instead of spaces.
+If precision is set, it means the number of significant bits.
+Otherwise, the highest 1 bit defines the number of significant bits.
+There is no alternate format when #
is used.
+9. Raw LONG Converter (
+%r
)#
flag, the byte order is changed to little
+endian, i.e. least significant byte first.
+long
,
+the value is sign extended.
+long
, only
+the least significant bytes are used.
+10. Packed BCD (Binary Coded Decimal) LONG Converter (
+%D
)0
... 9
).
+Thus a BCD byte is in the range from 0x00
to 0x99
.
+The normal byte order is big endian, i.e. most significant byte
+first.
+With the #
flag, the byte order is changed to little
+endian, i.e. least significant byte first.
+The +
flag defines that the value is signed, using the
+upper half of the most significant byte for the sign.
+Otherwise the value is unsigned.
+0xF
in their most significant half
+byte followed by the absolute value.
+11. Checksum Pseudo-Converter (
+%<checksum>
)"abcdefg%<xor>"
the checksum is calculated
+from abcdefg
,
+but in "abcdefg%2.1<xor>"
only from cdef
.
+#
flag, the byte order is changed to little
+endian, i.e. least significant byte first.
+0
flag changes the checksum representation from
+binary to hexadecimal ASCII (2 bytes per checksum byte).
+
+Implemented checksum functions
+
+
+
+%<SUM>
or %<SUM8>
%<SUM16>
%<SUM32>
%<NEGSUM>
or %<NSUM>
or %<-SUM>
%<NOTSUM>
or %<~SUM>
%<XOR>
%<CRC8>
%<CCITT8>
%<CRC16>
%<CRC16R>
%<CCITT16>
%<CCITT16A>
%<CRC32>
%<CRC32R>
%<JAMCRC>
%<ADLER32>
%<HEXSUM8>
+
+
+
+
+
diff --git a/doc/index.html b/doc/index.html
new file mode 100644
index 0000000..4c6c671
--- /dev/null
+++ b/doc/index.html
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
+
+
+
+
+ StreamDevice
+
+
+
+StreamDevice: longin Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):x=VAL
+ Input: VAL=x
+ %{
):x=VAL
+ Input: VAL=x
+ %s
):Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: longout Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):x=VAL
+ Input: VAL=x
+ %{
):x=VAL
+ Input: VAL=x
+ %s
):Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: mbbi Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):
+
+ ZRVL
... FFVL
is set
+ (is not 0
):x=RVAL&MASK
+ Input: RVAL=x&MASK
+ Note that the record shifts RVAL
right by
+ SHFT
bits, compares the result with all of
+ ZRVL
... FFVL
, and sets VAL
+ to the index of the first match.
+ MASK
is initialized to NOBT
1-bits shifted
+ left by SHFT
. If MASK==0
(because
+ NOBT
was not set) it is ignored, i.e.
+ x=RVAL
and RVAL=x
.
+ ZRVL
... FFVL
is set
+ (all are 0
):x=VAL
+ Input: VAL=x
+ %{
):x=VAL
+ Input: VAL=x
+ %s
):VAL
, one of ZRST
+ or FFST
is written. VAL
must be in the range
+ 0 ... 15.
+ Input: If input is equal one of ZRST
...
+ FFST
, VAL
is set accordingly.
+ Other input strings are not accepted.
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: mbbiDirect Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):
+
+ MASK==0
(because NOBT
is not set):x=VAL
+ Input: VAL=x
+ MASK!=0
:x=RVAL&MASK
+ Input: RVAL=x&MASK
+ MASK
is initialized to NOBT
1-bits shifted
+ left by SHFT
.
+ %{
):%s
):Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: mbbo Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):
+
+ ZRVL
... FFVL
is set
+ (is not 0
):x=RVAL&MASK
+ Note that the record calculates RVAL
by choosing one of
+ ZRVL
... FFVL
depending on VAL
+ and by shifting it left by SHFT
bits.
+ Input: RBV=x&MASK
+ MASK
is initialized to NOBT
1-bits shifted
+ left by SHFT
. If MASK==0
(because
+ NOBT
was not set) it is ignored, i.e.
+ x=RVAL
and RBV=x
.
+ ZRVL
... FFVL
is set
+ (all are 0
):x=VAL
+ Input: VAL=x
+ %{
):x=VAL
+ Input: VAL=x
+ %s
):VAL
, one of ZRST
+ ... FFST
is written. VAL
must be in the
+ range 0 ... 15.
+ Input: If input is equal one of ZRST
...
+ FFST
, VAL
is set accordingly.
+ Other input strings are not accepted.
+ Initialization
+@init
handler is executed, if
+ present. In contrast to normal operation, LONG input is put to
+ RVAL
as well as to RBV
and converted by the
+ record.
+
+StreamDevice: mbboDirect Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):
+
+ MASK==0
(because NOBT
is not set):x=VAL
+ Input: VAL=x
+ MASK!=0
:x=RVAL&MASK
+ Input: RBV=x&MASK
+ MASK
is initialized to NOBT
1-bits shifted
+ left by SHFT
.
+ %{
):%s
):Initialization
+@init
handler is executed, if
+ present. In contrast to normal operation, input is put to
+ RVAL
as well as to RBV
and converted by the
+ record if MASK!=0
.
+
+User's Guide
+
+
+
+
+
+
+ Programmer's Guide
+ StreamDevice: Operating System API
+
+Sorry, this documentation is still missing.
+
+
+StreamDevice: Record Processing
+
+
+1. Normal Processing
+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.
+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.
+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
.
+SEVR
field set to INVALID
and its
+STAT
field to something describing the error:
+
+
+
+TIMEOUT
LockTimeout
) or the
+ device did not reply (ReplyTimeout
).
+ WRITE
WriteTimeout
).
+ READ
ReadTimeout
).
+ COMM
CALC
in
command
+ or it contained values the record did not accept.
+ UDF
SEVR
and STAT
+will be set according to the original error.
+2. Initialization
+@init
handler is called as part of the
+initRecord()
function during iocInit
+before any scan task starts.
+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
.
+@init
handler has nothing to do with the
+PINI
field.
+The handler does not process the record nor does it trigger
+forward links or other PP links.
+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.
+@init
handler 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.
+@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).
+3. I/O Intr
+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.
+in
command until matching input
+is received.
+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.
+I/O Intr
mode.
+Example:
+
+ROI?
",
+it replies something like "ROI 17.3 58.7
",
+i.e. a string containing both values.
+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")
+}
+
+
+getROIstart {
+ out "ROI?";
+ in "ROI %f %*f";
+}
+getROIend {
+ in "ROI %*f %f";
+}
+
+%*
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.
+
+
+StreamDevice: Protocol Files
+
+1. General Information
+INP
+or OUT
link of the records which use it.
+The protocol file must be stored in one of the directories listed
+in the environment variable STREAM_PROTOCOL_PATH
+(see chapter Setup).
+'
or double "
) is not case sensitive.
+This includes the names of commands,
+protocols and variables.
+There may be any amount of whitespaces (space, tab, newline, ...) or
+comments between names, quoted strings and special
+characters, such as ={};
.
+A comment is everything starting from an unquoted #
+until the end of the line.
+Example Protocol File:
+
+# This is an example protocol file
+
+Terminator = CR LF;
+
+# Frequency is a float
+# use ai and ao records
+
+getFrequency {
+ out "FREQ?"; in "%f";
+}
+
+setFrequency {
+ out "FREQ %f";
+ @init { getFrequency; }
+}
+
+# Switch is an enum, either OFF or ON
+# use bi and bo records
+
+getSwitch {
+ out "SW?"; in "SW %{OFF|ON}";
+}
+
+setSwitch {
+ out "SW %{OFF|ON}";
+ @init { getSwitch; }
+}
+
+# Connect a stringout record to this to get
+# a generic command interface.
+# After processing finishes, the record contains the reply.
+
+debug {
+ ExtraInput = Ignore;
+ out "%s"; in "%39c"
+}
+
+
+
+2. Protocols
+{}
.
+The name must be unique within the protocol file.
+It is used to reference the protocol in the
+INP
or OUT
link of the record,
+thus keep it short.
+It should describe the function of the protocol.
+It must not contain spaces or any of the characters
+,;={}()$'"\#
.
+;
.
+Referencing other protocols
+@init
handlers in the above example.
+Limitations
+3. Commands
+out
, in
, wait
, event
,
+exec
, disconnect
, and connect
.
+Most protocols will consist only of a single out
command to
+write some value,
+or an out
command followed by an in
command to
+read a value.
+But there can be any number of commands in a protocol.
+
+
+
+
+out string;
in string;
in
command.
+ If a device, for example, acknowledges a setting, use an
+ in
command to check the acknowledge, even though
+ it contains no user data.
+ wait milliseconds;
event(eventcode) milliseconds;
eventcode
with some timeout.
+ What an event actually means depends on the used
+ bus.
+ Some buses do not support events at all, some provide many different
+ events.
+ If the bus supports only one event, (eventcode)
+ is dispensable.
+ exec string;
disconnect;
in
or out
command will automatically
+ reconnect.
+ Only records reading in
+ "I/O Intr" mode
+ will not cause a reconnect.
+ connect milliseconds;
milliseconds
+ timeout.
+ Since connection is handled automatically, this command is normally not
+ needed.
+ It may be useful after a disconnect
.
+ 4. Strings
+
+"That's a string."
+'Say "Hello"'
+out
or in
+commands, string literals can contain
+format converters.
+A format converter starts with %
and works similar
+to formats in the C functions printf() and scanf().
+\
to
+define some escape sequences in quoted string literals:
+\"
, \'
, \%
, and \\
+mean literal "
, '
, %
, and
+\
.
+\a
means alarm bell (ASCII code 7).
+\b
means backspace (ASCII code 8).
+\t
means tab (ASCII code 9).
+\n
means new line (ASCII code 10).
+\r
means carriage return (ASCII code 13).
+\e
means escape (ASCII code 27).
+\x
followed by up to two hexadecimal digits means a byte with
+that hex value.
+\0
followed by up to three octal digits means a byte with
+that octal value.
+\1
to \9
followed by up to two more decimal
+digits means a byte with that decimal value.
+\?
in the argument string of an in
+command matches any input byte
+\$
followed by the name of a
+protocol varible is replaced by the contents of that
+variable.
+
+EOT
means end of transmission (ASCII code 4).
+ACK
means acknowledge (ASCII code 6).
+BEL
means bell (ASCII code 7).
+BS
means backspace (ASCII code 8).
+HT
or TAB
mean horizontal tabulator
+(ASCII code 9).
+LF
or NL
mean line feed /
+new line (ASCII code 10).
+CR
means carriage return (ASCII code 13).
+ESC
means escape (ASCII code 27).
+DEL
means delete (ASCII code 127).
+SKIP
in the argument string of an in
+command matches any input byte.
+Example:
+
+"Hello world\r\n"
+'Hello',0x20,"world",CR,LF
+72 101 108 108 111 32 119 111 114 108 100 13 10
+5. Protocol Variables
+in
and out
commands.
+Protocol arguments work like function
+arguments and can be specified in the INP
or
+OUT
link of the record.
+User variables can be defined and used
+in the protocol as abbreviations for often used values.
+
+variable = value;
+$variable
or ${variable}
+and inside quoted strings by
+\$variable
or \${variable}
.
+The reference will be replaced by the value of the variable at
+this point.
+System variables
+
+
+
+
+LockTimeout = 5000;
out
command in a protocol.
+ If other records currently use the device, how many milliseconds
+ to wait for exclusive access to the device before giving up?
+ WriteTimeout = 100;
out
commands.
+ If we have access to the device but output cannot be written
+ immediately, how many milliseconds to wait before giving up?
+ ReplyTimeout = 1000;
in
commands.
+ Different devices need different times to calculate
+ a reply and start sending it.
+ How many milliseconds to wait for the first byte of the input
+ from the device?
+ Since several other records may be waiting to access the device
+ during this time, LockTimeout
should be larger than
+ ReplyTimeout
.
+ ReadTimeout = 100;
in
commands.
+ The device may send input in pieces (e.g. bytes).
+ When it stops sending, how many milliseconds to wait for more
+ input bytes before giving up?
+ If InTerminator = ""
, a read timeout is not an error
+ but a valid input termination.
+ PollPeriod = $ReplyTimeout;
in
command in
+ I/O Intr
mode (see chapter
+ Record Processing).
+ In that mode, some buses require periodic polling to get asynchronous
+ input if no other record executes an in
command at
+ the moment.
+ How many milliseconds to wait after last poll or last received
+ input before polling again?
+ If not set the same value as for ReplyTimeout
is
+ used.
+ Terminator
out
and in
commands.
+ Most devices send and expect terminators after each message,
+ e.g. CR LF
.
+ The value of the Terminator
variable is automatically
+ appended to any output.
+ It is also used to find the end of input.
+ It is removed before the input is passed to the in
+ command.
+ If no Terminator
or InTerminator
is defined,
+ the underlying driver may use its own terminator settings.
+ For example, asynDriver defines its own terminator settings.
+ OutTerminator = $Terminator;
out
commands.
+ If a device has different terminators for input and output,
+ use this for the output terminator.
+ InTerminator = $Terminator;
in
commands.
+ If a device has different terminators for input and output,
+ use this for the input terminator.
+ If no Terminator
or InTerminator
is defined,
+ the underlying driver may use its own terminator settings.
+ If InTerminator = ""
, a read timeout is not an error
+ but a valid input termination.
+ MaxInput = 0;
in
commands.
+ Some devices don't send terminators but always send a fixed message
+ size. How many bytes to read before terminating input even without
+ input terminator or read timeout?
+ The value 0
means "infinite".
+ Separator = "";
out
and in
commands.
+ When formatting or parsing array values in a format converter
+ (see formats and
+ waveform record), what string
+ to write or to expect between values?
+ If the first character of the Separator
is a
+ space, it matches any number of any whitespace characters in
+ an in
command.
+ ExtraInput = Error;
Error
or Ignore
.
+ Affects in
commands.
+ Normally, when input parsing has completed, any bytes left in the
+ input are treated as parse error.
+ If extra input bytes should be ignored, set
+ ExtraInput = Ignore;
+ Protocol arguments
+
+moveX { out "X GOTO %d"; }
+moveY { out "Y GOTO %d"; }
+moveZ { out "Z GOTO %d"; }
+
+
+move { out "\$1 GOTO %d"; }
+
+OUT
link
+of three different records as move(X)
,
+move(Y)
and move(Z)
.
+Up to 9 parameters, referenced as $1
... $9
+can be specified in parentheses, separated by comma.
+The variable $0
is replaced by the name of the protocol.
+User variables
+
+f = "FREQ"; # sets f to "FREQ" (including the quotes)
+f1 = $f " %f"; # sets f1 to "FREQ %f"
+
+getFrequency {
+ out $f "?"; # same as: out "FREQ?";
+ in $f1; # same as: in "FREQ %f";
+}
+
+setFrequency {
+ out $f1; # same as: out "FREQ %f";
+}
+
+
+6. Exception Handlers
+@
.
+
+
+@mismatch
in
command.
+ It means that the device has sent something else than what the
+ protocol expected.
+ If the handler starts with an in
command, then this
+ command reparses the old input from the unsuccessful in
.
+ Error messages from the unsuccessful in
are suppressed.
+ Nevertheless, the record will end up in INVALID/CALC
+ state (see chapter Record Processing).
+ @writetimeout
out
command.
+ It means that output cannot be written to the device.
+ Note that out
commands in the handler are
+ also likely to fail in this case.
+ @replytimeout
in
command.
+ It means that the device does not send any data.
+ Note that in
commands in the handler are
+ also likely to fail in this case.
+ @readtimeout
in
command.
+ It means that the device stopped sending data unexpectedly after
+ sending at least one byte.
+ @init
iocInit
during record
+ initialization.
+ It can be used to initialize an output record with a value read from
+ the device.
+ Also see chapter Record Processing.
+ Example:
+
+setPosition {
+ out "POS %f";
+ @init { out "POS?"; in "POS %f"; }
+}
+
+
+
+StreamDevice: Record API
+
+Sorry, this documentation is still missing.
+
+
+StreamDevice: Record Types
+
+Supported Record Types
+
+aai
+aao
+ai
+ao
+bi
+bo
+mbbi
+mbbo
+mbbiDirect
+mbboDirect
+longin
+longout
+stringin
+stringout
+waveform
+calcout
+scalcout
+
+StreamDevice: scalcout Records
+
+init_record
add
+before the final return(0)
:
+
+ if(pscalcoutDSET->init_record ) {
+ return (*pscalcoutDSET->init_record)(pcalc);
+ }
+
+
+Normal Operation
+x
stands for the written or read value.
+
+
+%f
):x=OVAL
+ Input: VAL=x
+ Note that the record calculates OVAL
from CALC
+ or OCAL
depending on DOPT
.
+ %i
):x=int(OVAL)
+ Input: VAL=x
+ %{
):x=int(OVAL)
+ Input: VAL=x
+ %s
):x=OSV
+ Input: SVAL=x
+ A
to L
and AA
to LL
+ directly (e.g. "%(A)f"
or "%(BB)s"
).
+ However, even if OVAL
is not used, it is calculated by the
+ record. Thus, CALC
must always contain a valid expression
+ (e.g. "0"
).
+Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: Setup
+
+
+1. Prerequisites
+
+epicsShareFunc int epicsShareAPI iocshCmd(const char *command);
+
+
+ASYN
+and (if installed) CALC
or SYNAPPS
+to your <top>/configure/RELEASE file:
+
+ASYN=/home/epics/asyn/4-5
+CALC=/home/epics/synApps/calc/2-7
+
+2. Build the StreamDevice Library
+3. Build an Application
+
+PROD_LIBS += stream
+PROD_LIBS += asyn
+
+
+include "base.dbd"
+include "stream.dbd"
+include "asyn.dbd"
+registrar(drvAsynIPPortRegisterCommands)
+registrar(drvAsynSerialPortRegisterCommands)
+
+4. The Startup Script
+STREAM_PROTOCOL_PATH
to a
+list of directories to search.
+On Unix and vxWorks systems, directories are separated by :
,
+on Windows systems by ;
.
+The default value is STREAM_PROTOCOL_PATH=.
,
+i.e. the current directory.
+Example:
+
+epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
+
+drvAsynSerialPortConfigure ("PS1","/dev/ttyS1")
+asynSetOption ("PS1", 0, "baud", "9600")
+asynSetOption ("PS1", 0, "bits", "8")
+asynSetOption ("PS1", 0, "parity", "none")
+asynSetOption ("PS1", 0, "stop", "1")
+asynSetOption ("PS1", 0, "clocal", "Y")
+asynSetOption ("PS1", 0, "crtscts", "N")
+
+
+
+epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
+
+drvAsynIPPortConfigure ("PS1", "192.168.164.10:23")
+
+
+
+epicsEnvSet ("STREAM_PROTOCOL_PATH", ".:../protocols")
+
+vxi11Configure ("PS1","192.168.164.10",1,1000,"hpib")
+
+
+
+
+5. The Protocol File
+iocInit
, streamDevice loads and parses
+the required protocol files.
+If the files contain errors, they are printed on the IOC shell.
+Put the protocol file in one of the directories listed in
+STREAM_PROTOCOL_PATH
.
+Example:
+"CURRENT 5.13"
.
+When asked with the string "CURRENT?"
, the device returns
+the last set value in a string like "CURRENT 5.13 A"
.
+setCurrent
.
+
+Terminator = CR LF;
+
+setCurrent {
+ out "CURRENT %.2f";
+ @init {
+ out "CURRENT?";
+ in "CURRENT %f A";
+ }
+}
+
+
+
+Reloading the Protocol File
+streamReload("record")
.
+If "record"
is not given, all records using
+StreamDevice reload their protocols.
+Furthermore, the streamReloadSub
function can be used
+with a subroutine record to reload all protocols.
+SEVR=INVALID
and STAT=UDF
.
+If a record can't reload its protocol file (e.g. because of a syntax
+error), it stays INVALID
/UDF
until a valid
+protocol is loaded.
+6. Configure the Records
+DTYP
field to
+"stream"
.
+The INP
or OUT
link has the form
+"@file protocol bus [address [parameters]]"
.
+file
is the name of the protocol file and
+protocol
is the name of a protocol defined in this file.
+If the protocol requires arguments,
+specify them enclosed in parentheses:
+protocol(arg1,arg2,...)
.
+bus
and
+addr
.
+If the bus does not have addresses, addr
is dispensable.
+Optional parameters
are passed to the bus driver.
+Example:
+
+record (ao, "PS1:I-set")
+{
+ field (DESC, "Set current of PS1")
+ field (DTYP, "stream")
+ field (OUT, "@ExamplePS.proto setCurrent PS1")
+ field (EGU, "A")
+ field (PREC, "2")
+ field (DRVL, "0")
+ field (DRVH, "60")
+ field (LOPR, "0")
+ field (HOPR, "60")
+}
+
+
+
+
+EPICS StreamDevice Documentation
+
+What is StreamDevice?
+RF:FREQ 499.655 MHZ
", StreamDevice can be used.
+How the strings exactly look like is defined in protocols.
+Formatting and interpretation of values is done with
+format converters similar to those
+known from the C functions printf() and scanf().
+To support other formats, it is possible to
+write your own converters.
+What is StreamDevice not?
+Recommended Readings
+
+
+StreamDevice: stringin Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):%{
):%s
):x=VAL
+ Input: VAL=x
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: stringout Records
+
+Normal Operation
+x
stands for the
+written or read value.
+
+
+
+%f
):%i
):%{
):%s
):x=VAL
+ Input: VAL=x
+ Initialization
+@init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+StreamDevice: Tips and Tricks
+
+
+I have many almost identical protocols
+INP
or OUT
link, write:
+
+field (OUT, "@protocolfile protocol(arg1,arg2,arg3) bus")
+
+$1 $2 $3
or inside strings
+as "\$1 \$2 \$3"
.
+
+moveaxis {out "move\$1 %.6f";}
+
+field (OUT, "@motor.proto moveaxis(X) motor1")
+
+readpressure {out 0x02 0x00 $1; in 0x82 0x00 $1 "%2r";}
+
+field (INP, "@vacuumgauge.proto readpressure(0x84) gauge3")
+I have a device that sends unsolicited data
+I/O Intr
processing.
+The record receives any input and processes only when the input matches.
+
+read {in "new value = %f";}
+
+
+record (ai, "$(RECORD)") {
+
+ field (DTYP, "stream")
+ field (INP, "@(DEVICETYPE).proto read $(BUS)")
+ field (SCAN, "I/O Intr")
+}
+I have a device that sends multi-line messages
+
+Here is the value:
+3.1415
+
+in
commands as you get input lines.
+
+read_value {in "Here is the value:"; in "%f";}
+
+I need to write more than one value in one message
+A) All values have the same type and are separated by the same string
+
+array_out {separator=", "; out "an array: (%.2f)";}
+
+%.2f
is repeated for each element of the array.
+All elements are separated by ", "
.
+Output will look like this:
+
+an array: (3.14, 17.30, -12.34)
+
+
+Use a calcout record and +field references in the format. +
+
+
+write_ABC {out "A=%(A).2f B=%(B).6f C=%(C).0f";}
+
+
+You must specify a valid expression in the CALC
field
+even if you don't use it.
+
+
+record (calcout, "$(RECORD)") {
+
+ field (INPA, "$(A_RECORD)")
+ field (INPB, "$(B_RECORD)")
+ field (INPC, "$(C_RECORD)")
+ field (CALC, "0")
+ field (DTYP, "stream")
+ field (OUT, "@(DEVICETYPE).proto write_ABC $(BUS)")
+}
+
+Use record references in the format. +
+
+
+acquire {out 'ACQUIRE "%(\$1:directory.VAL)s/%s",%(\$1:time.VAL).3f;';}
+
+
+You must specify the full record.FIELD
name,
+even for VAL
fields.
+To avoid record names in protocol files use
+protocol arguments.
+In the link, specify the record name or just the basename of the
+other records (device name) in parentheses.
+
+
+record (stringout, "$(DEVICE):getimage") {
+
+ field (DTYP, "stream")
+ field (OUT, "@(DEVICETYPE).proto acquire($(DEVICE)) $(BUS)")
+}
+
+Again, there is more than one solution to this problem. +
+
+Use array records (e.g. waveform,
+aai).
+
+
+array_in {separator=","; in "array = (%f)";}
+
+
+The format %f
is repeated for each element of the array.
+A ","
is expected beween element.
+Input may look like this:
+
+array = (3.14, 17.30, -12.34) ++ +
+Use I/O Intr
processing
+and value skipping (%*
)
+
+
+read_A {out "GET A,B"; in "A=%f, B=%*f";}
+
+read_B {in "A=%*f, B=%f";}
+
+
+record (ai, "$(DEVICE):A") {
+
+ field (DTYP, "stream")
+ field (INP, "@(DEVICETYPE).proto read_A $(BUS)")
+ field (SCAN, "1 second")
+}
+record (ai, "$(DEVICE):B") {
+ field (DTYP, "stream")
+ field (INP, "@(DEVICETYPE).proto read_B $(BUS)")
+ field (SCAN, "I/O Intr")
+}
+
+Record A actively requests values every second.
+The reply contains values A and B.
+Record A filters only value A from the input and ignores value B
+by using the *
flag.
+Nevertheless, a complete syntax check is performed: B must be a
+valid floating point number.
+Record B is I/O Intr
and gets (a copy of) any input, including
+input that was directed to record A.
+If it finds a matching string it ignores value A, reads value B and
+then processes.
+Any non-matching input is ignored by record B.
+
+Use record references in the format. +To avoid record names in protocol files, use +protocol arguments. +
+
+
+read_AB {out "GET A,B"; in "A=%f, B=%(\$1.VAL)f";}
+
+
+
+record (ai, "$(DEVICE):A") {
+
+ field (DTYP, "stream")
+ field (INP, "@(DEVICETYPE).proto read_AB($(DEVICE):B) $(BUS)")
+ field (SCAN, "1 second")
+}
+record (ai, "$(DEVICE):B") {
+}
+
+Whenever record A reads input, it stores the first value in its own VAL field +as usual and the second in the VAL field of record B. +Because the VAL field of record B has the PP attribute, this automatically +processes record B. +
+ + +
+Use a @mismatch
+exception handler and
+record references in the format.
+To avoid record names in protocol files, use
+protocol arguments.
+
+When asked "CURRENT?
", the device send something like
+"CURRENT 3.24 A
" or a message like
+"device switched off
".
+
+
+read_current {out "CURRENT?"; in "CURRENT %f A"; @mismatch {in "%(\1.VAL)39c";}}
+
+
+
+record (ai, "$(DEVICE):readcurrent") {
+
+ field (DTYP, "stream")
+ field (INP, "@(DEVICETYPE).proto read_current($(DEVICE):message) $(BUS)")
+}
+record (stringin, "$(DEVICE):message") {
+}
+
+After processing the readcurrent record, you can see from +SEVR/STAT if the read was successful or not. +With some more records, you can clean the message record if SEVR is not INVALID. +
+
+record (calcout, "$(DEVICE):clean_1") {
+ field (INPA, "$(DEVICE):readcurrent.SEVR CP")
+ field (CALC, "A!=2")
+ field (OOPT, "When Non-zero")
+ field (OUT, "$(DEVICE):clean_2.PROC")
+}
+record (stringout, "$(DEVICE):clean_2") {
+ field (VAL, "OK")
+ field (OUT, "$(DEVICE):message PP")
+}
+
+
+Dirk Zimoch, 2007
+ + + diff --git a/doc/waveform.html b/doc/waveform.html new file mode 100644 index 0000000..8614972 --- /dev/null +++ b/doc/waveform.html @@ -0,0 +1,126 @@ + + + +
+With waveform records, the format converter is applied to
+each element. Between the elements, a separator is printed
+or expected as specified by the Separator
+variable in the
+protocol.
+When parsing input, a space as the first character of the
+Separator
matches any number of any whitespace
+characters.
+
+During input, a maximum of NELM
elements is
+read and NORD
is updated accordingly.
+Parsing of elements stops when the separator does not match,
+conversion fails, or the end of the input is reached.
+A minimum of one element must be available.
+
+During output, the first NORD
elements are
+written.
+
+The format data type must be convertible to or from the type
+specified in the FTVL
field.
+The variable x[i]
stands for one element of
+the written or read value.
+
%f
):x[i]=double(VAL[i])
FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).VAL[i]=FTVL(x[i])
FTVL
must be "FLOAT"
or "DOUBLE"
+ %i
or %{
):x[i]=long(VAL[i])
FTVL
can be
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).VAL[i]=FTVL(x[i])
FTVL
can be "DOUBLE"
, "FLOAT"
,
+ "LONG"
, "ULONG"
, "SHORT"
,
+ "USHORT"
, "CHAR"
, "UCHAR"
,
+ or "ENUM"
(which is treated as "USHORT"
).FTVL
has a smaller data size than long
.
+ %s
):FTVL=="STRING"
:x[i]=VAL[i]
VAL[i]=x[i]
FTVL=="CHAR"
or FTVL="UCHAR"
:NORD
.
+ No separators are printed or expected.x=range(VAL,0,NORD)
NORD
characters are printed,
+ which might be less than NELM
.VAL=x, NORD=length(x)
NELM-1
characters can be read.
+ NORD
is updated to the index of the first of the
+ trailing zeros.
+ Usually, this is the same as the string length.
+ FTVL
are not allowed for this format.
+
+ During initialization, the @init
handler is executed, if
+ present. All format converters work like in normal operation.
+
+aai +aao +ai +ao +bi +bo +mbbi +mbbo +mbbiDirect +mbboDirect +longin +longout +stringin +stringout +calcout +scalcout +
+Dirk Zimoch, 2005
+ + + diff --git a/src/AsynDriverInterface.cc b/src/AsynDriverInterface.cc new file mode 100644 index 0000000..11b4e28 --- /dev/null +++ b/src/AsynDriverInterface.cc @@ -0,0 +1,1324 @@ +/*************************************************************** +* StreamDevice Support * +* * +* (C) 2005 Dirk Zimoch (dirk.zimoch@psi.ch) * +* * +* This is the interface to asyn driver for StreamDevice. * +* Please refer to the HTML files in ../doc/ for a detailed * +* documentation. * +* * +* If you do any changes in this file, you are not allowed to * +* redistribute it any more. If there is a bug or a missing * +* feature, send me an email and/or your patch. If I accept * +* your changes, they will go to the next release. * +* * +* DISCLAIMER: If this software breaks something or harms * +* someone, it's your problem. * +* * +***************************************************************/ + +#include "devStream.h" +#include "StreamBusInterface.h" +#include "StreamError.h" +#include "StreamBuffer.h" + +#ifdef EPICS_3_14 +#include