You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
814 lines
28 KiB
814 lines
28 KiB
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 BrilliantSightClient.Model.Attributes; |
|
using BrilliantSightClient.Model.Entity.ApiEntity; |
|
using BrilliantSightClient.Model.Extension; |
|
using BrilliantSightClient.Model.Helper; |
|
using BrilliantSightClient.Model.Services; |
|
using BrilliantSightClient.ViewModel.BaseWindow; |
|
using BrilliantSightClient.ViewModel.Configuration; |
|
using BrilliantSightClient.Views.Dialog; |
|
using SparkDotNetCore.DetectCore; |
|
using SparkDotNetCore.DetectCore.Entity; |
|
using MessageBox = BrilliantSightClient.Views.Dialog.MessageBox; |
|
|
|
namespace BrilliantSightClient.ViewModel.Grading; |
|
|
|
public class GradingLoadingVM : BaseViewModel,IDisposable |
|
{ |
|
private double _progress; |
|
public AlgorithmResultEntity Parameter; |
|
/// <summary> |
|
/// 进度 |
|
/// </summary> |
|
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 OvalScanner _ovalScanner; |
|
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}"); |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 开始检测 |
|
/// </summary> |
|
[Log] |
|
public async Task<int> 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<AlgorithmResultEntity>(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; |
|
Parameter.Status = "ok"; |
|
await CompleteProgressQuicklyAsync(); |
|
return 0; |
|
} |
|
else |
|
{ |
|
return -1; |
|
} |
|
} |
|
|
|
if(type == 0) |
|
{ |
|
|
|
Task<SocResultEntity> processImage = SOCClientService.Service.ProcessImageCollectionAsync(); |
|
//通知页面可以播放图片 |
|
LoadDefaultImages(); |
|
await processImage; |
|
// await Task.Delay(1000); |
|
|
|
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<string> mnFiles = new List<string>(); |
|
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); |
|
if ("OVAL P8 P8".Equals(_diamnondType)) |
|
{ |
|
algo_config = File.ReadAllText(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "SparkCore/config.json")); |
|
} |
|
|
|
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())}"); |
|
//算法启动前关闭气泵,算法执行完毕时按需打开,确保算法导致程序闪退后气泵未关闭 |
|
SOCClientService.Service.OpenPump(false); |
|
Task<DiaResult> detectTask = null; |
|
if ("OVAL P8 P8".Equals(_diamnondType)) |
|
{ |
|
_ovalScanner = new OvalScanner(diamond); |
|
detectTask = _ovalScanner.DetectAsyncByJsonStr(jsonData.ToString()); |
|
} |
|
else |
|
{ |
|
_scanner = new Scanner(diamond); |
|
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<int> ReslutGen(Task<DiaResult> 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<AlgorithmResultEntity>(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 == 1) |
|
{ |
|
//工厂模式下再打开 |
|
SOCClientService.Service.OpenPump(true); |
|
} |
|
|
|
_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(); |
|
_ovalScanner?.Cancel(); |
|
_monitorTimer.Dispose(); |
|
_isCancel = true; |
|
_progressCts.Cancel(); |
|
this.Dispose(); |
|
} |
|
|
|
} |
|
catch (Exception ex) |
|
{ |
|
|
|
} |
|
|
|
} |
|
[Log] |
|
private async Task RunProgressAsync(CancellationToken token) |
|
{ |
|
// var configValue = ConfigurationHelper.ReadConfigValue("ProgressTime"); |
|
try |
|
{ |
|
int totalDuration = 50000; |
|
int stepTime = totalDuration / 97; |
|
|
|
// 使用 IProgress 实现线程安全的进度报告 |
|
var progress = new Progress<double>(value => |
|
{ |
|
if (!token.IsCancellationRequested) |
|
Progress = value; |
|
}); |
|
|
|
await Task.Run(async () => |
|
{ |
|
for (int i = 0; i <= 97; i++) |
|
{ |
|
token.ThrowIfCancellationRequested(); |
|
|
|
// 报告进度 |
|
((IProgress<double>)progress).Report(i); |
|
|
|
// 使用可取消的延迟 |
|
await Task.Delay(stepTime, token); |
|
} |
|
}, token); |
|
} |
|
catch (Exception e) |
|
{ |
|
|
|
} |
|
} |
|
|
|
[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<string> imagePaths = new List<string>(); |
|
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<string> 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<bool> 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<BitmapImage> 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 // 暂停中 |
|
} |