Message Passing Facility
Industry Pack Support

Marty Kraimer, Tom Coleman, Joe Sullivan
                                      April 27, 1999

Contents

Introduction
MPF will replace HIDEOS
Hardware Configurations
Install and Build MPF
Multiple Processor VME system
Getting Started
Using MPF in Applications
mpf/bin
mpf/dbd
MPF Directory Structure
Epics Record to IP - Flow of Control
Message Passing Overview
Diagnostic Aids
Message Routers
Message
MPF Device support
Utility Classes
Industry Pack Support
Serial Support
Gpib Support
ip330Scan
EXAMPLE Implementation: ip330Scan
Performance

Introduction

This document describes a set of software which allows EPICS  to access Industry Pack devices attached to the same or to remote processors. The  following components are described:

Message Passing Facility

MPF provides client server message passing.  The messages are handled by message routers. Both local and remote message routers are provided.
Industry Pack Support
Support is provided for Industry Pack modules.
Epics Device Support  for the MPF
Device support is provided that communicates with MPF.
The support requires vxWorks version 5.3 or later. MPF has been tested with epic base version 3.13.beta12.

All code is written in C++. It is written to minimize dependencies between various components. For example the IP support can be used without MPF, MPF without the IP support, etc. The current implementation uses the standard C library but not the standard C++ library.

MPF will replace HIDEOS

Background:

Experience has shown that "bare" HIDEOS is too hard to use and extend. It's debugging environment is not only different than vxWorks but  more primitive. Porting Hideous to new architectures will be a major task because it requires generating a "board support package" for each architecture.  It must be mentioned, however, that many of the basic ideas are taken from Hideous. Hideos was doing a lot of things well. Thank you Jim Kowalkowski.

Strategy:

The basic strategy is to abandon "bare" hideos. Instead vxWorks will be required.

The most important goal is to provide a migration path for existing HIDEOS applications. An EPICS  Hideos application uses the following  hideos components:

EPICS Records

The DTYP and INP fields of records can specify a connection to HIDEOS device support.  For some devices, MPF supports the HIDEOS conventions, which means existing EPICS record instances do not have to be modified when converting from HIDEOS to EPICS. In other cases, e.g. the ip330, changes are necessary.
Hideos Device Support
Modified to interface to the new MPF.
Hideos IP tasks
 
These are more generic. IP support is independent of MPF. MPF servers are provided to talk to the IP support.
Message Types
Message types are more general than the Hideos messages. Network messages are passed in an architecture independent representation. Hideos only allowed in-line methods. MPF removes this restriction.
HIDEOS_SYS
Everything currently residing under the HIDEOS_SYS sub directory is abandoned.
MPF provides all the features needed to support existing Hideos IP tasks and the Hideos EPICS device support.  Existing support must, however, be modified. In addition it provides the following features not supported by Hideos.
  1. Network Transparent message passing. This allows messages to be passed between different machine architectures.
  2. Connection Management - The initial release allows either server or client to be started first. It allows either client or server to be rebooted without booting the partner. Rebooting the server usually works without rebooting the client. The converse has not been successfully tested.
  3. Messages can have regular methods. (HIDEOS required that all message methods be inline)

Hardware Configurations

The intention is to support the following configurations: The first option has been tested on an mv162 and an mv172 It should work on any IP processor configured to run epics base release 3.13.beta12 or later.  The second option has been tested with an mv167 communicating with a mv162 and with an mv172 communicating with an mv162.The third option is not yet supported.

When MPF is used between processors, communication is via a tcp connection. Thus the two processors can reside anywhere as long as it is possible for them to communicate via tcp.

Install and Build MPF

The following are instructions for building mpf in directory mpf under your home directory.

If you have access to the epics CVS repository.

cd
cvs checkout -d mpf epics/unbundled/mpf
Otherwise obtain a tar file from the APS WWW site and:
cd
tar -xvf <file>
Next edit config/RELEASE:
cd
cd mpf/config
vi RELEASE
cd ..
In RELEASE set EPICS_BASE to reference your epics base. This MUST be release baseR3.13.0.beta12 or later.

In order to run the tests included with MPF, the following files must be edited in mpf/iocBoot

iocgpibclient/st.cmd
iocgpibserver/st.cmd
iocmpfclient/st.cmd
iocmpfserver/startServers
iocrstclient/st.cmd
iocrstserver/st.cmd
ioctcpclient/testClientWaitBuffer100
ioctcpclient/testClientWaitNoBuffer
ioctcplocal/tcpTestWaitLocalBuffer100
ioctcplocal/tcpTestWaitLocalNoBuffer
ioctcpserver/testServerWaitBuffer100
ioctcpserver/testServerWaitNoBuffer
iocip330scanclient/st.cmd
iocip330scanserver/st.cmd
The inet address "164.54.8.197" must be replaced by the address of your test mv162.

Now issue the commands:

cd
cd mpf
gnumake
Now proceed to the next two sections.

Multiple Processor VME system

This section describes how to set up a system that has two or more processors in a single VME crate: The following IP address (all in same subnet) are needed: Board support packages for the mv167 and mv162 can be obtained from Joe Sullivan. The boot parameters have the following form:

MV167

boot device          : ei
processor number     : 0
host name            : <host>
file name            : <full path>/vxWorks
inet on ethernet (e) : <IPmv167>:<subnet mask>
inet on back plane (b): <IPgateway>:<subnet mask>
host inet (h)        : <IPhost>
gateway inet (g)     :
user (u)             : <user>
ftp password (pw) (blank = use rsh): <password>
flags (f)            : 0x0
target name (tn)     : ioc167
startup script (s)   : <full path name>/st.cmd
other (o)            :
MV162
boot device          : sm=0x80000600
processor number     : 1
host name            : <host>
file name            : <full path>/vxWorks
inet on ethernet (e) :
inet on back plane (b): <IPmv162>:<subnet mask>
host inet (h)        : <IPhost>
gateway inet (g)     : <IPgateway>
user (u)             : <user>
ftp password (pw) (blank = use rsh): <password>
flags (f)            : 0x0
target name (tn)     : ioc162
startup script (s)   : <full path name>/st.cmd

Getting Started

The mpf/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 remote processor. To start the test: NOTE: In order to use the serial port tests a greensprings octal uart ip module is required. The tests assume that each of the 8 ports echos each character written to it, i.e. just wire pin2 to pin 3. The test assumes the ip module is installed in site IP_b.

The test consists of the following:

Studying this example should give a good idea of how MPF works. In particular look at the following files because they give examples of things developers are likely to do.
 
mpf/mpfServerApp/testSrc/serverInt32.cc The echo server for Int32 messages. 
mpf/mpfServerApp/testSrc/ The makefiles, etc show how to build server applications
mpf/epicsDevApp/testSrc/DevMpfInt32Test.cc The device support described above
mpf/epicsDevApp/testSrc/ The makefiles, etc. show how to build client applications
mpf/epicsDevApp/Db/*.db  The databases for the example
mpf/iocBoot/iocrstclient All files. NOTE: cdCommands is generated by make
mpf/iocBoot/iocrstserver All files. NOTE: cdCommands is generated by make
Studying  the above will show how to write servers, device support, build servers and client, and boot servers and clients.

Using MPF in Applications

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 in the application <top>/config directory.

Edit the files CONFIG_APP and RELEASE.

In CONFIG_APP add the lines:

ifdef MPF
USR_INCLUDES += -I$(MPF)/include
MPF_BIN = $(MPF)/bin/$(T_A)
USER_DBDFLAGS += -I $(MPF)/dbd
endif
In RELEASE add the line:
MPF=<full path name>
Then in Makefile.Vx statements like
LIBOBJS += $(MPF_BIN)/mpfLib
can be added. In addition .dbd files can be taken from mpf. For example:
include "devMpf.dbd"

mpf/bin

This is where the various MPF binary files are installed. It contains a subdirectory for each target, e.g. mv167. This section describes the components most likely to be included by applications.

epics applications

server applications

complete list

mpf/dbd

The following provide device definitions for use by epics applications:

MPF Directory Structure

Components which are of interest in the mpf tree are:
<top>/
    config/
           RELEASE
    utilApp/
           utilSrc/
    mpfApp/
           kernelSrc/
           messageSrc/
           libSrc/
           testSrc/
    ipApp/
           src/
    mpfServerApp/
           serverSrc/
           testSrc/
                 clientInt32.cc
                 serverInt32.cc
    epicsDevApp/
           Db/
           adl/
           epicsDevSrc/
                 devStringMpf.cc
    iocBoot/
           iocrstclient/
           iocrstserver/
           iocmpfclient/
           iocmpfserver/
           iocmpflocal/
           ioctcpclient/
           ioctcpserver/
 
config This is almost exactly like <top>config described in the 3.13.1 version of makeBaseApp
RELEASE EPICS_BASE must be defined correctly.
utilSrc DLList, DataFreeList, FreeList, and WatchDog; Builds mpfUtilLib.
kernelSrc Contains the MPF code: Message, Routers, Tcp support; Builds mpfKernelLib
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.
libSrc Builds mpfLib which contains mpfUtilLib, mpfKernelLib, and mpfMessageLib.
testSrc Contains some low level test programs. Not of interest except for MPF core development.
ipApp/src The current ip support: Ip itself, Gpib, Serial Support.
mpfServerApp/serverSrc Contains gpibServer and serialServer. gpibServer is a generic server for gpib that is usable for remote gpib communication by any EPICS  applications which use the gpib support provided with epics base. serialServer is generic MPF SerialPort support but as described elsewhere is not usable by all  serial applications.
mpfServerApp/testSrc Contains serverInt32 and serverChar8Array which  just echoes the messages sent to them. They are intended for testing and examples. clientInt32 and clientChar8Array are MPF clients. They are good examples of an MPF client and are also used for testing.
epicsDevApp/Db and adl Db contains the epics databases for the example in iocrstclient. adl contains the example medm screen.
epicsDevApp/epicsDevSrc This contains epics device support. Look at devStringMpf.cc for an example of how to write MPF device support.  The code in epicsDevApp/testSrc/epicsDevApp/DevMpfInt32Test.cc is also a good example.
iocrstclient Epics (mv167) side of example described previously.
iocrstserver Server (mv162) side of example described previously.
iocmpfclient/iocmpfserver Tests for remote MPF communication
iocmpflocal Test for local MPF communication
ioctcpclient/ioctcpserver Test for TCP communication.

Epics Record to IP - Flow of Control

Assume that some device is attached to a greensprings octal  serial port Ip module. The device returns an ascii string whenever an ascii string command is given to it. Also assume that device support devSiExample is written which accepts INP fields with the format:
INP("C1 S0 @serialServer,<command>")
where When the record is processed the following sequence of events occur:

Message Passing Overview

The new message passing system follows a client server model. A client creates a connection to a server. Once a connection is established either the client or server can send messages to the other.

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.

Message Client

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:
  1. Create an instance of MessageClient for each server with which it wants to communicate.
  2. Issue a bind call to establish communication.
  3. Call send to send messages.
  4. Provide a callback which is called whenever a connection is made or broken and is also called whenever the server sends a message to the client.
NOTE: DevMpf, described below, is a message client. Thus, for epics, new MPF device support is written rather than new message clients.

Message Server

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
  1. Create an instance of MessageServer.
  2. Call waitForMessage if it wants to block until it receives messages.
  3. Call receive to accept messages.
  4. Call reply to send messages to a client.
  5. Call allocReplyMessage if it wants to send additional additional messages to a client

Messages

MPF is written so that an extensible set of message types can be provided. The following message types have been implemented: NOTE: The Int32Array, Float64, Float64Array, and SerialConfig messages have not been tested.

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.

Example

Look at mpf/mpfServer/testSrc/clientInt32.cc for an example client and mpf/mpfServer/testSrc/serverInt32.cc for an example server. Also see EXAMPLE Implementation: ip330Scan.

Diagnostic Aids

The diagnostics described in the section are commands issued via the vxWorks shell.

mrr

Message Router Report provides a list of all client and server routers, connection state, queue sizes and state, and some statistics.

For servers it provides a report like the following:

iocrstserver> mrr                       
clientRouterList
serverRouterList
 1 RMRServer stateConnected queueSize 100 inQueue 0 replyQueueFull 0
   sendPerSec 240 receivePerSec 240  tcpSendPerSec 10 tcpReceivePerSec 10
It provides a report for each server router. It shows the connection state. The meaning of the other fields are: The statistics are calculated periodically and are truncated thus a value <1 is reported as 0.

For clients it produces the following type of report

iocrstclient> mrr
clientRouterList
 1 RMRClient stateConnected queueSize 100 inQueue 0 sendQueueFull 0
   Server Int32 has 4 clients. bindState connected
   Server Char8Array has 4 clients. bindState connected
   Server serial[0] has 2 clients. bindState connected
   Server serial[1] has 2 clients. bindState connected
   Server serial[2] has 2 clients. bindState connected
   Server serial[3] has 2 clients. bindState connected
   Server serial[4] has 2 clients. bindState connected
   Server serial[5] has 2 clients. bindState connected
   Server serial[6] has 2 clients. bindState connected
   Server serial[7] has 2 clients. bindState connected
   sendPerSec 240 receivePerSec 240  tcpSendPerSec 10 tcpReceivePerSec 10
serverRouterList
It produces a report for each client router. It shows if the router is connected. The queueSize, inQueue, and sendQueueFull provide statistics about the send queue. It also shows a list of each server attached to the router.

msr ("serverName")
msr

Message Server Report provides statistics about the specified server or, if no argument is specified, all servers. For example:
iocrstserver> msr "serial[7]"           
serial[7]
          queueSize 2
            inQueue 0
      queueRequests 132171
 queueFullResponses 1
      replyRequests 132171
The meaning of the fields are:

serialPortSniff("portName",seconds)

This command show every character transmitted received by the specified port for the number of seconds specified. For example
 
serialPortSniff "UART[5]",1
value = 0 = 0x0
iocrstserver> 
T31 T34 R31 T35 R34 T00 R35 R00 T31 T34 14154.5.14
R31 T36 R34 T00 R36 R00 T34 T31 R34 T32 164.6.4142
R31 T00 R32 R00 T31 T34 R31 T37 R34 T00 1.2.14174.
R37 R00 T31 T34 R31 T38 R34 T00 R38 R00 7.14184.8.
T31 T34 R31 T39 R34 T00 R39 R00 T31 T35 14194.9.15
R31 T30 R35 T00 R30 R00 T31 T35 R31 T31 105.0.1511
R35 T00 R31 R00 T34 T31 R34 T33 R31 T00 5.1.41431.
R33 R00 T31 T35 R31 T32 R35 T00 R32 R00 3.15125.2.
T31 T35 R31 T33 R35 T00 R33 R00 T31 T35 15135.3.15
R31 T34 R35 T00 R34 R00 145.4.
serialPortSniff done
The output displays 10 characters per line. It first shows the hex value preceeded by T (transmit) or R (receive). It then shows the ascii values. Non-printable ascii values are shown as ".".

Message Routers

Local Message Router

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 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.

Remote Message Router

The remote message router consists of two parts: the client router and the server router. Message are sent  as architecture independent data(NOT TESTED). The sender deletes its message and the receiver has to allocate a new message, put the received data into the message and then pass it to the destination.

Router Configuration

Each processor that is a member of a set of MPF clients/servers is assigned a unique location, which is just an integer value. For example if the set is  an mv167/mv162 the mv167 could be assigned location 0 and the mv162 location 1. All that matters is that each cpu is assigned a unique location and all cpus agree on the assignments.

Each vxWorks system using the message passing system needs the following command in the startup file:

If local communication is desired then the following command must appear: 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.

If tcp communication is desired then for each remote system the client must have the following command:

Each system supporting remote servers must have the following command for each client  machine: NOTE CAREFULLY:  Each server router can only handle a single client router. A particular client/server pair is established by the client and server having EXACTLY the same (location,port,addresss) tuple. Also each server/client pair must use a unique port number.

Message

Overview

A message is something that is sent from a client to a server or from a server to a client. MPF only knows how to handle messages defined by the files messageType.h and messageType.cc. In addition there are firm rules about how messages are allocated and freed. The basic rules are: The definition of class Message is:
class Message
{
public:
    messageType  getType();
    virtual ~Message();
    virtual void init() = 0;
    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, getType, 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.

The virtual methods must be implement by any class that derives from Message. The init method is called whenever a messages in allocated. The toBuffer and fromBuffer methods are called to put and take messages from a buffer that passes over a communication link.

Architecture Independent Message Passing

MPF supports message passing between different architectures, i.e. different byte orders and/or different padding requirements. It does, however only work on architectures with 32 bit addressing. It provides support for the following data types: In order to support architecture independent message passing MPF provides some help but also requires that each message type provide methods to transfer data between a message instance and the network buffer.

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:

Adding New Message Types

In order to provide a new message type the following must be done: mpfApp/messageSrc contains files xxxMessage.h and xxxMessage.cc which are a skeleton header and source file for a new message type .  In addition look at other existing message types for examples.

ConnectMessage

A connect message is passed by MPF itself whenever a connection is made or broken or if a servers message queue is full. It contains the field status which can have one of the following values:

StandardFieldsMessage

This class is not an actual message type but is a class used by other types. These fields provide additional services. The meaning of these fields is entirely up to the client/server combination between which the message is passed. The fields are:
 
Field Recommended Meaning
timeout The timeout the server can use it it has to wait for a device. It should be in seconds.
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.

Free Lists for Array Messages

All array messages use the utility class DataFreeList for managing storage for the actual arrays. DataFreeList keeps free lists of various sizes from 16 to 4096 bytes. If an array is larger than 4096 bytes the storage is allocated via a call to calloc and freed via a call to free. Thus Char8Arrays can contain 4096 characters, Int32Arrays 1024 elements, and Float64Arrays 512 elements before calloc/free is invoked.

Char8ArrayMessage

This message type provides the standard fields in addition to 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 value field is just a pointer to a char8 array  The standard C library routines such as ::strcpy and ::memcpy can be used on the value field.

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.

Int32Message

This message type provides the standard fields in addition to the following:
 
Field Recommended Meaning
value The Int32 value being passed.

Int32ArrayMessage

This message type provides the standard fields in addition to an int32 array. The value field is just a pointer to an int32 array. An Int32ArrayMessage provides the following methods for allocating and freeing the array.
 
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.

Float64Message

This message type provides the standard fields in addition to the following:
 
Field Recommended Meaning
value The Float64 value being passed

Float64ArrayMessage

This message type provides the standard fields in addition to an float64 array. The value field is just a pointer to an float64 array. A Float64ArrayMessage provides the following methods for allocating and freeing the array.
 
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.

SerialConfigMessage

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.

MPF Device support

DevMpf is an abstract base class for implementing EPICS device support. See   EXAMPLE Implementation: ip330Scan below for an example of how to use this base class. File DevMpf.h defines the class. The important part is:
// return codes for startIO and completeIO.

#define MPF_OK                  0
#define MPF_NoConvert           2
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 connectIO(dbCommon*,Message *);     // connection message
    virtual long convert(dbCommon*,int pass);   // do linear conversion
    // 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*);
    // This routine gets a pointer to the user portion of the parm field
    const char* getUserParm() const { return((const char*)userParm); }
    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
private:
    ...
}
 
method Usage meaning
DevMpf Constructor The constructor must be given the address of the record and link (INP or OUT). iointValid specifies if the record can be set to be I/O Intr scanned.  The time-out value is for waiting for responses from the MPF server.
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.
completeIO Called by DevMpf DevMpf calls this whenever a reply message is received from the server.
connectIO Called by DevMpf
Default supplied by DevMpf.
DevMpf  calls this when it receives a ConnectMessage. DevMpf provides a default implementation. For devices that generate unsolicited output the device support can implement this method. Device support, when it connects to a server, sends a message to the server stating that it wants to receive the unsolicited output. The server then sends a message to the client every time it receives a complete set of output from the device. After sending the message, DevMpf::connectIO is called to ensure correct behavior.
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.
send Called by derived class Send a message to the server without expecting a reply.
sendReply Called by derived class Send a message to the server and complete the first phase of record processing. When the server sends a reply call completeIO.
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>")
 
Format of INP or OUT field
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.

Device Support Entry Tables

DevMpf.h provides macros for creating DSETs. They are:
MAKE_LINCONV_DSET(<dset name>,<dev init>)
MAKE_DSET(<dset name>,<dev init>)
The first is used for record types that support linear conversions, e.g. ai and ao,  and the second for other record types.

Utility Classes

Several utility classes are used by the MPF classes: At least the DLList class should be replaced by using the Standard Template Library, which is part of the C++ .standard library. For now the standard C++ library is not being used by MPF. Instead only the ANSI standard C library is used.  These classes are used extensively by MPF and can also be used by user code.

FreeList 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.
 

IndustryPack Support

NOTE: Since our only experience so far is with the mv162 IP support, the classes described in this section are likely to change. An attempt has been made to hide details from module specific code via the class IndustryPackModule. Only experience with other IP carriers will show if the correct methods were chosen.

IndustryPack Support consists of the following components:

IndustryPackCarrier

The base class for an IndustryPack carrier. It allows access to IndustryPack carrier configuration and control independent of the specific carrier hardware board.
IndustryPackSite
The base class for a specific site on a specific carrier, i.e. the place where an IP module resides.
IndustryPackMVME162
Implements the IndustryPackCarrier and IndustryPackSite classes for the MVME162 hardware board.
IndustryPackModule
Provides IndustryPack Module configuration independent of the specific IP carrier. This class is used by specific module support such as OctalUART and GpibGsTi9914.
 

IndustryPackCarrier

This is a base class for board level support for IndustryPack carriers. The carrier must be initialized by using a board specific initialization routine prior to use by IndustryPackModule.
class IndustryPackCarrier
{
 public:
  IndustyPackCarrier();
  int registerName(const char *carrierName);
  static IndustryPackCarrier *find(const char *carrierName);
  IndustryPackSite *findSite(const char *siteName);
  IndustryPackSite *addSite(IndustryPackSite *pSite);
  const char *getCarrierName();
  virtual volatile void *allocMemMapIP(int size) = 0;
 private:
    ...
};
class IndustryPackSite
{
 public:
  IndustryPackSite();
  const char *getSiteName();
  virtual volatile void* allocMemMapIP(int size) = 0;
  virtual volatile IP_ID_PROM* getMemBaseID() = 0;
  virtual volatile void* getMemBaseIO() = 0;
  virtual volatile void* getMemBaseIP() = 0;
  virtual int intConfig(int num) = 0;
  virtual void intEnable(int num) = 0;
  virtual void intDisable(int num) = 0;
 protected:
  const char *siteName;
  IndustryPackCarrier *pIpCarrier;
 private:
};
 
Method Implementor Usage Description
registerName IndustryPackCarrier Derived class. Register the carrier name.
find IndustryPackCarrier Public use. Find and return IndustryPackCarrier.
findSite IndustryPackCarrier IndustryPackModule Find and return site.
addSite IndustryPackCarrier Called by derived class. Add a site.
getCarrierName IndustryPackCarrier Public use. Return carrier name.
allocMemMapIP Derived Class  IndustryPackModule Allocate memory.
getSiteName IndustryPackSite Derived class. Get site name
allocMemMapIP Derived class. IndustryPackModule See IndustryPackModule
getMemBaseID Derived class. IndustryPackModule See IndustryPackModule
getMemBaseIO Derived class. IndustryPackModule See IndustryPackModule
getMemBaseIP Derived class. IndustryPackModule See IndustryPackModule
intConfig Derived class. IndustryPackModule See IndustryPackModule
intEnable Derived class. IndustryPackModule See IndustryPackModule
intDisable Derived class. IndustryPackModule See IndustryPackModule

IndustryPackMVME162

The following command initializes the IndustryPack Carrier on the MVME162. This board initialization must be completed before IndustryPack Modules can be initialized.
initIndustryPackMVME162("carrierName",memoryBase)
 
Parameter Value
carrierName The IP carrier name to register
memoryBase The base address for mapping IP module memory to local memory

IndustryPackModule

This is the class which IP module specific code uses, e.g. the Green Springs OctalUart support.
class IndustryPackModule
{
public:
  static IndustryPackModule * createIndustryPackModule(
      const char *moduleName,
      const char *carrierName,
      const char *siteName);
  static IndustryPackModule *find(const char *moduleName);
  static IndustryPackModule *find(
      const char *carrierName,
      const char *siteName);
  volatile void *allocMemMapIP(int size);
  int intConfig(int num);
  int intEnable(int num);
  int intDisable(int num);
  volatile IP_ID_PROM *getMemBaseID();
  volatile void *getMemBaseIO();
  volatile void *getMemBaseIP();
  int getMemSizeIP();
  unsigned char getManufacturer();
  unsigned char getModel();
  unsigned char getRevision();
private:
  
  ...
};
All public methods are implemented by IndustryPackModule and used by module specific code.
 
Method Description
createIndustryPackModule Creates an instance of IndustryPackModule. It prints an error message and returns 0 if carrierName or siteName does not exist or if moduleName already exists.
find Two find methods are provided. One finds modules by module name.  The other finds modules by carrier and site name.
allocMemMapIP Allocate IP memory.
intConfig Interrupt Configure. The argumenmt must be 0 or 1.
intEnable Enable interrupt. The argumenmt must be 0 or 1.
intDisable Disable interrupt. The argumenmt must be 0 or 1.
getMemBaseID Get the address of the module ID space.
getMemBaseIO Get the address of the module IO space.
getMemBaseIP Get the address of the module IP space.
getMemSizeIP Get the size of allocated IP memory.
getManufacturer Get the manufacter ID.
getModel Get the model ID.
getRevision Get the revision

Serial Support

 Serial Support consists of the following components:

SerialPort

An abstract base class for a serial port. It allows access to a serial port independent of the specific serial interface.
OctalSerial
Implements SerialPort for the Green Springs Quad/Octal IP carrier.
serialServer
A generic MPF server for SerialPort. It accepts Char8Array messages.  It can accept input from the serial port if the input has a known single character terminator.
devStringMpf
Provides EPICS device support to send a string to a MPF server and receive a reply string
SerialPort and OctalSerial can be used independent of MPF. SerialServer is the "glue" between MPF and SerialPort. DevStringMpf provides EPICS device support to send and receive string messages. It can be used with MPF servers other than serialServer. For example the MPF provides  serverChar8Array, which just echoes whatever messages it is sent.

Because devices attached to serial ports  have private protocols, serialServer and devStringMpf may not be usable. In such cases a new server and device support must be supplied. serialServer and devStringMpf provide a model of how to write special support. This section provides details about SerialPort so the the code for serialServer is easier to follow.

SerialPort is independent of MPF and Industry Pack. It can be used as the base class for any type of serial port, i.e. VME, IP, etc. The code for OctalSerial provides a model of what needs to be done to derive from SerialPort.

SerialPort

enum byteHandlerRC {byteHandlerOK,byteHandlerEndRead,byteHandlerError};
enum serialPortStatus 
    {serialPortSuccess, serialPortTimeout, serialPortFailure};
typedef byteHandlerRC (*BYTE_HANDLER)(void* parm,unsigned char byte);
typedef void (*PEEK_HANDLER)
    (void* parm,bool isReceive, unsigned char byte);
// If registered PEEK_HANDLER is called for every byte transmitted or received
class SerialPort
{
public:
    SerialPort();

public:
    SerialPort();
    int registerName(const char *portName);
    static SerialPort *find(const char *portName);
    static SerialPort *bind(const char *portName,void* bhparm,BYTE_HANDLER bh);
    void release();
    static bool setSniffHandler(
        const char *portName,void* phParm,PEEK_HANDLER ph);
    static void freeSniffHandler(const char *portName);
    serialPortStatus getStatus() { return(ioStatus);}
    virtual serialPortStatus write(
        unsigned char *data, int nbytes, int timeoutSeconds) = 0;
    virtual serialPortStatus read(int timeoutSeconds) =0;
    virtual serialPortStatus writeRead(
        unsigned char *data, int nbytes, int timeoutSeconds) =0;
    virtual bool config( int baud, int stopBits, int bitsPerChar,
        char parity, char flowControl) = 0;
protected:
    void startIoTimeout(int seconds);
    void waitIo();
    requestType request;
    serialPortStatus ioStatus;
    BYTE_HANDLER bh;
    void* bhParm;
    PEEK_HANDLER ph;
    void* phParm;
    SEM_ID ioComplete;
private:
    ...
};
 
Method Implementor Usage Description
find SerialPort Called by servers Given the name of a port, find and return SerialPort.
bind SerialPort Called by servers Bind the caller to the specified port and if successful return the address of the SerialPort. Only one caller can be bound to a particular port. After a successful bind the caller may call write, read, writeRead, and release.
setSniffHandler SerialPort Called by serialPortSniff  or other diagnostics SerialPort locates the serial port and sets the field ph. The Derived Class is expected to call the peek handler for every character trasmitted and received if ph has a value.
freeSniffHandler SerialPort Called by serialPortSniff  or other diagnostics SerialPort locates the serial port and clears the field ph. 
release SerialPort Called by servers Release the caller that is bound to the port.
getStatus SerialPort Called by servers Return the status of the last I/O request.
write Derived Class Called by servers. Send  data to the serial port. 
read Derived Class Called by servers Read data from the serial port. 
writeRead Derived Class Called by servers Send data to the serial port while simultaneously reading. 
registerName SerialPort Called by derived class Register the serial port.
startIoTimeout SerialPort Called by derived class Start a time out request.
waitIo SerialPort Called by derived class Wait for I/O to complete or timeout.

The bind call must supply a byte handler which is called for every input character received by the serial port. The byte handler returns one of the following values.
 
Byte Handler Return Codes
byteHandlerOK If any read request is outstanding, just continue.
byteHandlerEndRead If any read request is outstanding terminate it with success.
byteHandlerError If any read  request is outstanding terminate it with failure.

OctalUART Support

This is support for the GreenSprings Octal and Quad serial IP modules. Before initializing the OctalUART the IndustryPack Carrier must be initialized. The following command initializes an OctalUart module.
 
initOctalUART("moduleName","carrierName","carrierSite",nports,intVec)
parameter value
moduleName The IP Module name to be registered.
carrierName The name of the IP Carrier.
carrierSite The mezzanine slot location on a carrier. Eg: IP_a, IP_b, IP_c, IP_d
nports The number of ports. Must be 4 or 8.
intVec First interrupt vector to be used.

After the OctalUart is initialized each port can be initialized via the command.
 

initOctalUARTPort("portName","moduleName",port,baud,"parity",
    stop_bits,bits_char,"flowType")
parameter value
portName portName which is registered for port.
moduleName The IP module name for identification.
port port number. Must be between 0 and 7.
baud 1200, 2400, 4800, 9600, 19200, 38400
parity O, E, N for (Odd, Even, None)
stop_bits 1,2
bits_char 5,6,7,8
flowType H, N means (? , ?)

serialServer

This is a MPF server for SerialPort. It accepts SerialConfigMessage sand Char8Array messages.

When it receives a SerialConfigMessage it calls  SerialPort::config and returns an Int32Message. A status of 0 means success.

When it receives a Char8Array message it  returns either an Int32 message (failure) or a Char8Array message (success). The header file serialServer.h describes how it handles the request message. In addition to the value, it uses the timeout, cmd, and extra fields of the request message. The timeout is in seconds. The cmd field can have the values:
 
Commands accepted by serialServer
cmdWrite Write with no response from serial port. 
Failure returns Int32Message. 
Success returns Char8ArrayMessage of length 0.
cmdRead Read message from serial port. 
Failure returns Int32Message. 
Success returns Char8ArrayMessage.
cmdWriteRead Write and also get response. 
Failure returns Int32Message. 
Success returns Char8ArrayMessage.
cmdSetEom value contains end of message (up to 2 characters).
Returns Int32Message. 
status is 0 for success.

The following command initializes a serial server:

initSerialServer("serverName","portName",bufSize,"eomstr")
 
parameter value
serverName The MPF name for this server.
portName The name of the serial port to which the server is attached.
bufSize The maximum size for an input message from the serial port.
eomstr The initial input terminator. It must be one or two characters.

devStringMpf

This provides device support for a stringin record. File devStringMpf.dbd provides the device definition and devStringMpf.c provides the device support. A record which uses this support has DTYP and INP defined as follows:
record(stringin,"<pvname>")
{
    field(DTYP,"MPF stringin")
    field(INP, "#C<location> S0 @<server>,<inputRecord>")
}
 
Configurable parameters
pvname Record name
location Location of message server.
server Name of the message server
inputRecord Name of record (and optionally field) from which a serial output string is obtained.

When the record is processed the following happens:

Please note the following restrictions: Unless these restrictions are acceptable specialized device support will have to be written.

Gpib Support

Gpib support consists of the following: The Gpib support is complete in the sense that most users will not have to extend it. It is compatible with the Gpib support provided with epics base.

Restriction: Only one of GpibHideosLocal or GpibHideosRemote can be used on a particular processor. Removing this restriction requires changes to the version of drvGpib supplied with base.

ip330Scan

This is support for the Acromag IP330 analog input Industrial I/O Pack module. The support configures the 330 as a scanning ADC.  32 single-ended or 16 differential  channels are scanned. A complete set of readings is taken every millisecond. Every 200 samples the average of each channel is computed. The average value is returned to device support. Thus every .2 seconds a 200 sample average is available. Once a minute calibration is done automatically.

Device support sends Int32Messages with the following info:

address - channel
value - gain
The server returns
value  - 32 bit unsigned integer from 0 to 0xffff
status  - 0 means success. -1 means failure
The following command initializes the server:
initIp330Scan("modulename","carrier","site",
    "type","range",intVec,queueSize)
modulename The IP Module name to be registered. This is also the server name
carrier The name of the IP Carrier
site The slot location on the carrier.
type The type: Must be (D,S) for (Differential, Single Ended)
range Range: Must be one of -5to5, -10to10, 0to5, 0to10
intVec The interrupt vector location
queueSize Maximum number of messages to queue

The device definition is:

device(ai,VME_IO,devAiIp330Scan,"ip330Scan")
Analog input records have the fields defined as follows:
field(SCAN,"1 second") 
field(DTYP,"ip330Scan")
field(INP,"#C{card} S{signal} @{servername},{gain}
card The location of the server.
signal The input channel. For Differential 0-15. For single ended 0 - 31.
servername Must match the modulename specified with initIp330Scan. 
gain Optional. If given must be 0,1,2,or 3. Default is 0.

The following summerizes the configuration options. The DIP Switch Settings column correspond to the value switches 1 - 10. A value of 101100001 corresponds to switches 1,3,4,9 on and 2,5,6,7,8 off.
 
Range DIP Switch Gain Full Value Low Value
-5to5 1011000010 0 5 -5
-5to5 1011000010 1 2.5  -2.5
-5to5 1011000010 2 1.25 -1.25
-5to5 1011000010 3 0.625 -.0625
-10to10 0100110010 0 10  -10
-10to10 0100110010 1 -5
-10to10 0100110010 2 2.5 -2.5
-10to10 0100110010 3 1.25  -1.25
0to5 1010100100 0 0
0to5 1010100100 1 2.5 0
0to5 1010100100 2 1.25 0
0to5 1010100100 3 0.625 0
0to10 1011001000 0 10 0
0to10 1011001000 1 5 0
0to10 1011001000 2 2.5 0
0to10 1011001000 3 1.25 0

Note for Hideos Users

This support is similar to the hideos ip330adc support. The main differences for database records are:

EXAMPLE Implementation: ip330Scan

This section describes EPICS device support which communicates with a server that accesses an IP module.. The example is the scanning adc support for the Acromag IP330 AI module.  The complete code resides in mpf/ip330ScanApp/src. This section explains how things fit together. It does not show device specific details. The support consists of four source modules:  devAiIp330Scan.cc, ip330ScanServer.cc, Ip330Scan.h, and ip330Scan.cc

devAiIp330Scan.cc

class DevAiIp330Scan : public DevMpf
{
public:
        DevAiIp330Scan(dbCommon*,DBLINK*);
        long startIO(dbCommon* pr);
        long completeIO(dbCommon* pr,Message* m);
        long convert(dbCommon* pr,int pass);
        static long dev_init(void*);
private:
        int channel;
        int gain;
};
Class  DevAiIp330Scan implements device support which communicates with the ip330Server described below. It derives from class DevMpf. DevMpf  calls the methods DevAiIp330Scan, startIO, completeIO, and convert.
MAKE_LINCONV_DSET(devAiIp330Scan,DevAiIp330Scan::dev_init)
The macro MAKE_LINCONV_DSET  creates Device Support Entry Table devAiIp330Scan. DevAiIp330Scan::dev_init will be called by aiRecord during the second phase of record initialization.
DevAiIp330Scan::DevAiIp330Scan(dbCommon* pr,DBLINK* l)
: DevMpf(pr,l,false)
{
    vmeio* io = (vmeio*)&(l->value);
    gain = 0;
    channel=io->signal;
    aiRecord* prec=(aiRecord*)pr;
    const char* up = getUserParm();
    if(up && strlen(up)>0) {
        // decode the parm field, format: "gain"
        if((sscanf(up,"%d",&gain)<1) || gain<0 || gain>3) {
            epicsPrintf("%s Illegal INP parm field\n", prec->name);
            prec->pact=TRUE;
        }
    }
    convert((dbCommon *)prec,1);
    return;
}
Constructor DevAiIp330Scan is called by DevAiIp330Scan::dev_init. It first calls DevMpf(pr,l,false) and then does private initialization. DevMpf takes care of establishing communication with the server, and it also calls startIO and completeIO.
long DevAiIp330Scan::startIO(dbCommon* )
{
    Int32Message *message = new Int32Message;
    message->value =gain;
    message->address =channel;
    return(sendReply(message));
}
startIO is called by DevMpf when dbProcess requests record processing. It just sends a message to the server and returns.
long DevAiIp330Scan::completeIO(dbCommon* pr,Message* m)
{
    aiRecord* pai = (aiRecord*)pr;
    if((m->getType()) != messageTypeInt32) {
        epicsPrintf("%s ::completeIO illegal message type %d\n",
                    pai->name,m->getType());
        recGblSetSevr(pai,READ_ALARM,INVALID_ALARM);
        delete m;
        return(MPF_NoConvert);
    }
    Int32Message *pint32Message = (Int32Message *)m;
    if(pint32Message->status) {
        recGblSetSevr(pai,READ_ALARM,INVALID_ALARM);
        delete m;
        return(MPF_NoConvert);
    }
    pai->rval=pint32Message->value;
    pai->udf=0;
    delete m;
    return(MPF_OK);
}
completeIO is called when the server returns a message. It checks that it has received a valid message, sets rval equal  to the new value received from the server, deletes the message, and returns.
long DevAiIp330Scan::convert(dbCommon* pr,int /*pass*/)
{
    aiRecord* pai = (aiRecord*)pr;
    pai->eslo=(pai->eguf-pai->egul)/(double)0xffff;
    return 0;
}
convert is called by the aiRecord when conversion fields are changed and also by the constructor DevAiIp330Scan. This implementation knows that ip330Scan  always returns a value between 0 and 0xffff.
long DevAiIp330Scan::dev_init(void* v)
{
        aiRecord* pr = (aiRecord*)v;
        DevAiIp330Scan* pDevAiIp330Scan
            = new DevAiIp330Scan((dbCommon*)pr,&(pr->inp));
        return pDevAiIp330Scan->getStatus();
}
dev_init  calls the constructor and returns a status obtained from DevMpf.

ip330ScanServer.cc

ip330ScanServer receives messages from DevAiIp330Scan, calls ip330Scan, and sends a reply message. The server is written as a separate module so that ip330Scan is independent of MPF.
class Ip330ScanServer {
public:
    Ip330Scan *pIp330Scan;
    MessageServer *pMessageServer;
    static void ip330Server(Ip330ScanServer *);
};
This is a class definition that connects ip330Server to Ip330Scan.
static char taskname[] = "ip330";
extern "C" int initIp330Scan(
    const char *moduleName, const char *carrierName, const char *siteName,
    const char *typeString,const char *rangeString, int intVec,
    int queueSize)
{
    Ip330Scan *pIp330Scan = Ip330Scan::init(moduleName,carrierName,siteName,
        typeString,rangeString,intVec);
    if(!pIp330Scan) return(0);
    Ip330ScanServer *pIp330ScanServer = new Ip330ScanServer;
    pIp330ScanServer->pIp330Scan = pIp330Scan;
    pIp330ScanServer->pMessageServer = new MessageServer(moduleName,queueSize);
    int taskId = taskSpawn(taskname,100,VX_FP_TASK,2000,
        (FUNCPTR)Ip330ScanServer::ip330Server,(int)pIp330ScanServer,
        0,0,0,0,0,0,0,0,0);
    if(taskId==ERROR)
        printf("%s ip330Server taskSpawn Failure\n",
            pIp330ScanServer->pMessageServer->getName());
    return(0);
}
The initialization routine is called from the server st.cmd file. It does the following: The init routine is the "glue" between MPF and the IP support.
void Ip330ScanServer::ip330Server(Ip330ScanServer *pIp330ScanServer)
{
    while(true) {
        MessageServer *pMessageServer = pIp330ScanServer->pMessageServer;
        Ip330Scan *pIp330Scan = pIp330ScanServer->pIp330Scan;
        pMessageServer->waitForMessage();
        Message *inmsg;
        while((inmsg = pMessageServer->receive())) {
            if(inmsg->getType()!=messageTypeInt32) {
                printf("%s ip330Server got illegal message type %d\n",
                    pMessageServer->getName(), inmsg->getType());
                delete inmsg;
            } else {
                Int32Message *pmessage = (Int32Message *)inmsg;
                pmessage->status = 0;
                int gain = pmessage->value;
                int channel = pmessage->address;
                if(pIp330Scan->setGain( gain,channel)) pmessage->status= -1;
                if(pmessage->status==0) {
                    pmessage->value = (int32)pIp330Scan->getValue(channel);
                }
                pMessageServer->reply(pmessage);
            }
        }
    }
}
ip330Server accepts MPF messages, calls Ip330Scan methods, and sends a reply message.

Ip330Scan.h

class WatchDog;
class MessageServer;
class IndustryPackModule;
class ip330ADCregs;
class ip330ADCchans;

enum signalType {differential, singleEnded};
class Ip330Scan
{
public:
    static Ip330Scan * init(
        const char *moduleName, const char *carrierName,
        const char *siteName,
        const char *type,const char *range,int intVec);
    int getValue(int channel);
    int setGain(int gain,int channel);
private:
    Ip330Scan(IndustryPackModule *pIndustryPackModule,
         signalType type, int range, int intVec);
    ...

};
This is a typical example of the class definition for an IP support module.  It has the following public methods: init, getValue, and setGain. getValue and setGain are specific to the IP330 support. The init routine is typical of what is needed for many IP support modules. Note that the class constructor is private because a new instance should only be created via a call to init.

Ip330Scan.cc

NOTE: This is only part of the source module. All device specific code is suppressed.
Ip330Scan * Ip330Scan::init(
    const char *moduleName, const char *carrierName, const char *siteName,
    const char *typeString,const char *rangeString, int intVec)
{
    IndustryPackModule *pIPM = IndustryPackModule::createIndustryPackModule(
        moduleName,carrierName,siteName);
    if(!pIPM) return(0);
    unsigned char manufacturer = pIPM->getManufacturer();
    unsigned char model = pIPM->getModel();
    if(manufacturer!=ACROMAG_ID) {
        printf("initIp330Scan manufacturer 0x%x not ACROMAG_ID\n",
            manufacturer);
        return(0);
    }
    if(model!=ACRO_IP330) {
       printf("initIp330Scan model 0x%x not a ACRO_IP330\n",model);
       return(0);
    }
    .... // device specific details
    Ip330Scan *pIp330Scan = new Ip330Scan(pIPM,type,range,intVec);
    return(pIp330Scan);
}
This code creates and binds to a new IndustryPackModule. It then checks that the IP module is what it expects. Finally it creates a new Ip330Scan object.
Ip330Scan:: Ip330Scan(
    IndustryPackModule *pIndustryPackModule,
    signalType type, int range, int intVec)
: pIPM(pIndustryPackModule), type(type), range(range), accumulated(0)
{
    ... // device specific details
    pIPM->intConfig(0); pIPM->intConfig(1);
    if(intConnect(INUM_TO_IVEC(intVec),(VOIDFUNCPTR)intFunc,(int)this)==ERROR){
        printf("Ip330Scan intConnect Failure\n");
    }
    pIPM->intDisable(0);
    pIPM->intClear(0);
    ... // device specific details
}
This shows how to connect to an interrupt location.

Performance

The purpose of this section is to give some performance numbers that help understand some of the MPF design decisions and also to help users decide when  it might be worthwhile to have a non-EPICS MPF processor talking to an EPICS ioc.

The local tests were run on an mv162. The remote tests were run on an mv167 client  and mv162 server. I think they were both 25 MHz processors. msgPerSec and tcpPerSec are round trip, i.e. a send and receive. The message in all cases was just an Int32 value. The no buffer case means that a value was sent and the client waited for that response before sending the next message. The buffer case means that the specified number of values were sent before waiting for the responses.

Raw TCP  performance

 
Test msgPerSec tcpPerSec client idle server idle
tcpLocalNoBuffer 237 237 15% -
tcpLocalBuffer100 6050 60 5% -
tcpRemoteNoBuffer 485 485 54% 39%
tcpRemoteBuffer100 10050 100 52% 40%

MPF Performance

Test msgPerSec tcpPerSec client idle server idle
mpfLocalNoBuffer 3705 - 0% -
mpfLocalBuffer100 7966 - 0% -
mpfRemoteNoBuffer 331 331 52% 40%
mpfRemoteBuffer100 1440 14 52% 43%
mpfRemoteBuffer1000 2100 16 31% 18%

EPICS to remote server performance

The example described previously was started and all menu options set to .1 second scan rate.
 
Test msgPerSec tcpPerSec client idle server idle
local 240 - 51% -
remote 240 10 64% 73%

 

octalSerialPort performance

This test consisted of sending 100 character messages to each of the 8 serial ports. Each port was configured at 38400 baud. The results are for the server.
 
Test msgPerSec interrupt idle total
local 159 76% 0% 89%
remote 156 65% 10% 91%