using System.Configuration; using System.Data; using System.IO; using System.Text; using System.Windows; using System.Windows.Input; using System.Windows.Media; using System.Windows.Media.Imaging; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using SparkClient.Model.Common; using SparkClient.Model.Entity.ApiEntity; using SparkClient.Model.Helper; using SparkClient.Model.Services; using SparkClient.ViewModel.BaseWindow; using SparkClient.ViewModel.Configuration; using SparkClient.Views.Dialog; using SparkDotNetCore.DiamondScanner; using SparkDotNetCore.DiamondScanner.Entity; using MessageBox = SparkClient.Views.Dialog.MessageBox; namespace SparkClient.ViewModel.Grading; public class GradingLoadingVM : BaseViewModel,IDisposable { private double _progress; private SOCClientService _socClientService; public AlgorithmResultEntity Parameter; /// /// 进度 /// public double Progress { get => _progress; set { _progress = value; OnPropertyChanged(nameof(Progress)); } } public ICommand StopCommand { get; } private string _diamondCode; private string _diamnondType; private bool _disposed; private CancellationTokenSource _progressCts; private CancellationTokenSource? _playbackCts; private Diamond _diamond; private Scanner _scanner; private CancellationTokenSource _completionCts; #region 图片播放控制 private PlayStatus _currentStatus = PlayStatus.Stopped; public PlayStatus CurrentStatus { get => _currentStatus; set { _currentStatus = value; OnPropertyChanged(nameof(CurrentStatus)); OnPropertyChanged(nameof(ButtonText)); // 状态变更时更新按钮文本 } } public string ButtonText => CurrentStatus switch { PlayStatus.Playing => MultilingualHelper.getString("GradingLoadingPaused"), PlayStatus.Paused => MultilingualHelper.getString("GradingLoadingContinue"), _ => MultilingualHelper.getString("GradingLoadingReplay") // Stopped 状态 }; private CancellationTokenSource _cts; private int _playDelay = 100; // 默认播放速度 public ICommand PlayControlCommand { get; } public ICommand PreviousCommand { get; } public ICommand NextCommand { get; } private int _currentIndex; public int CurrentIndex { get => _currentIndex; set { _currentIndex = value; OnPropertyChanged(nameof(CurrentIndex)); UpdateCurrentImage(); } } public string[] ImagePaths { get; set; } private ImageSource _currentImage; public ImageSource CurrentImage { get => _currentImage; private set { _currentImage = value; OnPropertyChanged(nameof(CurrentImage)); } } private bool _imageIsEnable; public bool ImageIsEnable { get => _imageIsEnable; private set { _imageIsEnable = value; OnPropertyChanged(nameof(ImageIsEnable)); // 触发通知 } } #endregion public GradingLoadingVM(string diamnondType, string diamondCode) { _diamondCode = diamondCode; _diamnondType = diamnondType; StopCommand = new RelayCommand(Stop); PlayControlCommand = new RelayCommand(async _ => await HandlePlayControl()); PreviousCommand = new RelayCommand(_ => MovePrevious()); NextCommand = new RelayCommand(_ => MoveNext()); ImageIsEnable = false; _progressCts = new CancellationTokenSource(); _playbackCts = new CancellationTokenSource(); _completionCts = new CancellationTokenSource(); } /// /// 开始检测 /// public async Task Start(int type = 0) { try { var progress = RunProgressAsync(_progressCts.Token); if (type == 11) { JsonImport jsonImport = new JsonImport(); bool? a = jsonImport.ShowDialog(); if (a ?? false) { string fileName = jsonImport.FilePath.Text; string[] lines = File.ReadAllLines(fileName); StringBuilder stringBuilder = new StringBuilder(); foreach (var line in lines) { stringBuilder.Append(line); } Parameter = JsonConvert.DeserializeObject(stringBuilder.ToString()); await CompleteProgressQuicklyAsync(); return 0; } else { return -1; } } if(type == 0){ _socClientService = new SOCClientService(); var processImage = _socClientService.ProcessImageCollectionAsync(); //通知页面可以播放图片 await processImage; if (!("ok".Equals(processImage.Result.Status) || "S000".Equals(processImage.Result.Status))) { _progressCts.Cancel(); new MessageBox().Show( MultilingualHelper.getString(StatusCodes.GetConstantNameByValue(processImage.Result.Status))); return -1; } LoadImages(processImage.Result.Images); } if (type == 1) { List mnFiles = new List(); for (int i = 0; i < 100; i++) { mnFiles.Add($"{i}.bmp"); } LoadImages(mnFiles); } ImageIsEnable = true; StartPlayback(); Diamond diamond = new Diamond(); string circleSql = $"SELECT VALUE FROM CUTTER_CONFIG WHERE KEY = 'half_circle'"; DataTable circleTable = DataBaseHelper.ExecuteQuery(circleSql); object halfCircleValue = circleTable.Rows[0][0]; bool.TryParse(halfCircleValue.ToString(), out bool boolResult); bool half_circle = boolResult; //算法配置参数,初始化算法配置数据并获取 AlgorithmConfigJson var _algorithmConfigVM = new AlgorithmConfigVM(); _algorithmConfigVM.InitAlgorithmData(null); string algo_config = _algorithmConfigVM.AlgorithmConfigJson; //图片根目录 string? image_file_base_path = ConfigurationManager.AppSettings["ImageFileBasePath"]; if (string.IsNullOrEmpty(image_file_base_path)) { throw new InvalidOperationException("ImageFileBasePath is not configured in AppSettings."); } // 获取 log4net 日志文件所在的目录 string? log4NetLogDirectory = Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location); // 构建C++ DLL 日志文件路径 string algorithm_log_path = Path.Combine(log4NetLogDirectory, "logs"); // 将所有变量拼接成一个 JSON 对象 JObject jsonData = new JObject( new JProperty("shape", _diamnondType.Split(" ")[0]), new JProperty("shape_mode", _diamnondType.Split(" ")[1]), new JProperty("image_file_base_path", image_file_base_path), new JProperty("image_files", ImagePaths), new JProperty("half_circle", half_circle), new JProperty("algorithm_log_path", algorithm_log_path), new JProperty("algo_config", JObject.Parse(algo_config)) ); _scanner = new Scanner(diamond); var detectTask = _scanner.DetectAsyncByJsonStr(jsonData.ToString()); await detectTask; if (detectTask.Status == TaskStatus.RanToCompletion) { return ReslutGen(detectTask); } await progress; return ReslutGen(detectTask); } catch(Exception ex) { return -100; } } private int ReslutGen(Task detectTask) { CompleteProgressQuicklyAsync(); switch (detectTask.Result.Status) { case StatusCodes.AlgorithmFailed: new MessageBox().Show(MultilingualHelper.getString("AlgorithmFailed")); return -1; case StatusCodes.ImageFileReadFailure: new MessageBox().Show(MultilingualHelper.getString("ImageFileReadFailure")); return -1; case StatusCodes.JsonParseFailure: new MessageBox().Show(MultilingualHelper.getString("JsonParseFailure")); return -1; case StatusCodes.NoDiamond: new MessageBox().Show(MultilingualHelper.getString("NoDiamond")); return -1; } Progress = 100; string strParam = JsonConvert.SerializeObject(detectTask.Result); AlgorithmResultEntity parameter = JsonConvert.DeserializeObject(strParam); if (parameter == null) { new MessageBox().Show(MultilingualHelper.getString("JsonParseFailure")); return -1; } try { string parameterJson = JsonConvert.SerializeObject(parameter); parameterJson = JToken.Parse(parameterJson).ToString(); string outputPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "logs", "result"); if (!Directory.Exists(outputPath)) Directory.CreateDirectory(outputPath); string outputFilePath = $"{outputPath}/{_diamondCode}-{DateTime.Now:yyyyMMdd_HHmmss}.json"; using (var file = File.Create(outputFilePath)) using (StreamWriter stream = new StreamWriter(file)) { stream.Write(parameterJson); } } catch (Exception ex) { Logger.Error("output输出失败:" + ex.Message); } Parameter = parameter; return 0; } private async Task CompleteProgressSlowlyAsync() { // 缓慢完成剩余3%(总耗时保持原速度的3倍) const int remainingSteps = 3; int stepTime = 500; // 自定义慢速步长 int current = (int)Progress; for (int i = 1; i <= remainingSteps; i++) { if (_progressCts.IsCancellationRequested) break; UpdateProgress(current + i); await Task.Delay(stepTime); } } private async Task CompleteProgressQuicklyAsync() { // 取消原进度任务 _progressCts.Cancel(); // 快速完成剩余进度(0.5秒内完成) int current = (int)Progress; int remaining = 100 - current; if (remaining <= 0) return; int stepTime = Math.Max(50, 500 / remaining); // 动态计算步长 while (current < 100) { current = Math.Min(current + 1, 100); UpdateProgress(current); await Task.Delay(stepTime); } } private void UpdateProgress(int value) { // UI线程安全更新 Application.Current.Dispatcher.Invoke(() => { Progress = value; }); } private void Stop(object param) { //询问?停止:忽略 try { MessageBox messageBox = new MessageBox(); MessageBoxResult showAsk = messageBox.ShowAsk(MultilingualHelper.getString("GradingLoadingStopAsk")); if (showAsk == MessageBoxResult.OK) { WindowManager.mainViewModel.Content = WindowManager.PreviousVM(); _scanner?.Cancel(); _progressCts.Cancel(); this.Dispose(); } } catch (Exception ex) { } } private async Task RunProgressAsync(CancellationToken token) { var configValue = ConfigurationHelper.ReadConfigValue("ProgressTime"); int totalDuration = int.TryParse(configValue, out var result) ? result : 50000; int stepTime = totalDuration / 97; // 使用 IProgress 实现线程安全的进度报告 var progress = new Progress(value => { if (!token.IsCancellationRequested) Progress = value; }); await Task.Run(async () => { for (int i = 0; i <= 97; i++) { token.ThrowIfCancellationRequested(); // 报告进度 ((IProgress)progress).Report(i); // 使用可取消的延迟 await Task.Delay(stepTime, token); } }, token); } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (_disposed) return; if (disposing) { // 取消所有操作 _progressCts?.Cancel(); _playbackCts?.Cancel(); // 释放托管资源 _progressCts?.Dispose(); _playbackCts?.Dispose(); } _disposed = true; } #region 图片播放处理 public void LoadImages(string folderPath) { ImagePaths = Directory.GetFiles(folderPath, "*.bmp"); CurrentIndex = 0; CurrentStatus = PlayStatus.Stopped; } public void LoadImages(List folderPath) { ImagePaths = folderPath.ToArray(); CurrentIndex = 0; CurrentStatus = PlayStatus.Stopped; } private async Task HandlePlayControl() { switch (CurrentStatus) { case PlayStatus.Stopped: await StartPlayback(); // 开始或重播 break; case PlayStatus.Playing: PausePlayback(); // 暂停 break; case PlayStatus.Paused: await ResumePlayback();// 继续 break; } } private async Task StartPlayback() { CurrentStatus = PlayStatus.Playing; _cts = new CancellationTokenSource(); try { for (CurrentIndex = 0; CurrentIndex < ImagePaths.Length; CurrentIndex++) { if (_cts.Token.IsCancellationRequested) break; await Task.Delay(_playDelay); } // 播放完成处理 if (CurrentIndex >= ImagePaths.Length) { CurrentStatus = PlayStatus.Stopped; CurrentIndex = 0; // 重置为初始位置 } } catch (TaskCanceledException) { /* 正常取消处理 */ } } private void PausePlayback() { _cts?.Cancel(); CurrentStatus = PlayStatus.Paused; } private async Task ResumePlayback() { CurrentStatus = PlayStatus.Playing; _cts = new CancellationTokenSource(); await StartPlayback(); } private void UpdateCurrentImage() { if (ImagePaths == null || CurrentIndex < 0 || CurrentIndex >= ImagePaths.Length) return; string? savePath = ConfigurationManager.AppSettings["ImageFileBasePath"]; var bitmap = new BitmapImage(); bitmap.BeginInit(); bitmap.CacheOption = BitmapCacheOption.OnLoad; bitmap.UriSource = new Uri(savePath + @"\" + ImagePaths[CurrentIndex]); bitmap.EndInit(); bitmap.Freeze(); // 确保跨线程安全# CurrentImage = bitmap; OnPropertyChanged(nameof(CurrentImage)); } private void MovePrevious() { if (CurrentStatus == PlayStatus.Playing) PausePlayback(); CurrentIndex = (CurrentIndex - 1 + ImagePaths.Length) % ImagePaths.Length; } private void MoveNext() { if (CurrentStatus == PlayStatus.Playing) PausePlayback(); CurrentIndex = (CurrentIndex + 1) % ImagePaths.Length; } #endregion } public enum PlayStatus { Stopped, // 初始/停止状态 Playing, // 播放中 Paused // 暂停中 }