149 lines
5.6 KiB
C#
149 lines
5.6 KiB
C#
using System;
|
||
using System.Net.Sockets;
|
||
using System.Net;
|
||
|
||
namespace AmtScanner.Api.Services
|
||
{
|
||
/// <summary>
|
||
/// AMT platform data. e.g. provisioning state.
|
||
/// </summary>
|
||
public struct PlatformData
|
||
{
|
||
/// <summary>
|
||
/// The provisioning state of the AMT machine.
|
||
/// </summary>
|
||
public ProvisioningState ProvisioningState { get; internal set; }
|
||
|
||
/// <summary>
|
||
/// The major version of the AMT machine.
|
||
/// </summary>
|
||
public uint MajorVersion { get; internal set; }
|
||
|
||
/// <summary>
|
||
/// The minor version of the AMT machine.
|
||
/// </summary>
|
||
public uint MinorVersion { get; internal set; }
|
||
}
|
||
|
||
/// <summary>
|
||
/// The provisioning state of the AMT machine.
|
||
/// </summary>
|
||
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;
|
||
|
||
/// <summary>
|
||
/// 获取AMT平台数据,支持自定义超时
|
||
/// </summary>
|
||
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
|
||
};
|
||
}
|
||
}
|
||
}
|