使用新框架与技术代替旧框架与技术,实现涂装车间后道标签扫码程序

This commit is contained in:
2025-05-14 13:32:38 +08:00
parent 91cd88e285
commit 289e1e84ec
243 changed files with 4368 additions and 49365 deletions

View File

@@ -0,0 +1,42 @@
using Prism.Ioc;
using Prism.Modularity;
using Prism.Regions;
using RIZO_Application.Core;
using RIZO_Application.Modules.ModuleName.Views;
using RIZO_Application.Services.Interfaces;
using RIZO_Application.Services;
using System.Reflection;
using RIZO_Application.Core.Infrastructure.CustomExtensions;
namespace RIZO_Application.Modules.ModuleName
{
public class ModuleNameModule : IModule
{
private readonly IRegionManager _regionManager;
public ModuleNameModule(IRegionManager regionManager)
{
_regionManager = regionManager;
}
public void OnInitialized(IContainerProvider containerProvider)
{
_regionManager.RequestNavigate(RegionNames.ContentRegion, "ViewA");
_regionManager.RequestNavigate(RegionNames.MqttRegion, "MqttControl");
_regionManager.RequestNavigate(RegionNames.ScanRegion, "ScanControl");
_regionManager.RequestNavigate(RegionNames.PrintRegion, "PrintControl");
}
public void RegisterTypes(IContainerRegistry containerRegistry)
{
// 自动注册当前程序集中所有带 [AppService] 的类
containerRegistry.AutoRegisterServices(Assembly.GetExecutingAssembly());
// 自动注册当前程序集的所有带 [AutoRegisterView] 的视图
containerRegistry.AutoRegisterViews(Assembly.GetExecutingAssembly());
// containerRegistry.RegisterSingleton<IMessageService, MessageService>();
//containerRegistry.RegisterForNavigation<ViewA>();
}
}
}

View File

@@ -0,0 +1,25 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<UseWPF>true</UseWPF>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Prism.Wpf" Version="8.1.97" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\RIZO_Application.Models\RIZO_Application.Models.csproj" />
<ProjectReference Include="..\..\..\RIZO_Application.Repository\RIZO_Application.Repository.csproj" />
<ProjectReference Include="..\..\RIZO_Application.Core\RIZO_Application.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Compile Update="Views\MqttControl.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\PrintControl.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\ScanControl.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
</Project>

View File

@@ -0,0 +1,7 @@
namespace RIZO_Application.Services.Interfaces
{
public interface IMessageService
{
string GetMessage();
}
}

View File

@@ -0,0 +1,18 @@
using RIZO_Application.Infrastructure.CustomAttribute;
using RIZO_Application.Infrastructure.Model;
using RIZO_Application.Models;
using RIZO_Application.Repository;
using RIZO_Application.Services.Interfaces;
namespace RIZO_Application.Services
{
[AppService(ServiceType = typeof(IMessageService), Lifetime = ServiceLifetime.Transient)]
public class MessageService : BaseRepository<User> ,IMessageService
{
public string GetMessage()
{
return AppSettings.Current.AppName;
}
}
}

View File

@@ -0,0 +1,180 @@
using System;
using System.Collections.Generic;
using System.Text.Json;
using System.Threading.Tasks;
using MQTTnet.Client;
using Prism.Events;
using Prism.Regions;
using RIZO_Application.Core;
using RIZO_Application.Core.Mvvm;
using RIZO_Application.Infrastructure.Model;
using RIZO_Helper.Tools;
namespace RIZO_Application.Modules.ModuleName.ViewModels
{
public class MqttControlViewModel : RegionViewModelBase
{
private readonly IEventAggregator _eventAggregator;
private MqttHelper? _mqttHelper;
private SubscriptionToken _token;
public MqttControlViewModel(
IRegionManager regionManager,
IEventAggregator eventAggregator)
: base(regionManager)
{
_eventAggregator = eventAggregator;
_token = _eventAggregator.GetEvent<ScanEvent>().Subscribe(OnScanEventReceived, ThreadOption.UIThread, true);
InitializeMqttAsync().ConfigureAwait(false);
}
private async Task InitializeMqttAsync()
{
try
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"Mqtt初始化……");
string serverUrl = GetMqttConfigValue(MqttConfigs.Current?.ServerUrl);
string clientId = GetMqttConfigValue(MqttConfigs.Current?.ClientId);
_mqttHelper = new MqttHelper(serverUrl, 1883, clientId);
_mqttHelper.MessageReceived += HandleMqttMessage;
_mqttHelper.ConnectionFailed += HandleMqttConnectionFailed;
if (await ConnectMqttAsync())
{
await SubscribeToTopicsAsync();
await PublishInitialMessageAsync();
}
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"Mqtt初始化完成");
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"Mqtt初始化失败: {ex.Message}");
}
}
private string GetMqttConfigValue(string? configValue)
{
return configValue ?? "noConfig";
}
private async Task<bool> ConnectMqttAsync()
{
if (_mqttHelper == null) return false;
bool isConnected = await _mqttHelper.ConnectAsync();
if (!isConnected)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"MQTT连接失败");
}
return isConnected;
}
private async Task SubscribeToTopicsAsync()
{
if (_mqttHelper == null || MqttConfigs.Current == null || SiteConfigs.Current == null) return;
string printTopic = $"{MqttConfigs.Current.Topic}/print/{SiteConfigs.Current.SiteName}";
if (await _mqttHelper.SubscribeAsync(printTopic))
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"订阅:{printTopic}");
}
}
private async Task PublishInitialMessageAsync()
{
if (_mqttHelper == null || MqttConfigs.Current == null || SiteConfigs.Current == null) return;
string connectTopic = $"{MqttConfigs.Current.Topic}/connect/{SiteConfigs.Current.SiteName}";
if (await _mqttHelper.PublishAsync(connectTopic, "已连接"))
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"发布连接消息到: {connectTopic}");
}
}
public void HandleMqttMessage(object sender, MqttApplicationMessageReceivedEventArgs e)
{
try
{
string topic = e.ApplicationMessage.Topic;
string payload = System.Text.Encoding.UTF8.GetString(e.ApplicationMessage.Payload);
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"收到信息'{topic}': {payload}");
if (MqttConfigs.Current == null || SiteConfigs.Current == null) return;
// 处理mqtt打印消息
if (topic == $"{MqttConfigs.Current.Topic}/print/{SiteConfigs.Current.SiteName}")
{
var printMessage = ParsePrintMessage(payload);
if (printMessage != null)
{
_eventAggregator.GetEvent<PrintEvent>().Publish(printMessage);
}
}
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"处理MQTT消息时出错: {ex.Message}");
}
}
private PrintDto? ParsePrintMessage(string payload)
{
try
{
var jsonData = JsonSerializer.Deserialize<PrintDto>(payload);
return jsonData;
}
catch (JsonException ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"JSON 解析错误: {ex.Message}");
return null;
}
}
private void HandleMqttConnectionFailed(object sender, Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"MQTT连接失败: {ex.Message}");
}
private async void OnScanEventReceived(string message)
{
if (_mqttHelper == null || MqttConfigs.Current == null || SiteConfigs.Current == null || SerialConfigs.Current == null) return;
try
{
var jsonObject = new
{
SiteNo = SiteConfigs.Current.SiteName,
ComNo = SerialConfigs.Current.ComName,
Time = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
LabelCode = message
};
string jsonMessage = JsonSerializer.Serialize(jsonObject);
string topic = $"{MqttConfigs.Current.Topic}/SiteComLabelCode";
if (await _mqttHelper.PublishAsync(topic, jsonMessage))
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"发布扫描消息到: {topic}");
}
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"发布扫描消息时出错: {ex.Message}");
}
}
public async void Destroy()
{
_token?.Dispose();
if (_mqttHelper != null)
{
await _mqttHelper.DisconnectAsync();
_mqttHelper.Dispose();
}
}
}
}

View File

@@ -0,0 +1,144 @@
using linesider_screen_tool;
using System.Collections.Generic;
using System;
using Prism.Events;
using Prism.Regions;
using RIZO_Application.Core;
using RIZO_Application.Core.Mvvm;
using RIZO_Helper.Tools;
using RIZO_Application.Infrastructure.Model;
namespace RIZO_Application.Modules.ModuleName.ViewModels
{
public class PrintControlViewModel : RegionViewModelBase, IDisposable
{
private readonly IEventAggregator _eventAggregator;
private readonly BartenderPrintHelper _printHelper;
private SubscriptionToken _printEventToken;
private bool _isDisposed;
public PrintControlViewModel(
IRegionManager regionManager,
IEventAggregator eventAggregator,
BartenderPrintHelper printHelper)
: base(regionManager)
{
_eventAggregator = eventAggregator;
_printHelper = printHelper;
Initialize();
}
private void Initialize()
{
try
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印模块初始化开始");
// 订阅打印事件使用UI线程处理保持强引用
_printEventToken = _eventAggregator.GetEvent<PrintEvent>()
.Subscribe(OnPrintRequested, ThreadOption.UIThread, true);
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印模块初始化完成");
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印模块初始化失败: {ex.Message}");
throw;
}
}
private void OnPrintRequested(PrintDto printDto)
{
// 判断是否是打印主站
bool isPrintMain = SiteConfigs.Current.IsPrintMain.Value;
if (!isPrintMain)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印请求被忽略:此站点非打印主站");
return;
}
string siteNo = SiteConfigs.Current.SiteName;
if(printDto.SiteNo != siteNo)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印请求被忽略:站点号不匹配{printDto.SiteNo}-{siteNo}");
return;
}
if (_isDisposed)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印请求被忽略:打印资源已释放");
return;
}
try
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"收到打印请求: {printDto.Name}");
// 准备打印参数
var printParameters = new Dictionary<string, string>
{
{ "10", printDto.PartNumber },
{ "11", "02S" },
{ "10000", printDto.WorkOrder },
{ "10002", printDto.Team },
{ "10003", printDto.Sort.ToString() },
{ "10004", printDto.BatchCode },
{ "10005", printDto.Sort.ToString() },
{ "10006", printDto.BatchCode },
{ "10007", printDto.PackageNum.ToString() },
{ "10011", printDto.LabelType.ToString() }
};
// 执行打印
bool printSuccess = _printHelper.PrintLabel(
templatePath: printDto.Path,
subStringValues: printParameters,
copies: 1);
if (printSuccess)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印成功: {printDto.Name}");
}
else
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印失败: {printDto.Name}");
}
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"打印过程中发生错误: {ex.Message}");
}
}
public void Destroy()
{
Dispose();
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
if (disposing)
{
// 释放托管资源
_printEventToken?.Dispose();
_printHelper?.Dispose();
}
}
~PrintControlViewModel()
{
Dispose(false);
}
}
}

View File

@@ -0,0 +1,164 @@
using System;
using System.Threading.Tasks;
using System.Windows.Media;
using Prism.Events;
using Prism.Regions;
using RIZO_Application.Core;
using RIZO_Application.Core.Mvvm;
using RIZO_Application.Infrastructure.Model;
using RIZO_Helper.Tools;
using System.Windows;
using System.IO.Ports;
namespace RIZO_Application.Modules.ModuleName.ViewModels
{
public class ScanControlViewModel : RegionViewModelBase, IDisposable
{
private readonly IEventAggregator _eventAggregator;
private ComScanHelper _scanHelper;
private bool _isDisposed;
private bool _isConnected;
// 串口连接状态属性
public bool IsConnected
{
get => _isConnected;
set
{
if (SetProperty(ref _isConnected, value))
{
// 通知依赖属性更新
RaisePropertyChanged(nameof(ConnectionStatusText));
RaisePropertyChanged(nameof(ConnectionStatusForeground));
}
}
}
// 串口连接状态文字描述
public string ConnectionStatusText
{
get => IsConnected ? "扫码枪已连接" : "扫码枪未连接";
}
// 串口连接状态文字颜色
public Brush ConnectionStatusForeground
{
get => IsConnected ? Brushes.Green : Brushes.Red;
}
public ScanControlViewModel(
IRegionManager regionManager,
IEventAggregator eventAggregator)
: base(regionManager)
{
_eventAggregator = eventAggregator;
IsConnected = false; // 初始状态为未连接
InitializeScanHelperAsync().ConfigureAwait(false);
}
private async Task InitializeScanHelperAsync()
{
try
{
await StartComScan();
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"串口初始化异常: {ex.Message}");
}
}
public async Task StartComScan()
{
if (_isDisposed)
throw new ObjectDisposedException(nameof(ScanControlViewModel));
string comName = "COM1";
int baudRate = 9600;
if (SerialConfigs.Current != null)
{
comName = SerialConfigs.Current.ComName ?? string.Empty;
baudRate = SerialConfigs.Current.BaudRate ?? 9600;
}
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"串口扫码枪初始化……串口:{comName} 波特率:{baudRate}");
// 安全地释放现有实例
await DisposeScanHelperAsync();
_scanHelper = new ComScanHelper(comName, baudRate);
_scanHelper.DataReceived += HandleMessage;
_scanHelper.ErrorOccurred += HandleError;
if (await _scanHelper.OpenAsync())
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"串口扫码初始化完成");
IsConnected = true; // 连接成功
}
else
{
MessageBox.Show($"打开串口失败");
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"串口打开失败");
IsConnected = false; // 连接失败
}
}
private void HandleError(object sender, Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"串口通信错误: {ex.Message}");
IsConnected = false; // 发生错误时断开连接
}
// 定义事件处理方法
private void HandleMessage(object sender, string labelCode)
{
// 打印接收到的消息信息
//_eventAggregator.GetEvent<SystemLogEvent>().Publish($"收到串口信息: {labelCode}");
_eventAggregator.GetEvent<ScanEvent>().Publish(labelCode);
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
private async void Dispose(bool disposing)
{
if (_isDisposed)
return;
_isDisposed = true;
if (disposing)
{
await DisposeScanHelperAsync();
}
}
private async Task DisposeScanHelperAsync()
{
if (_scanHelper != null)
{
try
{
_scanHelper.DataReceived -= HandleMessage;
_scanHelper.ErrorOccurred -= HandleError;
await _scanHelper.DisposeAsync();
}
catch (Exception ex)
{
_eventAggregator.GetEvent<SystemLogEvent>().Publish($"释放串口资源异常: {ex.Message}");
}
finally
{
_scanHelper = null;
IsConnected = false; // 释放资源后断开连接
}
}
}
~ScanControlViewModel()
{
Dispose(false);
}
}
}

View File

@@ -0,0 +1,36 @@
using Prism.Events;
using Prism.Regions;
using RIZO_Application.Core.Mvvm;
using RIZO_Application.Services.Interfaces;
namespace RIZO_Application.Modules.ModuleName.ViewModels
{
public class ViewAViewModel : RegionViewModelBase
{
private string _message;
private string[] _Datas = new string[] { "123412", "qerqwer" };
public string Message
{
get { return _message; }
set { SetProperty(ref _message, value); }
}
public string[] Datas
{
get { return _Datas; }
set { SetProperty(ref _Datas, value); }
}
private readonly IEventAggregator _eventAggregator;
public ViewAViewModel(IRegionManager regionManager, IEventAggregator eventAggregator, IMessageService messageService) :
base(regionManager)
{
_eventAggregator = eventAggregator;
Message = messageService.GetMessage();
}
public override void OnNavigatedTo(NavigationContext navigationContext)
{
//do something
}
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="RIZO_Application.Modules.ModuleName.Views.MqttControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RIZO_Application.Modules.ModuleName.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
using RIZO_Application.Infrastructure.CustomAttribute;
namespace RIZO_Application.Modules.ModuleName.Views
{
[AutoRegisterView(ViewName = "MqttControl")]
/// <summary>
/// MqttController.xaml 的交互逻辑
/// </summary>
public partial class MqttControl : UserControl
{
public MqttControl()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,12 @@
<UserControl x:Class="RIZO_Application.Modules.ModuleName.Views.PrintControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RIZO_Application.Modules.ModuleName.Views"
xmlns:prism="http://prismlibrary.com/"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid>
</Grid>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
using RIZO_Application.Infrastructure.CustomAttribute;
namespace RIZO_Application.Modules.ModuleName.Views
{
[AutoRegisterView(ViewName = "PrintControl")]
/// <summary>
/// PrintControl.xaml 的交互逻辑
/// </summary>
public partial class PrintControl : UserControl
{
public PrintControl()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,39 @@
<UserControl x:Class="RIZO_Application.Modules.ModuleName.Views.ScanControl"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:RIZO_Application.Modules.ModuleName.Views"
xmlns:prism="http://prismlibrary.com/"
xmlns:hc="https://handyorg.github.io/handycontrol"
prism:ViewModelLocator.AutoWireViewModel="True">
<Grid HorizontalAlignment="Stretch" VerticalAlignment="Stretch">
<Grid.RowDefinitions>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
</Grid.ColumnDefinitions>
<StackPanel Orientation="Horizontal" HorizontalAlignment="Center" VerticalAlignment="Center">
<!-- 状态指示灯 -->
<Ellipse Width="24" Height="24" Margin="0,0,20,0">
<Ellipse.Style>
<Style TargetType="Ellipse">
<Setter Property="Fill" Value="Red"/>
<Style.Triggers>
<DataTrigger Binding="{Binding IsConnected}" Value="True">
<Setter Property="Fill" Value="Green"/>
</DataTrigger>
</Style.Triggers>
</Style>
</Ellipse.Style>
</Ellipse>
<!-- 状态文字描述 -->
<TextBlock Text="{Binding ConnectionStatusText}"
VerticalAlignment="Center"
Foreground="{Binding ConnectionStatusForeground}"
FontSize="24"/>
</StackPanel>
</Grid>
</UserControl>

View File

@@ -0,0 +1,17 @@
using System.Windows.Controls;
using RIZO_Application.Infrastructure.CustomAttribute;
namespace RIZO_Application.Modules.ModuleName.Views
{
[AutoRegisterView(ViewName = "ScanControl")]
/// <summary>
/// ScanControl.xaml 的交互逻辑
/// </summary>
public partial class ScanControl : UserControl
{
public ScanControl()
{
InitializeComponent();
}
}
}

View File

@@ -0,0 +1,21 @@
<UserControl x:Class="RIZO_Application.Modules.ModuleName.Views.ViewA"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:local="clr-namespace:RIZO_Application.Modules.ModuleName.Views"
xmlns:prism="http://prismlibrary.com/"
xmlns:hc="https://handyorg.github.io/handycontrol"
xmlns:system="clr-namespace:System;assembly=netstandard"
xmlns:core="clr-namespace:RIZO_Application.Core;assembly=RIZO_Application.Core"
prism:ViewModelLocator.AutoWireViewModel="True" >
<StackPanel Orientation="Vertical">
<GroupBox HorizontalAlignment="Stretch" Header="扫码枪功能区"
Padding="10" Margin="16" >
<ContentControl prism:RegionManager.RegionName="{x:Static core:RegionNames.ScanRegion}" />
</GroupBox>
<GroupBox HorizontalAlignment="Stretch" Header="打印功能区"
Padding="10" Margin="16"></GroupBox>
</StackPanel>
</UserControl>

View File

@@ -0,0 +1,17 @@
using RIZO_Application.Infrastructure.CustomAttribute;
using System.Windows.Controls;
namespace RIZO_Application.Modules.ModuleName.Views
{
/// <summary>
/// Interaction logic for ViewA.xaml
/// </summary>
[AutoRegisterView(ViewName = "ViewA")]
public partial class ViewA : UserControl
{
public ViewA()
{
InitializeComponent();
}
}
}