feat: [대면적 MOTF] 레시피 저장/로드 및 페이지 자동 생성 기반 구현 (26.04.10)

- [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] 기준)
This commit is contained in:
노도연 2026-04-10 17:31:31 +09:00
parent 5b686c503a
commit f5d68248c9
14 changed files with 1495 additions and 41 deletions

View File

@ -1 +0,0 @@
,DESKTOP-CN4O63O/User,DESKTOP-CN4O63O,09.04.2026 15:52,file:///C:/Users/User/AppData/Roaming/LibreOffice/4;

View File

@ -40,6 +40,9 @@ namespace ILSOS_PrismaticScanner
public const string SeqAlarmMonitor = "SeqAlarmMonitor";
public const string SeqStatusMonitor = "SeqStatusMonitor";
// 26.04.09 NDY - ACS MOTF AutoRun - > SeqAutoRun은 CommonSequenceIds에 존재함.
public const string SeqAutoRunACS = "SeqAutoRunACS";
}
public class StreamMessageNames

View File

@ -64,6 +64,9 @@ namespace ILSOS_PrismaticScanner
private SequenceBuilder _SeqAutoRunPrismaticScanner_Builder;
private SequenceBuilder _SeqAutoRunComponentWeld_Builder;
private SequenceBuilder _SeqAlarmMonitor_Builder;
#region 26.04.09 NDY - ACS MOTF AutoRun Sequence Builder
private SequenceBuilder _SeqAutoRunPrismaticScannerACS_Builder;
#endregion
public override void BuildMainStatusView(MainStatusBuilder builder)
@ -265,28 +268,30 @@ namespace ILSOS_PrismaticScanner
// Common Sequences register
SequenceManager.RegisterSequence(AuroraEnv, register);
base.RegisterSequences(register);
var equipSetting = AuroraEnv.SettingEntry[AuroraSettings.Equipment] as EquipmentInfo;
register(SequenceIds.SeqStreamTest, SeqStreamTest.Builder());
// Event Q Test 추가
var seqEventQTest = new SeqEventQTest();
register(SequenceIds.SeqEventQTest, seqEventQTest.Builder());
var seqAutoRunPrismaticScanner = new SeqAutoRunPrismaticScanner();
var seqAutoRunComponentWeld = new SeqAutoRunComponentWeld();
var seqAlarmMonitor = new SeqAlarmMonitor();
_SeqAutoRunPrismaticScanner_Builder = seqAutoRunPrismaticScanner.Builder();
_SeqAutoRunComponentWeld_Builder = seqAutoRunComponentWeld.Builder();
_SeqAlarmMonitor_Builder = seqAlarmMonitor.Builder();
register(SequenceIds.SeqAutoRun, _SeqAutoRunPrismaticScanner_Builder);
register(SequenceIds.SeqAutoRunComponentWeld, _SeqAutoRunComponentWeld_Builder);
register(SequenceIds.SeqAlarmMonitor, _SeqAlarmMonitor_Builder);
register(SequenceIds.SeqStatusMonitor, SeqStatusMonitor.Builder());
// SequenceManager.RegisterSequence(AuroraEnv, register);
#region 26.04.09 NDY - ACS MOTF AutoRun Sequence 등록
// 기존 SeqAutoRun(SeqAutoRunPrismaticScanner)은 건드리지 않고
// ACS MOTF 전용 시퀀스를 별도 Builder로 등록
var seqAutoRunPrismaticScannerACS = new SeqAutoRunPrismaticScannerACS();
_SeqAutoRunPrismaticScannerACS_Builder = seqAutoRunPrismaticScannerACS.Builder();
register(SequenceIds.SeqAutoRunACS, _SeqAutoRunPrismaticScannerACS_Builder);
#endregion
}
public override void RegisterSettings(SettingRegister register) {
@ -384,13 +389,13 @@ namespace ILSOS_PrismaticScanner
manager.RegisterMainMenu(calibration);
// Monitoring
#region 26 04 06 주석처리 PLCBitMonitoring PLCWordMonitoring IOInterfaceView (o) PLCInterfaceView (원래부터 주석처리)
//var monitoring = new MainMenu(CommonViewIds.Monitoring, MenuIcon.GetMenuIcon(eMenuIcon.MONITORING), PermissionLevel.NONE, true);
////manual.AddSubMenu(CommonViewIds.PLCInterfaceView, new UCPLCInterfaceView(AuroraEnv, CommonDeviceIds.PLCDevice));
//monitoring.AddSubMenu(CommonViewIds.PLCBitMonitoring, new UCPLCBit() { DataContext = PLCStatic.GetInstanceBitVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.M) });
//monitoring.AddSubMenu(CommonViewIds.PLCWordMonitoring, new UCPLCWord() { DataContext = PLCStatic.GetInstanceWordVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.D) });
//monitoring.AddSubMenu(CommonViewIds.IOInterfaceView, new UCIOInterfaceView(AuroraEnv, CommonDeviceIds.IODevice));
//manager.RegisterMainMenu(monitoring);
#region 26 04 06 주석처리 PLCBitMonitoring PLCWordMonitoring IOInterfaceView (o) PLCInterfaceView (원래부터 주석처리) - 04.10 잠시 추가
var monitoring = new MainMenu(CommonViewIds.Monitoring, MenuIcon.GetMenuIcon(eMenuIcon.MONITORING), PermissionLevel.NONE, true);
//manual.AddSubMenu(CommonViewIds.PLCInterfaceView, new UCPLCInterfaceView(AuroraEnv, CommonDeviceIds.PLCDevice));
monitoring.AddSubMenu(CommonViewIds.PLCBitMonitoring, new UCPLCBit() { DataContext = PLCStatic.GetInstanceBitVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.M) });
monitoring.AddSubMenu(CommonViewIds.PLCWordMonitoring, new UCPLCWord() { DataContext = PLCStatic.GetInstanceWordVM(AuroraEnv, CommonDeviceIds.PLCDevice, ePLCNetType.MXComponent, PLCBase.ePLCDevice.D) });
monitoring.AddSubMenu(CommonViewIds.IOInterfaceView, new UCIOInterfaceView(AuroraEnv, CommonDeviceIds.IODevice));
manager.RegisterMainMenu(monitoring);
#endregion
// Device

View File

@ -110,6 +110,7 @@
<Compile Include="Sequences\Monitoring\SeqStatusMonitor.cs" />
<Compile Include="Sequences\SeqAutoRunComponentWeld.cs" />
<Compile Include="Sequences\SeqAutoRunPrismaticScanner.cs" />
<Compile Include="Sequences\SeqAutoRunPrismaticScannerACS.cs" />
<Compile Include="Sequences\SeqEventQTest.cs" />
<Compile Include="Sequences\SeqStreamTest.cs" />
<Compile Include="Sequences\SequenceContextEx.cs" />

File diff suppressed because it is too large Load Diff

View File

@ -38,6 +38,12 @@ namespace ILSOS_PrismaticScanner.Sequences
SeqTeachingRun _SeqTeachingRun;
SeqComponentTeachingRun _SeqComponentTeachingRun;
#region 26.04.09 NDY - ACS MOTF AutoRun Sequence
// 기존 SeqAutoRunPrismaticScanner와 별개로 추가
// ACS 모션(Stage X 등) + 갈바노 스캐너 MOTF 자동운전 전용
SeqAutoRunPrismaticScannerACS _SeqAutoRunPrismaticScannerACS;
#endregion
private static Dictionary<string, SequenceBuilder> mapSequenceBuilders = new Dictionary<string, SequenceBuilder>();
static SequenceManager()
@ -56,12 +62,17 @@ namespace ILSOS_PrismaticScanner.Sequences
private void ChangeRunState(ISequenceEntry sequenceEntry, string sequenceId, bool isRunning, object param = null)
{
if (isRunning) {
if (sequenceEntry[sequenceId].RunState != SequenceRunState.Running) {
if (isRunning)
{
if (sequenceEntry[sequenceId].RunState != SequenceRunState.Running)
{
sequenceEntry[sequenceId].Start(param);
}
} else {
if (sequenceEntry[sequenceId].RunState == SequenceRunState.Running) {
}
else
{
if (sequenceEntry[sequenceId].RunState == SequenceRunState.Running)
{
sequenceEntry[sequenceId].Stop();
}
}
@ -84,66 +95,87 @@ namespace ILSOS_PrismaticScanner.Sequences
//register(CommonSequenceIds.DeviceMonitor, DeviceMonitoring.Builder());
base.Register(aurora, register);
if (_SeqMarkGrid == null) {
if (_SeqMarkGrid == null)
{
_SeqMarkGrid = new SeqMarkGrid();
}
register(CommonSequenceIds.SeqMarkGrid, _SeqMarkGrid.Builder());
if (_SeqScannerCalibration == null) {
if (_SeqScannerCalibration == null)
{
_SeqScannerCalibration = new SeqScannerCalibration();
}
register(CommonSequenceIds.SeqScannerCalibration, _SeqScannerCalibration.Builder());
if (_SeqReceiveCellInfo == null) {
if (_SeqReceiveCellInfo == null)
{
_SeqReceiveCellInfo = new SeqReceiveCellInfo();
}
register(CommonSequenceIds.SeqReceiveCellInfo, _SeqReceiveCellInfo.Builder());
if (_SeqReportCellResult == null) {
if (_SeqReportCellResult == null)
{
_SeqReportCellResult = new SeqReportCellResult();
}
register(CommonSequenceIds.SeqReportCellResult, _SeqReportCellResult.Builder());
if (_SeqScoutFlyingVision == null) {
if (_SeqScoutFlyingVision == null)
{
_SeqScoutFlyingVision = new SeqScoutFlyingVision();
}
register(CommonSequenceIds.SeqScoutFlyingVision, _SeqScoutFlyingVision.Builder());
if (_SeqScoutNormal == null) {
if (_SeqScoutNormal == null)
{
_SeqScoutNormal = new SeqScoutNormal();
}
register(CommonSequenceIds.SeqScoutNormal, _SeqScoutNormal.Builder());
if (_SeqExecutePlcBitInterface == null) {
if (_SeqExecutePlcBitInterface == null)
{
_SeqExecutePlcBitInterface = new SeqExecutePlcBitInterface();
}
register(CommonSequenceIds.SeqExecutePlcBitInterface, _SeqExecutePlcBitInterface.Builder());
if (_SeqExecutePlcBitInterfaceForStatusChange == null) {
if (_SeqExecutePlcBitInterfaceForStatusChange == null)
{
_SeqExecutePlcBitInterfaceForStatusChange = new SeqExecutePlcBitInterface();
}
register(CommonSequenceIds.SeqExecutePlcBitInterfaceForStatusChange, _SeqExecutePlcBitInterfaceForStatusChange.Builder());
if (_SeqCheckWeldingComplete == null) {
if (_SeqCheckWeldingComplete == null)
{
_SeqCheckWeldingComplete = new SeqCheckWeldingComplete();
}
register(CommonSequenceIds.SeqCheckWeldingComplete, _SeqCheckWeldingComplete.Builder());
if (_SeqTeachingRun == null) {
if (_SeqTeachingRun == null)
{
_SeqTeachingRun = new SeqTeachingRun();
}
register(CommonSequenceIds.SeqTeachingRun, _SeqTeachingRun.Builder());
if (_SeqComponentTeachingRun == null) {
if (_SeqComponentTeachingRun == null)
{
_SeqComponentTeachingRun = new SeqComponentTeachingRun();
}
register(CommonSequenceIds.SeqComponentTeachingRun, _SeqComponentTeachingRun.Builder());
if (_SeqWaitWeldOrChangePage == null) {
if (_SeqWaitWeldOrChangePage == null)
{
_SeqWaitWeldOrChangePage = new SeqWaitWeldOrChangePage();
}
register(CommonSequenceIds.SeqWaitWeldOrChangePage, _SeqWaitWeldOrChangePage.Builder());
#region 26.04.09 NDY - ACS MOTF AutoRun Sequence 등록
// 기존 SeqAutoRun(SeqAutoRunPrismaticScanner)은 건드리지 않고
// ACS MOTF 전용 시퀀스를 별도 ID(SeqAutoRunACS)로 등록
if (_SeqAutoRunPrismaticScannerACS == null)
{
_SeqAutoRunPrismaticScannerACS = new SeqAutoRunPrismaticScannerACS();
}
register(SequenceIds.SeqAutoRunACS, _SeqAutoRunPrismaticScannerACS.Builder());
#endregion
}
// MainProcess Sequence
@ -161,9 +193,13 @@ namespace ILSOS_PrismaticScanner.Sequences
public static SequenceBuilder GetSeqBuilder_SeqTeachingRun() { return Instance._SeqTeachingRun.Builder(); }
public static SequenceBuilder GetSeqBuilder_SeqComponentTeachingRun() { return Instance._SeqComponentTeachingRun.Builder(); }
#region 26.04.09 NDY - ACS MOTF AutoRun Sequence GetSeqBuilder
public static SequenceBuilder GetSeqBuilder_SeqAutoRunPrismaticScannerACS() { return Instance._SeqAutoRunPrismaticScannerACS.Builder(); }
#endregion
public virtual void Terminate()
{
}
}
}
}

View File

@ -53,13 +53,35 @@
</Grid.RowDefinitions>
<GroupBox Grid.Row="0" Header="Manual Control" Margin="0 0 0 0">
<Grid Height="50" Margin="0 0 0 0">
<Grid Height="100" Margin="0 0 0 0">
<Grid.RowDefinitions>
<RowDefinition />
<RowDefinition />
<RowDefinition />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="3 0 3 2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="auto" FontWeight="Bold" Style="{StaticResource StButtonAccentPurple}" Command="{Binding CMD_RunSequenceAutoRun}" Content="Run AutoRun" IsEnabled="{Binding IsSequenceRunning_AutoRun, Converter={StaticResource convReverse}}"/>
<Button Grid.Column="1" Margin="3 0 3 2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="auto" FontWeight="Bold" Style="{StaticResource StButtonAccentPurple}" Command="{Binding CMD_StopSequenceAutoRun}" Content="Stop" IsEnabled="{Binding IsSequenceRunning_AutoRun}"/>
<Button Grid.Row="0" Grid.Column="0" Margin="3 0 3 2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="auto" FontWeight="Bold" Style="{StaticResource StButtonAccentPurple}" Command="{Binding CMD_RunSequenceAutoRun}" Content="Run AutoRun" IsEnabled="{Binding IsSequenceRunning_AutoRun, Converter={StaticResource convReverse}}"/>
<Button Grid.Row="0" Grid.Column="1" Margin="3 0 3 2" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Height="auto" FontWeight="Bold" Style="{StaticResource StButtonAccentPurple}" Command="{Binding CMD_StopSequenceAutoRun}" Content="Stop" IsEnabled="{Binding IsSequenceRunning_AutoRun}"/>
<GridSplitter HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Height="2" ToolTip="구분선 -> 상단 기존 동작 / 하단 추가된 시퀸스 테스트 버튼"></GridSplitter>
<!-- 26.04.10 NDY AUTORUN ACS 시퀸스 테스트 버튼 -->
<Button Grid.Row="2" Grid.Column="0" Margin="3 0 3 2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentPurple}"
Command="{Binding CMD_StartMOTF}"
Content="Run MOTF"
IsEnabled="{Binding IsSequenceRunning_AutoRun, Converter={StaticResource convReverse}}"/>
<Button Grid.Row="2" Grid.Column="1" Margin="3 0 3 2"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentPurple}"
Command="{Binding CMD_StopMOTF}"
Content="Stop"
IsEnabled="{Binding IsSequenceRunning_AutoRun}"/>
</Grid>
</GroupBox>
<GroupBox Grid.Row="1" Header="Equipment Status" Margin="0 5 0 0">

View File

@ -43,6 +43,11 @@ namespace ILSOS_PrismaticScanner.Views
public ICommand CMD_StopSequenceAutoRun { get; }
public ICommand CMD_Reset { get; } // Totoal Product, Today Product
#region 26.04.10 NDY - MOTF AutoRun Command
public ICommand CMD_StartMOTF { get; }
public ICommand CMD_StopMOTF { get; }
#endregion
private readonly IRecipeManager _RecipeManager;
private readonly ISettingEntry _SettingEntry;
private readonly IAlarmService _AlarmService;
@ -56,6 +61,12 @@ namespace ILSOS_PrismaticScanner.Views
private ISequence _SeqPlcInterface;
private ISequence _SeqQTest;
#region 26.04.10 NDY - MOTF AutoRun Sequence
// 기존 _SeqAutoRun과 별개로 MOTF 전용 시퀀스 참조
private ISequence _SeqMOTF;
#endregion
public bool IsSequenceRunning_AutoRun { get => _IsSequenceRunning_AutoRun; set => SetProperty(ref _IsSequenceRunning_AutoRun, value); }
private bool _IsSequenceRunning_AutoRun;
@ -87,6 +98,12 @@ namespace ILSOS_PrismaticScanner.Views
// Q Test
_SeqQTest = _Aurora.SequenceEntry[SequenceIds.SeqEventQTest];
#region 26.04.10 NDY - MOTF AutoRun Sequence 참조
// 기존 SeqAutoRun과 별개로 MOTF 전용 시퀀스 ID로 참조
_SeqMOTF = _Aurora.SequenceEntry[SequenceIds.SeqAutoRunACS];
#endregion
InitVariable();
CMD_VisibleChanged = new DelegateCommand(VisibleChanged);
@ -95,6 +112,12 @@ namespace ILSOS_PrismaticScanner.Views
CMD_StopSequenceAutoRun = new DelegateCommand(OnStopSequenceAutoRun);
CMD_Reset = new DelegateCommand(PerformCmdReset);
#region 26.04.10 NDY - MOTF AutoRun Command 등록
// 기존 CMD_RunSequenceAutoRun / CMD_StopSequenceAutoRun 패턴과 동일
CMD_StartMOTF = new DelegateCommand(OnMOTFStartRequest);
CMD_StopMOTF = new DelegateCommand(OnStopMOTF);
#endregion
_MonitorTimer = new DispatcherTimer();
_MonitorTimer.Tick += OnMonitoring;
_MonitorTimer.Interval = TimeSpan.FromMilliseconds(100);
@ -150,6 +173,7 @@ namespace ILSOS_PrismaticScanner.Views
// Page UI 업데이트
RecipeHelper.UpdateAutorunRecipe();
}
private void VisibleChanged(object obj)
{
bool visible = (bool)obj;
@ -271,6 +295,54 @@ namespace ILSOS_PrismaticScanner.Views
_Scanner.SelectCorrectionTable(1);
}
#region 26.04.10 NDY - MOTF AutoRun 실행 / 정지
// 기존 OnAutoRunStartRequest / OnStopSequenceAutoRun 패턴과 동일
// 테스트 버튼용이므로 TimerMessageBox 확인 다이얼로그는 생략
private void OnMOTFStartRequest(object obj = null)
{
// 기존 AutoRun 인터락 조건 동일하게 적용
if (_SeqMOTF.RunState == SequenceRunState.Running) {
MessageUtil.ShowWarningToast("이미 MOTF 시퀀스가 Start 상태입니다.", Log);
return;
}
// 자동으로 Activated Recipe로 변경
OnUpdateCurrentActivatedRecipe(null);
Status.GetProcessingWeldingData().AutoRunStartTime = DateTime.Now;
Status.SetStatus(eEqpStatus.Auto);
var param = new SeqAutoRunPrismaticScannerACS_InitParam();
_SeqMOTF.Start(param, OnCompleteMOTF);
Log.I("MOTF Sequence Started");
}
private void OnCompleteMOTF(SequenceActionResult res)
{
// 기존 OnCompleteAutoRun 패턴과 동일
if (res != null) {
if (res.Param is SeqAutoRunPrismaticScannerACS_OutParam result && result.Success == true) {
MessageUtil.ShowSuccessToast("Success MOTF Sequence");
return;
}
}
MessageUtil.ShowWarningToast("Fail MOTF Sequence");
}
private void OnStopMOTF(object obj = null)
{
// 기존 OnStopSequenceAutoRun 패턴과 동일
if (_SeqMOTF.RunState == SequenceRunState.Idle) {
return;
}
_SeqMOTF.Stop();
Log.I("MOTF Sequence Stopped");
}
#endregion
private void PerformCmdReset(object commandParameter)
{
// SystemParameter를 저장하기 전에는 반드시 업데이트를 해주어야 한다.
@ -317,4 +389,4 @@ namespace ILSOS_PrismaticScanner.Views
keyboardInput,
ClickInput
}
}
}

View File

@ -1,3 +1,4 @@
using CoreUnit.Common;
using System;
using System.Collections.Generic;
using System.Drawing;
@ -31,9 +32,12 @@ namespace CoreUnit.Control.UserControl.Canvas
}
#region 26.04.09
// 26.04.09 NDY [대면적 MOTF] 페이지 레벨 Offset 파라미터 추가
// 26.04.09 NDY [대면적 MOTF] 페이지 레벨 Offset 파라미터 추가 - 26.04.10 XmlSerializable 어트리뷰트 추가
[XmlSerializable]
public double LargeMOTFOffsetX { get; set; } = 0.0;
[XmlSerializable]
public double LargeMOTFOffsetY { get; set; } = 0.0;
#endregion
public DrawingLayer(int no, string name, Color color, float width)

View File

@ -23,8 +23,10 @@ namespace DB.Recipe
{
#region 26.04.09 NDY
// 26.04.09 NDY [대면적 MOTF] 레시피 레벨 파라미터 추가
// 26.04.09 NDY [대면적 MOTF] 레시피 레벨 파라미터 추가 - 04.10 [XmlSerializable] 추가함.
[XmlSerializable]
public bool UseLargeMOTF { get; set; } = false;
[XmlSerializable]
public int LargeMOTFPageCount { get; set; } = 1;
#endregion
@ -90,8 +92,11 @@ namespace DB.Recipe
{
try
{
LoadWeldingParam(xmlElement);
Model.Load(xmlElement);
#region 26.04.10 NDY [대면적 MOTF] 레시피 레벨 파라미터 로드
CoreUnit.Common.XmlUtil.ParseProperties(xmlElement, this);
#endregion
LoadWeldingParam(xmlElement); // WeldingParameters만 로드
Model.Load(xmlElement); // DrawingObjects만 로드
}
catch (Exception ex)
{

View File

@ -13,6 +13,7 @@ using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
@ -34,6 +35,135 @@ namespace GUI
InitializeComponent();
}
#region 26.04.10 NDY에 추가함.
// MainPage.cs에 추가
public void UpdateLargeMOTFPosition()
{
foreach (ICanvasLayer layer in ScannerCanvasCtrlTeaching.Model.Layers)
{
var lines = layer.Objects
.OfType<Line>()
.OrderBy(l => l.No)
.ToList();
if (lines.Count == 0) continue;
float motfX = lines.Last().PF2.X;
float motfY = lines.Last().PF2.Y;
foreach (var line in lines)
{
line.LargeMOTFPositionX = motfX;
line.LargeMOTFPositionY = motfY;
}
}
}
public void GenerateFlippedPages(double largeMOTFOffsetX)
{
try
{
var model = ScannerCanvasCtrlTeaching.Model;
var layers = model.Layers;
Debug.WriteLine($"[GenerateFlippedPages] 시작 - Layer 수: {layers.Count()}");
if (layers.Count() < 2)
{
Debug.WriteLine("[GenerateFlippedPages] Layer 2개 미만 - 종료");
return;
}
// 기존 Layer[2], Layer[3] 삭제
while (layers.Count() > 2)
{
Debug.WriteLine($"[GenerateFlippedPages] Layer 삭제 - 현재 수: {layers.Count()}");
model.DeleteLayer(layers.Count() - 1);
}
Debug.WriteLine($"[GenerateFlippedPages] 삭제 후 Layer 수: {layers.Count()}");
var p1Lines = layers[0].Objects.OfType<Line>().OrderBy(l => l.No).ToList();
var p2Lines = layers[1].Objects.OfType<Line>().OrderBy(l => l.No).ToList();
Debug.WriteLine($"[GenerateFlippedPages] P1 Line 수: {p1Lines.Count}, P2 Line 수: {p2Lines.Count}");
if (p1Lines.Count == 0 || p2Lines.Count == 0)
{
Debug.WriteLine("[GenerateFlippedPages] Line 없음 - 종료");
return;
}
float flipYforP3 = p2Lines.Last().PF2.Y;
float flipYforP4 = p1Lines.First().PF1.Y;
Debug.WriteLine($"[GenerateFlippedPages] flipYforP3={flipYforP3:F3}, flipYforP4={flipYforP4:F3}");
// Page3 생성 = Page2 Y플립
model.CreateLayer();
Debug.WriteLine($"[GenerateFlippedPages] CreateLayer(P3) 후 Layer 수: {layers.Count()}");
if (layers.Count() < 3)
{
Debug.WriteLine("[GenerateFlippedPages] P3 Layer 생성 실패 - 종료");
return;
}
var layer2 = layers[2] as DrawingLayer;
if (layer2 == null)
{
Debug.WriteLine("[GenerateFlippedPages] layer2 캐스팅 실패 - 종료");
return;
}
foreach (var srcLine in p2Lines)
{
var newLine = new Line();
newLine.Copy(srcLine);
newLine.PF1 = new System.Drawing.PointF(
srcLine.PF1.X, 2 * flipYforP3 - srcLine.PF1.Y);
newLine.PF2 = new System.Drawing.PointF(
srcLine.PF2.X, 2 * flipYforP3 - srcLine.PF2.Y);
layer2.AddObject(newLine);
}
Debug.WriteLine($"[GenerateFlippedPages] P3 Line {p2Lines.Count}개 추가 완료");
// Page4 생성 = Page1 Y플립
model.CreateLayer();
Debug.WriteLine($"[GenerateFlippedPages] CreateLayer(P4) 후 Layer 수: {layers.Count()}");
if (layers.Count() < 4)
{
Debug.WriteLine("[GenerateFlippedPages] P4 Layer 생성 실패 - 종료");
return;
}
var layer3 = layers[3] as DrawingLayer;
if (layer3 == null)
{
Debug.WriteLine("[GenerateFlippedPages] layer3 캐스팅 실패 - 종료");
return;
}
foreach (var srcLine in p1Lines)
{
var newLine = new Line();
newLine.Copy(srcLine);
newLine.PF1 = new System.Drawing.PointF(
srcLine.PF1.X, 2 * flipYforP4 - srcLine.PF1.Y);
newLine.PF2 = new System.Drawing.PointF(
srcLine.PF2.X, 2 * flipYforP4 - srcLine.PF2.Y);
layer3.AddObject(newLine);
}
Debug.WriteLine($"[GenerateFlippedPages] P4 Line {p1Lines.Count}개 추가 완료");
UpdateWeldingLinesRecipe();
Debug.WriteLine($"[GenerateFlippedPages] 완료 - OffsetX={largeMOTFOffsetX:F3}");
}
catch (Exception ex)
{
Debug.WriteLine($"[GenerateFlippedPages] 예외 발생: {ex.Message}");
Debug.WriteLine($"[GenerateFlippedPages] StackTrace: {ex.StackTrace}");
}
}
#endregion
#region 2.메인화면: 다이얼로그 및 페이지 초기화 함수
private void ClearDisplayedError()
@ -457,6 +587,7 @@ namespace GUI
UtilUnit.logUnit.SetLog(LogUnit.eLogType.ALARM, MethodBase.GetCurrentMethod().Name, ex.Message);
}
}
public event Action OnRecipeReloaded; // 26.04.10 추가 MainPage에 콜백추가를 위한 액션
public void ReloadRecipe(string fullpath)
{
ScannerCanvasCtrlTeaching.Canvas.CommandEscape();
@ -467,12 +598,17 @@ namespace GUI
UpdateWeldingLinesRecipe();
// 26.04.10 NDY [대면적 MOTF]
OnRecipeReloaded?.Invoke();
//// Page 불러온 후, 실행되지 않아도 됨. 왜냐하면, 어차피 SelectedIndex 쪽으로 빠짐.
//SaveAndLoadFieldScanFromLasaDb();
//ChangeScoutImage();
}
#endregion
// 26.04.10 확인용 --- MainPage의 레시피 정보를 Get 하는 메소드
public RecipeWeldingLines getRecipeInfo(string fullpath)
{
var recipe = new RecipeWeldingLines();

View File

@ -294,6 +294,18 @@ namespace WeldingCommon.Views
}
// 26.04.09 NDY [대면적 MOTF] End Set 커맨드 구현
//최종 구현하고자 하는 방향성
//{
// 1. Layer[0] (P1), Layer[1] (P2) 원본 확인
// 2. Layer[2] 생성 = P3(P2 Y플립)
// - 플립 기준Y = Layer[1] 마지막 Line PF2.Y
// - P2의 Line들을 역순으로 복사 + Y좌표 플립
// 3. Layer[3] 생성 = P4(P1 Y플립)
// - 플립 기준Y = Layer[0] 첫 Line PF1.Y
// - P1의 Line들을 역순으로 복사 + Y좌표 플립
// 4. 기존 Layer[2],[3] 있으면 삭제 후 재생성
// 5. MessageUtil로 결과 안내
//}
private void PerformCmdLargeMOTFEndSet(object obj)
{
var result = MessageUtil.ShowYesNoMessageDialog("Large MOTF End Set",
@ -326,6 +338,9 @@ namespace WeldingCommon.Views
Log.I($" - 계산된 Offset Y (Scanner): {LargeMOTFOffsetY:F3} mm");
Log.I($" - 저장된 Layer: Page {mainPage.ScannerCanvasCtrlTeaching.Model.ActiveLayer.No + 1}");
Log.I($" - TODO: 모션→스캐너 좌표 변환 방향/스케일 이정인 선임 확인 필요");
// 26.04.10 NDY [대면적 MOTF] 플립 페이지 자동 생성
CommonHelper.GetMainPage().GenerateFlippedPages(LargeMOTFOffsetX);
}
private void GetPerformCmdStageXJogSpeed(object obj)
{

View File

@ -67,9 +67,22 @@ namespace WeldingCommon.Views
_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()
@ -165,12 +178,26 @@ namespace WeldingCommon.Views
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)
@ -198,13 +225,35 @@ namespace WeldingCommon.Views
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>();
@ -236,5 +285,36 @@ namespace WeldingCommon.Views
}
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
}
}