using System.Configuration;
using System.Data;
using System.Diagnostics;
using System.IO;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Threading;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SparkClient.Model.Attributes;
using SparkClient.Model.Common;
using SparkClient.Model.Entity.ApiEntity;
using SparkClient.Model.Extension;
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;
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;
string strImageModeImagePath = string.Empty;
#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));
}
}
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
private bool _isCancel = false;
private Timer _monitorTimer;
private static readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1);
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();
Logger.Info("周期检查舱门,周期500ms");
if (_monitorTimer == null)
{
_monitorTimer = new Timer(CheckSpeedCallback,
null,
500,
Timeout.Infinite);
}
}
private async void CheckSpeedCallback(object state)
{
try
{
GpioStatus gpioStatus = await SOCClientService.Service.GetGpioStatus();
Application.Current.Dispatcher.Invoke(() =>
{
if (gpioStatus.LensGpioIsOpen() || gpioStatus.DiamondGpioIsOpen())
{
new MessageBox().Show(MultilingualHelper.getString("OpenOfTheHatch"));
WindowManager.mainViewModel.Content = WindowManager.PreviousVM();
_scanner?.Cancel();
_isCancel = true;
_progressCts.Cancel();
this.Dispose();
_monitorTimer.Dispose();
SOCClientService.Service.OpenPump(false);
return;
}
_monitorTimer.Change(500, Timeout.Infinite);
}, DispatcherPriority.Normal);
}
catch (Exception ex)
{
Logger.Info($"监控异常: {ex.Message}");
}
}
///
/// 开始检测
///
[Log]
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();
Logger.Info($"Json模式,文件路径:{fileName}");
foreach (var line in lines)
{
stringBuilder.Append(line);
}
var settings = new JsonSerializerSettings
{
ContractResolver = new DefaultValueContractResolver(),
NullValueHandling = NullValueHandling.Ignore
};
Parameter = JsonConvert.DeserializeObject(stringBuilder.ToString(),settings);
Parameter.DiamondCode = _diamondCode;
Parameter.Standard = getStandardName();
Parameter.Shape = _diamnondType.Split(" ")[0];
Parameter.CrownType = _diamnondType.Split(" ")[1];
Parameter.PavType = _diamnondType.Split(" ")[2];
Parameter.ErrorMsg = _diamnondType;
await CompleteProgressQuicklyAsync();
return 0;
}
else
{
return -1;
}
}
if(type == 0)
{
await SOCClientService.Service.OpenPump(true);
// if (!StatusCodes.Success.Equals(openpupmStatus))
// {
// new MessageBox().Show("气泵开启失败!");
// return -1;
// }
Task processImage = SOCClientService.Service.ProcessImageCollectionAsync();
//通知页面可以播放图片
LoadDefaultImages();
await processImage;
await Task.Delay(1000);
// // 采集状态接口
// string acquisitionStatus = await SOCClientService.Service.CollectStatusAsync();
// // 成功
// if (acquisitionStatus != StatusCodes.Success)
// {
// Logger.Debug($"acquisitionStatus != StatusCodes.Success : {acquisitionStatus}");
// _progressCts.Cancel();
// new MessageBox().Show(
// MultilingualHelper.getString(StatusCodes.GetConstantNameByValue(acquisitionStatus)));
// return -1;
// }
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)
{
ImageSelect jsonImport = new ImageSelect();
bool? a = jsonImport.ShowDialog();
if (a ?? false)
{
strImageModeImagePath = jsonImport.FilePath.Text;
string name = jsonImport.FileName.Text;
List mnFiles = new List();
for (int i = 0; i < 100; i++)
{
mnFiles.Add(name.Replace("%d", i.ToString()));
}
Logger.Info($"Image模式,图片路径:{strImageModeImagePath}");
LoadImages(mnFiles);
}
else
{
return -1;
}
StartPlayback();
}
ImageIsEnable = true;
// StartPlayback();
if (_isCancel == true)
{
SOCClientService.Service.OpenPump(false);
return -100;
}
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(_diamnondType);
// string algo_config = _algorithmConfigVM.AlgorithmConfigJson;
string algo_config = _algorithmConfigVM.GetAlgorithmConfig(_diamnondType);
string imageFileBasePath = string.Empty;
//图片根目录
if (type == 1)
{
imageFileBasePath = strImageModeImagePath;
}
else
{
imageFileBasePath = ConfigurationManager.AppSettings["ImageFileBasePath"];
}
if (string.IsNullOrEmpty(imageFileBasePath))
{
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", imageFileBasePath),
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))
);
Logger.Info($"算法启动,输入参数(Encrypt):{AESHelper.Encrypt(jsonData.ToString())}");
_scanner = new Scanner(diamond);
var detectTask = _scanner.DetectAsyncByJsonStr(jsonData.ToString());
Logger.Info($"开始等待算法结果");
await detectTask;
if (detectTask.Status == TaskStatus.RanToCompletion)
{
return await ReslutGen(detectTask);
}
await progress;
return await ReslutGen(detectTask);
}
catch(Exception ex)
{
Logger.Error(ex.Message);
if (_isCancel)
{
return -100;
}
else
{
return -10;
}
}
}
[Log]
private async Task ReslutGen(Task detectTask)
{
bool hasErr = false;
Logger.Info($"算法运行完毕:{detectTask.Status}");
Logger.Info($"算法运行结果:[Status = {detectTask.Result.Status}; Message = {detectTask.Result.ErrorMsg}]");
try
{
CompleteProgressQuicklyAsync();
if (detectTask.Result != null)
{
switch (detectTask.Result.Status)
{
case StatusCodes.AlgorithmFailed:
new MessageBox().Show(MultilingualHelper.getString("AlgorithmFailed"));
hasErr = true;
return -1;
case StatusCodes.ImageFileReadFailure:
new MessageBox().Show(MultilingualHelper.getString("ImageFileReadFailure"));
hasErr = true;
return -1;
case StatusCodes.JsonParseFailure:
new MessageBox().Show(MultilingualHelper.getString("JsonParseFailure"));
hasErr = true;
return -1;
case StatusCodes.NoDiamond:
new MessageBox().Show(MultilingualHelper.getString("NoDiamond"));
hasErr = true;
return -1;
case "DETECTING_EXCEPTION":
new MessageBox().Show(MultilingualHelper.getString("DETECTING_EXCEPTION") + detectTask.Result.ErrorMsg);
hasErr = true;
return -1;
case "DETECTING_RESULT_ISNULL":
new MessageBox().Show(MultilingualHelper.getString("DETECTING_RESULT_ISNULL") + detectTask.Result.ErrorMsg);
hasErr = true;
return -1;
}
Progress = (100.00);
string strParam = JsonConvert.SerializeObject(detectTask.Result);
//Logger.Info("序列化字符串:" + strParam);
AlgorithmResultEntity parameter = JsonConvert.DeserializeObject(strParam);
if (parameter == null && _isCancel == false)
{
new MessageBox().Show(MultilingualHelper.getString("JsonParseFailure"));
hasErr = true;
return -1;
}
parameter.DiamondCode = _diamondCode;
parameter.Standard = getStandardName();
parameter.Shape = _diamnondType.Split(" ")[0];
parameter.CrownType = _diamnondType.Split(" ")[1];
parameter.PavType = _diamnondType.Split(" ")[2];
parameter.ErrorMsg = _diamnondType;
//实验室模式关闭气泵
if (Common.RunMode == 0)
{
SOCClientService.Service.OpenPump(false);
}
_monitorTimer.Dispose();
try
{
Logger.Info($"算法结果Json单独保存至log/result");
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";
byte[] data = Encoding.Unicode.GetBytes(parameterJson);
byte[] outData = AESHelper.Encrypt(data);
File.WriteAllBytes(outputFilePath, outData);
//using (var file = File.Create(outputFilePath))
// using (StreamWriter stream = new StreamWriter(file))
// {
// stream.Write(parameterJson);
// }
Logger.Info($"算法结果Json单独保存至log/result 完毕!");
}
catch (Exception ex)
{
Logger.Error("output输出失败:" + ex.Message);
}
Parameter = parameter;
if (_isCancel == true)
{
SOCClientService.Service.OpenPump(false);
return -100;
}
;
if (parameter.Status == StatusCodes.Recheck)
{
new MessageBox().Show(MultilingualHelper.getString("Recheck"));
}
return 0;
}
return -10;
}
finally
{
string strSafeSpace = ConfigurationHelper.ReadConfigValue("SafeSpaceReservation");
if (hasErr || "0".Equals(strSafeSpace) || Parameter.Status == StatusCodes.Recheck)
{
DiamondSelectVM.HandleAlgorithmFailure(ImagePaths, _diamondCode, _diamnondType);
}
}
}
[Log]
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/2);
}
}
[Log]
private void UpdateProgress(int value)
{
// UI线程安全更新
Application.Current.Dispatcher.Invoke(() =>
{
Progress = value;
});
}
[Log]
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();
_monitorTimer.Dispose();
_isCancel = true;
_progressCts.Cancel();
this.Dispose();
}
}
catch (Exception ex)
{
}
}
[Log]
private async Task RunProgressAsync(CancellationToken token)
{
// var configValue = ConfigurationHelper.ReadConfigValue("ProgressTime");
int totalDuration = 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);
}
[Log]
public void Dispose()
{
_monitorTimer.Dispose();
Dispose(true);
GC.SuppressFinalize(this);
}
[Log]
protected virtual void Dispose(bool disposing)
{
if (_disposed) return;
if (disposing)
{
// 取消所有操作
_progressCts?.Cancel();
_playbackCts?.Cancel();
// 释放托管资源
_progressCts?.Dispose();
_playbackCts?.Dispose();
}
_disposed = true;
}
[Log]
private string getStandardName()
{
string sql = $"select\r\nRULE_NAME AS NAME,\r\nRULE_EN_NAME AS EN_NAME\r\nfrom\r\nsetting\r\nleft join rule\r\non setting.SETTING_P = rule.RULE_ID\r\nwhere\r\nsetting.SETTING_ID = 'RuleId'\r\n";
DataTable dataTable = DataBaseHelper.ExecuteQuery(sql);
if (dataTable == null || dataTable.Rows.Count == 0)
{
return "";
}
else
{
return dataTable.Rows[0][MultilingualHelper.getString("NameType")].ToString()??"";
}
}
#region 图片播放处理
[Log]
public void LoadDefaultImages()
{
List imagePaths = new List();
string? savePath = ConfigurationHelper.ReadConfigValue("ImageFileBasePath");
int imageTotal = ConfigurationHelper.ReadConfigValueToInteger("DetectImageTotal", 100);
for (int i = 0; i < imageTotal; i++)
{
imagePaths.Add($"image_{i}.bmp");
}
ImagePaths = imagePaths.ToArray();
CurrentIndex = 0;
CurrentStatus = PlayStatus.Stopped;
MonitorAndPlayImagesAsync(savePath);
}
[Log]
public void LoadImages(List folderPath)
{
ImagePaths = folderPath.ToArray();
CurrentIndex = 0;
UpdateCurrentImage();
CurrentStatus = PlayStatus.Stopped;
}
[Log]
private async Task MonitorAndPlayImagesAsync(string folderPath)
{
var sw = new Stopwatch();
for (int i = 0; i < 100; i++)
{
sw.Restart();
// 等待文件就绪
var filePath = Path.Combine(folderPath, $"image_{i}.bmp");
while (!await IsFileReady(filePath))
{
await Task.Delay(50); // 每50ms检测一次文件状态
}
// 加载图片到内存
var image = await LoadImageSafely(filePath);
// 更新UI显示
await Application.Current.Dispatcher.InvokeAsync(() =>
{
CurrentImage = image;
OnPropertyChanged(nameof(CurrentImage));
});
// 强制最小显示间隔
var remainTime = Math.Max(100 - (int)sw.ElapsedMilliseconds, 0);
await Task.Delay(remainTime);
}
}
[Log]
private async Task IsFileReady(string path)
{
try
{
// 双重验证机制
if (!File.Exists(path)) return false;
// 尝试读取文件头验证完整性
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
byte[] header = new byte[2];
await fs.ReadAsync(header, 0, 2);
return header[0] == 0x42 && header[1] == 0x4D; // BMP文件头标识
}
}
catch
{
return false;
}
}
[Log]
private async Task LoadImageSafely(string path)
{
using (var fs = new FileStream(path, FileMode.Open, FileAccess.Read))
{
var bitmap = new BitmapImage();
await Application.Current.Dispatcher.InvokeAsync(() =>
{
bitmap.BeginInit();
bitmap.CacheOption = BitmapCacheOption.OnLoad;
bitmap.StreamSource = fs;
bitmap.EndInit();
bitmap.Freeze(); // 跨线程安全
});
return bitmap;
}
}
[Log]
private async Task HandlePlayControl()
{
switch (CurrentStatus)
{
case PlayStatus.Stopped:
await StartPlayback(); // 开始或重播
break;
case PlayStatus.Playing:
PausePlayback(); // 暂停
break;
case PlayStatus.Paused:
await ResumePlayback();// 继续
break;
}
}
[Log]
private async Task StartPlayback()
{
CurrentStatus = PlayStatus.Playing;
_cts = new CancellationTokenSource();
try
{
for (CurrentIndex = 0; CurrentIndex < ImagePaths.Length; CurrentIndex++)
{
if (_cts.Token.IsCancellationRequested) break;
UpdateCurrentImage();
await Task.Delay(_playDelay);
}
// 播放完成处理
if (CurrentIndex >= ImagePaths.Length)
{
CurrentStatus = PlayStatus.Stopped;
CurrentIndex = 0; // 重置为初始位置
}
}
catch (TaskCanceledException) { /* 正常取消处理 */ }
}
[Log]
private void PausePlayback()
{
_cts?.Cancel();
CurrentStatus = PlayStatus.Paused;
}
[Log]
private async Task ResumePlayback()
{
CurrentStatus = PlayStatus.Playing;
_cts = new CancellationTokenSource();
await StartPlayback();
}
[Log]
private void UpdateCurrentImage()
{
if (ImagePaths == null || CurrentIndex < 0 || CurrentIndex >= ImagePaths.Length)
return;
string? savePath = strImageModeImagePath.IsNullOrEmpty()? ConfigurationManager.AppSettings["ImageFileBasePath"]:strImageModeImagePath;
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));
}
[Log]
private void MovePrevious()
{
if (CurrentStatus == PlayStatus.Playing)
PausePlayback();
CurrentIndex = (CurrentIndex - 1 + ImagePaths.Length) % ImagePaths.Length;
UpdateCurrentImage();
}
[Log]
private void MoveNext()
{
if (CurrentStatus == PlayStatus.Playing)
PausePlayback();
CurrentIndex = (CurrentIndex + 1) % ImagePaths.Length;
UpdateCurrentImage();
}
#endregion
}
public enum PlayStatus
{
Stopped, // 初始/停止状态
Playing, // 播放中
Paused // 暂停中
}