using HandyControl.Controls; using LiveChartsCore; using LiveChartsCore.Defaults; using LiveChartsCore.SkiaSharpView; using LiveChartsCore.SkiaSharpView.Painting; using Prism.Events; using Prism.Mvvm; using SkiaSharp; using System.Collections.ObjectModel; using YY.Admin.Core; using YY.Admin.Core.Entity; using YY.Admin.Core.Events; using YY.Admin.Core.Services; using YY.Admin.Core.Session; namespace YY.Admin.ViewModels.RubberQuickTest; public class QuickTestInspectCellViewModel : BindableBase { public string? DataPointId { get; set; } public string PointName { get; set; } = string.Empty; public decimal? LowerLimit { get; set; } public decimal? UpperLimit { get; set; } private decimal? _value; public decimal? Value { get => _value; set { if (SetProperty(ref _value, value)) ValueChanged?.Invoke(this); } } public event Action? ValueChanged; } public class QuickTestInspectRowViewModel : BindableBase { private string _rowNo = string.Empty; public string RowNo { get => _rowNo; set => SetProperty(ref _rowNo, value); } public ObservableCollection Cells { get; } = new(); private string _inspectResultText = string.Empty; public string InspectResultText { get => _inspectResultText; set => SetProperty(ref _inspectResultText, value); } public string InspectResultCode { get; private set; } = "0"; public void RecalculateResult() { if (Cells.Count == 0) { InspectResultCode = "0"; InspectResultText = string.Empty; RaisePropertyChanged(nameof(InspectResultText)); return; } if (Cells.Any(c => c.Value == null)) { InspectResultCode = "0"; InspectResultText = "待填写"; RaisePropertyChanged(nameof(InspectResultText)); return; } var pass = Cells.All(c => { if (c.LowerLimit != null && c.Value < c.LowerLimit) return false; if (c.UpperLimit != null && c.Value > c.UpperLimit) return false; return true; }); InspectResultCode = pass ? "1" : "0"; InspectResultText = pass ? "合格" : "不合格"; RaisePropertyChanged(nameof(InspectResultText)); } } /// 胶料快检记录操作台 ViewModel(密炼计划、实验标准均读本地缓存) public class RubberQuickTestOperationViewModel : BaseViewModel { private readonly IRubberQuickTestOperationService _operationService; private readonly IMixingProductionPlanService _planService; private readonly IRubberQuickTestStdService _stdService; private readonly Random _rnd = new(); private List _allPlans = new(); private List _allStds = new(); private MesXslRubberQuickTestStd? _currentStd; private SubscriptionToken? _planChangedToken; private SubscriptionToken? _stdChangedToken; private const int ChartPointCount = 5; /// 曲线横坐标时间点(min):0、0.5、1、1.5、2 private static readonly double[] ChartTimeMinutes = { 0, 0.5, 1, 1.5, 2 }; public event Action? InspectColumnsChanged; public RubberQuickTestOperationViewModel( IRubberQuickTestOperationService operationService, IMixingProductionPlanService planService, IRubberQuickTestStdService stdService, IContainerExtension container, IRegionManager regionManager) : base(container, regionManager) { _operationService = operationService; _planService = planService; _stdService = stdService; _inspectTimesText = "1"; AddInspectRowCommand = new DelegateCommand(AddInspectRow, () => DataPointColumns.Count > 0); RemoveInspectRowCommand = new DelegateCommand(() => RemoveInspectRow(SelectedInspectRow), () => SelectedInspectRow != null); SaveCommand = new DelegateCommand(async () => await SaveAsync(), () => InspectRows.Count > 0); RefreshPlansCommand = new DelegateCommand(async () => await LoadLocalDataAsync(showSuccess: true)); RefreshChartDemoCommand = new DelegateCommand(FillRandomChartData); TemperatureSeries = new ObservableCollection { new LineSeries { Name = "上模温度", Values = UpperTempValues, GeometrySize = 4, LineSmoothness = 0.3 }, new LineSeries { Name = "下模温度", Values = LowerTempValues, GeometrySize = 4, LineSmoothness = 0.3 } }; TorqueSeries = new ObservableCollection { new LineSeries { Name = "S'(dNm)", Values = TorqueValues, GeometrySize = 4, LineSmoothness = 0.3 } }; FillRandomChartData(); InspectorDisplay = ResolveInspectorDisplay(); _planChangedToken = _eventAggregator.GetEvent() .Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread); _stdChangedToken = _eventAggregator.GetEvent() .Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread); _ = LoadLocalDataAsync(showSuccess: false); RecalculateOverallInspectResult(); } private static string ResolveInspectorDisplay() { var user = AppSession.CurrentUser; if (user == null) return string.Empty; if (!string.IsNullOrWhiteSpace(user.RealName)) return user.RealName.Trim(); if (!string.IsNullOrWhiteSpace(user.Account)) return user.Account.Trim(); return string.Empty; } /// 中间库未接入时的曲线演示说明 public string ChartDemoHint => "演示数据(中间库未接入,打开页面自动生成;接入后将显示实测曲线)"; public ObservableCollection TemperatureSeries { get; } public ObservableCollection TorqueSeries { get; } public ObservableCollection UpperTempValues { get; } = new(); public ObservableCollection LowerTempValues { get; } = new(); public ObservableCollection TorqueValues { get; } = new(); public Axis[] TemperatureXAxes { get; } = BuildTimeAxis(); public Axis[] TemperatureYAxes { get; } = BuildTempYAxis(); public Axis[] TorqueXAxes { get; } = BuildTimeAxis(); public Axis[] TorqueYAxes { get; } = BuildTorqueYAxis(); public ObservableCollection MachineOptions { get; } = new(); public ObservableCollection ShiftOptions { get; } = new(); public ObservableCollection PlanOptions { get; } = new(); public ObservableCollection StdOptions { get; } = new(); public ObservableCollection DataPointColumns { get; } = new(); public ObservableCollection InspectRows { get; } = new(); private QuickTestInspectRowViewModel? _selectedInspectRow; public QuickTestInspectRowViewModel? SelectedInspectRow { get => _selectedInspectRow; set { if (SetProperty(ref _selectedInspectRow, value)) RemoveInspectRowCommand.RaiseCanExecuteChanged(); } } private DateTime _mixingDate = DateTime.Today; public DateTime MixingDate { get => _mixingDate; set { if (!SetProperty(ref _mixingDate, value)) return; RefreshMachineOptions(); ClearPlanAndStdSelection(); } } private string? _selectedMachine; public string? SelectedMachine { get => _selectedMachine; set { if (!SetProperty(ref _selectedMachine, value)) return; RefreshShiftOptions(); ClearPlanAndStdSelection(); } } private WorkShiftOption? _selectedShift; public WorkShiftOption? SelectedShift { get => _selectedShift; set { if (!SetProperty(ref _selectedShift, value)) return; RefreshPlanOptions(); ClearPlanAndStdSelection(); } } private MesXslMixingProductionPlan? _selectedPlan; public MesXslMixingProductionPlan? SelectedPlan { get => _selectedPlan; set { if (!SetProperty(ref _selectedPlan, value)) return; ApplySelectedPlan(); } } private MesXslRubberQuickTestStd? _selectedStd; public MesXslRubberQuickTestStd? SelectedStd { get => _selectedStd; set { if (!SetProperty(ref _selectedStd, value)) return; _ = ApplySelectedStdAsync(); } } public string? MachineName => _selectedPlan?.MachineName ?? SelectedMachine; public string? WorkShiftDisplay => SelectedShift?.Name ?? string.Empty; public string? RubberMaterialName => _selectedPlan?.MaterialName ?? _selectedPlan?.FormulaName; private string? _testMethodName; public string? TestMethodName { get => _testMethodName; private set => SetProperty(ref _testMethodName, value); } private string? _trainNo; public string? TrainNo { get => _trainNo; set => SetProperty(ref _trainNo, value); } private string? _recordNo; /// 快检记录号(保存后由 MES 生成,只读) public string? RecordNo { get => _recordNo; private set => SetProperty(ref _recordNo, value); } private string _inspectorDisplay = string.Empty; /// 检验人(当前登录用户,只读) public string InspectorDisplay { get => _inspectorDisplay; private set => SetProperty(ref _inspectorDisplay, value); } private string _overallInspectResultDisplay = "不合格"; /// 检验结果(试验信息区汇总,只读) public string OverallInspectResultDisplay { get => _overallInspectResultDisplay; private set => SetProperty(ref _overallInspectResultDisplay, value); } /// 检验结果编码:1 合格,0 不合格 public string OverallInspectResultCode { get; private set; } = "0"; private string? _inspectTimesText; /// 试验次数(手填正整数) public string? InspectTimesText { get => _inspectTimesText; set => SetProperty(ref _inspectTimesText, value); } private double _upperMoldTemp; public double UpperMoldTemp { get => _upperMoldTemp; set => SetProperty(ref _upperMoldTemp, value); } private double _lowerMoldTemp; public double LowerMoldTemp { get => _lowerMoldTemp; set => SetProperty(ref _lowerMoldTemp, value); } private double _torqueS; public double TorqueS { get => _torqueS; set => SetProperty(ref _torqueS, value); } public DelegateCommand AddInspectRowCommand { get; } public DelegateCommand RemoveInspectRowCommand { get; } public DelegateCommand SaveCommand { get; } public DelegateCommand RefreshPlansCommand { get; } public DelegateCommand RefreshChartDemoCommand { get; } private async Task LoadLocalDataAsync(bool showSuccess) { IsLoading = true; try { _allPlans = await _planService.GetAllCachedAsync(); _allStds = await _stdService.GetAllCachedAsync(); RefreshMachineOptions(); if (showSuccess) Growl.Success("本地密炼计划与实验标准已刷新"); } catch (Exception ex) { Growl.Error($"加载本地数据失败:{ex.Message}"); } finally { IsLoading = false; } } private async Task ReloadLocalDataQuietAsync() { try { _allPlans = await _planService.GetAllCachedAsync(); _allStds = await _stdService.GetAllCachedAsync(); RefreshMachineOptions(); RefreshStdOptions(); } catch (Exception ex) { _logger.Warning($"[胶料快检记录] 本地数据刷新失败:{ex.Message}"); } } private IEnumerable FilteredByDate => _allPlans.Where(p => p.PlanDate?.Date == MixingDate.Date); private void RefreshMachineOptions() { MachineOptions.Clear(); foreach (var name in FilteredByDate.Select(o => o.MachineName).Where(n => !string.IsNullOrWhiteSpace(n)).Distinct().OrderBy(n => n)) MachineOptions.Add(name!); if (SelectedMachine != null && !MachineOptions.Contains(SelectedMachine)) SelectedMachine = null; if (SelectedMachine == null && MachineOptions.Count > 0) SelectedMachine = MachineOptions[0]; else RefreshShiftOptions(); } private void RefreshShiftOptions() { ShiftOptions.Clear(); var machine = SelectedMachine; var shifts = FilteredByDate .Where(o => string.IsNullOrWhiteSpace(machine) || o.MachineName == machine) .GroupBy(o => o.ShiftFlag?.ToString() ?? string.Empty) .Select(g => g.First()) .OrderBy(o => o.ShiftFlag); foreach (var s in shifts) ShiftOptions.Add(new WorkShiftOption(s.ShiftFlag?.ToString() ?? string.Empty, s.ShiftFlagText)); if (SelectedShift != null && !ShiftOptions.Any(x => x.Code == SelectedShift.Code)) SelectedShift = null; if (SelectedShift == null && ShiftOptions.Count > 0) SelectedShift = ShiftOptions[0]; else RefreshPlanOptions(); } private void RefreshPlanOptions() { PlanOptions.Clear(); var machine = SelectedMachine; var shiftCode = SelectedShift?.Code; foreach (var p in FilteredByDate .Where(x => string.IsNullOrWhiteSpace(machine) || x.MachineName == machine) .Where(x => string.IsNullOrWhiteSpace(shiftCode) || (x.ShiftFlag?.ToString() ?? string.Empty) == shiftCode) .Where(x => !string.IsNullOrWhiteSpace(x.PlanMaterialNo)) .OrderBy(x => x.PlanMaterialNo)) PlanOptions.Add(p); if (SelectedPlan != null && PlanOptions.All(p => p.Id != SelectedPlan.Id)) SelectedPlan = null; if (SelectedPlan == null && PlanOptions.Count > 0) SelectedPlan = PlanOptions[0]; } private void ClearPlanAndStdSelection() { _selectedPlan = null; RaisePropertyChanged(nameof(SelectedPlan)); RaisePropertyChanged(nameof(MachineName)); RaisePropertyChanged(nameof(WorkShiftDisplay)); RaisePropertyChanged(nameof(RubberMaterialName)); ClearStdSelection(); } private void ClearStdSelection() { _selectedStd = null; RaisePropertyChanged(nameof(SelectedStd)); TestMethodName = null; ClearStdAndResults(); StdOptions.Clear(); } private void ApplySelectedPlan() { RaisePropertyChanged(nameof(MachineName)); RaisePropertyChanged(nameof(WorkShiftDisplay)); RaisePropertyChanged(nameof(RubberMaterialName)); ClearStdSelection(); RefreshStdOptions(); } private void RefreshStdOptions() { StdOptions.Clear(); var rubberName = RubberMaterialName; if (string.IsNullOrWhiteSpace(rubberName)) return; foreach (var std in FilterApplicableStds(rubberName).OrderBy(s => s.StdName)) StdOptions.Add(std); if (StdOptions.Count == 0) Growl.Warning($"未找到胶料「{rubberName}」的使用中且已批准的快检实验标准,请先在「胶料快检实验标准」中同步数据"); RecalculateOverallInspectResult(); } private IEnumerable FilterApplicableStds(string rubberMaterialName) { var name = rubberMaterialName.Trim(); return _allStds.Where(std => string.Equals(std.RubberMaterialName?.Trim(), name, StringComparison.OrdinalIgnoreCase) && std.EnableStatus == "1" && std.AuditStatus == "1"); } private async Task ApplySelectedStdAsync() { DataPointColumns.Clear(); InspectRows.Clear(); _currentStd = null; TestMethodName = null; InspectColumnsChanged?.Invoke(); RecalculateOverallInspectResult(); if (SelectedStd == null || string.IsNullOrWhiteSpace(SelectedStd.Id)) return; IsLoading = true; try { _currentStd = await _stdService.GetWithLinesAsync(SelectedStd.Id); if (_currentStd == null) { Growl.Warning("所选实验标准在本地缓存中不存在,请刷新本地数据"); return; } TestMethodName = _currentStd.TestMethodName; if (_currentStd.LineList == null || _currentStd.LineList.Count == 0) { Growl.Warning($"标准「{_currentStd.StdName}」明细未同步到本地,请联网后重选,或先在「胶料快检实验标准」中查看该标准详情"); return; } foreach (var line in _currentStd.LineList.OrderBy(l => l.SortNo ?? 0)) DataPointColumns.Add(line); InspectColumnsChanged?.Invoke(); InitInspectRowsFromTimes(); AddInspectRowCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { Growl.Error($"加载实验标准失败:{ex.Message}"); } finally { IsLoading = false; RecalculateOverallInspectResult(); } } private void ClearStdAndResults() { DataPointColumns.Clear(); InspectRows.Clear(); _currentStd = null; InspectColumnsChanged?.Invoke(); RecalculateOverallInspectResult(); } private void FillRandomChartData() { UpperTempValues.Clear(); LowerTempValues.Clear(); TorqueValues.Clear(); for (int i = 0; i < ChartPointCount; i++) { var time = ChartTimeMinutes[i]; var upper = 189 + _rnd.NextDouble() * 12; var lower = Math.Max(189, upper - 2 - _rnd.NextDouble() * 2); var torque = Math.Min(14.8, i * 2.5 + _rnd.NextDouble() * 2.5); UpperTempValues.Add(new ObservablePoint(time, Math.Round(upper, 2))); LowerTempValues.Add(new ObservablePoint(time, Math.Round(lower, 2))); TorqueValues.Add(new ObservablePoint(time, Math.Round(torque, 2))); } UpperMoldTemp = UpperTempValues[^1].Y ?? 0; LowerMoldTemp = LowerTempValues[^1].Y ?? 0; TorqueS = TorqueValues[^1].Y ?? 0; } private void InitInspectRowsFromTimes() { InspectRows.Clear(); var times = 1; if (TryParseInspectTimes(out var n, out _)) times = n; for (var i = 0; i < times; i++) AddInspectRow(); } private void RenumberInspectRows() { for (var i = 0; i < InspectRows.Count; i++) InspectRows[i].RowNo = $"{i + 1}_1"; } private void AddInspectRow() { var rowIndex = InspectRows.Count + 1; var row = new QuickTestInspectRowViewModel { RowNo = $"{rowIndex}_1" }; foreach (var col in DataPointColumns) { var cell = new QuickTestInspectCellViewModel { DataPointId = col.DataPointId, PointName = col.PointName ?? string.Empty, LowerLimit = col.LowerLimit, UpperLimit = col.UpperLimit }; cell.ValueChanged += _ => { row.RecalculateResult(); RecalculateOverallInspectResult(); }; row.Cells.Add(cell); } row.RecalculateResult(); InspectRows.Add(row); SaveCommand.RaiseCanExecuteChanged(); RecalculateOverallInspectResult(); } private void RemoveInspectRow(QuickTestInspectRowViewModel? row) { if (row == null) return; InspectRows.Remove(row); SelectedInspectRow = null; RenumberInspectRows(); SaveCommand.RaiseCanExecuteChanged(); RecalculateOverallInspectResult(); } private async Task SaveAsync() { if (string.IsNullOrWhiteSpace(TrainNo)) { Growl.Warning("请填写车次"); return; } if (SelectedPlan == null) { Growl.Warning("请选择密炼计划"); return; } if (string.IsNullOrWhiteSpace(RubberMaterialName)) { Growl.Warning("请先选择密炼计划以带出胶料名称"); return; } if (SelectedStd == null || _currentStd == null) { Growl.Warning("请选择实验标准"); return; } if (InspectRows.Any(r => r.InspectResultText == "待填写" || string.IsNullOrWhiteSpace(r.InspectResultText))) { Growl.Warning("请手填全部检验行的检测值后再保存"); return; } if (!TryParseInspectTimes(out var inspectTimes, out var inspectTimesError)) { Growl.Warning(inspectTimesError); return; } var lineList = BuildAveragedLineList(); if (lineList.Count == 0) { Growl.Warning("无法根据试验结果计算明细数据"); return; } var rawLineList = BuildRawLineList(); if (rawLineList.Count == 0) { Growl.Warning("无法生成试验结果原始数据"); return; } var user = AppSession.CurrentUser; IsLoading = true; try { var record = BuildSaveRecord(lineList, rawLineList, inspectTimes, user); var recordNo = await _operationService.SaveRecordAsync(record); RecordNo = recordNo; Growl.Success(string.IsNullOrWhiteSpace(recordNo) ? "胶料快检记录已保存到 MES" : $"胶料快检记录已保存,单号:{recordNo}"); InspectRows.Clear(); SaveCommand.RaiseCanExecuteChanged(); } catch (Exception ex) { Growl.Error($"保存失败:{ex.Message}"); } finally { IsLoading = false; } } private bool TryParseInspectTimes(out int value, out string error) { error = string.Empty; var text = string.IsNullOrWhiteSpace(InspectTimesText) ? "1" : InspectTimesText.Trim(); if (!int.TryParse(text, out var n) || n <= 0) { error = "试验次数须填写正整数"; value = 0; return false; } value = n; return true; } private List BuildAveragedLineList() { var lines = new List(); for (int i = 0; i < DataPointColumns.Count; i++) { var col = DataPointColumns[i]; var values = InspectRows .Where(r => i < r.Cells.Count && r.Cells[i].Value != null) .Select(r => r.Cells[i].Value!.Value) .ToList(); if (values.Count == 0) continue; var avg = values.Average(v => (double)v); var avgDecimal = Math.Round((decimal)avg, 6); var line = new MesXslRubberQuickTestRecordLine { SortNo = i }; if (!string.IsNullOrWhiteSpace(col.DataPointId)) line.DataPointId = col.DataPointId; if (!string.IsNullOrWhiteSpace(col.PointName)) line.InspectItem = col.PointName; if (col.LowerLimit != null) line.LowerLimit = col.LowerLimit; if (col.UpperLimit != null) line.UpperLimit = col.UpperLimit; line.InspectValue = avgDecimal; lines.Add(line); } return lines; } private List BuildRawLineList() { var rawLines = new List(); int sort = 0; foreach (var row in InspectRows) { for (int i = 0; i < row.Cells.Count; i++) { var cell = row.Cells[i]; if (cell.Value == null) continue; var raw = new MesXslRubberQuickTestRecordRawLine { SortNo = sort++ }; if (!string.IsNullOrWhiteSpace(row.RowNo)) raw.RowNo = row.RowNo; if (!string.IsNullOrWhiteSpace(cell.DataPointId)) raw.DataPointId = cell.DataPointId; if (!string.IsNullOrWhiteSpace(cell.PointName)) raw.InspectItem = cell.PointName; if (cell.LowerLimit != null) raw.LowerLimit = cell.LowerLimit; if (cell.UpperLimit != null) raw.UpperLimit = cell.UpperLimit; raw.InspectValue = cell.Value; raw.RowInspectResult = row.InspectResultCode; rawLines.Add(raw); } } return rawLines; } private bool HasExperimentStandardForRubber(string? rubberMaterialName) { if (string.IsNullOrWhiteSpace(rubberMaterialName)) return false; return FilterApplicableStds(rubberMaterialName).Any(); } private void RecalculateOverallInspectResult() { string display; string code; if (!HasExperimentStandardForRubber(RubberMaterialName) || _currentStd == null || DataPointColumns.Count == 0) { display = "不合格"; code = "0"; } else if (InspectRows.Count == 0 || InspectRows.Any(r => !string.Equals(r.InspectResultText, "合格", StringComparison.Ordinal))) { display = "不合格"; code = "0"; } else { display = "合格"; code = "1"; } OverallInspectResultCode = code; OverallInspectResultDisplay = display; } private MesXslRubberQuickTestRecord BuildSaveRecord( List lineList, List rawLineList, int inspectTimes, SysUser? user) { var record = new MesXslRubberQuickTestRecord { LineList = lineList, RawLineList = rawLineList, InspectTime = DateTime.Now, InspectResult = OverallInspectResultCode }; if (!string.IsNullOrWhiteSpace(_currentStd?.RubberMaterialId)) record.RubberMaterialId = _currentStd.RubberMaterialId; if (!string.IsNullOrWhiteSpace(RubberMaterialName)) record.RubberMaterialName = RubberMaterialName; if (!string.IsNullOrWhiteSpace(_currentStd?.Id)) record.StdId = _currentStd.Id; if (!string.IsNullOrWhiteSpace(_selectedPlan?.MachineId)) record.ProdEquipmentLedgerId = _selectedPlan.MachineId; if (!string.IsNullOrWhiteSpace(MachineName)) record.ProdEquipmentName = MachineName; record.ProductionDate = MixingDate; if (!string.IsNullOrWhiteSpace(TrainNo)) record.TrainNo = TrainNo.Trim(); if (SelectedShift != null && !string.IsNullOrWhiteSpace(SelectedShift.Code)) record.WorkShift = SelectedShift.Code; record.InspectTimes = inspectTimes; if (!string.IsNullOrWhiteSpace(_selectedPlan?.PlanMaterialNo)) record.ProductionPlanNo = _selectedPlan.PlanMaterialNo; var inspectorId = user?.JeecgBizUserId ?? (user != null ? user.Id.ToString() : null); if (!string.IsNullOrWhiteSpace(inspectorId)) record.InspectorUserId = inspectorId; if (!string.IsNullOrWhiteSpace(user?.Account)) record.InspectorUsername = user.Account; if (!string.IsNullOrWhiteSpace(user?.RealName)) record.InspectorRealname = user.RealName; return record; } private static Axis[] BuildTimeAxis() => new[] { new Axis { Name = "时间(min)", MinLimit = 0, MaxLimit = 2, MinStep = 0.5, ForceStepToMin = true, LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static Axis[] BuildTempYAxis() => new[] { new Axis { Name = "温度(℃)", MinLimit = 189, MaxLimit = 201, MinStep = 3, ForceStepToMin = true, Labeler = v => FormatAxisTick(v, 189, 192, 195, 198, 201), LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static readonly double[] TorqueYTicks = { 0.0, 3.0, 5.9, 8.9, 11.8, 14.8 }; private static Axis[] BuildTorqueYAxis() => new[] { new Axis { Name = "S'(dNm)", MinLimit = 0, MaxLimit = 14.8, CustomSeparators = TorqueYTicks, Labeler = FormatTorqueAxisLabel, LabelsPaint = new SolidColorPaint(SKColors.Gray), NamePaint = new SolidColorPaint(SKColors.Gray) } }; private static string FormatTorqueAxisLabel(double value) { foreach (var tick in TorqueYTicks) { if (Math.Abs(value - tick) > 0.05) continue; return tick.ToString("0.0"); } return string.Empty; } private static string FormatAxisTick(double value, params double[] ticks) { foreach (var tick in ticks) { if (Math.Abs(value - tick) > 0.01) continue; return Math.Abs(tick - Math.Round(tick)) < 0.001 ? ((int)Math.Round(tick)).ToString() : tick.ToString("0.#"); } return string.Empty; } protected override void CleanUp() { if (_planChangedToken != null) { _eventAggregator.GetEvent().Unsubscribe(_planChangedToken); _planChangedToken = null; } if (_stdChangedToken != null) { _eventAggregator.GetEvent().Unsubscribe(_stdChangedToken); _stdChangedToken = null; } base.CleanUp(); } public new void Destroy() => base.Destroy(); } public class WorkShiftOption { public WorkShiftOption(string code, string name) { Code = code; Name = name; } public string Code { get; } public string Name { get; } }