- [Line.cs] LargeMOTFPositionX/Y 프로퍼티 추가 ([XmlSerializable]) - [DrawingLayer.cs] LargeMOTFOffsetX/Y 프로퍼티 추가 ([XmlSerializable]) - [RecipeWeldingLines.cs] UseLargeMOTF, LargeMOTFPageCount 프로퍼티 추가 ([XmlSerializable]) - Load() 에 XmlUtil.ParseProperties() 추가하여 로드 시 반영 - [MainPage.cs] - UpdateLargeMOTFPosition() 추가: 저장 직전 Layer별 마지막 Line PF2를 LargeMOTFPositionX/Y에 자동 SET - OnRecipeReloaded 이벤트 추가: 레시피 로드 완료 시 콜백 - GenerateFlippedPages() 추가: EndSet 시 Page3(P2 Y플립), Page4(P1 Y플립) 자동 생성 - [UCScannerObjectTeachingViewModel.cs] - OnSaved(): UpdateLargeMOTFPosition, UseLargeMOTF/LargeMOTFPageCount 저장 직전 반영 - OnChanged(), Reloaded(): 레시피 로드 시 IsUseLargeMOTF, LargeMOTFPageCount UI 반영 - 생성자: OnRecipeReloaded 이벤트 등록하여 프로그램 시작 시 UI 반영 - [UCScannerObjectTeachingControlBoxViewModel.cs] - PerformCmdLargeMOTFEndSet(): LargeMOTFOffsetX 계산 후 GenerateFlippedPages() 호출 - OnRecipeReloaded 시 LargeMOTFOffsetX UI 반영 (Layer[1] 기준)
321 lines
14 KiB
C#
321 lines
14 KiB
C#
using Aurora;
|
|
using Aurora.Api;
|
|
using Aurora.Api.Manager;
|
|
using Aurora.Framework.Core;
|
|
using Aurora.View;
|
|
using CoreUnit.Control.UserControl.Canvas;
|
|
using CoreUnit.Scanner.UserControl.ScannerCanvas;
|
|
using DB.Recipe;
|
|
using GUI;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Collections.ObjectModel;
|
|
using System.Linq;
|
|
using System.Reactive.Linq;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
using VisionCommon;
|
|
using WeldingCommon.Alarm;
|
|
using WeldingCommon.Devices;
|
|
using WeldingCommon.Recipes;
|
|
using WeldingCommon.Settings;
|
|
using WeldingCommon.Utils;
|
|
using WeldingCommon.Utils.Diff;
|
|
|
|
namespace WeldingCommon.Views
|
|
{
|
|
public class UCScannerObjectTeachingViewModel : NotifyPropertyChangedBase, IRecipeViewModel
|
|
{
|
|
private readonly Logger Log = new Logger("UCScannerObjectTeachingViewModel");
|
|
private readonly IAuroraEnvironment _Aurora;
|
|
private readonly IRecipeManager _RecipeManager;
|
|
private readonly IModelManager _ModelManager;
|
|
|
|
private PLCController _PLC;
|
|
|
|
private ScannerCanvasUserCtrl _ScannerCanvasUserCtrl;
|
|
public bool IsManualStatus { get => _IsManualStatus; set => SetProperty(ref _IsManualStatus, value); }
|
|
private bool _IsManualStatus = true;
|
|
|
|
public UCScannerObjectTeachingControlBoxViewModel ScannerObjectTeachingControlBoxViewModel { get; }
|
|
|
|
private RecipeWeldingLines _RecipeBeforeChanged;
|
|
|
|
public UCScannerObjectTeachingViewModel(IAuroraEnvironment aurora)
|
|
{
|
|
_Aurora = aurora;
|
|
_RecipeManager = aurora.RecipeManager;
|
|
_ModelManager = aurora.ModelManager;
|
|
|
|
_PLC = aurora.DeviceManager[CommonDeviceIds.PLCDevice] as PLCController;
|
|
|
|
ScannerObjectTeachingControlBoxViewModel = new UCScannerObjectTeachingControlBoxViewModel(_Aurora);
|
|
|
|
// For LASA image update, after scout
|
|
_Aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_SETTING_CHANGED)
|
|
.ObserveOnDispatcher()
|
|
.Subscribe(OnSettingReloaded);
|
|
|
|
// New, Delete, Copy, Rename
|
|
_Aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_RECIPE_ENTRY_CHANGED)
|
|
.ObserveOnDispatcher()
|
|
.Subscribe(OnRecipeEntryChanged);
|
|
|
|
_Aurora.EventObservable.AsObservable(AuroraSysEvent.SYS_RECIPE_ACTIVATED)
|
|
.ObserveOnDispatcher()
|
|
.Subscribe(OnActivatedRecipe);
|
|
|
|
_Aurora.EventObservable.AsObservable(CommonEventIds.EVENT_EQUIPMENT_STATUS_CHANGE)
|
|
.Subscribe(x => OnChangedEquipmentStatus());
|
|
#region 26.04.10 NDY [대면적 MOTF] 프로그램 시작 시 UI 반영
|
|
CommonHelper.GetMainPage().OnRecipeReloaded += () =>
|
|
{
|
|
ScannerObjectTeachingControlBoxViewModel.IsUseLargeMOTF
|
|
= DB.Dao.WeldingLinesRecipe.UseLargeMOTF;
|
|
ScannerObjectTeachingControlBoxViewModel.LargeMOTFPageCount
|
|
= DB.Dao.WeldingLinesRecipe.LargeMOTFPageCount;
|
|
};
|
|
#endregion
|
|
|
|
RecipeHelper.OnActiveReloaded();
|
|
|
|
|
|
|
|
CommonHelper.UpdateAutoRunPage();
|
|
|
|
}
|
|
|
|
private object OnChangedEquipmentStatus()
|
|
{
|
|
IsManualStatus = Status.EquipmentStatus == eEqpStatus.Manual;
|
|
//_Aurora.RecipeManager.SaveLock(CommonRecipeIds.ScannerObjectTeaching, !IsManualStatus);
|
|
return null;
|
|
}
|
|
|
|
private void OnSettingReloaded(object obj)
|
|
{
|
|
if (obj is EventMessage msg && msg.Param is string target && msg != null) {
|
|
if (target != CommonSettingIds.SystemParameter) {
|
|
Log.I($"Target is not {CommonSettingIds.SystemParameter} Update Skip");
|
|
return;
|
|
}
|
|
|
|
if (msg.From == "SeqAutoRunLHS_New"
|
|
|| msg.From == "SeqAutoRunPrismaticScanner_New"
|
|
|| msg.From == "UCMainAutoRunViewModel") {
|
|
Log.I($"SystemParameter Update Skip >> From({msg.From})");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// ScannerCanvasCtrlTeaching Update
|
|
CommonHelper.GetMainPage().ReloadSetting();
|
|
}
|
|
|
|
private void OnRecipeEntryChanged(object obj)
|
|
{
|
|
Log.I("SYS_RECIPE_ENTRY_CHANGED, OnRecipeEntryChanged");
|
|
|
|
if (obj == null || !(obj is EventMessage msg)) {
|
|
return;
|
|
}
|
|
|
|
if (msg.Param is RecipeEntryChangedParam param) {
|
|
if (param.AddedId != -1) { //New
|
|
var recipe = _RecipeManager.Recipes().FirstOrDefault(x => x.Id == param.AddedId);
|
|
if (recipe == null) return;
|
|
RecipeHelper.SelectedRecipe = (recipe.Id, recipe.Name);
|
|
RecipeHelper.SaveRecipe(recipe.Name, includeModel: false);
|
|
} else if (param.RemovedId != -1) { //Remove
|
|
|
|
} else if (param.CopiedToId != -1 && param.CopiedFromId != -1) { //Copy
|
|
|
|
} else if (param.RenameNewId != -1 && param.RenameOldId != -1) { //Rename
|
|
|
|
} else {
|
|
string stringFormat = string.Format($"Error : OnRecipeEntryChanged\n" +
|
|
$"[Add] [{param.AddedId}]\n" +
|
|
$"[Remove] [{param.RemovedId}]\n" +
|
|
$"[Copy] [{param.CopiedFromId}] [{param.CopiedToId}]\n" +
|
|
$"[Rename] [{param.RenameOldId}] [{param.RenameNewId}]");
|
|
MessageUtil.CreateAndShowMessageDialog("Error", stringFormat);
|
|
}
|
|
}
|
|
}
|
|
|
|
public void OnActivatedRecipe(object obj)
|
|
{
|
|
Log.D("SYS_RECIPE_ACTIVATED OnActivatedRecipe");
|
|
if (obj == null || !(obj is EventMessage msg)) {
|
|
return;
|
|
}
|
|
|
|
// @deprecated temporary support
|
|
// Recipe를 선택하는데 Z축 동작이 되는 것은 굉장히 위험한 상황이다.
|
|
// 한시적으로만 사용한다..
|
|
|
|
//LHS 라인에서 Recipe Rename시 Mdoel이 Activate 되지 않아서 Rename시가 아니라
|
|
//Recipe Active시 Model Active 될 수 있도로 추가
|
|
string modelname = _ModelManager.Models().FirstOrDefault();
|
|
if (modelname.Trim() == "" || modelname == null) {
|
|
string stringFormat = string.Format($"Error : Model, 현재 Recipe에 Model list가 없습니다.");
|
|
MessageUtil.CreateAndShowMessageDialog("Error", stringFormat);
|
|
return;
|
|
}
|
|
_ModelManager.ActivateModel(modelname);
|
|
Log.I($"Activate Model Name : {modelname}");
|
|
|
|
var activeRecipe = _RecipeManager.ActivatedRecipe();
|
|
RecipeHelper.SaveRecipe(activeRecipe.Name);
|
|
|
|
// Aurora에서 제공하는 이벤트(SYS_RECIPE_ACTIVATED)를 체인으로 사용하면 동작(Aurora가 저장 후 LASA가 저장하는)에 대한 우선순위가 보장이 안되기 때문에
|
|
_Aurora.EventPublisher.Publish(new EventMessage("OnActivatedRecipe", CommonEventIds.EVENT_CHANGE_RECIPE_INFO, activeRecipe));
|
|
Log.V("CommonEventIds.EVENT_CHANGE_RECIPE_INFO from [OnActivatedRecipe]");
|
|
}
|
|
|
|
public void OnChanged((int id, string name) recipe)
|
|
{
|
|
RecipeHelper.SelectedRecipe = recipe;
|
|
RecipeHelper.ReloadRecipe(recipe.name);
|
|
_RecipeBeforeChanged = RecipeHelper.getRecipeInfo(recipe.name);
|
|
|
|
#region 26.04.10 NDY [대면적 MOTF] 레시피 로드 시 UI 반영
|
|
ScannerObjectTeachingControlBoxViewModel.IsUseLargeMOTF
|
|
= DB.Dao.WeldingLinesRecipe.UseLargeMOTF;
|
|
ScannerObjectTeachingControlBoxViewModel.LargeMOTFPageCount
|
|
= DB.Dao.WeldingLinesRecipe.LargeMOTFPageCount;
|
|
|
|
#endregion
|
|
}
|
|
|
|
public void Reloaded((int id, string name) recipe)
|
|
{
|
|
RecipeHelper.SelectedRecipe = recipe;
|
|
RecipeHelper.ReloadRecipe(recipe.name);
|
|
|
|
// 26.04.10 NDY [대면적 MOTF] 레시피 로드 시 UI 반영
|
|
ScannerObjectTeachingControlBoxViewModel.IsUseLargeMOTF
|
|
= DB.Dao.WeldingLinesRecipe.UseLargeMOTF;
|
|
ScannerObjectTeachingControlBoxViewModel.LargeMOTFPageCount
|
|
= DB.Dao.WeldingLinesRecipe.LargeMOTFPageCount;
|
|
}
|
|
|
|
public void OnModelSelectChanged(string modelName)
|
|
{
|
|
//throw new NotImplementedException();
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
#region 26.04.10 찾음. ScannerCanvasCtrlTeaching.Model.Layers로 Layer 접근하고 각 Layer에서 DrawObject를 Get 할 수 있음. // 까먹을까봐 적어둠
|
|
//MainPage.SaveRecipe()
|
|
// → DB.Dao.WeldingLinesRecipe.Save() 전에
|
|
// → ScannerCanvasCtrlTeaching.Model.Layers
|
|
// → layer.Objects(List<IDrawObject>)
|
|
// → Line.LargeMOTFPositionX/Y SET
|
|
#endregion
|
|
public void OnSaved(int id, string recipeName)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(RecipeHelper.SelectedRecipe.name)) {
|
|
return;
|
|
}
|
|
|
|
#region 26.04.10 NDY [대면적 MOTF] 저장 직전 레시피에 반영
|
|
DB.Dao.WeldingLinesRecipe.UseLargeMOTF
|
|
= ScannerObjectTeachingControlBoxViewModel.IsUseLargeMOTF;
|
|
DB.Dao.WeldingLinesRecipe.LargeMOTFPageCount
|
|
= ScannerObjectTeachingControlBoxViewModel.LargeMOTFPageCount;
|
|
#endregion
|
|
#region 26.04.10 NDY [대면적 MOTF] 저장 직전 LargeMOTFPosition 자동 계산
|
|
// 방법1 현재 viewmodel에 구현함.
|
|
//UpdateLargeMOTFPosition();
|
|
// 방법2 현재 MainPage.cs에 메소드 추가함. 그것을 호출하는 식
|
|
|
|
CommonHelper.GetMainPage().UpdateLargeMOTFPosition();
|
|
|
|
#endregion
|
|
|
|
RecipeHelper.SaveRecipe(RecipeHelper.SelectedRecipe.name);
|
|
var ChangedRecipe = RecipeHelper.getRecipeInfo(RecipeHelper.SelectedRecipe.name);
|
|
var diffs = new ObservableCollection<PropertyDiff>();
|
|
for (int i = 0; i < 3; i++) {
|
|
CompareHelper.Fill(_RecipeBeforeChanged.ReadOnlyScanParams[i], ChangedRecipe.ReadOnlyScanParams[i], diffs,
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.MarkDelay), x => x.MarkDelay),
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.JumpDelay), x => x.JumpDelay),
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.LaserOnDelay), x => x.LaserOnDelay),
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.LaserOffDelay), x => x.LaserOffDelay),
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.LaserFrequency), x => x.LaserFrequency),
|
|
new CompareHelper.FieldSpec<ScannerParam>(nameof(ScannerParam.NumberOfCount), x => x.NumberOfCount)
|
|
);
|
|
if (diffs.Count > 0) {
|
|
Log.FuncI($"ScanParameter{i + 1} Recipe Changed");
|
|
_Aurora.RecipeManager.RchHandler().AppendChangedItem(RecipeHelper.SelectedRecipe.name + $"-ScanParam{i + 1}", diffs);
|
|
foreach (var diff in diffs)
|
|
Log.V($"{diff.ToString()}");
|
|
}
|
|
}
|
|
_RecipeBeforeChanged = ChangedRecipe;
|
|
|
|
var activeRecipe = _RecipeManager.ActivatedRecipe();
|
|
if (RecipeHelper.SelectedRecipe.id == activeRecipe.Id) {
|
|
CommonHelper.UpdateAutoRunPage();
|
|
|
|
// Aurora에서 제공하는 이벤트(SYS_RECIPE_ITEM_CHANGED)를 체인으로 사용하면 동작(Aurora가 저장 후 LASA가 저장하는)에 대한 우선순위가 보장이 안되기 때문에
|
|
_Aurora.EventPublisher.Publish(new EventMessage("OnSaved", CommonEventIds.EVENT_CHANGE_RECIPE_INFO, activeRecipe));
|
|
Log.V($"CommonEventIds.EVENT_CHANGE_RECIPE_INFO from [OnSaved] RecipeName [{RecipeHelper.SelectedRecipe.name}]");
|
|
}
|
|
Log.I($"Saved Recipe : [{RecipeHelper.SelectedRecipe.name}]");
|
|
}
|
|
#region 26.04.10 NDY [대면적 MOTF] Layer별 마지막 Line의 PF2를 LargeMOTFPosition에 SET
|
|
//주의사항:
|
|
//ScannerCanvasCtrlTeaching이 MainPage 내부 필드라 internal 또는 private일 수 있습니다.접근이 안 되면 MainPage에 별도 메서드를 추가하는 방향으로 가야 합니다:
|
|
|
|
private void UpdateLargeMOTFPosition()
|
|
{
|
|
var model = CommonHelper.GetMainPage()
|
|
.ScannerCanvasCtrlTeaching
|
|
.Model;
|
|
|
|
foreach (ICanvasLayer layer in model.Layers)
|
|
{
|
|
var lines = layer.Objects
|
|
.OfType<Line>()
|
|
.OrderBy(l => l.No)
|
|
.ToList();
|
|
|
|
if (lines.Count == 0) continue;
|
|
|
|
var lastLine = lines.Last();
|
|
float motfX = lastLine.PF2.X;
|
|
float motfY = lastLine.PF2.Y;
|
|
|
|
foreach (var line in lines)
|
|
{
|
|
line.LargeMOTFPositionX = motfX;
|
|
line.LargeMOTFPositionY = motfY;
|
|
}
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|