666
This commit is contained in:
237
CSharp调用说明.md
Normal file
237
CSharp调用说明.md
Normal 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.
BIN
XYParser/.vs/XYParser/v17/.suo
Normal file
BIN
XYParser/.vs/XYParser/v17/.suo
Normal file
Binary file not shown.
BIN
XYParser/.vs/XYParser/v17/Browse.VC.db
Normal file
BIN
XYParser/.vs/XYParser/v17/Browse.VC.db
Normal file
Binary file not shown.
BIN
XYParser/.vs/XYParser/v17/Browse.VC.db-shm
Normal file
BIN
XYParser/.vs/XYParser/v17/Browse.VC.db-shm
Normal file
Binary file not shown.
0
XYParser/.vs/XYParser/v17/Browse.VC.db-wal
Normal file
0
XYParser/.vs/XYParser/v17/Browse.VC.db-wal
Normal file
BIN
XYParser/.vs/XYParser/v17/Browse.VC.opendb
Normal file
BIN
XYParser/.vs/XYParser/v17/Browse.VC.opendb
Normal file
Binary file not shown.
171
XYParser/.vs/XYParser/v17/DocumentLayout.backup.json
Normal file
171
XYParser/.vs/XYParser/v17/DocumentLayout.backup.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
171
XYParser/.vs/XYParser/v17/DocumentLayout.json
Normal file
171
XYParser/.vs/XYParser/v17/DocumentLayout.json
Normal 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"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
BIN
XYParser/.vs/XYParser/v17/Solution.VC.db
Normal file
BIN
XYParser/.vs/XYParser/v17/Solution.VC.db
Normal file
Binary file not shown.
BIN
XYParser/.vs/XYParser/v17/ipch/da131652d6dd7f4a.ipch
Normal file
BIN
XYParser/.vs/XYParser/v17/ipch/da131652d6dd7f4a.ipch
Normal file
Binary file not shown.
2
XYParser/XYEegParser64.cpp
Normal file
2
XYParser/XYEegParser64.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "pch.h"
|
||||
#include "XYEegParser64.h"
|
||||
10
XYParser/XYEegParser64.h
Normal file
10
XYParser/XYEegParser64.h
Normal 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;
|
||||
};
|
||||
2
XYParser/XYEegParser8.cpp
Normal file
2
XYParser/XYEegParser8.cpp
Normal file
@@ -0,0 +1,2 @@
|
||||
#include "pch.h"
|
||||
#include "XYEegParser8.h"
|
||||
10
XYParser/XYEegParser8.h
Normal file
10
XYParser/XYEegParser8.h
Normal 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;
|
||||
};
|
||||
266
XYParser/XYEegParserCommon.h
Normal file
266
XYParser/XYEegParserCommon.h
Normal 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
|
||||
112
XYParser/XYEegParserExample.cpp
Normal file
112
XYParser/XYEegParserExample.cpp
Normal 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
31
XYParser/XYParser.sln
Normal 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
161
XYParser/XYParser.vcxproj
Normal 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>
|
||||
57
XYParser/XYParser.vcxproj.filters
Normal file
57
XYParser/XYParser.vcxproj.filters
Normal 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>
|
||||
BIN
XYParser/XYParser/x64/Debug/XYEegParser64.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/XYEegParser64.obj
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYEegParser8.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/XYEegParser8.obj
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYEegParserExample.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/XYEegParserExample.obj
Normal file
Binary file not shown.
11
XYParser/XYParser/x64/Debug/XYParser.dll.recipe
Normal file
11
XYParser/XYParser/x64/Debug/XYParser.dll.recipe
Normal 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>
|
||||
BIN
XYParser/XYParser/x64/Debug/XYParser.ilk
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.ilk
Normal file
Binary file not shown.
9
XYParser/XYParser/x64/Debug/XYParser.log
Normal file
9
XYParser/XYParser/x64/Debug/XYParser.log
Normal 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
|
||||
BIN
XYParser/XYParser/x64/Debug/XYParser.pch
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.pch
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.command.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.command.1.tlog
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.read.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.read.1.tlog
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.write.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/CL.write.1.tlog
Normal file
Binary file not shown.
6
XYParser/XYParser/x64/Debug/XYParser.tlog/Cl.items.tlog
Normal file
6
XYParser/XYParser/x64/Debug/XYParser.tlog/Cl.items.tlog
Normal 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
|
||||
@@ -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\|
|
||||
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.command.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.command.1.tlog
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.read.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.read.1.tlog
Normal file
Binary file not shown.
@@ -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
|
||||
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.write.1.tlog
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParser.tlog/link.write.1.tlog
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/XYParserApi.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/XYParserApi.obj
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/dllmain.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/dllmain.obj
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/pch.obj
Normal file
BIN
XYParser/XYParser/x64/Debug/pch.obj
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/vc143.idb
Normal file
BIN
XYParser/XYParser/x64/Debug/vc143.idb
Normal file
Binary file not shown.
BIN
XYParser/XYParser/x64/Debug/vc143.pdb
Normal file
BIN
XYParser/XYParser/x64/Debug/vc143.pdb
Normal file
Binary file not shown.
154
XYParser/XYParserApi.cpp
Normal file
154
XYParser/XYParserApi.cpp
Normal 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
42
XYParser/XYParserApi.h
Normal 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
19
XYParser/dllmain.cpp
Normal 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
5
XYParser/framework.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
|
||||
// Windows 头文件
|
||||
#include <windows.h>
|
||||
5
XYParser/pch.cpp
Normal file
5
XYParser/pch.cpp
Normal file
@@ -0,0 +1,5 @@
|
||||
// pch.cpp: 与预编译标头对应的源文件
|
||||
|
||||
#include "pch.h"
|
||||
|
||||
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
|
||||
13
XYParser/pch.h
Normal file
13
XYParser/pch.h
Normal file
@@ -0,0 +1,13 @@
|
||||
// pch.h: 这是预编译标头文件。
|
||||
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
|
||||
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
|
||||
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
|
||||
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
|
||||
|
||||
#ifndef PCH_H
|
||||
#define PCH_H
|
||||
|
||||
// 添加要在此处预编译的标头
|
||||
#include "framework.h"
|
||||
|
||||
#endif //PCH_H
|
||||
BIN
XYParser/x64/Debug/XYParser.dll
Normal file
BIN
XYParser/x64/Debug/XYParser.dll
Normal file
Binary file not shown.
BIN
XYParser/x64/Debug/XYParser.exp
Normal file
BIN
XYParser/x64/Debug/XYParser.exp
Normal file
Binary file not shown.
BIN
XYParser/x64/Debug/XYParser.lib
Normal file
BIN
XYParser/x64/Debug/XYParser.lib
Normal file
Binary file not shown.
BIN
XYParser/x64/Debug/XYParser.pdb
Normal file
BIN
XYParser/x64/Debug/XYParser.pdb
Normal file
Binary file not shown.
Reference in New Issue
Block a user