Jonny007-MKD
7704769468
Fix some issues with sending and receiving packets ModbusTcp.cin Don't assume the connection is ready when TcpOpen says so. Wait for OnTcpOpen() to confirm
236 lines
6.8 KiB
Plaintext
236 lines
6.8 KiB
Plaintext
/*@!Encoding:1252*/
|
||
|
||
// This file connected functions that abstract the UDP/IP API
|
||
/// It provides following methods
|
||
/// - _ModbusConnectTo() Prepare anything that sending works. Here: Open a TCP socket and connection
|
||
/// - _ModbusDisconnect() Gracefully disconnect from the device. Here: Close the TCP connection and socket
|
||
/// - _ModbusRecv() Receive data from the device. Here: Wait for a TCP packet on the connection
|
||
/// - _ModbusSnd() Send data to the device. Here: Send a packet on the TCP connection
|
||
/// - Some function that receives packets and hands them to _OnModbusReceive()
|
||
|
||
includes
|
||
{
|
||
#include "Common.cin"
|
||
#include "TcpUdpEilCommon.cin"
|
||
}
|
||
|
||
variables
|
||
{
|
||
TcpSocket gSocket;
|
||
}
|
||
|
||
// This method opens an TCP socket. It has to check for several errors.
|
||
word _TcpOpenSocket()
|
||
{
|
||
byte i;
|
||
char errorText[200];
|
||
long error;
|
||
|
||
if (EthGetAdapterStatus() != 2) // Not connected
|
||
{
|
||
writeDbg(ConnError, "_TcpOpenSocket: Adapter status not ok: %d!", EthGetAdapterStatus());
|
||
OnModbusClientPanics(ConnectionError);
|
||
return INVALID_IP;
|
||
}
|
||
|
||
// Try to open socket
|
||
gSocket = TcpSocket::Open(0, 0);
|
||
error = gSocket.GetLastSocketError();
|
||
if (error != 0)
|
||
{
|
||
gSocket.GetLastSocketErrorAsString(errorText, elcount(errorText));
|
||
writeDbg(ConnInfo, "_TcpOpenSocket: could not open socket: (%d) %s", error, errorText);
|
||
OnModbusClientPanics(ConnectionError);
|
||
return error;
|
||
}
|
||
else
|
||
{
|
||
writeDbg(ConnInfo, "_TcpOpenSocket: Tcp socket opened.");
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
// This method prepares anything in a way that sending with ModbusSnd() is possible.
|
||
// Here: The method will open a TCP socket and connect to the remote device
|
||
word _ModbusConnectTo(char Remote_IP[], word remotePort)
|
||
{
|
||
dword remoteIp;
|
||
|
||
// Convert IP string to Number
|
||
remoteIp = IpGetAddressAsNumber(Remote_IP);
|
||
if (remoteIp == INVALID_IP)
|
||
{
|
||
writeDbg(ConnError, "_ModbusConnectTo: invalid server Ip address: %s", Remote_IP);
|
||
OnModbusClientPanics(ConnectionError);
|
||
return 1;
|
||
}
|
||
|
||
return _ModbusConnectTo(remoteIp, remotePort);
|
||
}
|
||
|
||
// This method prepares anything in a way that sending with ModbusSnd() is possible.
|
||
// Here: The method will open a TCP socket and connect to the remote device
|
||
word _ModbusConnectTo(dword remoteIp, word remotePort)
|
||
{
|
||
long fehler;
|
||
|
||
// Try to open a socket
|
||
fehler = _TcpOpenSocket();
|
||
if (fehler != 0)
|
||
{
|
||
gSocketState = ERROR;
|
||
return fehler;
|
||
}
|
||
|
||
gRemoteIP = remoteIp;
|
||
gRemotePort = remotePort;
|
||
|
||
|
||
// Connect to Server
|
||
if (gSocket.Connect(remoteIp, remotePort) != 0)
|
||
{
|
||
fehler = gSocket.GetLastSocketError();
|
||
|
||
if (fehler != WSAEWOULDBLOCK) // OnTcpConnect will be called otherwise
|
||
{
|
||
writeDbg(ConnError, "_ModbusConnectTo: No connection established: %d", fehler);
|
||
gSocketState = ERROR;
|
||
OnModbusClientPanics(ConnectionError);
|
||
return fehler;
|
||
}
|
||
else
|
||
{
|
||
writeDbg(ConnDebug, "_ModbusConnectTo: WSAE would block");
|
||
gSocketState = CONNECTING;
|
||
}
|
||
return 0;
|
||
}
|
||
gSocketState = CONNECTING; // Don't set state OK because the Stack doesn't tell us WSAEWOULDBLOCK
|
||
return 0;
|
||
}
|
||
|
||
// This method will be called when TcpSocket.Connect() had to defer the result (and returned WSAEWOULDBLOCK).
|
||
// It checks whether the connection was opened successfully and sets the socket state accordingly
|
||
void OnTcpConnect(dword socket, long result)
|
||
{
|
||
if (result != 0)
|
||
{
|
||
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
|
||
writeDbg(ConnError, "OnTcpConnect: (%d) %s", gSocket.GetLastSocketError(), gIpLastErrStr);
|
||
gSocketState = ERROR;
|
||
OnModbusClientPanics(ConnectionError);
|
||
return;
|
||
}
|
||
else
|
||
{
|
||
writeDbg(ConnInfo, "OnTcpConnect: Successfully connected to server");
|
||
gSocketState = OK;
|
||
_ModbusStartQueue();
|
||
}
|
||
}
|
||
|
||
// This method will gracefully disconnect from the remote device.
|
||
// Here: Simply close the socket. This will also disconnect the remote device
|
||
void _ModbusDisconnect()
|
||
{
|
||
writeDbg(ConnDebug, "_ModbusDisconnect: Disconnecting");
|
||
gSocket.Close();
|
||
gSocketState = CLOSED;
|
||
}
|
||
|
||
// This method will check for data from the remote device.
|
||
// Here: Tell the API to check for new TCP telegrams. When data arrived OnTcpReceive() will be called by the API
|
||
void _ModbusRecv()
|
||
{
|
||
int result;
|
||
|
||
if (gSocketState != OK)
|
||
{
|
||
writeDbg(ConnWarning, "_ModbusRecv: Socket status is not OK!");
|
||
_ModbusDisconnect();
|
||
// OnModbusClientPanics(ConnectionError);
|
||
return;
|
||
}
|
||
|
||
result = gSocket.Receive(gRxBuffer, elcount(gRxBuffer));
|
||
|
||
if (result != 0) // Calling OnTcpReceive otherwise
|
||
{
|
||
gIpLastErr = gSocket.GetLastSocketError();
|
||
|
||
if (gIpLastErr != WSA_IO_PENDING) // Calling OnTcpReceive otherwise
|
||
{
|
||
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
|
||
writeDbg(ConnError, "_ModbusRecv: (%d) %s", gIpLastErr, gIpLastErrStr);
|
||
_ModbusDisconnect();
|
||
}
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
// This method will send the payload 'buffer' to the device.
|
||
// Here: Call the appropriate API function
|
||
word _ModbusSnd(byte buffer[], word length)
|
||
{
|
||
char str[20*3];
|
||
|
||
switch (gSocketState)
|
||
{
|
||
case CLOSED: // If the connection is closed
|
||
_ModbusConnectTo(gRemoteIP, gRemotePort); // Try to (re)connect
|
||
if (gSocketState < OK) // If this didn't work (state != OK)
|
||
{
|
||
if (gSocketState < CONNECTING)
|
||
{
|
||
writeDbg(ConnError, "_ModbusSnd: Reconnecting failed!");
|
||
OnModbusClientPanics(ConnectionError);
|
||
}
|
||
return 1; // Abort sending in all cases
|
||
}
|
||
case OK:
|
||
break;
|
||
case NULL: // Delay
|
||
case CONNECTING:
|
||
return 1;
|
||
case ERROR:
|
||
writeDbg(ConnError, "_ModbusSnd: Socket status is not OK!");
|
||
OnModbusClientPanics(ConnectionError);
|
||
return 1;
|
||
default:
|
||
writeDbg(ConnError, "_ModbusSnd: Unknown socket status: %d", gSocketState);
|
||
OnModbusClientPanics(SwitchArgumentInvalid);
|
||
return 1;
|
||
}
|
||
|
||
bin_to_strhex(buffer, str);
|
||
|
||
if (gSocket.Send(buffer, length) != 0)
|
||
{
|
||
gIpLastErr = gSocket.GetLastSocketError();
|
||
|
||
if (gIpLastErr != WSA_IO_PENDING)
|
||
{
|
||
gSocket.GetLastSocketErrorAsString(gIpLastErrStr, elcount(gIpLastErrStr));
|
||
writeDbg(ConnError, "_ModbusSnd: (%d) %s", gIpLastErr, gIpLastErrStr);
|
||
_ModbusDisconnect();
|
||
return 1;
|
||
}
|
||
// else: tough luck!
|
||
}
|
||
writeDbg(ConnDebug, "_ModbusSnd: %s (L<>nge: %d)", str, length);
|
||
return 0;
|
||
}
|
||
|
||
// This method simply combines the two EthGetLastError functions
|
||
long _ModbusGetLastConnectionError(char string[])
|
||
{
|
||
gSocket.GetLastSocketErrorAsString(string, elCount(string));
|
||
return gSocket.GetLastSocketError();
|
||
}
|
||
|
||
// This method receives telegrams (from the TCP/IP API). ModbusRecv() has to be called first
|
||
void OnTcpReceive(dword socket, long result, dword address, dword port, byte buffer[], dword size)
|
||
{
|
||
_OnModbusReceive(socket, result, address, port, buffer, size); // Hand to Modbus layer
|
||
} |