MPF is available via the open source license described at the end of this document.
MPFOSI (Message Passing Facility Operating System Independent) is the successor to MPF (See next section). The main goal is to provide an EPICS 3.14 migration path for existing MPF users. MPFOSI uses the Operating System Independent features of EPICS base 3.14 and should work on all 3.14 IOC platforms.
This document describes the following components:
Message Passing Facility
MPF provides client server message passing. The messages are handled by message routers. A local message router is provided.Epics Device Support for the MPF
A base class DevMpf is provided for writing epics device support that communicates with MPF.Serial support for the MPF
Serial support that interfaces to asynDriver. All low level asyn drivers that implement the asynCommon and asynOctet interfaces are this accessable by MPF.
All code is written in C++. It is written to minimize dependencies between various components. The standard C library but not the standard C++ library is used.
MPF is the successor to HIDEOS which was written with the following goals:
When other IP carriers became available, it was decided that that it was too time consuming to support processors besides the MV162. Thus MPF was developed. It supports the same type of client server relationship as Hideos but support two types of communication: 1) If the client and server reside on the same processor, messages are passed in memory, and 2) If the client and server reside on different processors, message are passed via TCP. The original MPF was only supported on vxWorks. When EPICS base evolved to be Operating System Independent (OSI), the OSI version of MPF was devloped. When this document refers to MPF it means the OSI version. When it refers to EPICS base it means the OSI version of base.
The following people have contributed to MPF:
.../support/mpfosi/
gunzip <file>.tar.gz tar xvf <file>.tar
.../support/mpfosi/mpfosiX-Ywhere X-Y is the release. For example.
.../support/mpfosi/mpf2-1
Now proceed to the next two sections.
The iocBoot directory has a number of sub directories for running mpf tests and/or examples. The best example is a test which connects an epics database to a mpf server. To start the test:
cd iocBoot/iocepicslocal ../../bin/<arch>/test st.cmd
testApp/src/DevMpfInt32Test.cpp | This is an example of device support. |
testApp/src/serverInt32.cpp | The echo server for Int32 messages. |
testApp/src | The subdirectory shows how to build clients and server applications. |
testApp/Db/*.db | The databases for the example. |
iocBoot/iocepicslocal/st.cmd | The startup file for the test. |
Studying the above will show how to write servers, device support, build servers and client, and boot servers and clients.
It is a good idea to keep mpf separate from applications which use it. An application can access mpf components by making the following changes:
MPF=<full path name>
<prod>_LIBS += mpf asyn # For vxWorks applications only #<prod>_LIBS += Ipac TyGSOctal <prod>_LIBS += $(EPICS_BASE_IOC_LIBS)
The xxxInclude.dbd file must include some combination of the following:
include "mpf.dbd" include "devStringMpf.dbd" include "mpfSerial.dbd" include "mpfSerialVx.dbd"
NOTE:
mpfSerialVx.dbd contains a registrar command for the IPAC carrier support that works on an MV162. It also includes registrar commands for other IPAC carrier support. Most applications will have to copy mpfSerialVx.dbd to where the application is build and modify it so that the correct carrier support is chosen.
The st.cmd file must contain commands to start MPF routers and MPF servers. Look at the iocBoot/iocxxx/st.cmd files provided with MPF for examples.
Messages are sent through a router. Two routers are provided: localRouter and RMR (Remote Message Router). The localRouter passes messages between a client and server residing on the same processor. RMR sends messages between a client and server on different processors via a tcp connection.
typedef void (*clientCallback)(Message *message,void *clientPvt); class MessageClient { public: MessageClient(clientCallback,void *clientPvt); int bind(char *server, int location); int send(Message *message); void *getClientPvt(); private: ... }A client of the message passing system must:
class MessageServer { public: MessageServer(const char* name); void waitForMessage(); Message *receive(); Message *allocReplyMessage(Message *clientMessage,messageType type); int reply(Message *); void setQueueSize(int size); const char *getName() const; void report() const; private: ... }A message server must
Message instances are kept on a free list. Thus malloc is called only if the free list is empty. Free is never called. Each message type has it's own free list.
whereINP("C1 S0 @Int32")
It provides a report like the following:
mrr clientRouterList 1 LMRClient sendPerSec 0 serverRouterList 1 LMRServer replyPerSec 0The statistics are calculated periodically and are truncated thus a value <1 is reported as 0.
msr "Int32" Int32 queueSize 10 inQueue 0 queueRequests 132171 queueFullResponses 0 replyRequests 132171The meaning of the fields are:
This version only supports a local message router. All mention of router should be removed from MPFOSI.
NOTE: In the future I hope that support for client and server on separate IOCs can be abandoned. If it is then the concept of message routers will go away. For now the localMessageRouter must be started.
The local message router passes message between a client and server residing in the same processor. When the client passes a message to a server the message is just appended to the server's queue. When the server sends a reply to the client, the client processes the message immediately. Thus when a client sends to the server the client never waits for the server. The server, however, will wait for the client to process a reply message.
The local message router must be started.
localMessageRouterStart(location)The parameter is the location of the local server. Note that the client specifies a location when it binds to a server. Each cpu can start a local router.
<message class> *pmessage = new <message class>;for example an Int32 message can be allocated as follows:
Int32Message *pmessage = new Int32Message;
Int32Message *pmessage =(Int32Message*)pMessageServer-> allocReplyMessage(preceive,messageTypeInt32);where preceive must be a message received from the client. The reason is that the receive message contains information describing where the client is located.
delete pmessage;Thus the server must delete any message received from a client and a client must delete reply messages from the server.
The definition of class Message is:
class Message { public: messageType getType(); int32 getClientType(); void setClientType(int32 type); int32 getClientExtra(); void setClientExtra(int32 extra); virtual ~Message(); virtual int toBuffer(char **buffer); virtual int fromBuffer(const char **buffer); virtual int fromBufferSwitch(const char **buffer); virtual void print() const; static Message *allocate(messageType type); protected: Message(messageType type); .... }
Client and server code normally only call new, delete, the methods described in the following table, and message specific methods. The other methods are called by MPF itself. A message is allocated via a call to new and released via a call to delete. When delete is called the message is placed on a free list. Only one of the message types derived from Message can be created, i.e. it is not possible to create an object of type Message.
The virtual methods must be implement by any class that derives from Message. The toBuffer and fromBuffer methods are called to put and take messages from a buffer that passes over a communication link.
method | Recommended Meaning |
getType | Returns the message type |
getClientType setClientType |
These methods are for use by a client to access field clientType. When the server allocates a reply message via a call to allocateReplyMessage, the clientType from the clients message is copied to the reply message. In general a server should not use these methods. They are intended for clients that send multiple messages to a server and need to match reply messages with messages sent. Classes derived from DevMpf MUST NOT use these because DevMpf itself uses them. Use clientExtra instead. |
getClientExtra setClientExtra |
These access a field clientExtra which is similar to clientType. They can be used by classes derived from DevMpf. |
MPF provides a header file "mpfType.h" which defines typedefs for int16, uint16, int32, uint32, float32, and float64. MPF provides static methods to transfer the following to/from network buffers:
Field | Recommended Meaning |
timeoutUnits | timeoutUnitsSeconds or timeoutUnitsMilliseconds. Default is seconds |
timeout | The timeout the server can use it it has to wait for a device. The units are determined by timeoutUnits.. |
cmd | A command. Servers can supply a header file defining an enum for the commands. |
status | A status value. |
address | For anything that is an int32 and can be interpreted as an address. |
extra | An extra int32 word that client/server can use for anything they want as long as they agree on the meaning. |
Field | Recommended Meaning |
value | The address of a char8 array. The characters are not interpreted or translated by the message class, i.e. they are just a stream of octet values. The standard C library routines such as ::strcpy and ::memcpy can be used on the value field. |
numberRetrys | A useful field for servers. It should be the number of retrys if a command fails. |
eomLen | A useful field for servers. It should be the length of an end of message string. |
eomString | A useful field for servers. It should be a 1 or 2 character end of message string. |
Char8ArrayMessage provides the following methods for allocating and
freeing the string value.
method | usage |
allocValue | This is a static method which looks for the smallest freelist that can provide the needed space. If the requested size is larger than the size for the largest freelist, it calls new to allocate space. |
setSize | Sets the current size for the array. This size MUST be less than that allocated by allocValue. |
getSize | Gets the current array size. |
getMaxSize | This gets the size allocated by the call to allocValue |
freeValue | This puts storage obtained by allocValue back on the appropriate free list or calls delete if the storage was obtained via a call to new. This method is called automatically when a Char8Array message is deleted. |
Field | Recommended Meaning |
value | The Int32 value being passed. |
method | usage |
allocValue | This is a static method which looks for the smallest freelist that can provide the needed space. If the requested size is larger than the size for the largest freelist, it calls new to allocate space. |
setLength | Sets the current length for the array. This length MUST be less than that allocated by allocValue. |
getLength | Gets the current array length. |
getMaxLength | This gets the length allocated by the call to allocValue |
freeValue | This puts storage obtained by allocValue back on the appropriate free list or calls delete if the storage was obtained via a call to new. This method is called automatically when an Int32Array message is deleted. |
Field | Recommended Meaning |
value | The Float64 value being passed |
method | usage |
allocValue | This is a static method which looks for the smallest freelist that can provide the needed space. If the requested size is larger than the size for the largest freelist, it calls new to allocate space. |
setLength | Sets the current length for the array. This length MUST be less than that allocated by allocValue. |
getLength | Gets the current array length. |
getMaxLength | This gets the length allocated by the call to allocValue |
freeValue | This puts storage obtained by allocValue back on the appropriate free list or calls delete if the storage was obtained via a call to new. This method is called automatically when an Float64Array message is deleted. |
field | usage |
baud | Baud rate. 0 means don't change |
stopBits | Stop bits. Normally 1 or 2. 0 means don't change. |
bitsPerChar | Bits per character. Normally 5,6,7, or 8. 0 means don't change. |
parity | Single character. Normally 'E', 'O', or 'N'. 0 means don't change. |
flowControl | Single character. Normally 'H' or 'N'. 0 means don't change. |
DevMpf is an abstract base class for implementing EPICS device support.
// return codes for startIO and completeIO. #define MPF_OK 0 #define MPF_NoConvert 2 enum replyType {replyTypeNone, replyTypeCompleteIO, replyTypeReceiveReply};
class DevMpf { public: DevMpf(dbCommon*,link*,bool iointValid); // Following must be implemented by device support modules virtual long startIO(dbCommon*)=0; // start async IO virtual long completeIO(dbCommon*,Message *)=0; // end async IO // the following can be implemented by device support modules virtual void receiveReply(dbCommon*,Message *); virtual void connectIO(dbCommon*,Message *); // connection message virtual void outOfBandIO(dbCommon*,OutOfBandMessage *);// outOfBand virtual long convert(dbCommon*,int pass); // do linear conversion // The following must be called after object is constructed void bind(); // The following are usefull during record initilization bool connectWait() {return(connectWait(5.0));} bool connectWait(double timeout); // send a message to MPF server with no reply expected int send(Message*); // send a message to MPF server and wait for reply via completeIO int sendReply(Message*); // send a message stating the type of reply int send(Message*,replyType); int sendWait(Message *pmessage,replyType type) {return(sendWait(pmessage,type,5.0));} int sendWait(Message*,replyType,double timeout); // This routine gets a pointer to the user portion of the parm field long getStatus() const { return(status);} // Following are DSET routines static long read_write(void*); // generic DSET read/write routine static long ioint(int cmd,dbCommon*,IOSCANPVT* iopvt); // DSET i/o intr static long linconv(void*,int); // calls convert IOSCANPVT ioscanpvt; bool iointValid; bool isConnected() { return((connectState==connectYes) ? true : false);} private: ...
}Before describing the methods a few comments may be helpful.
If the support is for a simple device, i.e. a device that can be supported via the following:
For devices that are not simple, e.g. devices that must take special action when connecting to the server, additional methods are available: In particular:
ioscanpvt is for provided for convenience. If a derived class
wishes to support io interrupt processing it can request that the base class
perform the necessary initialization. The derived class must, however, call
scanIoRequest.
method | Usage | meaning |
DevMpf | Constructor | The constructor must be given the address of the record and link (INP or OUT). iointValid specifies if the base class should initialize ioscanpvt. Normally this is only invoked via the constructor of a class derived from class DevMpf. After an object of the derived class is completely constructed, the bind method MUST be called. |
startIO | Called by DevMpf | When a request is made to process a record this is called only when
the following conditions are all true: The server is connected, PACT
is false, and no reply message from the server is available. This
MUST be implemented by any class derived from DevMpf.
This method is always called as a result of a record being processed and the record is not active. If startIO issues a send(message,replyTypeCompleteIO) then PACT is set TRUE, i.e. the record will not complete processing until the reply message is received and completeIO is called. |
completeIO | Called by DevMpf | DevMpf calls this when the reply to a
send(message,replyTypeCompleteIO) is received. This MUST be
implemented by any class that derives from DevMpf.
This method is always called as a result of a record being processed |
receiveReply | Called by DevMpf | DevMpf calls this when the reply to a
send(message,replyTypeReceiveReply) is received. This call does not
involve record processing.
The default method just prints a message and deletes the message it received. |
connectIO | Called by DevMpf Default supplied by DevMpf. |
DevMpf calls this when it receives a ConnectMessage. DevMpf
provides a default implementation.
If device support implements this method it should call DevMpf::connectIO (just before returning) to ensure correct behavior. |
outOfBandIO | Called by DevMpf Default supplied by DevMpf. |
DevMpf calls this when it receives an OutOfBand message. For
example if server receives a send (no reply) message that can not be
processed, the server can send an OutOfBand message to the client.
The default method just prints a message and deletes the message it received. |
convert | Called by DevMpf, Default supplied by DevMpf | This is the DSET convert routine for ai, ao type records. The default version does nothing. Device support derived from base can provide it's own version. |
bind | Called immediately after any object derived from class DevMpf is created. | This binds to the MPF server. Since binding may result in virtual class methods being called, binding MUST be done after construction. Thus it is not possible to bind in the constructor for DevMpf. |
connectWait | Called by derived class | Wait for connection to server. It can be called by the constructor of the derived class or by a private thread belonging to the derived class. Do NOT call this as part of record processing. |
send(Message*) | Equivalent to send(message,replyTypeNone) | |
sendReply | Equivalent to send(message,replyTypeCompleteIO) | |
send(Message*,replyType) | Called by derived class | Send a message to the server. SetClientType is called to specify
the replyType. If the message is successfully sent and replyType is
replyTypeCompleteIO pact is set true.
NOTE: Only one send(message,replyTypeCompleteIO) should be outstanding, i.e. after calling send(message,replyTypeCompleteIO) wait for completeIO to be called before issuing another send. |
sendWait | Called by derived class | Send a message and wait for the reply. It can be called by the constructor of the derived class or by a private thread belonging to the derived class. Do NOT call this as part of record processing. |
getUserParm | Called by derived class | Get the portion of the parm of the INP or OUT link that follows the server name. |
getStatus | Called by derived class | Get the status. A 0 value is success. Any other value is device dependent. |
read_write | Called by record support | This is the read or write DSET routine. Handled automatically by DevMpf |
ioint | Called by record support | This is DSET get_io_intr routine. Handled automatically by DevMpf |
linconv | Called by record support | This is DSET linr_conv routine. It calls convert. |
The INP or OUT field MUST have the format:
field(INP or OUT, "#C<location> S<signal> @<server>,<deviceSpecific>")
parameter | meaning |
location | Location of the server. Must be integer which determines which message router handles messages. |
signal | For optional use by device support derived from DevMpf |
server | Name of the server which receives messages. |
deviceSpecific | For optional use by device support derived from DevMpf. Either a blank or comma can separate the server name from the device specific information. |
The first is used for record types that support linear conversions, e.g. ai and ao, and the second for other record types.MAKE_LINCONV_DSET(<dset name>,<dev init>) MAKE_DSET(<dset name>,<dev init>)
<top>/ configure/ RELEASE mpfApp/ coreSrc/ messageSrc/ registerSrc/ testSrc/ utilSrc/ devMpfSrc/ serialSrc testApp/ adl/ Db/ src/ testSerialApp/ adl/ Db/ src/ iocBoot/ iocepicslocal/ iocmpflocal/ iocserialhost/ iocserialvxWorks/
configure | This is almost exactly like <top>configure described in the 3.14 version of makeBaseApp |
RELEASE | EPICS_BASE must be defined correctly. |
coreSrc | Contains the MPF code: Message, Routers, Tcp support. |
messageSrc | Contains the code to build specific message types. If new messages type are added this is the place. If it proves necessary to support an endless number of message types a better way of building new message types must be developed. Builds mpfMessageLib. |
registerSrc | Builds <top>/bin/<arch>/mpfRegister.o, which registers the mpf configuration commands. |
testSrc | Contains a test for watchDog |
utilSrc | DLList, DataFreeList, mpfFreeList, Reboot and WatchDog. |
devMpfSrc | Contains DevMpf.h and DevMpf.cpp. This is epics device support for mpf. |
serialSrc | Contains the code for serial support. |
testApp/Db and adl | Db contains the epics databases for the examples in iocepicsxxx. adl contains the example medm screen. |
testApp/src | Contains tcpTest, mpfTest, and DevMpfInt32Test. The Makefile is a good place to see how to build mpf applications. |
testSerialApp | Contains src, adl, and Db directories for testing serial support. |
iocepicslocal | The example where client and server run on the same processor. |
iocmpflocal | Test for local MPF communication |
iocserialhost | Test for serial support on non-vxWorks systems. |
iocserialvxWorks | Test for serial support on vxWorks systems. |
mpfFreeList provides the same functionality as the freeList facility provided with epics base, in fact it is just freeList redone in C++.
DLList is a double linked list class that does not require nodes to be embedded in objects placed in a list. This is different than the ellList facility provided by epics base.
WatchDog is a class that provides functionality similar to the vxWorks wdLib. The major exception is that the user supplied callback is called by a WatchDog supplied task rather than being called at interrupt level.
DataFreeList is a class for data free lists. It provides free lists of sizes ranging from 16 bytes to 4096 bytes. It is used by the array message types.
Reboot can be used to turn off interrupts when a soft reboot is being performed.
These classes are not described in this document. If you want to use them in new code look at examples in existing code.
NOTE: serialServer is now implemented by using asynDriver. serialServer and asynDriver BOTH create a thread for each serial port. This causes unnecessary context switching. If remote MPF servers are abandoned then a better method can be developed that uses only one thread.
mpfSerial provides support for EPICS records to communicate with serial devices, i.e. RS232, RS485, etc. The basic concept is:
The support is structured so that special requirements can be satisfied by writing device support and/or a serial server. As long as this support satisfies the conventions defined in serialserver.h then the low level drivers should still work.
mpfSerial consists of the folowing:
The major difference between the OSI (Operating System Independent) version of mpfSerial and the previous version is that the low lever driver support is asynDriver. This approach allows mpfSerial to be used with anything that asynDriver supports.
NOTE: The only serial device support discussed in this manual is devStringMpf. Most mpfSerial users use the genericBus/serialMpfApp support that comes with SYNAPPS.
To use the the version that uses writeRead requests on Unix/Linux do the following:
cd iocBoot/iocserialhost ../../bin/<arch>/testSerial st.cmd
NOTE: The st.cmd file is for a linux system and MOXA box in my office. It will have to be modified for your environment.
The test starts a serialServer and creates records for each port. The following is the record template for a port.record(calc,"serial$(port):calc$(ind)") { field(SCAN, "Event") field(EVNT, "1") field(CALC,"a=1000?0:a+1") field(FLNK,"serial$(port):StringOut$(ind)") field(INPA,"serial$(port):calc$(ind)") } record(stringout,"serial$(port):StringOut$(ind)") { field(DOL,"serial$(port):calc$(ind)") field(OMSL,"closed_loop") field(FLNK,"serial$(port):StringIn$(ind)") } record(stringin,"serial$(port):StringIn$(ind)") { field(DTYP,"MPFwriteRead") field(INP, "#C$(card) S0 @serial$(port),serial$(port):StringOut$(ind)") }The calc record counts from 0 to 1000 and links to a stringout record which reads the value of the calc record and forward links to a stringin record. The stringin record reads the value from the stringout record and sends it to serverChar8array. The reply from serverChar8array is put into the val field.
This section describes initialization commands that appear in a st.cmd file. A simple example may make this section easier to understand. The following commands initialize everything needed for accessing a single serial port on an x86 Linux based ioc.
localMessageRouterStart(1) drvGenericSerialConfigure("port0","/dev/ttyS0",0,0) initSerialServer("serial0","port0",0,1000,2,"")
The following is an example record that connects to this port.
record(stringin,"name") { field(DTYP,"MPFread") field(INP, "#C1 S0 @serial0") }
NOTE: #C1 means DevMpf location 1, which is the argument given to localMessageRouterStart.
Consult the asynDriver documentation for how to configure port drivers. For example look at the documentation for drvGenericSerialConfigure. The only configuration command provided by mpfSerial is:
int initSerialServer(const char *serverName,const char *portName, int addr, int bufsize, int queueSize, const char *eomstr);
The arguments are:
serverName | The server name which is specified in INP or OUT fields |
portName | The port name. This must match a port name from an asynDriver configuration command. |
addr | The address of the instrument on the port. If the port driver does not support multiple instruments just set addr to 0. |
buffersize | Input buffer size. Both the server and low level drivers need a buffer. |
queueSize | The number of message a server is willing to buffer. |
eomString | End of message string for input messages. The initial value can be
overridden by device support. The eomString can be any of the
following:
|
IPAC and asynDriver initialization commands are also required. Look at the st.cmd files in iocserialhost and iocserialvxWorks for examples. The initialization commands are described in the IPAC and asynDriver documentation.
The following example starts two serial ports on the ioc and four serial ports on a MOXA ethernet serial server that resides at address :164.54.9.90 and responds to TCP connect requests at TCP ports 4001,...,4004.
localMessageRouterStart(1) drvGenericSerialConfigure("port0","/dev/ttyS0",0,0) drvGenericSerialConfigure("port1","/dev/ttyS1",0,0) drvGenericSerialConfigure("port2","164.54.9.90:4001",0,0) drvGenericSerialConfigure("port3","164.54.9.90:4002",0,0) drvGenericSerialConfigure("port4","164.54.9.90:4003",0,0) drvGenericSerialConfigure("port5","164.54.9.90:4004",0,0) initSerialServer("serial0","port0",1000,2,"") initSerialServer("serial1","port1",1000,2,"") initSerialServer("serial2","port2",1000,2,"") initSerialServer("serial3","port3",1000,2,"") initSerialServer("serial4","port4",1000,2,"") initSerialServer("serial5","port5",1000,2,"")
The vxWorks support uses the standard vxWorks serial ports and/or drvIpac. The mpf installation includes a test for vxWorks that works on an MV162 with a Green Springs OctalUART installed in slot 2. In the mpf installation look at iocBoot/iocserialvxWorks/st.cmd. See the drvIpac documentation for how to configure other carriers. The example starts eight serial ports for a Green Springs OctalUART Industry Pack module.
mpfSerial support consists of three levels:
mpfSerial provides code for all the first two levels and uses asynDriver for the lowest level. It is designed so that additional code can be written at either bthe Device Support or Serial server levels in order to support special requirements.
The mpfSerial support interface is described in
serialServer.h
The interface between device support and the serial server. The definitions use fields of messages that are defined by MPF.
#define cmdWrite 0x1 #define cmdRead 0x2 #define cmdWriteRead (cmdWrite|cmdRead) //Following are additional options #define cmdFlush 0x4 #define cmdSetEom 0x8 //Following are for serialGetConfig #define cmdGetConfig 0x1 /* * cmdFlush - flush before starting write and/or read * cmdSetEom - change current eom * * for example the following could be specified * message->cmd = cmdWriteRead|cmdFlush; */ /* Generic serial server messages SerialConfigMessage. This just calls SerialPort::config. It returns an Int32Message. status is 0 for success. Int32Message. cmdGetConfig Returns a SerialConfigMessage with current settings Char8ArrayMessage cmdWrite : write request cmdRead : read request cmdWriteRead : writeRead request other fields from input message timeoutUnits timeout units. timeout The timeout value in timeoutUnits extra If >0 the maximum number of chars to read If cmdSetEom is set eomSize If 0 dont look for eom; otherwise must be 1 or 2 eomString 1 or 2 character eomString Return values A Char8ArrayMessage message, which contains any input characters read status is one of the serialStatus values */
serialServer.h is the interface between device suport and a serial server.
Some serial devices have a protocal that is not compatible with serialServer and/or devStringMpf. In such cases new device support and/or a new server must be supplied. SerialServer and devStringMpf provide a model of how to write special support. If a special application requires BOTH new device support and a new server than serialServer.h may not be applicable. If, however, either serialserver or devStringMpf is used then the conventions described in serialServer.h must be followed.
NOTE: Perhaps NO current mpfSerial users use this for device support, because it provides no nice way of handling termination characters.
devStringMpf implements three versions of device support, corresponding to the three commands that can be given to serialServer. The first two types are associated with a stringin record and the third with a stringout record.A record which uses MPFwriteRead has DTYP and INP defined as follows:
record(stringin,"<pvname>") { field(DTYP,"MPFwriteRead") field(INP, "#C<location> S0 @<server>,<inputRecord>") }
|
When the record is processed the following happens:
A record which uses MPFread has DTYP and INP defined as follows:
record(stringin,"<pvname>") { field(DTYP,"MPFread") field(INP, "#C<location> S0 @<server>") }A record which uses MPFwrite has DTYP and OUT defined as follows:
record(stringout,"<pvname>") { field(DTYP,"MPFwrite") field(OUT, "#C<location> S0 @<server>") }
serialServer is a generic server which implements the serialSever.h interface and communicates with low level drivers via the SerialPort interface. It accepts SerialConfig, Int32, and Char8Array messages. Client code must include file "serialServer.h", which contains definitions for commands, options, and for return status.
When serialServer receives a SerialConfigMessage it calls SerialPort::config and returns an Int32Message. The status field in Int32Message is one of the serialStatus values. Low level drivers may only support a subset of the config options.
If an Int32Message is sent to serialServer, the only valid command is cmdGetConfig. It returns a SerialConfigMessage with the current configuration. Low level drivers may only support a subset of the config options.
When serialServer receives a Char8Array message it returns a Char8Array
message. The return message contains any characters read and a serialStatus
value.
The cmd field of the Char8Array message can contain a command and
also options. The commands are:
cmdWrite | Write value to the port. A Char8Array message is returned. Only the status field is of interest. |
cmdRead | Read from from the serial port. A Char8Array message is returned. The value contains the input. Status should always be checked. Input is read until the first of the following occurs: 1) the end of message string is read, 2) extra is non zero and exactly extra bytes are read, 3) A read buffer overflow occurs, or 4) a timeout occurs. |
cmdWriteRead | The output string is sent to the serial port and a input string read. |
The cmd field can also contain the following options:
cmdFlush | Flush before starting I/O. |
cmdSetEom | Set end of message string. If the option is set then the fields eomLen and eomString determine the new end of message string. |
The following additional fields of the Char8ArrayMessage are also honored
by the serialServer:
timeout | The timeout value. This field must be set. The field timeoutUnits determines if the units are milliSeconds or seconds. |
numberRetrys | Number of times to retry the request if the initial request fails. |
extra | If extra <= 0 then it is ignored. If extra is > 0 then it is the maximum number of bytes to read before ending the read. Disabling the end of message string and specifying extra allows serialServer to read binary data. |
eomLen | If cmdSetEom is set, this field determines the length of the end of
message string. It can have the value 0, 1, or 2. A zero value disables the end of message string. If the value is 1 or 2 the string is determined by eomString |
eomString | If cmdSetEom is set and eomLen is 1 or 2, this is the end of message string. |
Copyright (c) 2002 University of Chicago. All rights reserved. MPF - Message Passing Facility MPF is distributed subject to the following license conditions: SOFTWARE LICENSE AGREEMENT Software: MPF 1. The "Software", below, refers to MPF (in either source code, or binary form and accompanying documentation). Each licensee is addressed as "you" or "Licensee." 2. The copyright holders shown above and their third-party licensors hereby grant Licensee a royalty-free nonexclusive license, subject to the limitations stated herein and U.S. Government license rights. 3. You may modify and make a copy or copies of the Software for use within your organization, if you meet the following conditions: a. Copies in source code must include the copyright notice and this Software License Agreement. b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy. 4. You may modify a copy or copies of the Software or any portion of it, thus forming a work based on the Software, and distribute copies of such work outside your organization, if you meet all of the following conditions: a. Copies in source code must include the copyright notice and this Software License Agreement; b. Copies in binary form must include the copyright notice and this Software License Agreement in the documentation and/or other materials provided with the copy; c. Modified copies and works based on the Software must carry prominent notices stating that you changed specified portions of the Software. 5. Portions of the Software resulted from work developed under a U.S. Government contract and are subject to the following license: the Government is granted for itself and others acting on its behalf a paid-up, nonexclusive, irrevocable worldwide license in this computer software to reproduce, prepare derivative works, and perform publicly and display publicly. 6. WARRANTY DISCLAIMER. THE SOFTWARE IS SUPPLIED "AS IS" WITHOUT WARRANTY OF ANY KIND. THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, AND THEIR EMPLOYEES: (1) DISCLAIM ANY WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE OR NON-INFRINGEMENT, (2) DO NOT ASSUME ANY LEGAL LIABILITY OR RESPONSIBILITY FOR THE ACCURACY, COMPLETENESS, OR USEFULNESS OF THE SOFTWARE, (3) DO NOT REPRESENT THAT USE OF THE SOFTWARE WOULD NOT INFRINGE PRIVATELY OWNED RIGHTS, (4) DO NOT WARRANT THAT THE SOFTWARE WILL FUNCTION UNINTERRUPTED, THAT IT IS ERROR-FREE OR THAT ANY ERRORS WILL BE CORRECTED. 7. LIMITATION OF LIABILITY. IN NO EVENT WILL THE COPYRIGHT HOLDERS, THEIR THIRD PARTY LICENSORS, THE UNITED STATES, THE UNITED STATES DEPARTMENT OF ENERGY, OR THEIR EMPLOYEES: BE LIABLE FOR ANY INDIRECT, INCIDENTAL, CONSEQUENTIAL, SPECIAL OR PUNITIVE DAMAGES OF ANY KIND OR NATURE, INCLUDING BUT NOT LIMITED TO LOSS OF PROFITS OR LOSS OF DATA, FOR ANY REASON WHATSOEVER, WHETHER SUCH LIABILITY IS ASSERTED ON THE BASIS OF CONTRACT, TORT (INCLUDING NEGLIGENCE OR STRICT LIABILITY), OR OTHERWISE, EVEN IF ANY OF SAID PARTIES HAS BEEN WARNED OF THE POSSIBILITY OF SUCH LOSS OR DAMAGES.