按照软件设计文档开始重构代码01
This commit is contained in:
216
软件设计文档/原始文档/06-实现步骤与示例.md
Normal file
216
软件设计文档/原始文档/06-实现步骤与示例.md
Normal file
@@ -0,0 +1,216 @@
|
||||
# 软件开发文档 - 06. 实现步骤与示例
|
||||
|
||||
本文档提供关键功能的具体实现步骤和代码示例,以指导开发过程。
|
||||
|
||||
## 1. 添加新设备 (End-to-End)
|
||||
|
||||
这是一个从UI到数据库的完整流程示例。
|
||||
|
||||
**步骤 1: View (`DeviceListView.xaml`)**
|
||||
|
||||
用户点击“添加”按钮,该按钮绑定到ViewModel的 `AddDeviceCommand`。
|
||||
|
||||
```xml
|
||||
<Button Content="添加设备" Command="{Binding AddDeviceCommand}" />
|
||||
```
|
||||
|
||||
**步骤 2: ViewModel (`DeviceListViewModel.cs`)**
|
||||
|
||||
`AddDeviceCommand` 执行,调用对话框服务来显示一个用于输入新设备信息的窗口。
|
||||
|
||||
```csharp
|
||||
public ICommand AddDeviceCommand { get; }
|
||||
|
||||
public DeviceListViewModel(IDeviceAppService deviceAppService, IDialogService dialogService)
|
||||
{
|
||||
_deviceAppService = deviceAppService;
|
||||
_dialogService = dialogService;
|
||||
AddDeviceCommand = new RelayCommand(async () => await ExecuteAddDevice());
|
||||
}
|
||||
|
||||
private async Task ExecuteAddDevice()
|
||||
{
|
||||
var vm = new AddDeviceDialogViewModel(); // 一个专门用于对话框的ViewModel
|
||||
var result = await _dialogService.ShowDialogAsync(vm);
|
||||
|
||||
if (result != null) // 假设对话框返回创建的DTO
|
||||
{
|
||||
await _deviceAppService.CreateDeviceAsync(result);
|
||||
await LoadDevicesAsync(); // 刷新列表
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 3: Application Service (`DeviceAppService.cs`)**
|
||||
|
||||
应用服务接收DTO,将其映射为领域模型,并调用仓储来保存。
|
||||
|
||||
```csharp
|
||||
public async Task CreateDeviceAsync(CreateDeviceDto deviceDto)
|
||||
{
|
||||
// 参数校验
|
||||
if (string.IsNullOrWhiteSpace(deviceDto.Name) || string.IsNullOrWhiteSpace(deviceDto.IpAddress))
|
||||
{
|
||||
throw new ArgumentException("设备名称和IP地址不能为空");
|
||||
}
|
||||
|
||||
var device = _mapper.Map<Device>(deviceDto);
|
||||
device.IsActive = true; // 设置默认值
|
||||
|
||||
await _deviceRepository.AddAsync(device);
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 4: Repository (`DeviceRepository.cs`)**
|
||||
|
||||
仓储将领域模型映射为数据库实体,并使用 `SqlSugar` 将其插入数据库。
|
||||
|
||||
```csharp
|
||||
public async Task AddAsync(Device entity)
|
||||
{
|
||||
var dbEntity = _mapper.Map<DbDevice>(entity);
|
||||
await _db.Insertable(dbEntity).ExecuteCommandAsync();
|
||||
}
|
||||
```
|
||||
|
||||
## 2. S7数据采集后台服务
|
||||
|
||||
后台服务将轮询所有激活的S7设备,读取变量值,并启动数据处理链。
|
||||
|
||||
**步骤 1: 创建后台服务 (`S7BackgroundService.cs`)**
|
||||
|
||||
这个服务应该是一个长时间运行的服务,可以使用 `IHostedService` (如果项目是.NET Core) 或一个简单的 `Task.Run` 循环。
|
||||
|
||||
```csharp
|
||||
// 文件: DMS.WPF/Services/S7BackgroundService.cs
|
||||
public class S7BackgroundService : IDisposable
|
||||
{
|
||||
private readonly IDeviceRepository _deviceRepo;
|
||||
private readonly IS7CommunicationService _s7Service;
|
||||
private readonly DataAcquisitionService _acquisitionService; // 包含处理链逻辑的服务
|
||||
private CancellationTokenSource _cts;
|
||||
|
||||
public S7BackgroundService(IDeviceRepository deviceRepo, IS7CommunicationService s7Service, DataAcquisitionService acquisitionService)
|
||||
{
|
||||
// ... 注入依赖
|
||||
}
|
||||
|
||||
public void Start()
|
||||
{
|
||||
_cts = new CancellationTokenSource();
|
||||
Task.Run(async () => await ExecuteAsync(_cts.Token));
|
||||
}
|
||||
|
||||
protected async Task ExecuteAsync(CancellationToken stoppingToken)
|
||||
{
|
||||
while (!stoppingToken.IsCancellationRequested)
|
||||
{
|
||||
var activeDevices = await _deviceRepo.GetActiveDevicesWithDetailsAsync();
|
||||
|
||||
foreach (var device in activeDevices.Where(d => d.Protocol == ProtocolType.S7))
|
||||
{
|
||||
await _s7Service.ConnectAsync(device); // 确保连接
|
||||
|
||||
foreach (var table in device.VariableTables.Where(t => t.IsActive))
|
||||
{
|
||||
foreach (var variable in table.Variables.Where(v => v.IsActive))
|
||||
{
|
||||
try
|
||||
{
|
||||
var value = await _s7Service.ReadVariableAsync(device, variable);
|
||||
// 触发数据处理链
|
||||
await _acquisitionService.OnDataReceived(variable, value);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// Log error
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
await Task.Delay(1000, stoppingToken); // 轮询间隔
|
||||
}
|
||||
}
|
||||
|
||||
public void Stop()
|
||||
{
|
||||
_cts?.Cancel();
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 2: 在 `App.xaml.cs` 中启动服务**
|
||||
|
||||
在应用程序启动时,从DI容器获取服务实例并启动它。
|
||||
|
||||
```csharp
|
||||
// 在 App.xaml.cs OnStartup
|
||||
var s7Service = _serviceProvider.GetRequiredService<S7BackgroundService>();
|
||||
s7Service.Start();
|
||||
```
|
||||
|
||||
## 3. 菜单导航实现
|
||||
|
||||
**步骤 1: `MainViewModel`**
|
||||
|
||||
```csharp
|
||||
public class MainViewModel : BaseViewModel
|
||||
{
|
||||
private readonly INavigationService _navigationService;
|
||||
public ICommand NavigateCommand { get; }
|
||||
|
||||
public MainViewModel(INavigationService navigationService)
|
||||
{
|
||||
_navigationService = navigationService;
|
||||
NavigateCommand = new RelayCommand<Type>(OnNavigate);
|
||||
}
|
||||
|
||||
private void OnNavigate(Type viewModelType)
|
||||
{
|
||||
_navigationService.NavigateTo(viewModelType);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 2: `NavigationService`**
|
||||
|
||||
这个服务负责实际的ViewModel切换,它会更新 `MainViewModel` 的 `CurrentViewModel` 属性。
|
||||
|
||||
```csharp
|
||||
public class NavigationService : INavigationService
|
||||
{
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly MainViewModel _mainViewModel; // 持有MainViewModel的引用
|
||||
|
||||
public NavigationService(IServiceProvider serviceProvider, MainViewModel mainViewModel)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_mainViewModel = mainViewModel;
|
||||
}
|
||||
|
||||
public void NavigateTo(Type viewModelType)
|
||||
{
|
||||
var viewModel = (BaseViewModel)_serviceProvider.GetRequiredService(viewModelType);
|
||||
_mainViewModel.CurrentViewModel = viewModel;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**步骤 3: View (`MainWindow.xaml`)**
|
||||
|
||||
`ContentControl` 绑定到 `CurrentViewModel`,并使用 `DataTemplate` 来根据ViewModel的类型选择对应的View。
|
||||
|
||||
```xml
|
||||
<ContentControl Content="{Binding CurrentViewModel}">
|
||||
<ContentControl.Resources>
|
||||
<DataTemplate DataType="{x:Type vm:DashboardViewModel}">
|
||||
<views:DashboardView />
|
||||
</DataTemplate>
|
||||
<DataTemplate DataType="{x:Type vm:DeviceListViewModel}">
|
||||
<views:DeviceListView />
|
||||
</DataTemplate>
|
||||
<!-- 其他模板 -->
|
||||
</ContentControl.Resources>
|
||||
</ContentControl>
|
||||
```
|
||||
Reference in New Issue
Block a user