Home > APIs > AMP protocol > AMP socket connection example
Below is a simple C# program that shows how to connect to and communicate with a K2 using an AMP socket connection. This program connects, sends two commands, and then disconnects.
A full project of this can be downloaded here: AMPSocketExample.zip
namespace AMPSocketExample
{
class AMPClient
{
private Socket _socket;
///
/// Creates an AMP connection to the hostname and channel specified.
///
public AMPClient(string hostName, string channelName)
{
if (hostName == null || hostName=="") throw new Exception("Hostname cannot be blank.");
// Resolve the hostname using DNS
IPHostEntry hostEntry = Dns.GetHostEntry(hostName);
if (hostEntry.AddressList.Length == 0) throw new Exception("Could not resolve hostname.");
IPAddress address = hostEntry.AddressList[0];
// Create an endpoint to the IP address that was resolved, using AMP port # 3811.
IPEndPoint endPoint = new IPEndPoint(address, 3811);
// Create and connect a TCP socket
Console.WriteLine("Connecting to host {0}...", hostName);
_socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
_socket.Connect(endPoint);
// Set the send and receive timeouts.
_socket.ReceiveTimeout = 5000;
_socket.SendTimeout = 5000;
// Create the AMP connection. Refer to the document "K2 Protocol Developer's Guide" for details.
string strCommand;
if (channelName == null || channelName == "")
{
// AMP channel-less connection
Console.WriteLine("Creating AMP channel-less connection...");
strCommand = ("CRAT00014\n");
}
else
{
// AMP channel connection
Console.WriteLine("Creating AMP connection to channel {0}...", channelName);
strCommand = String.Format("CRAT{0:0000}{1}{2:00}{3}\n", channelName.Length + 3, 2, channelName.Length, channelName);
}
// Convert the command string to a byte array, and send the data to the socket.
byte[] byteData = System.Text.Encoding.ASCII.GetBytes(strCommand);
if (_socket.Send(byteData) != byteData.Length) throw new Exception("Error sending socket data.");
// Receive the response to the connection request. The reply will either be "1001" (ACK) or "1111" (NAK).
const int replySize = 4;
byte[] receiveBuffer = new byte[replySize];
if (_socket.Receive(receiveBuffer) != replySize)
{
throw new Exception("Response to connection request did not return the expected number of bytes.");
}
// Make sure the connection was successful.
string strReply = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, replySize);
if (strReply == "1111")
{
throw new Exception("CRAT command failed -- system returned NAK reply. Verify that the channel is configured for AMP control.");
}
else if (strReply != "1001")
{
throw new Exception(string.Format("CRAT command failed. System returned unknown reply {0}", strReply));
}
}
///
/// Sends the "STOP" command to close the AMP connection, and then closes the socket connection.
///
public void Disconnect()
{
if (_socket == null || !_socket.Connected) throw new Exception("Socket is not yet connected.");
string strCommand = "STOP0000\n";
byte[] byteData = System.Text.Encoding.ASCII.GetBytes(strCommand);
if (_socket.Send(byteData) != byteData.Length) throw new Exception("Error sending socket data.");
// Receive the response. The reply will either be "1001" (ACK) or "1111" (NAK).
byte[] receiveBuffer = new byte[4];
int replyLen = _socket.Receive(receiveBuffer);
string strReply = System.Text.Encoding.ASCII.GetString(receiveBuffer, 0, replyLen);
if (strReply != "1001") Console.WriteLine("Warning, STOP command did not return ACK reply.");
// Close the socket
_socket.Close();
_socket = null;
}
///
/// Sends the specified AMP command, and reads the response. The command is specified as a string of hexadecimal bytes,
/// and the reply is returned as an array of byte values. If the isExtendedReply argument is set to true,
/// when reading the reply data we will use the number of bytes specified by the "actual byte count" value.
///
public byte[] SendCommand(string command, bool isExtendedReply)
{
byte[] sendBytes;
byte[] replyBytes;
if (_socket == null || !_socket.Connected) throw new Exception("Socket is not yet connected.");
// AMP protocol only allows the complete command to take up to a maximum of 9999 bytes
// due to the format of the length (4 decimal characters) after the CMDS string.
if (command.Length > 9999)
{
throw new Exception(string.Format("Error sending AMP command. Data size ({0} bytes) exceeds maximum allowed of 9999 bytes.", command.Length));
}
// Format the socket command by adding the CMDSxxxx header.
string sendString = string.Format("CMDS{0:0000}{1}\n", command.Length, command);
sendBytes = System.Text.Encoding.ASCII.GetBytes(sendString);
// Send the command to the server.
_socket.Send(sendBytes);
// Read the first byte of the response (CMD1)
byte cmd1 = ReadByte();
// If we are expecting a response in the extended format, use the "actual byte count" value
// to determine how much reply data is to be expected. Otherwise, read the amount of data
// based on the data length specified in the lower nibble of the CMD1 byte.
if (isExtendedReply)
{
// Read the second byte of the response (CMD2)
byte cmd2 = ReadByte();
// Read the extended byte count.
byte[] extBc = new byte[2];
extBc[0] = ReadByte();
extBc[1] = ReadByte();
byte[] tempExtBc = new byte[] { extBc[1], extBc[0] };
UInt16 dataLen = BitConverter.ToUInt16(tempExtBc, 0);
// Read the expected amount of data. Socket commands do not include an extra checksum byte as RS422 does.
byte[] data = ReadBytes(dataLen);
// Total length of data for a socket reply in extended format: CMD1 + CMD2 + BC1 + BC2 + DATALEN
int totalLen = dataLen + 4;
// Assemble the complete reply into the 'replyBytes' array.
replyBytes = new byte[totalLen];
replyBytes[0] = cmd1;
replyBytes[1] = cmd2;
replyBytes[2] = extBc[0];
replyBytes[3] = extBc[1];
Array.Copy(data, 0, replyBytes, 4, data.Length);
}
else
{
// Extract the response data length from the lower nibble of the CMD1 byte.
int dataLen = (cmd1 & 0x0F);
// Bytes remaining to read: the CMD2 byte, plus the length of data expected.
// For socket connections, there is no checksum byte added like there is for RS422.
int bytesToRead = dataLen + 1;
// Read the remaining bytes of the response.
byte[] data = ReadBytes(bytesToRead);
// Total length also includes the cmd1 byte that was read previously.
int totalLen = bytesToRead + 1;
// Assemble the complete reply into the 'replyBytes' array.
replyBytes = new byte[totalLen];
replyBytes[0] = cmd1;
Array.Copy(data, 0, replyBytes, 1, data.Length);
}
return replyBytes;
}
///
/// Reads and returns one byte of data from an AMP command reply.
/// This actually involves reading two bytes of data from the network, since data is returned as a hexadecimal string.
///
private byte ReadByte()
{
byte[] b = new byte[2];
if (_socket.Receive(b) != 2) throw new Exception("Failed to read data from socket");
string str = ASCIIEncoding.ASCII.GetString(b);
return Convert.ToByte(str, 16);
}
///
/// Reads and returns the specified number of bytes of data from an AMP command reply.
/// This actually involves reading two bytes of data from the network for each byte, since data is returned as a hexadecimal string.
///
private byte[] ReadBytes(int count)
{
int totalToRead = count * 2;
byte[] b = new byte[totalToRead];
int totalRead = 0;
while (totalRead < totalToRead)
{
int numRead = _socket.Receive(b, totalRead, (totalToRead - totalRead), SocketFlags.None);
totalRead += numRead;
}
string str = ASCIIEncoding.ASCII.GetString(b);
return HexStringToByteArray(str);
}
///
/// Converts a string representing hex numbers into their equivalent byte values (2 characters per byte).
/// Example: "A005" --> { 0xA0, 0x05 }
///
public byte[] HexStringToByteArray(string hexString)
{
if (hexString == null) return new byte[] { };
if (hexString.Length % 2 != 0) throw new Exception("HexStringToByteArray: Input string must be an even number of characters.");
byte[] byteArray = new byte[hexString.Length / 2];
for (int i = 0; i < (hexString.Length / 2); i++)
{
byteArray[i] = Convert.ToByte(hexString.Substring(i * 2, 2), 16);
}
return byteArray;
}
///
/// Sends the A0.0F "Get Working Folder Request" command and returns the response.
///
public string GetWorkingFolderRequest()
{
// Send the command and receive the response.
byte[] reply = SendCommand("A00F", true);
// Format of the expected reply data: [0x82] [0x0F] [ExtBC1] [ExtBC2] [Len1] [Len2] [FolderName]
// Verify that the expected response was received, and extract the folder name string from the reply data.
if (reply[0] == 0x82 && reply[1] == 0x0F)
{
// Extract the folder name length
byte[] lengthBytes = new byte[] { reply[5], reply[4] };
Int16 folderNameLen = BitConverter.ToInt16(lengthBytes, 0);
// Extract and return the folder name string.
return Encoding.UTF8.GetString(reply, 6, folderNameLen);
}
else
{
throw new Exception("GetWorkingFolderRequest returned an invalid reply.");
}
}
///
/// Sends the 20.61 "EE On" command (enables E-to-E mode on the channel).
///
public void EEOn()
{
// Send the command and receive the response.
byte[] reply = SendCommand("2061", false);
// Verify that the expected ACK reply was received.
if (! (reply[0] == 0x10 && reply[1] == 0x01))
{
throw new Exception("EEOn command did not return ACK reply.");
}
}
}
}
