421 lines
19 KiB
C#
421 lines
19 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.Api.Devices;
|
|
using Aurora.Api.Manager;
|
|
using Aurora.Framework.Core;
|
|
using Aurora.Util.HislogPlus;
|
|
using Aurora.View;
|
|
using AuroraACS;
|
|
using AuroraHeightSensor;
|
|
using CoreUnit.Scanner.UserControl.ScannerCanvas;
|
|
using DB.Recipe;
|
|
using Hermes;
|
|
using Hermes.Functional;
|
|
using ILSOS_PrismaticGun.Alarm;
|
|
using ILSOS_PrismaticGun.Device.ACS;
|
|
using ILSOS_PrismaticGun.Process;
|
|
using ILSOS_PrismaticGun.Recipes;
|
|
using ILSOS_PrismaticGun.Settings;
|
|
using OpenCvSharp.Dnn;
|
|
using PropertyTools.Wpf;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Runtime.Remoting.Contexts;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using System.Windows;
|
|
using System.Windows.Forms;
|
|
using System.Windows.Input;
|
|
using VisionCommon;
|
|
using WeldingCommon;
|
|
using WeldingCommon.Alarm;
|
|
using WeldingCommon.Settings;
|
|
using WeldingCommon.Utils;
|
|
using WeldingCommon.Utils.Diff;
|
|
using DelegateCommand = Aurora.View.DelegateCommand;
|
|
|
|
namespace ILSOS_PrismaticGun.Views.Recipe
|
|
{
|
|
internal class UCMainRecipeViewModel : NotifyPropertyChangedBase, IRecipeViewModel
|
|
{
|
|
private Logger Log = new Logger("UCMainRecipeViewModel");
|
|
private readonly IAuroraEnvironment _env;
|
|
public ICommand CMD_VisibleChanged { get; }
|
|
public ICommand CMD_GetPosition { get; }
|
|
public ICommand CMD_MovePosition { get; }
|
|
public ICommand CMD_GetHeight { get; }
|
|
public ICommand CMD_AddHeightParam { get; }
|
|
public ICommand CMD_DeleteHeightParam { get; }
|
|
|
|
public double JogMm {
|
|
get => _JogMm;
|
|
set {
|
|
if (value > 3) {
|
|
System.Windows.MessageBox.Show("3보다 클 수 없습니다.");
|
|
SetProperty(ref _JogMm, 3);
|
|
return;
|
|
}
|
|
SetProperty(ref _JogMm, value);
|
|
}
|
|
}
|
|
private double _JogMm = 0.05;
|
|
public ICommand CMD_YP { get; }
|
|
public ICommand CMD_XM { get; }
|
|
public ICommand CMD_XP { get; }
|
|
public ICommand CMD_YM { get; }
|
|
|
|
public bool IsManualStatus { get => _IsManualStatus; set => SetProperty(ref _IsManualStatus, value); }
|
|
private bool _IsManualStatus = true;
|
|
|
|
public bool IsDoorLock { get => _IsDoorLock; set => SetProperty(ref _IsDoorLock, value); }
|
|
private bool _IsDoorLock = true;
|
|
|
|
private readonly IRecipeManager _Manager;
|
|
private (int id, string name) _RecipeId;
|
|
|
|
private IPrismaticGunACS _motion;
|
|
private PrismaticGunACS _acs;
|
|
private ACSMotor _motorX;
|
|
private ACSMotor _motorY;
|
|
private HeightSensor _height;
|
|
// OutputType 콤보박스 항목 목록 (eOutType 열거형 전체)
|
|
// 파라미터 패널 전환은 XAML DataTemplate + DataTrigger로 처리 — Visibility 속성 불필요
|
|
public List<MainRecipe.eOutType> MotorIDList { get; } =
|
|
Enum.GetValues(typeof(MainRecipe.eOutType)).Cast<MainRecipe.eOutType>().ToList();
|
|
|
|
/// <summary>OutputType 콤보박스 편집 가능 여부. lockedOutputType 지정 시 false.</summary>
|
|
public bool IsOutputTypeEditable { get; private set; } = true;
|
|
private readonly MainRecipe.eOutType? _lockedOutputType;
|
|
|
|
public MainRecipe Recipe { get => _Recipe; private set => SetProperty(ref _Recipe, value); }
|
|
private MainRecipe _Recipe;
|
|
|
|
private MainRecipe _RecipeBeforeChanged;
|
|
public string _recipe;
|
|
|
|
public HeightMeareUnit SelectedHeightParam { get => _SelectedHeightParam; set => SetProperty(ref _SelectedHeightParam, value); }
|
|
private HeightMeareUnit _SelectedHeightParam;
|
|
|
|
public string PositionSettingGuide { get; set; }
|
|
public string LaserOnOffRamping { get; set; }
|
|
public string HeightSettingGuide { get; set; }
|
|
|
|
public bool EnableControlForRMS { get => _DisableControlForRMS; set => SetProperty(ref _DisableControlForRMS, value); }
|
|
private bool _DisableControlForRMS = false;
|
|
|
|
private readonly eStage stage;
|
|
public UCMainRecipeViewModel(IAuroraEnvironment auroraEnv, (int, string) recipe, IRecipeItem recipeItem, string recipeId,
|
|
MainRecipe.eOutType? lockedOutputType = null)
|
|
{
|
|
_env = auroraEnv;
|
|
_lockedOutputType = lockedOutputType;
|
|
if (lockedOutputType.HasValue)
|
|
IsOutputTypeEditable = false;
|
|
_motion = _acs = _env.DeviceManager[DeviceIds.ACS] as PrismaticGunACS;
|
|
_RecipeId = recipe;
|
|
_recipe = recipeId;
|
|
_Manager = auroraEnv.RecipeManager;
|
|
|
|
var setting = _env.SettingEntry[CommonSettingIds.SystemParameter] as WeldingCommon.Settings.SystemParameter;
|
|
PositionSettingGuide = Path.Combine(setting.ResourceFolderPath, "TeachingPosition.jpg");
|
|
LaserOnOffRamping = Path.Combine(setting.ResourceFolderPath, "LaserOnOff.jpg");
|
|
HeightSettingGuide = Path.Combine(setting.ResourceFolderPath, "HeightMeasure.jpg");
|
|
|
|
if (recipeId.Contains("A")) {
|
|
stage = eStage.A;
|
|
_motorX = _env.DeviceManager[MotorIds.ALineX] as ACSMotor;
|
|
_motorY = _env.DeviceManager[MotorIds.ALineY] as ACSMotor;
|
|
} else {
|
|
stage = eStage.B;
|
|
_motorX = _env.DeviceManager[MotorIds.BLineX] as ACSMotor;
|
|
_motorY = _env.DeviceManager[MotorIds.BLineY] as ACSMotor;
|
|
}
|
|
_height = _env.DeviceManager[DeviceIds.HeightSensor] as HeightSensor;
|
|
|
|
Recipe = recipeItem as MainRecipe;
|
|
|
|
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
|
|
CMD_GetPosition = new DelegateCommand(GetPosition);
|
|
CMD_GetHeight = new DelegateCommand(GetHeight);
|
|
CMD_MovePosition = new DelegateCommand(MovePosition);
|
|
//CMD_AddHeightParam = new DelegateCommand(AddHeightParam);
|
|
//CMD_DeleteHeightParam = new DelegateCommand(DeleteHeightParam);
|
|
|
|
CMD_YP = new DelegateCommand(OnYPlus);
|
|
CMD_XM = new DelegateCommand(OnXMinus);
|
|
CMD_XP = new DelegateCommand(OnXPlus);
|
|
CMD_YM = new DelegateCommand(OnYMinus);
|
|
|
|
_env.EventObservable.AsObservable(CommonEventIds.EVENT_EQUIPMENT_STATUS_CHANGE)
|
|
.Subscribe(x => OnChangedEquipmentStatus());
|
|
|
|
_env.EventObservable.AsObservable(CommonEventIds.EVENT_CHANGE_DOOR_LOCK)
|
|
.Where(param => param.Param is bool && param.Param != null)
|
|
.Select(param => (bool)param.Param)
|
|
.Subscribe(param => IsDoorLock = !param);
|
|
}
|
|
|
|
private object OnChangedEquipmentStatus()
|
|
{
|
|
IsManualStatus = Status.EquipmentStatus == eEqpStatus.Manual;
|
|
//_env.RecipeManager.SaveLock(_recipe, !IsManualStatus);
|
|
return null;
|
|
}
|
|
|
|
private void OnYPlus(object obj)
|
|
{
|
|
if (!_acs.IsActivated) {
|
|
System.Windows.MessageBox.Show("ACS Deactivated");
|
|
return;
|
|
}
|
|
_motorY.SetTargetPos(JogMm, TargetPosType.REL);
|
|
_motorY.SetMoveVelocityImm(Speed.L);
|
|
_motorY.Move();
|
|
}
|
|
|
|
private void OnXMinus(object obj)
|
|
{
|
|
if (!_acs.IsActivated) {
|
|
System.Windows.MessageBox.Show("ACS Deactivated");
|
|
return;
|
|
}
|
|
_motorX.SetTargetPos(-JogMm, TargetPosType.REL);
|
|
_motorX.SetMoveVelocityImm(Speed.L);
|
|
_motorX.Move();
|
|
}
|
|
|
|
private void OnXPlus(object obj)
|
|
{
|
|
if (!_acs.IsActivated) {
|
|
System.Windows.MessageBox.Show("ACS Deactivated");
|
|
return;
|
|
}
|
|
_motorX.SetTargetPos(JogMm, TargetPosType.REL);
|
|
_motorX.SetMoveVelocityImm(Speed.L);
|
|
_motorX.Move();
|
|
}
|
|
|
|
private void OnYMinus(object obj)
|
|
{
|
|
if (!_acs.IsActivated) {
|
|
System.Windows.MessageBox.Show("ACS Deactivated");
|
|
return;
|
|
}
|
|
_motorY.SetTargetPos(-JogMm, TargetPosType.REL);
|
|
_motorY.SetMoveVelocityImm(Speed.L);
|
|
_motorY.Move();
|
|
}
|
|
|
|
private void GetPosition(object obj)
|
|
{
|
|
if (!_acs.IsActivated) {
|
|
System.Windows.MessageBox.Show("ACS Deactivated");
|
|
return;
|
|
}
|
|
|
|
if (System.Windows.MessageBox.Show("위치를 가져오시겠습니까?", "Get Position", MessageBoxButton.YesNo) == MessageBoxResult.Yes) {
|
|
if (obj is ProcessUnit param) {
|
|
param.PosX = _motorX.GetActualPos();
|
|
param.PosY = _motorY.GetActualPos();
|
|
} else if (obj is HeightMeareUnit measureUnit) {
|
|
measureUnit.PosX = _motorX.GetActualPos();
|
|
measureUnit.PosY = _motorY.GetActualPos();
|
|
}
|
|
}
|
|
}
|
|
|
|
private void GetHeight(object obj)
|
|
{
|
|
if(!_height.IsActivated)
|
|
System.Windows.MessageBox.Show("HeightSensor Deactivated");
|
|
|
|
_height.MeasureAll(out List<double> val);
|
|
if (val == null || val.Count < 4) {
|
|
System.Windows.MessageBox.Show("측정 실패, 장치가 올바르게 설치되지 않았습니다.");
|
|
return;
|
|
}
|
|
MessageUtil.CreateAndShowMessageDialog("Height", $"높이센서 측정값 : {val[(2 * (int)stage)]} / {val[(2 * (int)stage) + 1]}");
|
|
}
|
|
|
|
//private void AddHeightParam(object obj)
|
|
//{
|
|
// if (Recipe.HeightSensorMeasurePoints.Count >= 10) {
|
|
// MessageUtil.CreateAndShowMessageDialog("Fail", $"List Limit Over [Current:{Recipe.HeightSensorMeasurePoints.Count} Max: 5]");
|
|
// return;
|
|
// }
|
|
|
|
// if (SelectedHeightParam == null) {
|
|
// Recipe.HeightSensorMeasurePoints.Add(new HeightMeareUnit() { Index = Recipe.HeightSensorMeasurePoints.Count + 1 });
|
|
// return;
|
|
// }
|
|
|
|
// Recipe.HeightSensorMeasurePoints.Insert(Recipe.HeightSensorMeasurePoints.IndexOf(SelectedHeightParam) + 1, new HeightMeareUnit());
|
|
// for (int i = 0, j = 1; i < Recipe.HeightSensorMeasurePoints.Count; i++, j++)
|
|
// Recipe.HeightSensorMeasurePoints[i].Index = j;
|
|
//}
|
|
|
|
//private void DeleteHeightParam(object obj)
|
|
//{
|
|
// if (SelectedHeightParam == null)
|
|
// return;
|
|
|
|
// Recipe.HeightSensorMeasurePoints.Remove(SelectedHeightParam);
|
|
|
|
// for (int i = 0, j = 1; i < Recipe.HeightSensorMeasurePoints.Count; i++, j++)
|
|
// Recipe.HeightSensorMeasurePoints[i].Index = j;
|
|
//}
|
|
public bool CheckValidPosition(double xPos, double yPos)
|
|
{
|
|
if (!_acs.IsActivated)
|
|
return false;
|
|
|
|
return false;
|
|
}
|
|
private void MovePosition(object obj)
|
|
{
|
|
if (!_acs.IsActivated)
|
|
return;
|
|
|
|
if (System.Windows.MessageBox.Show("이동하시겠습니까?", "Motion Move", MessageBoxButton.YesNo) == MessageBoxResult.Yes) {
|
|
if (obj is ProcessUnit param) {
|
|
var teachingSetting = _env.SettingEntry[SettingIds.TeachingPos] as TeachingPositionSetting;
|
|
var EnableLoadNegX = teachingSetting.Get(stage, ePLCEnableLimitPosition.LoadNegativeLimit).AxisX;
|
|
var EnableUnloadPositX = teachingSetting.Get(stage, ePLCEnableLimitPosition.UnloadPositiveLimit).AxisX;
|
|
if(EnableLoadNegX > _motorX.GetActualPos() || _motorX.GetActualPos() > EnableUnloadPositX){
|
|
System.Windows.MessageBox.Show($"현재위치({_motorX.GetActualPos()})에서 이동이 불가합니다. 가능영역({EnableLoadNegX} ~ {EnableUnloadPositX})");
|
|
return;
|
|
}
|
|
|
|
_motorX.SetTargetPos(param.PosX, TargetPosType.ABS);
|
|
_motorX.SetMoveVelocityImm(Speed.L);
|
|
_motorX.Move();
|
|
|
|
_motorY.SetTargetPos(param.PosY, TargetPosType.ABS);
|
|
_motorY.SetMoveVelocityImm(Speed.L);
|
|
_motorY.Move();
|
|
}
|
|
else if(obj is HeightMeareUnit measureUnit) {
|
|
var teachingSetting = _env.SettingEntry[SettingIds.TeachingPos] as TeachingPositionSetting;
|
|
var EnableNegativePos = teachingSetting.Get(stage, ePLCEnableLimitPosition.LoadNegativeLimit).AxisX;
|
|
var PLCLoadEnableOffPosition = measureUnit.PosX <= EnableNegativePos;
|
|
var MoveDirectionNeg = measureUnit.PosX + 0.1 < _motorX.GetActualPos();
|
|
|
|
var XList = Recipe.HeightSensorMeasurePoints.Select(t => t.PosX).ToList();
|
|
var minValue = XList.Min();
|
|
if (_motorX.GetActualPos() < minValue - 0.01) {
|
|
System.Windows.MessageBox.Show($"현재위치({_motorX.GetActualPos()})가 측정위치의 최소값({minValue})보다 작은 경우, 이동이 불가합니다.");
|
|
return;
|
|
}
|
|
|
|
if (PLCLoadEnableOffPosition && MoveDirectionNeg) {
|
|
if (!MessageUtil.ShowYesNoMessageDialog("측정 실패", $"PLC 이재기가 내려올 경우 충돌 위치 입니다. 이동하시겠습니까?"))
|
|
return;
|
|
}
|
|
|
|
_motorX.SetTargetPos(measureUnit.PosX, TargetPosType.ABS);
|
|
_motorX.SetMoveVelocityImm(Speed.L);
|
|
_motorX.Move();
|
|
|
|
_motorY.SetTargetPos(measureUnit.PosY, TargetPosType.ABS);
|
|
_motorY.SetMoveVelocityImm(Speed.L);
|
|
_motorY.Move();
|
|
}
|
|
}
|
|
}
|
|
private void VisibleChanged(object obj)
|
|
{
|
|
bool visible = (bool)obj;
|
|
if (visible){
|
|
Recipe.UpdateCurveRingPower();
|
|
}
|
|
else
|
|
;
|
|
}
|
|
public void OnChanged((int id, string name) recipe)
|
|
{
|
|
Recipe = _Manager.RecipeItem(recipe.id, _recipe) as MainRecipe;
|
|
if (_lockedOutputType.HasValue)
|
|
Recipe.OutputType = _lockedOutputType.Value;
|
|
_RecipeBeforeChanged = Recipe.Clone() as MainRecipe;
|
|
}
|
|
|
|
public void Reloaded((int id, string name) recipe)
|
|
{
|
|
Recipe = _Manager.RecipeItem(recipe.id, _recipe) as MainRecipe;
|
|
if (_lockedOutputType.HasValue)
|
|
Recipe.OutputType = _lockedOutputType.Value;
|
|
}
|
|
|
|
public void OnModelSelectChanged(string modelName)
|
|
{
|
|
|
|
}
|
|
|
|
public bool PreSaved(int id, string recipeName)
|
|
{
|
|
try {
|
|
if(ApplicationExt.PermissionLevel > (uint)PermissionLevel.LEVEL3) {
|
|
MessageUtil.ShowErrorToast("Engineer 권한으로 로그인 하시기 바랍니다.", Log);
|
|
return false;
|
|
}
|
|
|
|
if (!IsManualStatus) {
|
|
MessageUtil.ShowErrorToast("Auto 상태에서 레시피 변경 불가", Log);
|
|
return false;
|
|
}
|
|
return true;
|
|
|
|
} catch (Exception ex) {
|
|
Log.E($"PreSaved failed: {ex}");
|
|
}
|
|
|
|
return false;
|
|
}
|
|
public void OnSaved(int id, string recipeName) {
|
|
var recipe = _Manager.RecipeItem(id, recipeName) as MainRecipe;
|
|
var diffs = new ObservableCollection<PropertyDiff>();
|
|
CompareHelper.Fill(_RecipeBeforeChanged, recipe, diffs,
|
|
new CompareHelper.FieldSpec<MainRecipe>("Acceleration", x => x.WeldingAcc),
|
|
new CompareHelper.FieldSpec<MainRecipe>("StartPosX", x => x.StartPos.PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("StartPosY", x => x.StartPos.PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("EndPosX", x => x.EndPos.PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("EndPosY", x => x.EndPos.PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("LaserOnPosition", x => x.LaserOnPosition),
|
|
new CompareHelper.FieldSpec<MainRecipe>("RampingOffStartPos", x => x.RampingOffStartPos),
|
|
new CompareHelper.FieldSpec<MainRecipe>(nameof(MainRecipe.Arc1), x => x.Arc1.Size),
|
|
new CompareHelper.FieldSpec<MainRecipe>(nameof(MainRecipe.Arc2), x => x.Arc2.Size),
|
|
new CompareHelper.FieldSpec<MainRecipe>(nameof(MainRecipe.Arc3), x => x.Arc3.Size),
|
|
new CompareHelper.FieldSpec<MainRecipe>(nameof(MainRecipe.Arc4), x => x.Arc4.Size),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos1X", x => x.Units[0].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos1Y", x => x.Units[0].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos2X", x => x.Units[1].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos2Y", x => x.Units[1].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos3X", x => x.Units[2].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos3Y", x => x.Units[2].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos4X", x => x.Units[3].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos4Y", x => x.Units[3].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos5X", x => x.Units[4].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos5Y", x => x.Units[4].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos6X", x => x.Units[5].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos6Y", x => x.Units[5].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos7X", x => x.Units[6].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos7Y", x => x.Units[6].PosY),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos8X", x => x.Units[7].PosX),
|
|
new CompareHelper.FieldSpec<MainRecipe>("TeachingPos8Y", x => x.Units[7].PosY)
|
|
);
|
|
if (diffs.Count > 0) {
|
|
Log.FuncI($"Stage{stage.ToString()} Recipe Changed");
|
|
var recipeInfo = _Manager.Recipes().FirstOrDefault(x => x.Id == id);
|
|
_Manager.RchHandler().AppendChangedItem(recipeInfo.Name + $"-Stage{stage.ToString()}", diffs);
|
|
foreach (var diff in diffs)
|
|
Log.V($"{diff.ToString()}");
|
|
}
|
|
_RecipeBeforeChanged = recipe.Clone() as MainRecipe;
|
|
}
|
|
}
|
|
}
|