This commit is contained in:
노도연 2026-04-09 15:42:59 +09:00
parent 75eeb62af3
commit ab9b08a53c
12 changed files with 598 additions and 71 deletions

View File

@ -116,6 +116,7 @@ namespace AuroraACS
return alarms;
}
// 실제 위치 읽어오는 메소드
public override double GetActualPos()
{
return ACSLibrary.ReadMotorPosition(AxisNo);

View File

@ -338,8 +338,8 @@ namespace AuroraLaserScanner
#region 26.04.08 NDY 오차 추적 메소드 OVERRIDE 추가 RTC6 (부모클래스 -자식클래스에서 override 가능하도록 abstract 처리 )
public abstract void SetFlyLimit(uint portNo, int xMin, int xMax, int yMin, int yMax);
public abstract void SetFlyLimitZ(uint portNo, int zMin, int zMax);
//public abstract void SetFlyLimit(uint portNo, int xMin, int xMax, int yMin, int yMax);
//public abstract void SetFlyLimitZ(uint portNo, int zMin, int zMax);
#endregion
}

View File

@ -154,23 +154,23 @@ namespace AuroraLaserScanner
#region 좌표 변환 작업 수행
// 26.04.08 NDY - MOTF Tracking Error Limit 설정 추가 - 요기서 처리해야 한다고 한다.
// 하드웨어 보호를 위해 실제 좌표 변환 및 가공 시작 전 울타리를 칩니다.
if (config.UseMarkingOnTheFly)
{
// 1. mm 단위를 bits 단위로 변환
int xLimitBits = Convert.ToInt32(config.FlyLimitXmm * m_ScaleXYFactor);
int yLimitBits = Convert.ToInt32(config.FlyLimitYmm * m_ScaleXYFactor);
//if (config.UseMarkingOnTheFly)
//{
// // 1. mm 단위를 bits 단위로 변환
// int xLimitBits = Convert.ToInt32(config.FlyLimitXmm * m_ScaleXYFactor);
// int yLimitBits = Convert.ToInt32(config.FlyLimitYmm * m_ScaleXYFactor);
// 2. 오버라이드된 메서드 호출 (중앙 0 기준 +- 범위 설정)
this.SetFlyLimit(m_deviceNo, -xLimitBits, xLimitBits, -yLimitBits, yLimitBits);
Log.D($"[RTC6ScannerUnit] MOTF Limit 적용: ±{config.FlyLimitXmm}mm (±{xLimitBits} bits)");
// // 2. 오버라이드된 메서드 호출 (중앙 0 기준 +- 범위 설정)
// this.SetFlyLimit(m_deviceNo, -xLimitBits, xLimitBits, -yLimitBits, yLimitBits);
// Log.D($"[RTC6ScannerUnit] MOTF Limit 적용: ±{config.FlyLimitXmm}mm (±{xLimitBits} bits)");
if (config.Use3DMode)
{
int zLimitBits = Convert.ToInt32(config.FlyLimitZmm * m_ScaleZFactor);
this.SetFlyLimitZ(m_deviceNo, -zLimitBits, zLimitBits);
Log.D($"[RTC6ScannerUnit] MOTF Z-Limit 적용: ±{config.FlyLimitZmm}mm (±{zLimitBits} bits)");
}
}
// if (config.Use3DMode)
// {
// int zLimitBits = Convert.ToInt32(config.FlyLimitZmm * m_ScaleZFactor);
// this.SetFlyLimitZ(m_deviceNo, -zLimitBits, zLimitBits);
// Log.D($"[RTC6ScannerUnit] MOTF Z-Limit 적용: ±{config.FlyLimitZmm}mm (±{zLimitBits} bits)");
// }
//}
@ -1123,16 +1123,16 @@ namespace AuroraLaserScanner
}
#region 26.04.08 NDY - 오차 추적 메소드 OVERRIDE 추가 RTC6 (자식클래스 -실제 RTC6Wrap전달 지점)
public override void SetFlyLimit(uint portNo,int xMin, int xMax , int yMin,int yMax )
{
// mm 를 bits로 처리해줘야한다.
//public override void SetFlyLimit(uint portNo,int xMin, int xMax , int yMin,int yMax )
//{
// // mm 를 bits로 처리해줘야한다.
RTC6Wrap.n_set_fly_limits(portNo, xMin, xMax, yMin, yMax);
}
public override void SetFlyLimitZ(uint portNo, int zMin, int zMax)
{
RTC6Wrap.n_set_fly_limits_z(portNo, zMin, zMax);
}
// RTC6Wrap.n_set_fly_limits(portNo, xMin, xMax, yMin, yMax);
//}
//public override void SetFlyLimitZ(uint portNo, int zMin, int zMax)
//{
// RTC6Wrap.n_set_fly_limits_z(portNo, zMin, zMax);
//}
#endregion
}

View File

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

View File

@ -125,6 +125,36 @@ namespace CoreUnit.Control.UserControl.Canvas
public class Line : DrawObjectBase, IDrawObject, ISerialize
{
#region 26.04.09
// 26.04.09 NDY [대면적 MOTF] Large MOTF Position 파라미터 추가
// P1 + 이전 페이지의 LargeMOTFOffsetX/Y가 더해진 실제 가공 위치
float m_largeMOTFPositionX = 0.0f;
[Browsable(false)]
[ReadOnly(true)]
[Category("Large MOTF")]
[DisplayName("Large MOTF Position X")]
[Description("P1 + Large MOTF Offset X 가 반영된 실제 가공 위치 X")]
[XmlSerializable]
public float LargeMOTFPositionX
{
get { return m_largeMOTFPositionX; }
set { m_largeMOTFPositionX = value; }
}
float m_largeMOTFPositionY = 0.0f;
[Browsable(false)]
[ReadOnly(true)]
[Category("Large MOTF")]
[DisplayName("Large MOTF Position Y")]
[Description("P1 + Large MOTF Offset Y 가 반영된 실제 가공 위치 Y")]
[XmlSerializable]
public float LargeMOTFPositionY
{
get { return m_largeMOTFPositionY; }
set { m_largeMOTFPositionY = value; }
}
#endregion
UnitPoint m_p1, m_p2;
[Browsable(false)]
@ -210,6 +240,11 @@ namespace CoreUnit.Control.UserControl.Canvas
m_alignOffset = acopy.m_alignOffset;
m_MadebyRCC = acopy.m_MadebyRCC;
m_RCC_PowerRatio = acopy.m_RCC_PowerRatio;
// 26.04.09 NDY [대면적 MOTF] 추가
m_largeMOTFPositionX = acopy.m_largeMOTFPositionX;
m_largeMOTFPositionY = acopy.m_largeMOTFPositionY;
}
#region IDrawObject Members
@ -495,8 +530,10 @@ namespace CoreUnit.Control.UserControl.Canvas
public virtual IDrawObject Clone()
{
Line l = new Line();
l.Copy(this);
return l;
}

View File

@ -30,6 +30,12 @@ namespace CoreUnit.Control.UserControl.Canvas
set { m_name = value; }
}
#region 26.04.09
// 26.04.09 NDY [대면적 MOTF] 페이지 레벨 Offset 파라미터 추가
public double LargeMOTFOffsetX { get; set; } = 0.0;
public double LargeMOTFOffsetY { get; set; } = 0.0;
#endregion
public DrawingLayer(int no, string name, Color color, float width)
{
m_no = no;

View File

@ -16,10 +16,21 @@ using JsonSerializer = System.Text.Json.JsonSerializer;
namespace DB.Recipe
{
// 설명 추가 : XML 기반 저장/로딩 구조 - XmlUtil.WriteProperties 프로퍼티를 자동을 저장하는 방식이라서 프로퍼티만 추가하면 된다고 한다.
// XmlUtil.WriteProperties(this, wr) 가 this의 프로퍼티를 자동 직렬화 하니깐 Save() / Load() 수정 없이 프로퍼티 추가만으로 XML에 자동 저장 / 로딩되는 형태
public class RecipeWeldingLines : Recipe
{
#region 26.04.09 NDY
// 26.04.09 NDY [대면적 MOTF] 레시피 레벨 파라미터 추가
public bool UseLargeMOTF { get; set; } = false;
public int LargeMOTFPageCount { get; set; } = 1;
#endregion
DataModel m_model;
public DataModel Model {
public DataModel Model
{
get { lock (this) { return m_model; } }
set { lock (this) { m_model = value; } }
}
@ -28,26 +39,33 @@ namespace DB.Recipe
List<PowerProfile> m_powerProfiles = new List<PowerProfile>();
public List<PowerProfile> PowerProfiles {
public List<PowerProfile> PowerProfiles
{
get { lock (this) { return m_powerProfiles; } }
set { lock (this) { m_powerProfiles = value; } }
}
// 생성자
public RecipeWeldingLines()
{
}
#region 메서드 영역
public void Reset()
{
}
public override void Save(XmlWriter wr, bool includeModel)
{
try {
try
{
XmlUtil.WriteProperties(this, wr);
SaveWeldingParam(wr);
if (includeModel) Model.Save(wr);
} catch (Exception ex) {
}
catch (Exception ex)
{
UtilUnit.logUnit.SetLog(LogUnit.eLogType.ALARM, MethodBase.GetCurrentMethod().Name, ex.Message);
}
}
@ -56,10 +74,13 @@ namespace DB.Recipe
{
string jsonString = string.Empty;
var options = new JsonSerializerOptions { WriteIndented = true };
try {
try
{
jsonString += JsonSerializer.Serialize(ScannerCanvasUserCtrl.ScannerParams, options);
jsonString += Model.Save();
} catch (Exception ex) {
}
catch (Exception ex)
{
UtilUnit.logUnit.SetLog(LogUnit.eLogType.ALARM, MethodBase.GetCurrentMethod().Name, ex.Message);
}
return jsonString;
@ -67,10 +88,13 @@ namespace DB.Recipe
public override void Load(XmlElement xmlElement)
{
try {
try
{
LoadWeldingParam(xmlElement);
Model.Load(xmlElement);
} catch (Exception ex) {
}
catch (Exception ex)
{
UtilUnit.logUnit.SetLog(LogUnit.eLogType.ALARM, MethodBase.GetCurrentMethod().Name, ex.Message);
}
}
@ -86,7 +110,8 @@ namespace DB.Recipe
#region 1.Scanner Objects에 대한 데이터 저장
wr.WriteStartElement("WeldingParameters");
wr.WriteStartElement("items");
foreach (ScannerParam param in ScannerCanvasUserCtrl.ScannerParams) {
foreach (ScannerParam param in ScannerCanvasUserCtrl.ScannerParams)
{
wr.WriteStartElement("WeldingParameter");
CoreUnit.Common.XmlUtil.WriteProperties(param, wr);
wr.WriteEndElement();
@ -98,18 +123,24 @@ namespace DB.Recipe
private void LoadWeldingParam(XmlElement root)
{
try {
try
{
ScannerCanvasUserCtrl.ScannerParams.Clear();
int paramNo = 0;
foreach (XmlElement node in root.ChildNodes) {
if (node.Name == "WeldingParameters") {
foreach (XmlElement node in root.ChildNodes)
{
if (node.Name == "WeldingParameters")
{
#region 1.Scanner Objects에 대한 데이터 로딩
foreach (XmlElement subNode in node.ChildNodes) {
if (subNode.Name == "items") {
foreach (XmlElement subNode in node.ChildNodes)
{
if (subNode.Name == "items")
{
//ScannerCanvasUserCtrl.ScannerParams.Clear();
foreach (XmlElement itemNode in subNode.ChildNodes) {
foreach (XmlElement itemNode in subNode.ChildNodes)
{
ScannerParam param = new ScannerParam();
CoreUnit.Common.XmlUtil.ParseProperties(itemNode, param);
param.No = paramNo;
@ -122,16 +153,20 @@ namespace DB.Recipe
}
}
if (ScannerCanvasUserCtrl.ScannerParams.Count < 128) {
if (ScannerCanvasUserCtrl.ScannerParams.Count < 128)
{
CreateScannerParams();
}
for (int i = 0; i < ScannerCanvasUserCtrl.ScannerParams.Count; i++) {
for (int i = 0; i < ScannerCanvasUserCtrl.ScannerParams.Count; i++)
{
WeldingParameterIDEditor.UpdateColorList(i, ScannerCanvasUserCtrl.ScannerParams[i].Color);
}
} catch (Exception e) {
}
catch (Exception e)
{
throw e;
}
}
@ -140,7 +175,8 @@ namespace DB.Recipe
{
ScannerCanvasUserCtrl.ScannerParams.Clear();
for (int i = 0; i < 128; i++) {
for (int i = 0; i < 128; i++)
{
ScannerParam param = new ScannerParam();
param.No = i;
ScannerCanvasUserCtrl.ScannerParams.Add(param);
@ -231,17 +267,20 @@ namespace DB.Recipe
public List<ScannerParam> ReadOnlyScanParams { get; private set; }
public void LoadFileReadOnly(string fileName)
{
try {
try
{
XmlDocument doc = new XmlDocument();
var extenstion = Path.GetExtension(fileName);
if (extenstion == ".json") {
if (extenstion == ".json")
{
var jsonString = File.ReadAllText(fileName);
doc = JsonConvert.DeserializeXmlNode(jsonString, "RecipeBody");
}
else {
else
{
FileInfo fi = new FileInfo(fileName);
StreamReader sr = new StreamReader(fileName);
doc.Load(sr);
@ -250,44 +289,58 @@ namespace DB.Recipe
XmlElement root = doc.DocumentElement;
foreach (XmlElement childnode in root.ChildNodes) {
foreach (Recipe recipe in recipeList) {
if (childnode.Name == recipe.GetType().Name) {
foreach (XmlElement childnode in root.ChildNodes)
{
foreach (Recipe recipe in recipeList)
{
if (childnode.Name == recipe.GetType().Name)
{
recipe.LoadReadOnly(childnode);
}
}
}
} catch (Exception ex) {
}
catch (Exception ex)
{
throw ex;
}
}
public override void LoadReadOnly(XmlElement xmlElement)
{
try {
try
{
ReadOnlyScanParams = LoadWeldingParamReadOnly(xmlElement);
if(Model is null)
if (Model is null)
Model = new DataModel();
Model.Load(xmlElement);
} catch (Exception ex) {
}
catch (Exception ex)
{
UtilUnit.logUnit.SetLog(LogUnit.eLogType.ALARM, MethodBase.GetCurrentMethod().Name, ex.Message);
}
}
private List<ScannerParam> LoadWeldingParamReadOnly(XmlElement root)
{
try {
try
{
List<ScannerParam> weldingParam = new List<ScannerParam>();
weldingParam.Clear();
int paramNo = 0;
foreach (XmlElement node in root.ChildNodes) {
if (node.Name == "WeldingParameters") {
foreach (XmlElement node in root.ChildNodes)
{
if (node.Name == "WeldingParameters")
{
#region 1.Scanner Objects에 대한 데이터 로딩
foreach (XmlElement subNode in node.ChildNodes) {
if (subNode.Name == "items") {
foreach (XmlElement itemNode in subNode.ChildNodes) {
foreach (XmlElement subNode in node.ChildNodes)
{
if (subNode.Name == "items")
{
foreach (XmlElement itemNode in subNode.ChildNodes)
{
ScannerParam param = new ScannerParam();
CoreUnit.Common.XmlUtil.ParseProperties(itemNode, param);
param.No = paramNo;
@ -300,10 +353,14 @@ namespace DB.Recipe
}
}
return weldingParam;
} catch (Exception e) {
}
catch (Exception e)
{
throw e;
}
}
#endregion
#endregion
}
}

View File

@ -23,7 +23,12 @@
<ColumnDefinition Width="300"></ColumnDefinition>
<ColumnDefinition Width="4*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<!-- ControlBox 요 안에서 수정해야 함. -->
<local:UCScannerObjectTeachingControlBox Grid.Column="0" Margin="0 0 0 0" DataContext="{Binding DataContext.ScannerObjectTeachingControlBoxViewModel, ElementName=uc}"></local:UCScannerObjectTeachingControlBox>
<!-- MainPage -->
<WindowsFormsHost Grid.Column="1">
<lasa:MainPage x:Name="MainPageView" IsMdiContainer="False" TopLevel="False" />
</WindowsFormsHost>

View File

@ -18,18 +18,22 @@
<view:BoolConverter x:Key="convTrueVisible" TrueValue="Visible" FalseValue="Collapsed"/>
<view:BoolConverter x:Key="convFalseVisible" TrueValue="Collapsed" FalseValue="Visible"/>
</UserControl.Resources>
<behavior:Interaction.Triggers>
<behavior:PropertyChangedTrigger Binding="{Binding IsVisible, ElementName=uc}">
<behavior:InvokeCommandAction Command="{Binding CMD_VisibleChanged}" CommandParameter="{Binding IsVisible, ElementName=uc}"/>
</behavior:PropertyChangedTrigger>
</behavior:Interaction.Triggers>
<Grid>
<GroupBox Header="Control">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<!-- 26.04.09 JOG 영역 그룹박스 추가 -->
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<GroupBox Header="Scout Image" Grid.Row="0" Margin="0 3 0 0" Style="{StaticResource StGroupBox2nd}">
@ -76,6 +80,7 @@
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"></ColumnDefinition>
@ -122,12 +127,102 @@
</GroupBox>
<GroupBox Header="Object Select" Grid.Row="1" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 3 0 0" Style="{StaticResource StGroupBox4th}">
<mah:ToggleSwitch Grid.Column="0" HorizontalAlignment="Center" FontSize="12" FontWeight="Bold"
IsOn="{Binding IsObjectSelectAll}"
OffContent="Selected Object"
OnContent="All Object"/>
<Grid>
<Grid.RowDefinitions>
<RowDefinition />
</Grid.RowDefinitions>
<mah:ToggleSwitch Grid.Row="0" HorizontalAlignment="Center" FontSize="12" FontWeight="Bold"
IsOn="{Binding IsObjectSelectAll}"
OffContent="Selected Object"
OnContent="All Object"/>
</Grid>
</GroupBox>
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 5 0 0" Height="50">
<GroupBox Header="가공 Select" Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 3 0 0" Style="{StaticResource StGroupBox4th}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<!-- MOTF 대면적 가공 토글 -->
<mah:ToggleSwitch Grid.Row="0" HorizontalAlignment="Center" FontSize="12" FontWeight="Bold"
IsOn="{Binding IsUseLargeMOTF}"
OffContent="일반 가공"
OnContent="MOTF 대면적 가공"/>
<!-- 페이지 N개 설정 -->
<Grid Grid.Row="1" Margin="0 4 0 0" IsEnabled="{Binding IsUseLargeMOTF}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="50"/>
<ColumnDefinition Width="auto"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="5 0" FontSize="11" Foreground="#FFBBBBBB"
FontWeight="Bold" Text="페이지 N개"
VerticalAlignment="Center"/>
<TextBox Grid.Column="1" Margin="2" FontSize="11"
Text="{Binding LargeMOTFPageCount, UpdateSourceTrigger=PropertyChanged}"
VerticalContentAlignment="Center" HorizontalContentAlignment="Center"/>
<TextBlock Grid.Column="2" Margin="3 0" FontSize="11" Foreground="#FFBBBBBB"
Text="page" VerticalAlignment="Center"/>
<Button Grid.Column="3" Margin="2" FontSize="11" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_ApplyPageCount}"
Content="적용"/>
</Grid>
<!-- Start Set / End Set -->
<Grid Grid.Row="2" Height="40" Margin="0 4 0 0" IsEnabled="{Binding IsUseLargeMOTF}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_LargeMOTFStartSet}"
Content="Start Set"/>
<Button Grid.Column="1" Margin="3" HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_LargeMOTFEndSet}"
Content="End Set"/>
</Grid>
<!-- Offset X / Y -->
<Grid Grid.Row="3" Margin="0 4 0 0" IsEnabled="{Binding IsUseLargeMOTF}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<TextBlock Grid.Column="0" Margin="3 0" FontSize="11" Foreground="#FFBBBBBB"
FontWeight="Bold" Text="Offset X"
VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Border Grid.Column="1" BorderThickness="1" BorderBrush="Gray">
<TextBlock Margin="3 0" FontSize="11" Foreground="#FFBBBBBB"
Text="{Binding LargeMOTFOffsetX, StringFormat={}{0:F3}}"
VerticalAlignment="Center"/>
</Border>
<TextBlock Grid.Column="2" Margin="3 0" FontSize="11" Foreground="#FFBBBBBB"
FontWeight="Bold" Text="Offset Y"
VerticalAlignment="Center" HorizontalAlignment="Right"/>
<Border Grid.Column="3" BorderThickness="1" BorderBrush="Gray">
<TextBlock Margin="3 0" FontSize="11" Foreground="#FFBBBBBB"
Text="{Binding LargeMOTFOffsetY, StringFormat={}{0:F3}}"
VerticalAlignment="Center"/>
</Border>
</Grid>
</Grid>
</GroupBox>
<Grid Grid.Row="3" Grid.Column="0" Grid.ColumnSpan="2" Margin="0 5 0 0" Height="50">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="2*"></ColumnDefinition>
<ColumnDefinition Width="1*"></ColumnDefinition>
@ -147,6 +242,90 @@
</StackPanel>
</Button>
</GroupBox>
<!-- 26.04.09 NDY [Stage X Jog] GroupBox 추가 (Grid.Row="3") -->
<GroupBox Header="Stage X Jog" Grid.Row="3" Margin="0 5 0 0" Style="{StaticResource StGroupBox2nd}">
<Grid>
<Grid.RowDefinitions>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
<RowDefinition Height="auto"/>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<!-- 26.04.09 NDY [Stage X Jog] 속도 선택 행 -->
<Grid Grid.Row="0" Grid.Column="0" Grid.ColumnSpan="2" Height="30" Margin="0 3 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="2" FontSize="11" FontWeight="Bold"
Command="{Binding CMD_StageXJogSpeed}" CommandParameter="L" Content="L"
IsEnabled="{Binding IsJogSpeedL, Converter={StaticResource convReverse}}"
Background="{Binding IsJogSpeedL, Converter={StaticResource convStatusColor}}"/>
<Button Grid.Column="1" Margin="2" FontSize="11" FontWeight="Bold"
Command="{Binding CMD_StageXJogSpeed}" CommandParameter="M" Content="M"
IsEnabled="{Binding IsJogSpeedM, Converter={StaticResource convReverse}}"
Background="{Binding IsJogSpeedM, Converter={StaticResource convStatusColor}}"/>
<Button Grid.Column="2" Margin="2" FontSize="11" FontWeight="Bold"
Command="{Binding CMD_StageXJogSpeed}" CommandParameter="H" Content="H"
IsEnabled="{Binding IsJogSpeedH, Converter={StaticResource convReverse}}"
Background="{Binding IsJogSpeedH, Converter={StaticResource convStatusColor}}"/>
<TextBlock Grid.Column="3" Margin="5 0" FontSize="11" Foreground="#FFBBBBBB"
Text="{Binding StageXJogSpeedValue, StringFormat={}{0:F0} mm/s}"
VerticalAlignment="Center"/>
</Grid>
<!-- Position X 표시 -->
<Border Grid.Row="1" Grid.Column="0" MinHeight="28">
<TextBlock Margin="5 0" FontSize="12" Foreground="#FFBBBBBB"
FontWeight="Bold" Text="StageX Pos"
VerticalAlignment="Center" HorizontalAlignment="Right"/>
</Border>
<Border Grid.Row="1" Grid.Column="1" BorderThickness="1" BorderBrush="Gray">
<StackPanel Orientation="Horizontal" HorizontalAlignment="Stretch">
<TextBlock Margin="5 0" FontSize="12" Foreground="#FFBBBBBB"
FontWeight="Bold"
Text="{Binding StageXPos, StringFormat={}{0:F3}}"
VerticalAlignment="Center" HorizontalAlignment="Left"/>
</StackPanel>
</Border>
<!-- X- Stop X+ 버튼 -->
<Grid Grid.Row="2" Grid.Column="0" Grid.ColumnSpan="2"
Height="50" Margin="0 5 0 0">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
<ColumnDefinition Width="1*"/>
</Grid.ColumnDefinitions>
<Button Grid.Column="0" Margin="3"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_StageXJogMinus}"
Content="X -"/>
<Button Grid.Column="1" Margin="3"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_StageXJogStop}"
Content="Stop"/>
<Button Grid.Column="2" Margin="3"
HorizontalAlignment="Stretch" VerticalAlignment="Stretch"
Height="auto" FontWeight="Bold"
Style="{StaticResource StButtonAccentBlue}"
Command="{Binding CMD_StageXJogPlus}"
Content="X +"/>
</Grid>
</Grid>
</GroupBox>
</Grid>
</GroupBox>

View File

@ -1,9 +1,13 @@
using Aurora;
using Aurora.Api;
using Aurora.View;
// 26.04.09 NDY [Stage X Jog] using 추가
using AuroraACS;
using AuroraIO;
using AuroraLaser;
using AuroraLaserScanner;
using AuroraMotor;
using CoreUnit.Control.UserControl.Canvas;
using System;
using System.Collections.Generic;
using System.ComponentModel;
@ -15,11 +19,14 @@ using System.Windows;
using System.Windows.Input;
using System.Windows.Threading;
using VisionCommon;
using WeldingCommon.Devices.ACS;
using WeldingCommon.Models;
using WeldingCommon.Sequences;
using WeldingCommon.Settings;
using WeldingCommon.Utils;
using static AuroraLaser.Ekspla.NL202;
using JogDirection = AuroraACS.JogDirection;
using Speed = AuroraACS.Speed;
namespace WeldingCommon.Views
{
@ -27,6 +34,77 @@ namespace WeldingCommon.Views
{
private readonly Logger Log = new Logger("UCScannerObjectTeachingControlBoxViewModel");
#region
// 26.04.09 NDY [Stage X Jog] 필드 추가
private ACSMotor _MotorX;
// 26.04.09 NDY [Stage X Jog] 프로퍼티 추가
public double StageXJogSpeed { get; set; } = 10.0;
public double StageXPos { get => _StageXPos; set => SetProperty(ref _StageXPos, value); }
private double _StageXPos;
// 26.04.09 NDY [Stage X Jog] 커맨드 선언 추가
public ICommand CMD_StageXJogPlus { get; }
public ICommand CMD_StageXJogMinus { get; }
public ICommand CMD_StageXJogStop { get; }
// 26.04.09 NDY [대면적 MOTF] 토글 프로퍼티 추가
private bool _IsUseLargeMOTF ;
public bool IsUseLargeMOTF { get => _IsUseLargeMOTF; set => SetProperty(ref _IsUseLargeMOTF, value); }
// 26.04.09 NDY [Stage X Jog] 속도 선택 프로퍼티 기본 속도 M으로 변경
private Speed _StageXJogSpeed = Speed.M;
public bool IsJogSpeedL { get => _StageXJogSpeed == Speed.L; }
public bool IsJogSpeedM { get => _StageXJogSpeed == Speed.M; }
public bool IsJogSpeedH { get => _StageXJogSpeed == Speed.H; }
// 26.04.09 NDY [Stage X Jog] null 방어 추가
public double StageXJogSpeedValue
{
get
{
if (_MotorX == null || !_MotorX.IsActivated) return 0;
return _MotorX.GetJogSpeed(_StageXJogSpeed);
}
}
// 26.04.09 NDY [Stage X Jog] 속도 선택 커맨드 선언
public ICommand CMD_StageXJogSpeed { get; }
//
// 26.04.09 NDY [대면적 MOTF] 프로퍼티 추가
private int _LargeMOTFPageCount = 1;
public int LargeMOTFPageCount
{
get => _LargeMOTFPageCount;
set => SetProperty(ref _LargeMOTFPageCount, value);
}
private double _LargeMOTFOffsetX = 0.0;
public double LargeMOTFOffsetX
{
get => _LargeMOTFOffsetX;
set => SetProperty(ref _LargeMOTFOffsetX, value);
}
private double _LargeMOTFOffsetY = 0.0;
public double LargeMOTFOffsetY
{
get => _LargeMOTFOffsetY;
set => SetProperty(ref _LargeMOTFOffsetY, value);
}
// 26.04.09 NDY [대면적 MOTF] 커맨드 선언
public ICommand CMD_ApplyPageCount { get; }
public ICommand CMD_LargeMOTFStartSet { get; }
public ICommand CMD_LargeMOTFEndSet { get; }
#endregion
public ICommand CMD_VisibleChanged { get; }
public ICommand CMD_RunScoutNormal { get; }
@ -125,8 +203,163 @@ namespace WeldingCommon.Views
_MonitorTimer.Tick += OnMonitoring;
_MonitorTimer.Interval = TimeSpan.FromMilliseconds(100);
#region
// 26.04.09 NDY [Stage X Jog] 생성자 내부 - 디바이스 취득 및 커맨드 등록
try { _MotorX = _Aurora.DeviceManager[CommonDeviceIds.AxisX2] as ACSMotor; }
catch { _MotorX = null; }
CMD_StageXJogPlus = new DelegateCommand(PerformCmdStageXJogPlus);
CMD_StageXJogMinus = new DelegateCommand(PerformCmdStageXJogMinus);
CMD_StageXJogStop = new DelegateCommand(PerformCmdStageXJogStop);
// 26.04.09 NDY [Stage X Jog] 속도 선택 커맨드 등록
CMD_StageXJogSpeed = new DelegateCommand(GetPerformCmdStageXJogSpeed);
// 생성자 커맨드 등록
CMD_ApplyPageCount = new DelegateCommand(PerformCmdApplyPageCount);
CMD_LargeMOTFStartSet = new DelegateCommand(PerformCmdLargeMOTFStartSet);
CMD_LargeMOTFEndSet = new DelegateCommand(PerformCmdLargeMOTFEndSet);
#endregion
}
#region 26.04.09 NDY [Stage X Jog]
// 26.04.09 NDY [대면적 MOTF] 커맨드 구현
// 26.04.09 NDY [대면적 MOTF] 페이지 N개 적용 커맨드 구현
private void PerformCmdApplyPageCount(object obj)
{
var result = MessageUtil.ShowYesNoMessageDialog("페이지 설정",
$"페이지를 {LargeMOTFPageCount}개로 설정하시겠습니까?\n기존 페이지는 유지되고 부족한 페이지만 추가됩니다.");
if (result == false) return;
var mainPage = CommonHelper.GetMainPage();
if (mainPage == null) return;
// 현재 페이지 수 확인 후 부족한 만큼만 추가
int currentPageCount = mainPage.ScannerCanvasCtrlTeaching.Model.Layers.Count();
int needCount = LargeMOTFPageCount - currentPageCount;
for (int i = 0; i < needCount; i++)
{
mainPage.ScannerCanvasCtrlTeaching.Model.CreateLayer();
}
mainPage.UpdateLayersInfo();
Log.I($"[대면적 MOTF] 페이지 {LargeMOTFPageCount}개 설정 완료 (추가: {Math.Max(0, needCount)}개)");
}
// 26.04.09 NDY [대면적 MOTF] Start Set 시점 모터 위치 저장용
private double _startSetPositionX = 0.0;
private double _startSetPositionY = 0.0;
private double _startMotorPos = 0.0;
// 26.04.09 NDY [대면적 MOTF] Start Set 커맨드 구현
private void PerformCmdLargeMOTFStartSet(object obj)
{
var result = MessageUtil.ShowYesNoMessageDialog("Large MOTF Start Set",
"현재 DrawObject EndPosition을 Start 기준으로 저장하시겠습니까?");
if (result == false) return;
// 26.04.09 NDY - 모터 위치는 DrawObject 유무와 무관하게 무조건 저장
_startMotorPos = _MotorX?.GetActualPos() ?? 0.0;
Log.I($"[대면적 MOTF] Start Set 완료");
Log.I($" - Stage X 현재 위치 (Motor): {_startMotorPos:F3} mm");
var mainPage = CommonHelper.GetMainPage();
if (mainPage == null) return;
var activeLayer = mainPage.ScannerCanvasCtrlTeaching.Model.ActiveLayer as DrawingLayer;
var objects = activeLayer?.Objects;
if (objects == null || objects.Count == 0)
{
Log.I($" - DrawObject 없음 (Scanner 기준위치 저장 생략)");
return;
}
var lastObj = objects.LastOrDefault() as Line;
if (lastObj == null)
{
Log.I($" - 마지막 객체가 Line 타입 아님: {objects.LastOrDefault()?.GetType().Name}");
return;
}
_startSetPositionX = lastObj.P2.X;
_startSetPositionY = lastObj.P2.Y;
Log.I($" - DrawObject EndPosition (Scanner): X={_startSetPositionX:F3}, Y={_startSetPositionY:F3}");
}
// 26.04.09 NDY [대면적 MOTF] End Set 커맨드 구현
private void PerformCmdLargeMOTFEndSet(object obj)
{
var result = MessageUtil.ShowYesNoMessageDialog("Large MOTF End Set",
"현재 Stage X 위치를 End 기준으로 저장하고 Offset을 계산하시겠습니까?");
if (result == false) return;
var mainPage = CommonHelper.GetMainPage();
if (mainPage == null) return;
// 현재 모터 이동량 → Scanner 좌표계 변환 (방향은 TODO: 이정인 선임 확인 필요)
double motorPos = _MotorX?.GetActualPos() ?? 0.0;
double motorDelta = motorPos - _startMotorPos; // Start Set 시점의 모터 위치 필요
// TODO: 모션 좌표 → 스캐너 필드 좌표 변환 계수 적용
LargeMOTFOffsetX = motorDelta; // 임시: 1:1 적용, 방향/스케일 확인 후 수정
LargeMOTFOffsetY = 0.0;
// 현재 Active Layer에 저장
var activeLayer = mainPage.ScannerCanvasCtrlTeaching.Model.ActiveLayer as DrawingLayer;
if (activeLayer != null)
{
activeLayer.LargeMOTFOffsetX = LargeMOTFOffsetX;
activeLayer.LargeMOTFOffsetY = LargeMOTFOffsetY;
}
Log.I($"[대면적 MOTF] End Set: OffsetX={LargeMOTFOffsetX:F3}, OffsetY={LargeMOTFOffsetY:F3}");
Log.I($"[대면적 MOTF] End Set 완료");
Log.I($" - Stage X 이동량 (Motor Delta): {motorDelta:F3} mm");
Log.I($" - 계산된 Offset X (Scanner): {LargeMOTFOffsetX:F3} mm");
Log.I($" - 계산된 Offset Y (Scanner): {LargeMOTFOffsetY:F3} mm");
Log.I($" - 저장된 Layer: Page {mainPage.ScannerCanvasCtrlTeaching.Model.ActiveLayer.No + 1}");
Log.I($" - TODO: 모션→스캐너 좌표 변환 방향/스케일 이정인 선임 확인 필요");
}
private void GetPerformCmdStageXJogSpeed(object obj)
{
switch ((string)obj)
{
case "L": _StageXJogSpeed = Speed.L; break;
case "M": _StageXJogSpeed = Speed.M; break;
case "H": _StageXJogSpeed = Speed.H; break;
}
NotifyPropertyChanged(nameof(IsJogSpeedL));
NotifyPropertyChanged(nameof(IsJogSpeedM));
NotifyPropertyChanged(nameof(IsJogSpeedH));
NotifyPropertyChanged(nameof(StageXJogSpeedValue));
}
// 26.04.09 NDY [Stage X Jog] 커맨드 구현 추가
private void PerformCmdStageXJogPlus(object obj)
{
_MotorX?.MoveToSWLimit(JogDirection.plus, _StageXJogSpeed);
}
private void PerformCmdStageXJogMinus(object obj)
{
_MotorX?.MoveToSWLimit(JogDirection.minus, _StageXJogSpeed);
}
private void PerformCmdStageXJogStop(object obj)
{
_MotorX?.Stop();
}
#endregion
private void InitVariable()
{
if (_SystemParameter.ScoutType == eScoutType.Normal) {
@ -189,6 +422,14 @@ namespace WeldingCommon.Views
}
// TODO: PRIBAT_L1, PRIBAT_L2 는 Blow 의 상태 check 가 들어 있음. 장비 한정이므로 이를 check 하는 것을 modelItem 이나 setting 으로 빼서 진행되도록 함.
#region
// 26.04.09 NDY [Stage X Jog] OnMonitoring 내부 - X축 위치 갱신
if (_MotorX != null && _MotorX.IsActivated)
{
StageXPos = _MotorX.GetActualPos();
}
#endregion
}
private void PerformCmdRunScoutNormal(object obj)