Files
qhmes/yy-admin-master/YY.Admin/ViewModels/RubberQuickTest/RubberQuickTestOperationViewModel.cs

1897 lines
42 KiB
C#
Raw Normal View History

using HandyControl.Controls;
using LiveChartsCore;
using LiveChartsCore.Defaults;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using Prism.Events;
using Prism.Mvvm;
using Prism.Navigation;
using SkiaSharp;
using System.Collections.ObjectModel;
using System.Threading;
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<QuickTestInspectCellViewModel>? ValueChanged;
}
public class QuickTestInspectRowViewModel : BindableBase
{
private string _rowNo = string.Empty;
public string RowNo
{
get => _rowNo;
set => SetProperty(ref _rowNo, value);
}
public ObservableCollection<QuickTestInspectCellViewModel> 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));
}
}
/// <summary>胶料快检记录操作台 ViewModel密炼计划、实验标准均读本地缓存</summary>
public class RubberQuickTestOperationViewModel : BaseViewModel, INavigationAware
{
private readonly IRubberQuickTestRecordService _recordService;
private readonly IMixingProductionPlanService _planService;
private readonly IRubberQuickTestStdService _stdService;
private readonly Random _rnd = new();
private bool _saveInProgress;
private string? _loadedDetailLocalId;
private string? _loadedDetailMesId;
private int _navigationApplyVersion;
private List<MesXslMixingProductionPlan> _allPlans = new();
private List<MesXslRubberQuickTestStd> _allStds = new();
private MesXslRubberQuickTestStd? _currentStd;
private SubscriptionToken? _planChangedToken;
private SubscriptionToken? _stdChangedToken;
private const int ChartPointCount = 5;
/// <summary>曲线横坐标时间点min0、0.5、1、1.5、2</summary>
private static readonly double[] ChartTimeMinutes = { 0, 0.5, 1, 1.5, 2 };
public event Action? InspectColumnsChanged;
public RubberQuickTestOperationViewModel(
IRubberQuickTestRecordService recordService,
IMixingProductionPlanService planService,
IRubberQuickTestStdService stdService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_recordService = recordService;
_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(), () => CanSave);
RefreshPlansCommand = new DelegateCommand(async () => await LoadLocalDataAsync(showSuccess: true));
RefreshChartDemoCommand = new DelegateCommand(FillRandomChartData);
TemperatureSeries = new ObservableCollection<ISeries>
{
new LineSeries<ObservablePoint> { Name = "上模温度", Values = UpperTempValues, GeometrySize = 4, LineSmoothness = 0.3 },
new LineSeries<ObservablePoint> { Name = "下模温度", Values = LowerTempValues, GeometrySize = 4, LineSmoothness = 0.3 }
};
TorqueSeries = new ObservableCollection<ISeries>
{
new LineSeries<ObservablePoint> { Name = "S'(dNm)", Values = TorqueValues, GeometrySize = 4, LineSmoothness = 0.3 }
};
FillRandomChartData();
InspectorDisplay = ResolveInspectorDisplay();
_planChangedToken = _eventAggregator.GetEvent<MixingProductionPlanChangedEvent>()
.Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread);
_stdChangedToken = _eventAggregator.GetEvent<RubberQuickTestStdChangedEvent>()
.Subscribe(async _ => await ReloadLocalDataQuietAsync(), ThreadOption.UIThread);
RecalculateOverallInspectResult();
}
private bool _isReadOnly;
public bool IsReadOnly
{
get => _isReadOnly;
private set
{
if (!SetProperty(ref _isReadOnly, value)) return;
RaisePropertyChanged(nameof(IsEditable));
RaisePropertyChanged(nameof(ShowSaveButton));
NotifySaveStateChanged();
InspectColumnsChanged?.Invoke();
}
}
public bool IsEditable => !IsReadOnly;
public bool ShowSaveButton => !IsReadOnly;
public bool CanSave => InspectRows.Count > 0 && !_saveInProgress && !IsReadOnly;
private void NotifySaveStateChanged()
{
RaisePropertyChanged(nameof(CanSave));
SaveCommand.RaiseCanExecuteChanged();
}
protected override void OnIsLoadingChanged()
{
base.OnIsLoadingChanged();
NotifySaveStateChanged();
}
public void OnNavigatedTo(NavigationContext navigationContext)
{
_ = ApplyNavigationAsync(navigationContext.Parameters);
}
private async Task ApplyNavigationAsync(INavigationParameters parameters)
{
var version = Interlocked.Increment(ref _navigationApplyVersion);
if (parameters.TryGetValue<bool>("readOnly", out var readOnly) && readOnly)
{
if (parameters.TryGetValue<string>("localId", out var localId) && !string.IsNullOrWhiteSpace(localId))
{
_loadedDetailLocalId = localId;
_loadedDetailMesId = null;
await LoadFromLocalItemAsync(localId);
return;
}
if (parameters.TryGetValue<string>("mesId", out var mesId) && !string.IsNullOrWhiteSpace(mesId))
{
_loadedDetailMesId = mesId;
_loadedDetailLocalId = null;
await LoadFromMesIdAsync(mesId);
return;
}
}
if (version != _navigationApplyVersion) return;
_loadedDetailLocalId = null;
_loadedDetailMesId = null;
IsReadOnly = false;
ResetFormForNewEntry();
await LoadLocalDataAsync(showSuccess: false);
}
public bool IsNavigationTarget(NavigationContext navigationContext)
{
var parameters = navigationContext.Parameters;
if (parameters.TryGetValue<bool>("readOnly", out var readOnly) && readOnly)
{
if (parameters.TryGetValue<string>("localId", out var localId) && !string.IsNullOrWhiteSpace(localId))
return IsReadOnly && string.Equals(_loadedDetailLocalId, localId, StringComparison.OrdinalIgnoreCase);
if (parameters.TryGetValue<string>("mesId", out var mesId) && !string.IsNullOrWhiteSpace(mesId))
return IsReadOnly && string.Equals(_loadedDetailMesId, mesId, StringComparison.OrdinalIgnoreCase);
return false;
}
return !IsReadOnly && _loadedDetailLocalId == null && _loadedDetailMesId == null;
}
private void ResetFormForNewEntry()
{
RecordNo = null;
TrainNo = null;
InspectTimesText = "1";
ClearPlanAndStdSelection();
RecalculateOverallInspectResult();
NotifySaveStateChanged();
}
public void OnNavigatedFrom(NavigationContext navigationContext) { }
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;
}
/// <summary>中间库未接入时的曲线演示说明</summary>
public string ChartDemoHint => "演示数据(中间库未接入,打开页面自动生成;接入后将显示实测曲线)";
public ObservableCollection<ISeries> TemperatureSeries { get; }
public ObservableCollection<ISeries> TorqueSeries { get; }
public ObservableCollection<ObservablePoint> UpperTempValues { get; } = new();
public ObservableCollection<ObservablePoint> LowerTempValues { get; } = new();
public ObservableCollection<ObservablePoint> 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<string> MachineOptions { get; } = new();
public ObservableCollection<WorkShiftOption> ShiftOptions { get; } = new();
public ObservableCollection<MesXslMixingProductionPlan> PlanOptions { get; } = new();
public ObservableCollection<MesXslRubberQuickTestStd> StdOptions { get; } = new();
public ObservableCollection<MesXslRubberQuickTestStdLine> DataPointColumns { get; } = new();
public ObservableCollection<QuickTestInspectRowViewModel> 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;
2026-06-17 17:52:31 +08:00
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;
/// <summary>快检记录号(保存后由 MES 生成,只读)</summary>
public string? RecordNo
{
get => _recordNo;
private set => SetProperty(ref _recordNo, value);
}
private string _inspectorDisplay = string.Empty;
/// <summary>检验人(当前登录用户,只读)</summary>
public string InspectorDisplay
{
get => _inspectorDisplay;
private set => SetProperty(ref _inspectorDisplay, value);
}
private string _overallInspectResultDisplay = "不合格";
/// <summary>检验结果(试验信息区汇总,只读)</summary>
public string OverallInspectResultDisplay
{
get => _overallInspectResultDisplay;
private set => SetProperty(ref _overallInspectResultDisplay, value);
}
/// <summary>检验结果编码1 合格0 不合格</summary>
public string OverallInspectResultCode { get; private set; } = "0";
private string? _inspectTimesText;
/// <summary>试验次数(手填正整数)</summary>
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()
{
if (IsReadOnly) return;
try
{
_allPlans = await _planService.GetAllCachedAsync();
_allStds = await _stdService.GetAllCachedAsync();
RefreshMachineOptions();
RefreshStdOptions();
}
catch (Exception ex)
{
_logger.Warning($"[胶料快检记录] 本地数据刷新失败:{ex.Message}");
}
}
private IEnumerable<MesXslMixingProductionPlan> 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<MesXslRubberQuickTestStd> 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);
NotifySaveStateChanged();
RecalculateOverallInspectResult();
}
private void RemoveInspectRow(QuickTestInspectRowViewModel? row)
{
if (row == null) return;
InspectRows.Remove(row);
SelectedInspectRow = null;
RenumberInspectRows();
NotifySaveStateChanged();
RecalculateOverallInspectResult();
}
private async Task SaveAsync()
{
if (_saveInProgress) return;
_saveInProgress = true;
NotifySaveStateChanged();
try
{
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 rawLineList = BuildRawLineList();
if (rawLineList.Count == 0)
{
Growl.Warning("无法生成试验结果原始数据");
return;
}
var stdLineList = BuildStdLineList();
if (stdLineList.Count == 0)
{
Growl.Warning("无法生成数据标准明细");
return;
}
var chartPointList = BuildChartPointList();
var user = AppSession.CurrentUser;
IsLoading = true;
var record = BuildSaveRecord(stdLineList, rawLineList, chartPointList, inspectTimes, user);
var saved = await _recordService.SaveAsync(record);
RecordNo = saved.Record.RecordNo;
var syncHint = saved.SyncStatus == "Synced" ? "并已同步到 MES" : "MES 同步待重试)";
Growl.Success(string.IsNullOrWhiteSpace(RecordNo)
? $"胶料快检记录已保存到本地{syncHint}"
: $"胶料快检记录已保存,快检记录号:{RecordNo}{syncHint}");
InspectRows.Clear();
NotifySaveStateChanged();
}
catch (Exception ex)
{
Growl.Error($"保存失败:{ex.Message}");
}
finally
{
IsLoading = false;
_saveInProgress = false;
NotifySaveStateChanged();
}
}
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<MesXslRubberQuickTestRecordLine> BuildAveragedLineList()
{
var lines = new List<MesXslRubberQuickTestRecordLine>();
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<MesXslRubberQuickTestRecordRawLine> BuildRawLineList()
{
var rawLines = new List<MesXslRubberQuickTestRecordRawLine>();
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<MesXslRubberQuickTestRecordStdLine> stdLineList,
List<MesXslRubberQuickTestRecordRawLine> rawLineList,
List<MesXslRubberQuickTestRecordChartPoint> chartPointList,
int inspectTimes,
SysUser? user)
{
var record = new MesXslRubberQuickTestRecord
{
StdLineList = stdLineList,
RawLineList = rawLineList,
ChartPointList = chartPointList,
InspectTime = DateTime.Now,
CreateTime = DateTime.Now,
InspectResult = OverallInspectResultCode,
RecordNo = _recordService.GenerateRecordNo(RubberMaterialName ?? string.Empty)
};
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(_currentStd?.StdName))
record.StdName = _currentStd.StdName;
if (!string.IsNullOrWhiteSpace(_currentStd?.TestMethodId))
record.TestMethodId = _currentStd.TestMethodId;
if (!string.IsNullOrWhiteSpace(TestMethodName))
record.TestMethodName = TestMethodName;
if (!string.IsNullOrWhiteSpace(_currentStd?.QuickTestTypeId))
record.QuickTestTypeId = _currentStd.QuickTestTypeId;
if (!string.IsNullOrWhiteSpace(_currentStd?.QuickTestTypeName))
record.QuickTestTypeName = _currentStd.QuickTestTypeName;
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?.PlanNo))
record.ProductionPlanNo = _selectedPlan.PlanNo;
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 List<MesXslRubberQuickTestRecordStdLine> BuildStdLineList()
{
var lines = new List<MesXslRubberQuickTestRecordStdLine>();
int sort = 0;
foreach (var col in DataPointColumns)
{
lines.Add(new MesXslRubberQuickTestRecordStdLine
{
DataPointId = col.DataPointId,
PointName = col.PointName,
LowerLimit = col.LowerLimit,
LowerWarn = col.LowerWarn,
TargetValue = col.TargetValue,
UpperWarn = col.UpperWarn,
UpperLimit = col.UpperLimit,
SortNo = sort++
});
}
return lines;
}
private List<MesXslRubberQuickTestRecordChartPoint> BuildChartPointList()
{
var points = new List<MesXslRubberQuickTestRecordChartPoint>();
int sort = 0;
for (int i = 0; i < ChartPointCount && i < UpperTempValues.Count; i++)
{
points.Add(new MesXslRubberQuickTestRecordChartPoint
{
TimeMin = (decimal)(UpperTempValues[i].X ?? ChartTimeMinutes[i]),
UpperTemp = UpperTempValues[i].Y.HasValue ? (decimal)UpperTempValues[i].Y!.Value : null,
LowerTemp = i < LowerTempValues.Count && LowerTempValues[i].Y.HasValue ? (decimal)LowerTempValues[i].Y!.Value : null,
TorqueS = i < TorqueValues.Count && TorqueValues[i].Y.HasValue ? (decimal)TorqueValues[i].Y!.Value : null,
SortNo = sort++
});
}
return points;
}
private async Task LoadFromLocalItemAsync(string localId)
{
var item = _recordService.GetByLocalId(localId);
if (item == null)
{
Growl.Warning("未找到快检记录");
return;
}
await ApplyRecordDetailAsync(item.Record);
}
private async Task LoadFromMesIdAsync(string mesId)
{
var record = await _recordService.GetByIdAsync(mesId);
if (record == null)
{
Growl.Warning("未找到快检记录");
return;
}
await ApplyRecordDetailAsync(record);
}
private async Task ApplyRecordDetailAsync(MesXslRubberQuickTestRecord r)
{
IsReadOnly = true;
RecordNo = r.RecordNo;
MixingDate = r.ProductionDate ?? DateTime.Today;
SelectedMachine = r.ProdEquipmentName;
TrainNo = r.TrainNo;
InspectTimesText = r.InspectTimes?.ToString() ?? "1";
InspectorDisplay = r.InspectorRealname ?? ResolveInspectorDisplay();
OverallInspectResultCode = r.InspectResult ?? "0";
OverallInspectResultDisplay = string.Equals(r.InspectResult, "1", StringComparison.Ordinal) ? "合格" : "不合格";
TestMethodName = r.TestMethodName;
MachineOptions.Clear();
if (!string.IsNullOrWhiteSpace(r.ProdEquipmentName))
MachineOptions.Add(r.ProdEquipmentName);
ShiftOptions.Clear();
var shift = new WorkShiftOption(r.WorkShift ?? "", r.WorkShift ?? "");
ShiftOptions.Add(shift);
SelectedShift = shift;
PlanOptions.Clear();
var plan = new MesXslMixingProductionPlan
{
PlanNo = r.ProductionPlanNo,
MaterialName = r.RubberMaterialName,
MachineName = r.ProdEquipmentName
};
PlanOptions.Add(plan);
_selectedPlan = plan;
RaisePropertyChanged(nameof(SelectedPlan));
RaisePropertyChanged(nameof(MachineName));
RaisePropertyChanged(nameof(RubberMaterialName));
StdOptions.Clear();
var std = new MesXslRubberQuickTestStd { Id = r.StdId, StdName = r.StdName, TestMethodName = r.TestMethodName };
StdOptions.Add(std);
_selectedStd = std;
RaisePropertyChanged(nameof(SelectedStd));
DataPointColumns.Clear();
InspectRows.Clear();
foreach (var sl in (r.StdLineList ?? new List<MesXslRubberQuickTestRecordStdLine>()).OrderBy(x => x.SortNo ?? 0))
{
DataPointColumns.Add(new MesXslRubberQuickTestStdLine
{
DataPointId = sl.DataPointId,
PointName = sl.PointName,
LowerLimit = sl.LowerLimit,
LowerWarn = sl.LowerWarn,
TargetValue = sl.TargetValue,
UpperWarn = sl.UpperWarn,
UpperLimit = sl.UpperLimit,
SortNo = sl.SortNo
});
}
InspectColumnsChanged?.Invoke();
var grouped = (r.RawLineList ?? new List<MesXslRubberQuickTestRecordRawLine>())
.GroupBy(x => x.RowNo ?? string.Empty)
.OrderBy(g => g.Key, StringComparer.Ordinal);
foreach (var g in grouped)
{
var row = new QuickTestInspectRowViewModel { RowNo = g.Key };
for (int i = 0; i < DataPointColumns.Count; i++)
{
var col = DataPointColumns[i];
var raw = g.FirstOrDefault(x => x.DataPointId == col.DataPointId || x.InspectItem == col.PointName);
var cell = new QuickTestInspectCellViewModel
{
DataPointId = col.DataPointId,
PointName = col.PointName ?? string.Empty,
LowerLimit = col.LowerLimit,
UpperLimit = col.UpperLimit,
Value = raw?.InspectValue
};
row.Cells.Add(cell);
}
row.RecalculateResult();
InspectRows.Add(row);
}
UpperTempValues.Clear();
LowerTempValues.Clear();
TorqueValues.Clear();
foreach (var pt in (r.ChartPointList ?? new List<MesXslRubberQuickTestRecordChartPoint>()).OrderBy(x => x.SortNo ?? 0))
{
var time = (double)(pt.TimeMin ?? 0);
if (pt.UpperTemp != null) UpperTempValues.Add(new ObservablePoint(time, (double)pt.UpperTemp.Value));
if (pt.LowerTemp != null) LowerTempValues.Add(new ObservablePoint(time, (double)pt.LowerTemp.Value));
if (pt.TorqueS != null) TorqueValues.Add(new ObservablePoint(time, (double)pt.TorqueS.Value));
}
if (UpperTempValues.Count > 0) UpperMoldTemp = UpperTempValues[^1].Y ?? 0;
if (LowerTempValues.Count > 0) LowerMoldTemp = LowerTempValues[^1].Y ?? 0;
if (TorqueValues.Count > 0) TorqueS = TorqueValues[^1].Y ?? 0;
await Task.CompletedTask;
}
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<MixingProductionPlanChangedEvent>().Unsubscribe(_planChangedToken);
_planChangedToken = null;
}
if (_stdChangedToken != null)
{
_eventAggregator.GetEvent<RubberQuickTestStdChangedEvent>().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; }
}