# XYParser 的 C# 调用说明

## 1. 项目现状

当前工程已经是一个 **C++ 原生 DLL**，不是 .NET DLL。  
C# 调用它的推荐方式是使用 **P/Invoke**。

相关接口定义见：

- [XYParserApi.h](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.h#L12-L40)
- [XYParserApi.cpp](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.cpp#L59-L154)

当前主要导出函数：

- `XYParser_CreateParser`
- `XYParser_DestroyParser`
- `XYParser_SetAdcParams`
- `XYParser_SetBypassChecksum`
- `XYParser_Feed`
- `XYParser_GetLastError`

## 2. 使用方式

### 2.1 先编译 DLL

在 Visual Studio 中打开：

- [XYParser.sln](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParser.sln)

建议编译配置：

- `Release | x64`

编译后得到：

- `XYParser.dll`

运行 C# 程序时，需要把 `XYParser.dll` 放到：

- C# 可执行程序同目录
- 或系统可搜索到的 DLL 路径中

## 3. C# P/Invoke 声明

```csharp
using System;
using System.Runtime.InteropServices;

internal static class NativeMethods
{
    private const string DllName = "XYParser.dll";

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr XYParser_CreateParser(byte channel_count);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern void XYParser_DestroyParser(IntPtr handle);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern void XYParser_SetAdcParams(IntPtr handle, double vref, double gain);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern void XYParser_SetBypassChecksum(IntPtr handle, int bypass);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern int XYParser_Feed(
        IntPtr handle,
        byte[] data,
        UIntPtr size,
        [Out] XYParserFrameSummary[] outSummaries,
        int maxSummaries);

    [DllImport(DllName, CallingConvention = CallingConvention.Cdecl)]
    public static extern IntPtr XYParser_GetLastError(IntPtr handle);
}
```

## 4. 结构体定义

C++ 中的结构体定义见：

- [XYParserFrameSummary](file:///c:/Users/xyyl666/Desktop/XYParser/XYParser/XYParserApi.h#L21-L29)

对应的 C# 写法：

```csharp
using System.Runtime.InteropServices;

[StructLayout(LayoutKind.Sequential)]
public struct XYParserFrameSummary
{
    public uint frame_index;
    public byte channel_count;
    public byte battery;
    public byte sample_count;

    // C++: double channel_values_uv[5][64]
    // C#: 按一维数组接收，总长度 = 5 * 64
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5 * 64)]
    public double[] channel_values_uv;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public byte[] trigger_types;

    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
    public byte[] trigger_indices;
}
```

## 5. 调用示例

```csharp
using System;
using System.IO;
using System.Runtime.InteropServices;

class Program
{
    static void Main()
    {
        IntPtr handle = NativeMethods.XYParser_CreateParser(8);
        if (handle == IntPtr.Zero)
        {
            throw new Exception("创建解析器失败");
        }

        try
        {
            NativeMethods.XYParser_SetAdcParams(handle, 4.5, 6.0);
            NativeMethods.XYParser_SetBypassChecksum(handle, 1);

            byte[] input = File.ReadAllBytes("test.bin");

            XYParserFrameSummary[] summaries = new XYParserFrameSummary[8];
            for (int i = 0; i < summaries.Length; i++)
            {
                summaries[i].channel_values_uv = new double[5 * 64];
                summaries[i].trigger_types = new byte[5];
                summaries[i].trigger_indices = new byte[5];
            }

            int count = NativeMethods.XYParser_Feed(
                handle,
                input,
                (UIntPtr)input.Length,
                summaries,
                summaries.Length);

            string lastError = Marshal.PtrToStringAnsi(
                NativeMethods.XYParser_GetLastError(handle)) ?? "";

            Console.WriteLine($"解析出的帧数: {count}");
            Console.WriteLine($"最后错误: {lastError}");

            if (count > 0)
            {
                double firstValue = summaries[0].channel_values_uv[0];
                Console.WriteLine($"第一帧第一个采样点第一个通道值: {firstValue}");
            }
        }
        finally
        {
            NativeMethods.XYParser_DestroyParser(handle);
        }
    }
}
```

## 6. 二维数组取值说明

C++ 中：

```cpp
double channel_values_uv[5][64];
```

C# 中按一维数组接收后，索引方式为：

```csharp
double value = summary.channel_values_uv[sampleIndex * 64 + channelIndex];
```

例如：

- 第 `0` 个采样点，第 `0` 个通道：`[0 * 64 + 0]`
- 第 `1` 个采样点，第 `3` 个通道：`[1 * 64 + 3]`

## 7. 注意事项

- `CallingConvention` 要使用 `Cdecl`
- `XYParser_GetLastError` 返回的是 `const char*`，需用 `Marshal.PtrToStringAnsi`
- C# 程序位数必须和 DLL 一致
- 你当前更适合使用 `x64`
- `channel_count` 当前只支持 `8` 和 `64`

## 8. 推荐封装方式

如果后续要长期在 C# 中使用，建议再包一层托管类，例如：

```csharp
public sealed class XYParser : IDisposable
{
    private IntPtr _handle;

    public XYParser(byte channelCount)
    {
        _handle = NativeMethods.XYParser_CreateParser(channelCount);
        if (_handle == IntPtr.Zero)
            throw new InvalidOperationException("创建解析器失败");
    }

    public void SetAdcParams(double vref, double gain)
    {
        NativeMethods.XYParser_SetAdcParams(_handle, vref, gain);
    }

    public void SetBypassChecksum(bool enabled)
    {
        NativeMethods.XYParser_SetBypassChecksum(_handle, enabled ? 1 : 0);
    }

    public void Dispose()
    {
        if (_handle != IntPtr.Zero)
        {
            NativeMethods.XYParser_DestroyParser(_handle);
            _handle = IntPtr.Zero;
        }
    }
}
```

## 9. 总结

- 当前工程已经能生成原生 `XYParser.dll`
- C# 不需要重写解析逻辑，直接用 `DllImport` 即可调用
- 最关键的是函数声明匹配、结构体布局匹配、位数一致
