using System; using System.Net.Sockets; using System.Net; namespace AmtScanner.Api.Services { /// /// AMT platform data. e.g. provisioning state. /// public struct PlatformData { /// /// The provisioning state of the AMT machine. /// public ProvisioningState ProvisioningState { get; internal set; } /// /// The major version of the AMT machine. /// public uint MajorVersion { get; internal set; } /// /// The minor version of the AMT machine. /// public uint MinorVersion { get; internal set; } } /// /// The provisioning state of the AMT machine. /// public enum ProvisioningState { Pre = 0, In = 1, Post = 2, Unknown } public class RMCPPingApi { private const int RMCP_PORT = 623; private const int RMCP_HEADER_SIZE = 4; private const int ASF_RMCP_DATA_MINIMUM_SIZE = 8; private const int RMCP_RESENCE_PONG_DATA_SIZE = 16; /// /// 获取AMT平台数据,支持自定义超时 /// public static PlatformData GetPlatformData(string address, int timeoutMs = 5000) { return GetPlatformDataByRMCPPing(address, timeoutMs); } private static PlatformData GetPlatformDataByRMCPPing(string address, int timeoutMs) { const int DASH_SUPPORT_BYTE_OFFSET_DSP_0232_100B = 9; const int DASH_SUPPORT_INDICATING_BIT = 32; const int VERS_BYTE = 0; const int RSVD_BYTE = 1; const int SEQ_BYTE = 2; const int CLASS_BYTE = 3; const int VERS = 0x06; const int RSVD = 0x0; const int SEQ = 0x0; const int CLASS = 0x86; using (Socket clientSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)) { try { // 按照SDK的方式,先用IPEndPoint连接,再用地址和端口连接 IPEndPoint ipEndPoint = new IPEndPoint(IPAddress.Parse(address), RMCP_PORT); clientSocket.Connect(ipEndPoint as EndPoint); clientSocket.Connect(address, RMCP_PORT); byte[] rmcpPingPacketData = GetRMCPPingPacketData(); clientSocket.Send(rmcpPingPacketData, 0, rmcpPingPacketData.Length, SocketFlags.None); clientSocket.ReceiveTimeout = timeoutMs; byte[] byteData = new byte[4]; clientSocket.Receive(byteData, 0, byteData.Length, SocketFlags.None); if ((byteData[VERS_BYTE] == VERS) && (byteData[RSVD_BYTE] == RSVD) && (byteData[SEQ_BYTE] == SEQ) && (byteData[CLASS_BYTE] == CLASS)) { byte[] pongBuffer = new byte[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + RMCP_RESENCE_PONG_DATA_SIZE]; clientSocket.Receive(pongBuffer, 0, pongBuffer.Length, SocketFlags.None); if ((pongBuffer[12] == 0) && (pongBuffer[13] == 0) && (pongBuffer[14] == 0x01) && (pongBuffer[15] == 0x57)) { ushort dashSupportByteVersionB = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + DASH_SUPPORT_BYTE_OFFSET_DSP_0232_100B]; if (0 != (dashSupportByteVersionB & DASH_SUPPORT_INDICATING_BIT)) { return ParsePlatformDataFromRMCPPing(pongBuffer); } } } throw new Exception("Not an AMT device"); } catch (Exception ex) { throw new Exception($"Failed to get platform data: {ex.Message}", ex); } finally { clientSocket.Close(); } } } private static PlatformData ParsePlatformDataFromRMCPPing(byte[] pongBuffer) { const int CONFIGURATION_STATE_BYTE = 7; const int VERSION_BYTE = 6; const int UPDATED_VERSION_BYTE = 10; PlatformData platformData = new PlatformData(); if (pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] == 0) { platformData.MinorVersion = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + UPDATED_VERSION_BYTE + 1]; platformData.MajorVersion = pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + UPDATED_VERSION_BYTE]; } else { platformData.MinorVersion = (uint)(pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf); platformData.MajorVersion = (uint)((pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + VERSION_BYTE] & 0xf0) >> 4); } platformData.ProvisioningState = (ProvisioningState)(pongBuffer[RMCP_HEADER_SIZE + ASF_RMCP_DATA_MINIMUM_SIZE + CONFIGURATION_STATE_BYTE] & 0x3); return platformData; } private static byte[] GetRMCPPingPacketData() { return new byte[] { 0x06, 0x00, 0x00, 0x06, // RMCP Header 0x00, 0x00, 0x11, 0xBE, // ASF IANA 0x80, 0x00, 0x00, 0x00 // Presence Ping }; } } }