Files
turboPmac/src/pmacAsynIPPort.h
T
wall_e f5eba004fb
Test And Build / Lint (push) Successful in 5s
Test And Build / Build (push) Failing after 8s
more cleaning
2026-08-07 16:04:43 +02:00

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 */