GTR is available via the open source license described at the end of this document
- SerialPort - A abstract base class for serial interfaces.
- serialPortSniff - The module that implements sniff for serial ports.
- OctalUART - Support for the Green Springs OctalUart serial IP module.
- serialServer - An MPF server that connects MPF to SerialPort
- devStringMpf - Device support
Edit config/RELEASE to add the lineThe following database definition file must be included:MPF_SERIAL=<full path name>Then just type
gnumake
In your source directory Makefile.Vx:include "devMpfString.dbd"
LIBOBJS += $(MPF_BIN)/mpfLib LIBOBJS += $(MPF_BIN)/ipLib LIBOBJS += $(MPF_BIN)/DevMpf.o LIBOBJS += $(MPF_SERIAL_BIN)/devStringMpf.o LIBOBJS += $(MPF_SERIAL_BIN)/mpfserialserverLib
LIBOBJS += $(MPF_BIN)/mpfLib LIBOBJS += $(MPF_BIN)/DevMpf.o LIBOBJS += $(MPF_SERIAL_BIN)/devStringMpf.o
LIBOBJS += $(MPF_BIN)/mpfLib LIBOBJS += $(MPF_BIN)/ipLib LIBOBJS += $(MPF_SERIAL_BIN)/mpfserialserverLib
See below for details. Looking at the <top>/iocBoot/ioc* shows a complete set of commands for the st.cmd files.... initIpacCarrier("carrierName", 0) initOctalUART("moduleName","carrierName:,...) initOctalUARTPort(... initSerialServer(...
< ../startServerNoCircularBuffer < ../startSerialThe test consists of the following:
record(calc,"Char8Array:calc$(ind)") { field(SCAN, "Event") field(EVNT, "1") field(CALC,"a=1000?0:a+1") field(FLNK,"Char8Array:solnk$(ind)") field(INPA,"Char8Array:calc$(ind)") } record(stringout,"Char8Array:solnk$(ind)") { field(DOL,"Char8Array:calc$(ind)") field(OMSL,"closed_loop") field(FLNK,"Char8Array:StringIn$(ind)") } record(stringin,"Char8Array:StringIn$(ind)") { field(DTYP,"MPFwriteRead") field(INP, "#C1 S0 @Char8Array,Char8Array: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.
The example also contains a test that uses circular buffer mode.
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 doneThe 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 ".".
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.devStringMpf
Provides EPICS device support to send 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 new device support and/or a new server must be supplied. Usually only new device support is necessary, because serialServer is sufficiently generic. For special devices a new server may also be needed. 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.
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 write( unsigned char *data, int nbytes, double timeoutSeconds) = 0; virtual serialPortStatus read(int timeoutSeconds) =0; virtual serialPortStatus read(double timeoutSeconds) =0; virtual serialPortStatus writeRead( unsigned char *data, int nbytes, int timeoutSeconds) =0; virtual serialPortStatus writeRead( unsigned char *data, int nbytes, double timeoutSeconds) =0; virtual bool config( int baud, int stopBits, int bitsPerChar, char parity, char flowControl) = 0; void clientStartIoTimeout(int seconds); void clientStartIoTimeout(double seconds); protected: void startIoTimeout(int seconds); void startIoTimeout(double 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. |
clientStartIoTimeout | SerialPort | Called by servers | Starts a timeout BEFORE a call to read, write, or writeRead. |
startIoTimeout | SerialPort | Called by derived class | Start a time out request. If server calls clientStartIoTimeout before calling write, read, or writeRead then startIoTimout does nothing. NOTE that this does a semTake with NO_WAIT before starting a watchdog. If a server doesnt desire this action it must call clientStartIoTimeout. |
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.
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. |
initOctalUART("moduleName","carrierName","carrierSite",nports,intVec)
|
After the OctalUart is initialized each port can be initialized via the
command.
initOctalUARTPort("portName","moduleName",port,baud,"parity", stop_bits,bits_char,"flowType")
|
When serialServer receives a SerialConfigMessage it calls SerialPort::config and returns an Int32Message. A status of 0 means success.
When serialServer receives a Char8Array message it returns a Char8Array
message. The return message contains any characters read and a status value
which has one of the following values:
|
The cmd field of the Char8Array message can contain a command and also
options. The commands are:
|
The cmd field can also contain the following options:
|
The following additional fields of the Char8ArrayMessage are also honored
by the serialServer:
|
The following command initializes a serial server:
initSerialServer("serverName","portName",bufSize,queuesize,"eomstr",circularBufferMode)
|
Discussion of Circular Buffer Mode
Circular Buffer mode is provided for devices that respond with multiple lines of output in response to a single command request. Although it is possible to run with circular buffer mode always enabled this is not recommended because, if any errors occur, it is possible for writes and reads to get "out of sync". A better way to handle multiline requests is the following:
cmd = cmdWrite | cmdFlush | cmdStartCircularBuffer
cmd = cmdRead
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>") }
Also what do we do about null terminators for strings? serialServer does not add or remove null terminator nor should it. What about device or record support? If we have serialPortRecord we could have additional options to handle this. What about flow control? How do we handle this in a generic way? What else am I missing?
Copyright (c) 2002 University of Chicago. All rights reserved. MPFSerial is distributed subject to the following license conditions: SOFTWARE LICENSE AGREEMENT Software: MPFSerial 1. The "Software", below, refers to MPFSerial (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.