diff --git a/Model/Services/AlgorithmServer.cs b/Model/Services/AlgorithmServer.cs new file mode 100644 index 0000000..3920f59 --- /dev/null +++ b/Model/Services/AlgorithmServer.cs @@ -0,0 +1,76 @@ +using Newtonsoft.Json; +using System; +using System.Net.Http; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Input; +using HandyControl.Controls; +using HandyControl.Tools; +using Newtonsoft.Json.Linq; +using MessageBox = HandyControl.Controls.MessageBox; + +namespace SparkClient.Model.Services +{ + public class AlgorithmServer + { + // 使用 P/Invoke 声明 C++ 函数 + [DllImport("D:/workspace/dayuAI/SparkClient/Libs/AlgorithmServer.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern double Add(double a, double b); + + [DllImport("D:D:/workspace/dayuAI/SparkClient/Libs/AlgorithmServer.dll", CallingConvention = CallingConvention.Cdecl)] + private static extern double Multiply(double a, double b); + + // 导入 C++ DLL 中的 ParseJsonAndReturnActions 函数 + [DllImport("D:/workspace/dayuAI/SparkClient/Libs/AlgorithmServer.dll", CharSet = CharSet.Ansi, CallingConvention = CallingConvention.Cdecl)] + private static extern IntPtr ParseJsonAndReturnActions(byte[] jsonData); + + + public AlgorithmServer() + { + + // 调用 C++ 函数进行加法运算 + double resultAdd = Add(3.5, 4.5); + MessageBox.Show($"Add(3.5, 4.5) = {resultAdd}"); + + // 调用 C++ 函数进行乘法运算 + double resultMultiply = Multiply(3.5, 4.5); + MessageBox.Show($"Multiply(3.5, 4.5) = {resultMultiply}"); + + // 测试 JSON 数据 + // 定义并初始化 JObject + JObject jsonData = new JObject( + new JProperty("dataAttributes", new JArray( + new JObject(new JProperty("name", "你好")), + new JObject(new JProperty("name", "hello")), + new JObject(new JProperty("name", "666")), + new JObject(new JProperty("name", "是的")), + new JObject(new JProperty("name", "确认")) + )) + ); + + // 将 JObject 序列化为字符串 + string jsonDataString = jsonData.ToString(); + + // 将 JSON 字符串转换为字节数组 + byte[] jsonDataBytes = System.Text.Encoding.UTF8.GetBytes(jsonDataString); + + // 调用 C++ DLL 函数解析 JSON + IntPtr resultPtr = ParseJsonAndReturnActions(jsonDataBytes); + + // 检查返回的指针是否为空 + if (resultPtr != IntPtr.Zero) + { + string? result = Marshal.PtrToStringAnsi(resultPtr); + MessageBox.Show(result, "解析结果", MessageBoxButton.OK, MessageBoxImage.Information); + + } + else + { + MessageBox.Show("解析结果为空", "解析结果", MessageBoxButton.OK, MessageBoxImage.Warning); + } + } + } + +} diff --git a/Model/Services/SOCClientService.cs b/Model/Services/SOCClientService.cs new file mode 100644 index 0000000..dffd822 --- /dev/null +++ b/Model/Services/SOCClientService.cs @@ -0,0 +1,88 @@ +using Newtonsoft.Json; +using System; +using System.Net.Http; +using System.Text; +using System.Threading.Tasks; + +namespace SparkClient.Model.Services +{ + public class SOCClientService + { + private readonly string _baseUrl; + private readonly string _authToken; + + public SOCClientService(string baseUrl, string authToken) + { + _baseUrl = baseUrl; + _authToken = authToken; + } + + // 通用GET请求方法 + private async Task SendGetRequestAsync(string url) + { + using (var client = new HttpClient()) + { + client.DefaultRequestHeaders.Add("Authorization", "Basic " + _authToken); + return await client.GetAsync(url); + } + } + + // 1. 启动图片收集任务 + public async Task CollectImagesAsync(int lightLevel) + { + string url = $"{_baseUrl}/collect_images?light_level={lightLevel}"; + var response = await SendGetRequestAsync(url); + + if (response.IsSuccessStatusCode) + { + var jsonResponse = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject(jsonResponse); + return result.Status; + } + else + { + return "Error: " + response.StatusCode; + } + } + + // 2. 获取图片 + public async Task RetrieveImageAsync(int index) + { + string url = $"{_baseUrl}/retrieve_image/{index}"; + var response = await SendGetRequestAsync(url); + + if (response.IsSuccessStatusCode) + { + return await response.Content.ReadAsByteArrayAsync(); + } + else + { + throw new Exception("Error retrieving image: " + response.StatusCode); + } + } + + // 3. 获取采集状态 + public async Task CollectStatusAsync() + { + string url = $"{_baseUrl}/collect_status"; + var response = await SendGetRequestAsync(url); + + if (response.IsSuccessStatusCode) + { + var jsonResponse = await response.Content.ReadAsStringAsync(); + var result = JsonConvert.DeserializeObject(jsonResponse); + return result.Status; + } + else + { + return "Error: " + response.StatusCode; + } + } + } + + public class ResponseStatus + { + public string Status { get; set; } + public string Message { get; set; } + } +} diff --git a/Resource/Document/开发规约.md b/Resource/Document/开发规约.md new file mode 100644 index 0000000..534066a --- /dev/null +++ b/Resource/Document/开发规约.md @@ -0,0 +1,172 @@ +# 开发规约 +## 一、总体架构与设计原则 +### (一)MVVM 架构概述 +在软件开发中,为实现良好代码结构、可维护性与可扩展性,应分离视图逻辑与业务逻辑,MVVM 设计模式是有效的解决方案。 +View(视图层):包含用户界面控件与 XAML 元素,是用户与应用交互界面,负责展示数据和接收用户操作。应保持简洁,避免复杂业务逻辑,依据 ViewModel 数据展示,并将用户操作传递给 ViewModel 处理。 +ViewModel(视图模型层):是视图层和模型层的桥梁,处理控件数据绑定,将视图层控件属性与自身属性绑定实现数据双向流动,还处理视图交互逻辑,调用模型层方法完成业务功能并反馈结果给视图层。 +Model(模型层):处理数据和业务逻辑,包括数据获取、存储、处理和验证。对于复杂数据关系,可借助 ORM 技术或自定义数据结构;简单数据存储用普通类和属性。模型层独立于视图层和视图模型层,保证可复用性。 +通过将控件属性绑定到 ViewModel,再由 ViewModel 与 Model 交互,实现数据和逻辑在各层有效流转,提高应用可维护性和扩展性。 + +## 二、具体实现细节 +### (一)ViewModel +### 1. 命令定义与实现 +#### (1)命名规范 +ViewModel 中的命令应采用 “动词 + 名词 + Command” 格式命名,清晰表明功能。对应的执行方法命名与命令中的动词一致,便于理解命令与执行逻辑的关联。 +#### (2)代码位置 +与视图交互的命令在对应的 ViewModel 类中定义和实现,ViewModel 类放在 “ViewModels” 文件夹下。大型项目中,按功能模块细分该文件夹,如 3D 模型相关的 ViewModel 放在 “ViewModels/3DModelViewModels” 文件夹。 +#### (3)实现方式 +使用合适的命令类实现:建议用实现 ICommand 接口的命令类,如自定义的 “RelayCommand”。它要处理执行逻辑和可执行状态判断,依据条件(如数据准备情况、用户权限)决定命令是否可执行。 +命令初始化与方法绑定:在 ViewModel 构造函数中初始化命令并与执行方法绑定,如 “LoadModelCommand = new RelayCommand (LoadModel);”,确保命令触发时调用正确执行方法。 +参数处理:命令执行需传递参数时,命令类要能正确处理参数传递和接收。修改命令类构造函数或执行方法以接受参数,在视图层绑定命令时设置参数值。 +### 2. 数据绑定属性 +#### (1)命名规范 +数据绑定属性名称用驼峰命名法,清晰反映绑定的数据内容,如 “DisplayName” 绑定文本框显示文本,“DataList” 绑定列表数据。 +#### (2)数据类型选择 +根据绑定控件需求和数据性质选择合适数据类型。简单文本显示控件用 “string” 类型;列表控件用 “ObservableCollection” 等集合类型,实现数据更新自动刷新。 +#### (3)通知机制 +数据绑定属性值变化时,用合适机制通知视图更新。在支持数据绑定的框架(如 WPF)中,可利用相关接口(如 WPF 的 “INotifyPropertyChanged” 接口)。示例如下: +```csharp +private string _displayName; +public string DisplayName +{ + get { return _displayName; } + set + { + _displayName = value; + OnPropertyChanged(nameof(DisplayName)); + } +} +``` +属性 “set” 方法调用时,通过 “OnPropertyChanged” 方法通知视图层属性值变化,促使控件更新显示内容。 +### 3. 事件处理与通知 +#### (1)事件命名 +ViewModel 中通知视图的事件命名采用 “名词 + 过去分词” 形式,表明动作完成或状态改变,如 “ModelLoaded” 表示模型加载完成,便于理解事件含义和业务逻辑状态。 +#### (2)事件参数类型 +事件参数类型准确反映事件携带的信息。如 “ModelLoaded” 事件传递加载后的模型对象,参数类型为 “Model3D”。若事件需传递多个信息,创建自定义事件参数类封装信息。 +#### (3)事件触发 +业务逻辑完成后(如模型加载成功、数据保存完成),在合适位置触发事件,用标准事件触发机制,如 “ModelLoaded?.Invoke (model);”。触发前确保事件初始化和订阅正确,避免空引用等问题。 + + +## (二)View +### 1. XAML 结构与布局 +#### (1)命名空间引用 +XAML 文件开头只引入实际使用的命名空间,按字母顺序排序,减少复杂性、提高编译速度,如: +.xaml +```csharp + +``` +#### (2)布局设计原则 +选择合适的布局容器:优先用合适布局容器实现灵活可维护布局。简单水平或垂直排列控件用 “StackPanel”,通过 “Orientation” 属性控制排列方向。需精确行列布局用 “Grid”,通过定义行和列定位控件。 +避免绝对坐标定位:尽量不用绝对坐标定位控件,以免界面在不同分辨率和窗口大小下适应性差。使用布局容器可实现自适应,保证良好显示效果。 +#### (3)控件命名 +为重要控件命名,采用驼峰命名法且以控件类型开头,如 “btnLoadModel”(加载模型按钮)、“txtInputFileName”(输入文件名文本框),提高代码可读性。复杂界面可分组控件并命名,方便在代码 - behind 中访问和操作,如 “LoginControls” 包含登录相关控件。 +### 2. 数据绑定与命令绑定 +#### (1)绑定表达式规范 +数据绑定表达式:数据绑定用 “{Binding Path=PropertyName}” 格式,“Path” 可省略(绑定当前数据上下文直接属性时)。绑定深层属性需指定 “Path” 值,如: +.xaml +```csharp + + +``` +命令绑定表达式:命令绑定用 “{Binding CommandName}” 格式,确保命令在 ViewModel 数据上下文中存在,如: +.xaml +```csharp +