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

750 lines
25 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
using HandyControl.Controls;
using LiveChartsCore;
using LiveChartsCore.SkiaSharpView;
using LiveChartsCore.SkiaSharpView.Painting;
using Prism.Mvvm;
using SkiaSharp;
using System.Collections.ObjectModel;
using YY.Admin.Core;
using YY.Admin.Core.Entity;
using YY.Admin.Core.Services;
using YY.Admin.Core.Session;
namespace YY.Admin.ViewModels.RubberQuickTest;
/// <summary>密炼生产计划班次展开项(桌面端筛选用)</summary>
public class MixingPlanShiftOption
{
public string PlanRowId { get; set; } = string.Empty;
public string? MachineId { get; set; }
public string? MachineName { get; set; }
public string ShiftKey { get; set; } = string.Empty;
public string ShiftCode { get; set; } = string.Empty;
public string ShiftName { get; set; } = string.Empty;
public DateTime? OrderDate { get; set; }
public string? PlanId { get; set; }
public string? OrderNo { get; set; }
public string? FormulaName { get; set; }
public string? MaterialName { get; set; }
public string DisplayText => string.IsNullOrWhiteSpace(OrderNo)
? MaterialName ?? FormulaName ?? string.Empty
: $"{OrderNo} | {MaterialName ?? FormulaName}";
}
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
{
public string RowNo { get; set; } = string.Empty;
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
{
private readonly IRubberQuickTestOperationService _operationService;
private readonly Random _rnd = new();
private List<MesXslMixingProductionPlan> _allPlans = new();
private List<MixingPlanShiftOption> _allShiftOptions = new();
private MesXslRubberQuickTestStd? _currentStd;
private const int ChartPointCount = 5;
public event Action? InspectColumnsChanged;
public RubberQuickTestOperationViewModel(
IRubberQuickTestOperationService operationService,
IContainerExtension container,
IRegionManager regionManager) : base(container, regionManager)
{
_operationService = operationService;
_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 LoadPlansAsync());
RefreshChartDemoCommand = new DelegateCommand(FillRandomChartData);
TemperatureSeries = new ObservableCollection<ISeries>
{
new LineSeries<double> { Name = "上模温度", Values = UpperTempValues, GeometrySize = 4, LineSmoothness = 0.3 },
new LineSeries<double> { Name = "下模温度", Values = LowerTempValues, GeometrySize = 4, LineSmoothness = 0.3 }
};
TorqueSeries = new ObservableCollection<ISeries>
{
new LineSeries<double> { Name = "S'(dNm)", Values = TorqueValues, GeometrySize = 4, LineSmoothness = 0.3 }
};
FillRandomChartData();
_ = LoadPlansAsync();
}
/// <summary>中间库未接入时的曲线演示说明</summary>
public string ChartDemoHint => "演示数据(中间库未接入,打开页面自动生成;接入后将显示实测曲线)";
public ObservableCollection<ISeries> TemperatureSeries { get; }
public ObservableCollection<ISeries> TorqueSeries { get; }
public ObservableCollection<double> UpperTempValues { get; } = new();
public ObservableCollection<double> LowerTempValues { get; } = new();
public ObservableCollection<double> 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<MixingPlanShiftOption> PlanOptions { 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();
ClearPlanSelection();
}
}
private string? _selectedMachine;
public string? SelectedMachine
{
get => _selectedMachine;
set
{
if (!SetProperty(ref _selectedMachine, value)) return;
RefreshShiftOptions();
ClearPlanSelection();
}
}
private WorkShiftOption? _selectedShift;
public WorkShiftOption? SelectedShift
{
get => _selectedShift;
set
{
if (!SetProperty(ref _selectedShift, value)) return;
RefreshPlanOptions();
ClearPlanSelection();
}
}
private MixingPlanShiftOption? _selectedPlan;
public MixingPlanShiftOption? SelectedPlan
{
get => _selectedPlan;
set
{
if (!SetProperty(ref _selectedPlan, value)) return;
ApplySelectedPlan();
}
}
public string? ProductionOrderNo => _selectedPlan?.OrderNo;
public string? MachineName => _selectedPlan?.MachineName ?? SelectedMachine;
public string? WorkShiftDisplay => SelectedShift?.Name ?? string.Empty;
public string? RubberMaterialName => _selectedPlan?.MaterialName ?? _selectedPlan?.FormulaName;
private string? _trainNo;
public string? TrainNo
{
get => _trainNo;
set => SetProperty(ref _trainNo, value);
}
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 LoadPlansAsync()
{
IsLoading = true;
try
{
_allPlans = await _operationService.GetMixingProductionPlansAsync();
BuildAllShiftOptions();
RefreshMachineOptions();
Growl.Success("密炼生产计划已刷新");
}
catch (Exception ex)
{
Growl.Error($"加载密炼生产计划失败:{ex.Message}");
}
finally
{
IsLoading = false;
}
}
private void BuildAllShiftOptions()
{
_allShiftOptions.Clear();
foreach (var row in _allPlans)
{
if (string.IsNullOrWhiteSpace(row.PlanId)) continue;
var shiftCode = row.ShiftFlag?.ToString() ?? string.Empty;
_allShiftOptions.Add(new MixingPlanShiftOption
{
PlanRowId = row.Id ?? string.Empty,
MachineId = row.MachineId,
MachineName = row.MachineName,
ShiftKey = shiftCode,
ShiftCode = shiftCode,
ShiftName = row.ShiftFlagText,
OrderDate = row.PlanDate,
PlanId = row.PlanId,
OrderNo = row.OrderNo,
FormulaName = row.FormulaName,
MaterialName = row.MaterialName
});
}
}
private IEnumerable<MixingPlanShiftOption> FilteredByDate =>
_allShiftOptions.Where(o => o.OrderDate?.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.ShiftCode)
.Select(g => g.First())
.OrderBy(o => o.ShiftCode);
foreach (var s in shifts)
ShiftOptions.Add(new WorkShiftOption(s.ShiftCode, s.ShiftName));
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 o in FilteredByDate
.Where(x => string.IsNullOrWhiteSpace(machine) || x.MachineName == machine)
.Where(x => string.IsNullOrWhiteSpace(shiftCode) || x.ShiftCode == shiftCode)
.OrderBy(x => x.OrderNo))
PlanOptions.Add(o);
if (SelectedPlan != null && PlanOptions.All(p => p.PlanId != SelectedPlan.PlanId))
SelectedPlan = null;
if (SelectedPlan == null && PlanOptions.Count > 0)
SelectedPlan = PlanOptions[0];
}
private void ClearPlanSelection()
{
_selectedPlan = null;
RaisePropertyChanged(nameof(SelectedPlan));
RaisePropertyChanged(nameof(ProductionOrderNo));
RaisePropertyChanged(nameof(MachineName));
RaisePropertyChanged(nameof(WorkShiftDisplay));
RaisePropertyChanged(nameof(RubberMaterialName));
ClearStdAndResults();
}
private void ApplySelectedPlan()
{
RaisePropertyChanged(nameof(ProductionOrderNo));
RaisePropertyChanged(nameof(MachineName));
RaisePropertyChanged(nameof(WorkShiftDisplay));
RaisePropertyChanged(nameof(RubberMaterialName));
_ = LoadStdAsync();
}
private async Task LoadStdAsync()
{
DataPointColumns.Clear();
InspectRows.Clear();
_currentStd = null;
InspectColumnsChanged?.Invoke();
var rubberName = RubberMaterialName;
if (string.IsNullOrWhiteSpace(rubberName)) return;
IsLoading = true;
try
{
_currentStd = await _operationService.GetStdByRubberMaterialNameAsync(rubberName);
if (_currentStd?.LineList == null || _currentStd.LineList.Count == 0)
{
Growl.Warning($"未找到胶料「{rubberName}」的使用中且已批准的快检实验标准");
return;
}
foreach (var line in _currentStd.LineList.OrderBy(l => l.SortNo ?? 0))
DataPointColumns.Add(line);
InspectColumnsChanged?.Invoke();
AddInspectRow();
AddInspectRowCommand.RaiseCanExecuteChanged();
}
catch (Exception ex)
{
Growl.Error($"加载实验标准失败:{ex.Message}");
}
finally
{
IsLoading = false;
}
}
private void ClearStdAndResults()
{
DataPointColumns.Clear();
InspectRows.Clear();
_currentStd = null;
InspectColumnsChanged?.Invoke();
}
/// <summary>打开页面或中间库无数据时填充曲线演示数据5 点对应 02min</summary>
private void FillRandomChartData()
{
UpperTempValues.Clear();
LowerTempValues.Clear();
TorqueValues.Clear();
for (int i = 0; i < ChartPointCount; i++)
{
var upper = 192 + _rnd.NextDouble() * 8;
var lower = upper - 2 - _rnd.NextDouble() * 2;
var torque = Math.Min(14, i * 2.5 + _rnd.NextDouble() * 2.5);
UpperTempValues.Add(Math.Round(upper, 2));
LowerTempValues.Add(Math.Round(lower, 2));
TorqueValues.Add(Math.Round(torque, 2));
}
UpperMoldTemp = UpperTempValues[^1];
LowerMoldTemp = LowerTempValues[^1];
TorqueS = TorqueValues[^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();
row.Cells.Add(cell);
}
row.RecalculateResult();
InspectRows.Add(row);
SaveCommand.RaiseCanExecuteChanged();
}
private void RemoveInspectRow(QuickTestInspectRowViewModel? row)
{
if (row == null) return;
InspectRows.Remove(row);
SelectedInspectRow = null;
SaveCommand.RaiseCanExecuteChanged();
}
private async Task SaveAsync()
{
if (string.IsNullOrWhiteSpace(TrainNo))
{
Growl.Warning("请填写车次");
return;
}
if (string.IsNullOrWhiteSpace(ProductionOrderNo))
{
Growl.Warning("请选择密炼计划以带出生产订单号");
return;
}
if (string.IsNullOrWhiteSpace(RubberMaterialName))
{
Growl.Warning("请先选择密炼计划以带出胶料名称");
return;
}
if (_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);
await _operationService.SaveRecordAsync(record);
Growl.Success("快检记录已保存到 MES");
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;
}
/// <summary>试验结果各数据点检测值取平均,写入快检记录明细</summary>
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;
}
/// <summary>试验结果全部检测值写入原始数据明细</summary>
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;
}
/// <summary>主表检验结果:任一行不合格则不合格</summary>
private static string JudgeInspectResultFromRows(IEnumerable<QuickTestInspectRowViewModel> rows)
{
if (!rows.Any())
return "0";
return rows.Any(r => r.InspectResultCode != "1") ? "0" : "1";
}
private MesXslRubberQuickTestRecord BuildSaveRecord(
List<MesXslRubberQuickTestRecordLine> lineList,
List<MesXslRubberQuickTestRecordRawLine> rawLineList,
int inspectTimes,
SysUser? user)
{
var record = new MesXslRubberQuickTestRecord
{
LineList = lineList,
RawLineList = rawLineList,
InspectTime = DateTime.Now,
InspectResult = JudgeInspectResultFromRows(InspectRows)
};
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(ProductionOrderNo))
record.ProductionPlanNo = ProductionOrderNo;
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,
Labels = new[] { "0", "0.5", "1", "1.5", "2" },
LabelsPaint = new SolidColorPaint(SKColors.Gray),
NamePaint = new SolidColorPaint(SKColors.Gray)
}
};
private static Axis[] BuildTempYAxis() => new[]
{
new Axis
{
Name = "温度(℃)",
MinLimit = 189,
MaxLimit = 201,
Labeler = v => v switch
{
189 => "189",
192 => "192",
195 => "195",
198 => "198",
201 => "201",
_ => string.Empty
},
LabelsPaint = new SolidColorPaint(SKColors.Gray),
NamePaint = new SolidColorPaint(SKColors.Gray)
}
};
private static Axis[] BuildTorqueYAxis() => new[]
{
new Axis
{
Name = "S'(dNm)",
MinLimit = 0,
MaxLimit = 14,
Labeler = v => v switch
{
0 => "0",
3 => "3",
6 => "6",
8 => "8",
11 => "11",
14 => "14",
_ => string.Empty
},
LabelsPaint = new SolidColorPaint(SKColors.Gray),
NamePaint = new SolidColorPaint(SKColors.Gray)
}
};
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; }
}