This commit is contained in:
2026-06-06 10:07:53 +08:00
commit b51e2b451e
54 changed files with 1500 additions and 0 deletions

237
CSharp调用说明.md Normal file
View File

@@ -0,0 +1,237 @@
# 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` 即可调用
- 最关键的是函数声明匹配、结构体布局匹配、位数一致

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,171 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYParserApi.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYParserApi.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParserExample.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser64.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParserCommon.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser8.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser64.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser8.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "XYParserApi.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h",
"RelativeDocumentMoniker": "XYParserApi.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h",
"RelativeToolTip": "XYParserApi.h",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAA0AAAAUAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T06:13:17.908Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "XYParserApi.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp",
"RelativeDocumentMoniker": "XYParserApi.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp",
"RelativeToolTip": "XYParserApi.cpp",
"ViewState": "AgIAAH4AAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T06:13:04.173Z",
"EditorCaption": ""
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "XYEegParserExample.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp",
"RelativeDocumentMoniker": "XYEegParserExample.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp",
"RelativeToolTip": "XYEegParserExample.cpp",
"ViewState": "AgIAAFwAAAAAAAAAAAAAAFAAAAADAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:55:44.898Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "XYEegParser64.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h",
"RelativeDocumentMoniker": "XYEegParser64.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h",
"RelativeToolTip": "XYEegParser64.h",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAQAAAAkAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:51:57.408Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "XYEegParserCommon.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h",
"RelativeDocumentMoniker": "XYEegParserCommon.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h",
"RelativeToolTip": "XYEegParserCommon.h",
"ViewState": "AgIAAMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:50:06.166Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "XYEegParser8.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h",
"RelativeDocumentMoniker": "XYEegParser8.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h",
"RelativeToolTip": "XYEegParser8.h",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:49:55.73Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "XYEegParser64.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp",
"RelativeDocumentMoniker": "XYEegParser64.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp",
"RelativeToolTip": "XYEegParser64.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:49:51.523Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "XYEegParser8.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp",
"RelativeDocumentMoniker": "XYEegParser8.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp",
"RelativeToolTip": "XYEegParser8.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:49:48.717Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "dllmain.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp",
"RelativeDocumentMoniker": "dllmain.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp",
"RelativeToolTip": "dllmain.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T03:55:05.048Z"
}
]
}
]
}
]
}

View File

@@ -0,0 +1,171 @@
{
"Version": 1,
"WorkspaceRootPath": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYParserApi.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYParserApi.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParserExample.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser64.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParserCommon.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser8.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser64.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:XYEegParser8.h||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
},
{
"AbsoluteMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}",
"RelativeMoniker": "D:0:0:{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}|XYParser.vcxproj|solutionrelative:dllmain.cpp||{D0E1A5C6-B359-4E41-9B60-3365922C2A22}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "XYParserApi.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h",
"RelativeDocumentMoniker": "XYParserApi.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.h",
"RelativeToolTip": "XYParserApi.h",
"ViewState": "AgIAABsAAAAAAAAAAAAAACMAAAA4AAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T06:13:17.908Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 1,
"Title": "XYParserApi.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp",
"RelativeDocumentMoniker": "XYParserApi.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYParserApi.cpp",
"RelativeToolTip": "XYParserApi.cpp",
"ViewState": "AgIAAI4AAAAAAAAAAADwvwAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T06:13:04.173Z",
"EditorCaption": ""
},
{
"$type": "Bookmark",
"Name": "ST:0:0:{aa2115a1-9712-457b-9047-dbb71ca2cdd2}"
},
{
"$type": "Document",
"DocumentIndex": 2,
"Title": "XYEegParserExample.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp",
"RelativeDocumentMoniker": "XYEegParserExample.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserExample.cpp",
"RelativeToolTip": "XYEegParserExample.cpp",
"ViewState": "AgIAAFwAAAAAAAAAAAAAAFAAAAADAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:55:44.898Z",
"EditorCaption": ""
},
{
"$type": "Document",
"DocumentIndex": 3,
"Title": "XYEegParser64.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h",
"RelativeDocumentMoniker": "XYEegParser64.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.h",
"RelativeToolTip": "XYEegParser64.h",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAQAAAAkAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:51:57.408Z"
},
{
"$type": "Document",
"DocumentIndex": 4,
"Title": "XYEegParserCommon.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h",
"RelativeDocumentMoniker": "XYEegParserCommon.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParserCommon.h",
"RelativeToolTip": "XYEegParserCommon.h",
"ViewState": "AgIAAMkAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:50:06.166Z"
},
{
"$type": "Document",
"DocumentIndex": 7,
"Title": "XYEegParser8.h",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h",
"RelativeDocumentMoniker": "XYEegParser8.h",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.h",
"RelativeToolTip": "XYEegParser8.h",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000680|",
"WhenOpened": "2026-06-04T05:49:55.73Z"
},
{
"$type": "Document",
"DocumentIndex": 6,
"Title": "XYEegParser64.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp",
"RelativeDocumentMoniker": "XYEegParser64.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser64.cpp",
"RelativeToolTip": "XYEegParser64.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:49:51.523Z"
},
{
"$type": "Document",
"DocumentIndex": 5,
"Title": "XYEegParser8.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp",
"RelativeDocumentMoniker": "XYEegParser8.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\XYEegParser8.cpp",
"RelativeToolTip": "XYEegParser8.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T05:49:48.717Z"
},
{
"$type": "Document",
"DocumentIndex": 8,
"Title": "dllmain.cpp",
"DocumentMoniker": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp",
"RelativeDocumentMoniker": "dllmain.cpp",
"ToolTip": "C:\\Users\\xyyl666\\Desktop\\XYParser\\XYParser\\dllmain.cpp",
"RelativeToolTip": "dllmain.cpp",
"ViewState": "AgIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000677|",
"WhenOpened": "2026-06-04T03:55:05.048Z"
}
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,2 @@
#include "pch.h"
#include "XYEegParser64.h"

10
XYParser/XYEegParser64.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include "XYEegParserCommon.h"
using XYEegFrame64 = xyparser::XYEegFrame<64>;
class XYEegParser64 final : public xyparser::XYEegTcpParserCommon<64> {
public:
using Frame = XYEegFrame64;
};

View File

@@ -0,0 +1,2 @@
#include "pch.h"
#include "XYEegParser8.h"

10
XYParser/XYEegParser8.h Normal file
View File

@@ -0,0 +1,10 @@
#pragma once
#include "XYEegParserCommon.h"
using XYEegFrame8 = xyparser::XYEegFrame<8>;
class XYEegParser8 final : public xyparser::XYEegTcpParserCommon<8> {
public:
using Frame = XYEegFrame8;
};

View File

@@ -0,0 +1,266 @@
#pragma once
#include <algorithm>
#include <array>
#include <cstddef>
#include <cstdint>
#include <string>
#include <vector>
namespace xyparser {
template <std::size_t ChannelCount>
struct XYEegSample {
std::array<double, ChannelCount> channel_values_uv{};
std::uint8_t trigger_type = 0;
std::uint8_t trigger_index = 0;
};
template <std::size_t ChannelCount>
struct XYEegFrame {
static constexpr std::size_t kSamplesPerFrame = 5;
std::uint8_t header = 0;
std::uint32_t index = 0;
std::uint16_t payload_length = 0;
std::uint8_t battery = 0;
std::uint8_t channel_count = 0;
std::int16_t elevation_angle = 0;
std::int16_t rolling_angle = 0;
std::int16_t yaw_angle = 0;
std::int16_t ecg = 0;
std::int16_t blood_oxygen = 0;
std::array<std::uint8_t, 6> reserved{};
std::array<XYEegSample<ChannelCount>, kSamplesPerFrame> samples{};
std::uint8_t crc = 0;
std::array<std::uint8_t, 2> tails{};
};
template <std::size_t ChannelCount>
class XYEegTcpParserCommon {
public:
using Frame = XYEegFrame<ChannelCount>;
static constexpr std::uint8_t kFrameHeader = 0xAA;
static constexpr std::uint8_t kFrameTail = 0x55;
static constexpr std::size_t kSamplesPerFrame = 5;
static constexpr std::size_t kFrameHeaderLen = 1;
static constexpr std::size_t kFrameTailLen = 2;
static constexpr std::size_t kFrameTagLen = 25;
void SetAdcParams(double vref, double gain) noexcept
{
if (vref > 0.0) {
vref_ = vref;
}
if (gain > 0.0) {
gain_ = gain;
}
}
void SetBypassChecksum(bool bypass) noexcept
{
bypass_checksum_ = bypass;
}
bool BypassChecksum() const noexcept
{
return bypass_checksum_;
}
void Reset()
{
buffer_.clear();
last_error_.clear();
}
const std::string &LastError() const noexcept
{
return last_error_;
}
std::vector<Frame> Feed(const std::vector<std::uint8_t> &bytes)
{
return Feed(bytes.data(), bytes.size());
}
std::vector<Frame> Feed(const std::uint8_t *data, std::size_t size)
{
std::vector<Frame> frames;
if (data == nullptr || size == 0) {
return frames;
}
buffer_.insert(buffer_.end(), data, data + size);
while (true) {
const auto header_it = std::find(buffer_.begin(), buffer_.end(), kFrameHeader);
if (header_it == buffer_.end()) {
buffer_.clear();
break;
}
const std::size_t header_index = static_cast<std::size_t>(std::distance(buffer_.begin(), header_it));
if (buffer_.size() < header_index + 7) {
if (header_index > 0) {
buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast<std::ptrdiff_t>(header_index));
}
break;
}
const std::uint16_t payload_length = ReadBE16(buffer_, header_index + 5);
const std::size_t total_frame_len = payload_length + kFrameTagLen + kFrameTailLen + kFrameHeaderLen;
if (buffer_.size() - header_index < total_frame_len) {
if (header_index > 0) {
buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast<std::ptrdiff_t>(header_index));
}
break;
}
const std::size_t tail_index = header_index + payload_length + kFrameTagLen + kFrameHeaderLen;
if (tail_index + 1 >= buffer_.size() || buffer_[tail_index] != kFrameTail || buffer_[tail_index + 1] != kFrameTail) {
buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast<std::ptrdiff_t>(header_index + 1));
continue;
}
std::vector<std::uint8_t> frame_bytes(
buffer_.begin() + static_cast<std::ptrdiff_t>(header_index),
buffer_.begin() + static_cast<std::ptrdiff_t>(tail_index + 2));
buffer_.erase(buffer_.begin(), buffer_.begin() + static_cast<std::ptrdiff_t>(tail_index + 2));
Frame frame;
if (TryParseFrame(frame_bytes, frame)) {
frames.push_back(frame);
}
}
return frames;
}
private:
static std::uint16_t ReadBE16(const std::vector<std::uint8_t> &bytes, std::size_t offset) noexcept
{
return static_cast<std::uint16_t>((static_cast<std::uint16_t>(bytes[offset]) << 8) |
static_cast<std::uint16_t>(bytes[offset + 1]));
}
static std::uint16_t ReadLE16(const std::vector<std::uint8_t> &bytes, std::size_t offset) noexcept
{
return static_cast<std::uint16_t>(static_cast<std::uint16_t>(bytes[offset]) |
(static_cast<std::uint16_t>(bytes[offset + 1]) << 8));
}
static std::uint32_t ReadLE32(const std::vector<std::uint8_t> &bytes, std::size_t offset) noexcept
{
return static_cast<std::uint32_t>(bytes[offset]) |
(static_cast<std::uint32_t>(bytes[offset + 1]) << 8) |
(static_cast<std::uint32_t>(bytes[offset + 2]) << 16) |
(static_cast<std::uint32_t>(bytes[offset + 3]) << 24);
}
double ConvertAdcToUv(const std::uint8_t raw0, const std::uint8_t raw1, const std::uint8_t raw2) const noexcept
{
double value = static_cast<double>(raw0) * 65536.0 +
static_cast<double>(raw1) * 256.0 +
static_cast<double>(raw2);
const double factor = vref_ / gain_ / 8388608.0 * 1000000.0;
if (value < 8388608.0) {
return value * factor;
}
return -(16777216.0 - value) * factor;
}
bool CheckChecksum(const std::vector<std::uint8_t> &frame_bytes) const noexcept
{
if (frame_bytes.size() < 4) {
return false;
}
const std::size_t crc_index = frame_bytes.size() - 3;
std::uint8_t checksum = 0;
for (std::size_t i = 0; i < crc_index; ++i) {
checksum = static_cast<std::uint8_t>(checksum + frame_bytes[i]);
}
return checksum == frame_bytes[crc_index];
}
bool TryParseFrame(const std::vector<std::uint8_t> &frame_bytes, Frame &frame)
{
if (!bypass_checksum_ && !CheckChecksum(frame_bytes)) {
last_error_ = "checksum failed";
return false;
}
const std::size_t min_frame_len = kFrameHeaderLen + kFrameTagLen + kFrameTailLen;
if (frame_bytes.size() < min_frame_len) {
last_error_ = "frame too short";
return false;
}
const std::size_t channel_count_offset = kFrameHeaderLen + 4 + 2 + 1;
const std::uint8_t channel_count = frame_bytes[channel_count_offset];
if (channel_count != ChannelCount) {
last_error_ = "unexpected channel count";
return false;
}
const std::size_t sample_bytes = ChannelCount * 3 + 2;
const std::size_t expected_frame_len = kFrameHeaderLen + kFrameTagLen +
kSamplesPerFrame * sample_bytes + kFrameTailLen;
if (frame_bytes.size() != expected_frame_len) {
last_error_ = "frame length mismatch";
return false;
}
std::size_t offset = 0;
frame.header = frame_bytes[offset++];
frame.index = ReadLE32(frame_bytes, offset);
offset += 4;
frame.payload_length = ReadBE16(frame_bytes, offset);
offset += 2;
frame.battery = frame_bytes[offset++];
frame.channel_count = frame_bytes[offset++];
frame.elevation_angle = static_cast<std::int16_t>(ReadLE16(frame_bytes, offset));
offset += 2;
frame.rolling_angle = static_cast<std::int16_t>(ReadLE16(frame_bytes, offset));
offset += 2;
frame.yaw_angle = static_cast<std::int16_t>(ReadLE16(frame_bytes, offset));
offset += 2;
frame.ecg = static_cast<std::int16_t>(ReadLE16(frame_bytes, offset));
offset += 2;
frame.blood_oxygen = static_cast<std::int16_t>(ReadLE16(frame_bytes, offset));
offset += 2;
for (std::size_t i = 0; i < frame.reserved.size(); ++i) {
frame.reserved[i] = frame_bytes[offset++];
}
for (auto &sample : frame.samples) {
for (std::size_t channel = 0; channel < ChannelCount; ++channel) {
sample.channel_values_uv[channel] = ConvertAdcToUv(
frame_bytes[offset],
frame_bytes[offset + 1],
frame_bytes[offset + 2]);
offset += 3;
}
sample.trigger_type = frame_bytes[offset++];
sample.trigger_index = frame_bytes[offset++];
}
frame.crc = frame_bytes[offset++];
frame.tails[0] = frame_bytes[offset++];
frame.tails[1] = frame_bytes[offset++];
last_error_.clear();
return true;
}
private:
std::vector<std::uint8_t> buffer_;
std::string last_error_;
double vref_ = 4.5;
double gain_ = 6.0;
bool bypass_checksum_ = true;
};
} // namespace xyparser

View File

@@ -0,0 +1,112 @@
#include "pch.h"
#include "XYParserApi.h"
#include <array>
#include <cstdint>
#include <vector>
namespace {
std::vector<std::uint8_t> BuildMinimalFrame(std::uint8_t channel_count)
{
constexpr std::size_t kSamplesPerFrame = 5;
constexpr std::uint8_t kHeader = 0xAA;
constexpr std::uint8_t kTail = 0x55;
constexpr std::size_t kTagLen = 25;
const std::size_t sample_bytes = static_cast<std::size_t>(channel_count) * 3 + 2;
const std::uint16_t payload_length = static_cast<std::uint16_t>(sample_bytes * kSamplesPerFrame);
const std::size_t frame_size = 1 + kTagLen + payload_length + 2;
std::vector<std::uint8_t> frame(frame_size, 0);
std::size_t offset = 0;
frame[offset++] = kHeader;
// index, little endian
frame[offset++] = 0x01;
frame[offset++] = 0x00;
frame[offset++] = 0x00;
frame[offset++] = 0x00;
// payload length, big endian
frame[offset++] = static_cast<std::uint8_t>((payload_length >> 8) & 0xFF);
frame[offset++] = static_cast<std::uint8_t>(payload_length & 0xFF);
frame[offset++] = 95; // battery
frame[offset++] = channel_count; // channelCount
// elevation / roll / yaw / ecg / spo2 / reserved[6]
offset += 2 + 2 + 2 + 2 + 2 + 6;
// Build 5 samples. Each channel uses 3 ADC bytes, followed by trigger fields.
for (std::size_t sample = 0; sample < kSamplesPerFrame; ++sample) {
for (std::size_t channel = 0; channel < channel_count; ++channel) {
frame[offset++] = 0x00;
frame[offset++] = 0x00;
frame[offset++] = static_cast<std::uint8_t>(sample + channel + 1);
}
frame[offset++] = 0x00; // triggerType
frame[offset++] = 0x00; // triggerIndex
}
frame[offset++] = 0x00; // crc, ignored when bypass checksum is enabled
frame[offset++] = kTail;
frame[offset++] = kTail;
return frame;
}
} // namespace
void MinimalExampleFor8ChParser()
{
XYParserHandle parser = XYParser_CreateParser(8);
if (parser == nullptr) {
return;
}
XYParser_SetAdcParams(parser, 4.5, 6.0);
XYParser_SetBypassChecksum(parser, 1);
const std::vector<std::uint8_t> raw_bytes = BuildMinimalFrame(8);
std::array<XYParserFrameSummary, 1> summaries{};
const int frame_count = XYParser_Feed(parser,
raw_bytes.data(),
raw_bytes.size(),
summaries.data(),
static_cast<int>(summaries.size()));
if (frame_count > 0) {
const XYParserFrameSummary& frame = summaries.front();
const double first_channel_uv = frame.channel_values_uv[0][0];
(void)first_channel_uv;
}
XYParser_DestroyParser(parser);
}
void MinimalExampleFor64ChParser()
{
XYParserHandle parser = XYParser_CreateParser(64);
if (parser == nullptr) {
return;
}
XYParser_SetAdcParams(parser, 4.5, 6.0);
XYParser_SetBypassChecksum(parser, 1);
const std::vector<std::uint8_t> raw_bytes = BuildMinimalFrame(64);
std::array<XYParserFrameSummary, 1> summaries{};
const int frame_count = XYParser_Feed(parser,
raw_bytes.data(),
raw_bytes.size(),
summaries.data(),
static_cast<int>(summaries.size()));
if (frame_count > 0) {
const XYParserFrameSummary& frame = summaries.front();
const std::uint8_t battery = frame.battery;
(void)battery;
}
XYParser_DestroyParser(parser);
}

31
XYParser/XYParser.sln Normal file
View File

@@ -0,0 +1,31 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36429.23 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "XYParser", "XYParser.vcxproj", "{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Debug|x64.ActiveCfg = Debug|x64
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Debug|x64.Build.0 = Debug|x64
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Debug|x86.ActiveCfg = Debug|Win32
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Debug|x86.Build.0 = Debug|Win32
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Release|x64.ActiveCfg = Release|x64
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Release|x64.Build.0 = Release|x64
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Release|x86.ActiveCfg = Release|Win32
{CB1FF804-BB1F-41C8-92FA-7B15F6B86347}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {F6206831-0432-4ABB-B939-97EC2B8EAB79}
EndGlobalSection
EndGlobal

161
XYParser/XYParser.vcxproj Normal file
View File

@@ -0,0 +1,161 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{cb1ff804-bb1f-41c8-92fa-7b15f6b86347}</ProjectGuid>
<RootNamespace>XYParser</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;XYPARSER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;XYPARSER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;XYPARSER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;XYPARSER_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framework.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="XYParserApi.h" />
<ClInclude Include="XYEegParser8.h" />
<ClInclude Include="XYEegParser64.h" />
<ClInclude Include="XYEegParserCommon.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="XYParserApi.cpp" />
<ClCompile Include="XYEegParser8.cpp" />
<ClCompile Include="XYEegParser64.cpp" />
<ClCompile Include="XYEegParserExample.cpp" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -0,0 +1,57 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="XYParserApi.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="XYEegParser8.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="XYEegParser64.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="XYEegParserCommon.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="XYParserApi.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="XYEegParser8.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="XYEegParser64.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="XYEegParserExample.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<ProjectOutputs>
<ProjectOutput>
<FullPath>C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.dll</FullPath>
</ProjectOutput>
</ProjectOutputs>
<ContentFiles />
<SatelliteDlls />
<NonRecipeFileRefs />
</Project>

Binary file not shown.

View File

@@ -0,0 +1,9 @@
pch.cpp
dllmain.cpp
XYParserApi.cpp
XYEegParser8.cpp
XYEegParser64.cpp
XYEegParserExample.cpp
正在生成代码...
正在创建库 C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.lib 和对象 C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.exp
XYParser.vcxproj -> C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.dll

Binary file not shown.

View File

@@ -0,0 +1,6 @@
C:\Users\xyyl666\Desktop\XYParser\XYParser\dllmain.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\dllmain.obj
C:\Users\xyyl666\Desktop\XYParser\XYParser\pch.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\pch.obj
C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParserApi.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\XYParserApi.obj
C:\Users\xyyl666\Desktop\XYParser\XYParser\XYEegParser8.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\XYEegParser8.obj
C:\Users\xyyl666\Desktop\XYParser\XYParser\XYEegParser64.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\XYEegParser64.obj
C:\Users\xyyl666\Desktop\XYParser\XYParser\XYEegParserExample.cpp;C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\XYEegParserExample.obj

View File

@@ -0,0 +1,2 @@
PlatformToolSet=v143:VCToolArchitecture=Native32Bit:VCToolsVersion=14.44.35207:TargetPlatformVersion=10.0.26100.0:
Debug|x64|C:\Users\xyyl666\Desktop\XYParser\XYParser\|

View File

@@ -0,0 +1,4 @@
^C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\DLLMAIN.OBJ|C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\PCH.OBJ|C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\XYEEGPARSER64.OBJ|C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\XYEEGPARSER8.OBJ|C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\XYEEGPARSEREXAMPLE.OBJ|C:\USERS\XYYL666\DESKTOP\XYPARSER\XYPARSER\XYPARSER\X64\DEBUG\XYPARSERAPI.OBJ
C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.lib
C:\Users\xyyl666\Desktop\XYParser\XYParser\x64\Debug\XYParser.EXP
C:\Users\xyyl666\Desktop\XYParser\XYParser\XYParser\x64\Debug\XYParser.ilk

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

154
XYParser/XYParserApi.cpp Normal file
View File

@@ -0,0 +1,154 @@
#include "pch.h"
#include "XYParserApi.h"
#include "XYEegParser8.h"
#include "XYEegParser64.h"
#include <algorithm>
#include <new>
#include <string>
#include <vector>
namespace {
struct ParserContext {
std::uint8_t channel_count = 0;
XYEegParser8 parser8;
XYEegParser64 parser64;
std::string last_error;
};
void FillSummary(const XYEegFrame8& frame, XYParserFrameSummary& summary)
{
summary.frame_index = frame.index;
summary.channel_count = frame.channel_count;
summary.battery = frame.battery;
summary.sample_count = static_cast<std::uint8_t>(frame.samples.size());
for (std::size_t sample_index = 0; sample_index < XYPARSER_SAMPLES_PER_FRAME; ++sample_index) {
summary.trigger_types[sample_index] = frame.samples[sample_index].trigger_type;
summary.trigger_indices[sample_index] = frame.samples[sample_index].trigger_index;
for (std::size_t channel_index = 0; channel_index < XYPARSER_MAX_CHANNELS; ++channel_index) {
summary.channel_values_uv[sample_index][channel_index] =
channel_index < frame.channel_count
? frame.samples[sample_index].channel_values_uv[channel_index]
: 0.0;
}
}
}
void FillSummary(const XYEegFrame64& frame, XYParserFrameSummary& summary)
{
summary.frame_index = frame.index;
summary.channel_count = frame.channel_count;
summary.battery = frame.battery;
summary.sample_count = static_cast<std::uint8_t>(frame.samples.size());
for (std::size_t sample_index = 0; sample_index < XYPARSER_SAMPLES_PER_FRAME; ++sample_index) {
summary.trigger_types[sample_index] = frame.samples[sample_index].trigger_type;
summary.trigger_indices[sample_index] = frame.samples[sample_index].trigger_index;
for (std::size_t channel_index = 0; channel_index < XYPARSER_MAX_CHANNELS; ++channel_index) {
summary.channel_values_uv[sample_index][channel_index] =
channel_index < frame.channel_count
? frame.samples[sample_index].channel_values_uv[channel_index]
: 0.0;
}
}
}
} // namespace
extern "C" {
XYParserHandle XYParser_CreateParser(std::uint8_t channel_count)
{
if (channel_count != 8 && channel_count != 64) {
return nullptr;
}
ParserContext* context = new (std::nothrow) ParserContext();
if (context == nullptr) {
return nullptr;
}
context->channel_count = channel_count;
return context;
}
void XYParser_DestroyParser(XYParserHandle handle)
{
ParserContext* context = static_cast<ParserContext*>(handle);
delete context;
}
void XYParser_SetAdcParams(XYParserHandle handle, double vref, double gain)
{
ParserContext* context = static_cast<ParserContext*>(handle);
if (context == nullptr) {
return;
}
if (context->channel_count == 8) {
context->parser8.SetAdcParams(vref, gain);
} else {
context->parser64.SetAdcParams(vref, gain);
}
}
void XYParser_SetBypassChecksum(XYParserHandle handle, int bypass)
{
ParserContext* context = static_cast<ParserContext*>(handle);
if (context == nullptr) {
return;
}
const bool enabled = bypass != 0;
if (context->channel_count == 8) {
context->parser8.SetBypassChecksum(enabled);
} else {
context->parser64.SetBypassChecksum(enabled);
}
}
int XYParser_Feed(XYParserHandle handle,
const std::uint8_t* data,
std::size_t size,
XYParserFrameSummary* out_summaries,
int max_summaries)
{
ParserContext* context = static_cast<ParserContext*>(handle);
if (context == nullptr || data == nullptr || size == 0) {
return 0;
}
if (max_summaries < 0) {
max_summaries = 0;
}
if (context->channel_count == 8) {
const std::vector<XYEegFrame8> frames = context->parser8.Feed(data, size);
context->last_error = context->parser8.LastError();
const int write_count = std::min<int>(static_cast<int>(frames.size()), max_summaries);
for (int i = 0; i < write_count; ++i) {
FillSummary(frames[static_cast<std::size_t>(i)], out_summaries[i]);
}
return write_count;
}
const std::vector<XYEegFrame64> frames = context->parser64.Feed(data, size);
context->last_error = context->parser64.LastError();
const int write_count = std::min<int>(static_cast<int>(frames.size()), max_summaries);
for (int i = 0; i < write_count; ++i) {
FillSummary(frames[static_cast<std::size_t>(i)], out_summaries[i]);
}
return write_count;
}
const char* XYParser_GetLastError(XYParserHandle handle)
{
ParserContext* context = static_cast<ParserContext*>(handle);
if (context == nullptr) {
return "invalid parser handle";
}
return context->last_error.c_str();
}
}

42
XYParser/XYParserApi.h Normal file
View File

@@ -0,0 +1,42 @@
#pragma once
#include <cstddef>
#include <cstdint>
#ifdef XYPARSER_EXPORTS
#define XYPARSER_API __declspec(dllexport)
#else
#define XYPARSER_API __declspec(dllimport)
#endif
extern "C" {
typedef void* XYParserHandle;
enum {
XYPARSER_MAX_CHANNELS = 64,
XYPARSER_SAMPLES_PER_FRAME = 5
};
struct XYParserFrameSummary {
std::uint32_t frame_index;
std::uint8_t channel_count;
std::uint8_t battery;
std::uint8_t sample_count;
double channel_values_uv[XYPARSER_SAMPLES_PER_FRAME][XYPARSER_MAX_CHANNELS];
std::uint8_t trigger_types[XYPARSER_SAMPLES_PER_FRAME];
std::uint8_t trigger_indices[XYPARSER_SAMPLES_PER_FRAME];
};
XYPARSER_API XYParserHandle XYParser_CreateParser(std::uint8_t channel_count);
XYPARSER_API void XYParser_DestroyParser(XYParserHandle handle);
XYPARSER_API void XYParser_SetAdcParams(XYParserHandle handle, double vref, double gain);
XYPARSER_API void XYParser_SetBypassChecksum(XYParserHandle handle, int bypass);
XYPARSER_API int XYParser_Feed(XYParserHandle handle,
const std::uint8_t* data,
std::size_t size,
XYParserFrameSummary* out_summaries,
int max_summaries);
XYPARSER_API const char* XYParser_GetLastError(XYParserHandle handle);
}

19
XYParser/dllmain.cpp Normal file
View File

@@ -0,0 +1,19 @@
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}

5
XYParser/framework.h Normal file
View File

@@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>

5
XYParser/pch.cpp Normal file
View File

@@ -0,0 +1,5 @@
// pch.cpp: 与预编译标头对应的源文件
#include "pch.h"
// 当使用预编译的头时,需要使用此源文件,编译才能成功。

13
XYParser/pch.h Normal file
View File

@@ -0,0 +1,13 @@
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#endif //PCH_H

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.