208 lines
6.6 KiB
C++
208 lines
6.6 KiB
C++
#ifndef pmacAsynIPPort_H
|
|
#define pmacAsynIPPort_H
|
|
|
|
#include <macros.h>
|
|
#include <shareLib.h>
|
|
|
|
#include "asynPortDriver.h"
|
|
|
|
#include <cstddef>
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#include <osiSock.h>
|
|
|
|
/*
|
|
Value is chosen arbitrarily, it just needs to be unique
|
|
*/
|
|
#define FLUSH_HARDWARE 1
|
|
|
|
constexpr size_t ETHERNET_DATA_SIZE = 1492;
|
|
|
|
/* PMAC ethernet command structure */
|
|
#pragma pack(push, 1)
|
|
struct EthernetCmd {
|
|
uint8_t RequestType;
|
|
uint8_t Request;
|
|
uint16_t wValue;
|
|
uint16_t wIndex;
|
|
uint16_t wLength; /* length of bData */
|
|
uint8_t bData[ETHERNET_DATA_SIZE];
|
|
};
|
|
#pragma pack(pop)
|
|
|
|
constexpr size_t ETHERNET_CMD_HEADER = sizeof(EthernetCmd) - ETHERNET_DATA_SIZE;
|
|
|
|
/**
|
|
* @brief asyn driver speaking the PMAC ethernet protocol directly over a TCP
|
|
* socket.
|
|
*
|
|
* This driver replaces the generic drvAsynIPPort driver for the communication
|
|
* with a Turbo PMAC controller. It handles the socket layer itself and builds
|
|
* and parses the PMAC ethernet packets in its asynOctet methods. The
|
|
* asynCommon connect/disconnect handling and the asynInt32 interface (used for
|
|
* FLUSH_HARDWARE) are implemented here as well.
|
|
*/
|
|
class HIDDEN pmacAsynIPPort : public asynPortDriver {
|
|
public:
|
|
/**
|
|
* @brief Construct a new pmacAsynIPPort object
|
|
*
|
|
* The port is registered with asynManager in the asynPortDriver base class
|
|
* constructor and can therefore safely be "leaked" here. See
|
|
* turboPmacCreateAxis in turboPmacAxis.cpp for a similar pattern.
|
|
*
|
|
* @param portName The Asyn Port name string.
|
|
* @param hostInfo The hostname or IP address followed by the IP port (eg.
|
|
* 172.23.243.156:1025)
|
|
*/
|
|
pmacAsynIPPort(const char *portName, const char *hostInfo);
|
|
|
|
virtual ~pmacAsynIPPort();
|
|
|
|
// asynCommon interface overrides
|
|
virtual asynStatus connect(asynUser *pasynUser) override;
|
|
virtual asynStatus disconnect(asynUser *pasynUser) override;
|
|
|
|
// asynOctet interface overrides
|
|
virtual asynStatus writeOctet(asynUser *pasynUser, const char *data,
|
|
size_t numchars,
|
|
size_t *nbytesTransfered) override;
|
|
virtual asynStatus readOctet(asynUser *pasynUser, char *data,
|
|
size_t maxchars, size_t *nbytesTransfered,
|
|
int *eomReason) override;
|
|
virtual asynStatus flushOctet(asynUser *pasynUser) override;
|
|
|
|
// asynInt32 interface overrides
|
|
virtual asynStatus writeInt32(asynUser *pasynUser,
|
|
epicsInt32 value) override;
|
|
|
|
private:
|
|
/**
|
|
* @brief Parse a "<host>:<port>" string into IPHostName_ and port_.
|
|
*
|
|
* @throws std::runtime_error if hostInfo is not of the form
|
|
* "<host>:<port>".
|
|
*/
|
|
void parseHostInfo(const char *hostInfo);
|
|
|
|
/**
|
|
* @brief Open a TCP connection to the PMAC.
|
|
*
|
|
* The host name lookup is delayed until this point, so that a device which
|
|
* gets a new IP address via DHCP is found again on the next reconnect
|
|
* attempt.
|
|
*/
|
|
asynStatus connectIt(asynUser *pasynUser);
|
|
|
|
/**
|
|
* @brief Close the TCP connection and notify asynManager.
|
|
*
|
|
* This marks the port as disconnected, so that asynManager's autoConnect
|
|
* logic retries to establish a connection.
|
|
*/
|
|
void closeConnection(asynUser *pasynUser, const char *why);
|
|
|
|
/**
|
|
* @brief Raw read from the TCP socket.
|
|
*
|
|
* Polls the socket for up to pasynUser->timeout and returns any received
|
|
* data. A timeout with no data returns asynTimeout and does not close the
|
|
* connection.
|
|
*/
|
|
asynStatus socketRead(asynUser *pasynUser, char *data, size_t maxchars,
|
|
size_t *nbytesRead);
|
|
|
|
/**
|
|
* @brief Raw write to the TCP socket.
|
|
*
|
|
* If nbytesTransferred is not null, it receives the number of bytes that
|
|
* were sent to the socket.
|
|
*/
|
|
asynStatus socketWrite(asynUser *pasynUser, const char *data,
|
|
size_t numchars, size_t *nbytesWritten);
|
|
|
|
/**
|
|
* @brief Poll the socket fd, retrying while poll() is interrupted by
|
|
* EINTR.
|
|
*
|
|
* @return asynSuccess with *pollstatus holding poll()'s return value, or
|
|
* asynError if poll() failed for a different reason.
|
|
*/
|
|
asynStatus pollSocket(asynUser *pasynUser, struct pollfd *pollfd,
|
|
int pollmsec, int *pollstatus);
|
|
|
|
/**
|
|
* @brief Fill in a PMAC ethernet command header and send it over the
|
|
* socket.
|
|
*
|
|
* Only the 8 byte command header is sent. Commands that carry a payload
|
|
* (e.g. GETRESPONSE in writeOctet) are sent directly via socketWrite.
|
|
*/
|
|
asynStatus sendPmacCommand(asynUser *pasynUser, EthernetCmd &cmd,
|
|
uint8_t requestType, uint8_t request,
|
|
uint16_t wValue, uint16_t wLength,
|
|
size_t *nbytesTransfered);
|
|
|
|
/**
|
|
* @brief Read response data from the PMAC into the internal input buffer
|
|
*
|
|
* If there is no data in the socket buffer then a GETBUFFER command is
|
|
* issued to get any outstanding data still on the PMAC.
|
|
*/
|
|
asynStatus readResponse(asynUser *pasynUser, size_t maxchars,
|
|
size_t *nbytesTransfered, int *eomReason);
|
|
|
|
/**
|
|
* @brief Send a ReadReady command to the PMAC to discover if there is any
|
|
* data to read from it.
|
|
*
|
|
* @return 0 - no data available, 1 - data available
|
|
*/
|
|
int pmacReadReady(asynUser *pasynUser);
|
|
|
|
/**
|
|
* @brief Send a Flush command to the PMAC and wait for confirmation.
|
|
*
|
|
* @return 0 - failed, 1 - success
|
|
*/
|
|
int pmacFlush(asynUser *pasynUser);
|
|
|
|
/**
|
|
* @brief Send a GETBUFFER command to the PMAC.
|
|
*/
|
|
asynStatus sendPmacGetBuffer(asynUser *pasynUser, size_t maxchars);
|
|
|
|
// Host name and TCP port of the PMAC
|
|
std::string IPHostName_;
|
|
unsigned short port_ = 0;
|
|
|
|
// TCP socket used for the connection
|
|
SOCKET fd_ = INVALID_SOCKET;
|
|
|
|
// Command buffers used for the PMAC ethernet protocol
|
|
EthernetCmd poutCmd_{};
|
|
EthernetCmd pinCmd_{};
|
|
|
|
// Buffer for incoming response data
|
|
std::vector<char> inBuf_;
|
|
size_t inBufHead_ = 0;
|
|
size_t inBufTail_ = 0;
|
|
};
|
|
|
|
/**
|
|
* @brief Initialize a special asyn IP Port for PMAC controllers.
|
|
*
|
|
* Wrapper which creates a pmacAsynIPPort object and interposes the EOS
|
|
* handling layer above it.
|
|
*
|
|
* @param portName The Asyn Port name string.
|
|
* @param hostInfo The hostname or IP address followed by the IP port (eg.
|
|
* 172.23.243.156:1025)
|
|
* @return status
|
|
*/
|
|
int pmacAsynIPPortConfigure(const char *portName, const char *hostInfo);
|
|
|
|
#endif /* pmacAsynIPPort_H */
|