CCW ad37cb326a [BugFix] Ramping 미반영 케이스 제거
- 실수 <-> 정수 컨버팅 과정에서 (1/180) 모수로 미반영된 데이터 존재, 기본값이 Max라 튀는 현상
2026-02-28 10:56:12 +09:00

2505 lines
146 KiB
C#

using Aurora.Api;
using Aurora.Api.Devices;
using Aurora.View;
using AuroraLaser;
using AuroraLaserScanner;
using CoreUnit.Control.UserControl.Canvas;
using CoreUnit.Scanner.UserControl.ScannerCanvas;
using DB;
using Hermes;
using Matrox.MatroxImagingLibrary;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.Remoting.Contexts;
using System.Security.RightsManagement;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Web.UI.WebControls.WebParts;
using VisionCommon;
using WeldingCommon.Alarm;
using WeldingCommon.Data;
using WeldingCommon.Models;
using WeldingCommon.Settings;
using WeldingCommon.Utils;
using static CoreUnit.Common.UtilUnit;
namespace WeldingCommon.Sequences {
class SeqUploadWeldingList {
static readonly Logger Log = new Logger("SeqUploadWeldingList");
const string SEQ_ENTER = "[SeqUploadWeldingList] SEQ_ENTER"; // inputParam 으로 받자.
const string SEQ_CHECK_INIT_CONDITION = "[SeqUploadWeldingList] Check Init Condition"; // Check Init Condition
const string SEQ_MARKING_ON_THE_FLY_ON = "[SeqUploadWeldingList] Marking OnTheFly On";
const string SEQ_RECORD_SIGNAL_ON = "[SeqUploadWeldingList] Record Signal On";
const string SEQ_LOAD_DRAW_OBJECT_SCANNER_PARAM = "[SeqUploadWeldingList] Load DrawObjectScannerParam";
const string SEQ_MARKING_ON_THE_FLY_OFF = "[SeqUploadWeldingList] Marking OnTheFly Off";
const string SEQ_RECORD_SIGNAL_OFF = "[SeqUploadWeldingList] Record Signal Off";
const string SEQ_SAVE_RESULT_DATA = "[SeqUploadWeldingList] Save Result Data";
const string SEQ_WRITE_COMPLETION_PLC_BIT = "[SeqUploadWeldingList] Write Completion PLC Bit";
const string SEQ_COMPLETE = "[SeqUploadWeldingList] SEQ_COMPLETE"; // 완료
static class ContextKey {
public const string KEY_SEQUENCE_RESULT = "KEY_SEQUENCE_RESULT_SeqUploadWeldingList";
public const string KEY_SEQUENCE_START_TIME = "KEY_SEQUENCE_START_TIME_SeqUploadWeldingList";
}
private SeqUploadWeldingList_InitParam _InitParam;
private Scanner _Scanner;
private LaserPowerCalParam _LaserPowerCalParam;
private SystemParameter _SystemParameter;
private double _LastX = 0;
private double _LastY = 0;
private double _FlyOffsetX;
private double _FlyOffsetY;
private PointF _ExternalCommandOffset = new PointF(0, 0);
private static DateTime _UploadstartTime;
private static TimeSpan _UploadSpanTime;
private double Rotation_Degree = 0;
public SequenceBuilder Builder()
{
var sb = new SequenceBuilder(1000, true);
////var sb = new SequenceBuilder(1000, false); // With log
//MOTF와 RecordSignal이 동시 수행 가능한지 모르니깐 우선 순차적으로 실행될 수 있도록 LASA와 동일하게 구성..
//동시 수행 가능하면 Fork로 구성해도 될 듯
sb.AddAction(SEQ_ENTER, Sequence_Enter);
sb.AddDecision(SEQ_CHECK_INIT_CONDITION, CheckInitCondition); //분기
sb.AddAction(SEQ_MARKING_ON_THE_FLY_ON, MarkingOnTheFlyOn);
sb.AddAction(SEQ_RECORD_SIGNAL_ON, RecordSignalOn);
sb.AddAction(SEQ_LOAD_DRAW_OBJECT_SCANNER_PARAM, LoadDrawObjectScannerParam);
sb.AddAction(SEQ_MARKING_ON_THE_FLY_OFF, MarkingOnTheFlyOff);
sb.AddAction(SEQ_RECORD_SIGNAL_OFF, RecordSignalOff);
sb.AddAction(SEQ_SAVE_RESULT_DATA, SaveResultData); // 해당 공정의 정보를 기록 함.
sb.AddAction(SEQ_WRITE_COMPLETION_PLC_BIT, WriteCompletionPLCBit); // 정상적으로 종료 되면, complete bit write
sb.AddAction(SEQ_COMPLETE, Complete);
sb.Flows(sb.Init > sb[SEQ_ENTER] > sb[SEQ_CHECK_INIT_CONDITION]
> sb[SEQ_MARKING_ON_THE_FLY_ON] > sb[SEQ_RECORD_SIGNAL_ON] > sb[SEQ_LOAD_DRAW_OBJECT_SCANNER_PARAM]
> sb[SEQ_MARKING_ON_THE_FLY_OFF] > sb[SEQ_RECORD_SIGNAL_OFF]
> sb[SEQ_SAVE_RESULT_DATA] > sb[SEQ_WRITE_COMPLETION_PLC_BIT] > sb[SEQ_COMPLETE]);
sb.Flows(sb[SEQ_CHECK_INIT_CONDITION] > sb[SEQ_COMPLETE] > sb.Final);
sb.OnStopped(OnStopped);
sb.OnForceError(OnForceError);
//sb.Draw("SeqUploadWeldingList.pdf");
return sb;
}
private SequenceActionResult Sequence_Enter(SequenceContext context, string id, SequenceActionResult param)
{
Log.I("SeqUploadWeldingList Start");
_InitParam = param.Param as SeqUploadWeldingList_InitParam;
_LaserPowerCalParam = context.GetSetting<LaserPowerCalParam>(CommonSettingIds.LaserPowerCalParam);//우선 AuroraRecipe로 상속받아서 함수 사용하도록 함
_SystemParameter = context.GetSetting<SystemParameter>(CommonSettingIds.SystemParameter);
InitDev(context);
if (!CheckDeviceReady(context)) { //Check Device
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_DeviceNotReady);
Log.E("The devices are not active.");
return null;
}
if (InitSequenceVariable(context) == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_InitVariableError);
return null;
}
// ian0513, 이게 여기 있어야 할까??, 무엇을 측정 하기 위한건지 명확히 알고 실제 공정이면 다른 밑의 Node 로 변경
_UploadstartTime = DateTime.Now;
return SequenceAction.Ok();
}
private bool InitSequenceVariable(SequenceContext context)
{
//laserPowerCalParam 초기화(CSV 파일 불러오기)
_LaserPowerCalParam.Initialize(_LaserPowerCalParam.PowerCalibrationCenterBeamCSVFile, _LaserPowerCalParam.PowerCalibrationRingBeamCSVFile);
Rotation_Degree = _InitParam.Rotation_Degree;
_FlyOffsetX = 0;
_FlyOffsetY = 0;
return true;
}
private void InitDev(SequenceContext context)
{
//Device Init
_Scanner = context.GetLaserScanner();
}
private bool CheckDeviceReady(SequenceContext context)
{
List<string> deviceListToCheck = new List<string>() { CommonDeviceIds.Scanner };
bool isAllDeviceActivated = true;
foreach (string item in deviceListToCheck) {
if (!context.Devices[item].IsActivated) {
isAllDeviceActivated = isAllDeviceActivated && false;
}
}
return isAllDeviceActivated;
}
private bool CheckCondition()
{
if (CheckCondition_Interlock()) {
if (InitializeDeviceStatus()) {
return true;
}
}
return false;
}
private bool CheckCondition_Interlock()
{
// PLC, Data, IO 등 Interlock checking
return true;
}
private bool InitializeDeviceStatus()
{
if (_InitParam.IncludeListStartEnd) {
_Scanner.SetStartList(_InitParam.WeldingScannerListNo);
}
return true;
}
private string CheckInitCondition(SequenceContext context, string id, ICollection<string> branches, SequenceActionResult param)
{
if (!CheckCondition()) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_CheckInitCondition);
Log.E("Condition is not in state to start sequence.");
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, false);
return SEQ_COMPLETE;
}
var startTime = DateTime.Now;
context.PutValue(ContextKey.KEY_SEQUENCE_START_TIME, startTime);
Log.D("UploadWeldingList Sequence CheckInitCondition");
return SEQ_MARKING_ON_THE_FLY_ON;
}
private SequenceActionResult MarkingOnTheFlyOn(SequenceContext context, string id, SequenceActionResult param)
{
if (_Scanner.Config.UseMarkingOnTheFly) {
if (_Scanner.Config.OnTheFlyCoordinate == eOntheFlyCoordinate.XY) {
//CIT의 경우 Disk Motor의 Encoder값이라 변환이 필요하다.
if (_SystemParameter.MachineType == eMachineName.CIT) {
//CIT Disk Motor의 직경
double diameter = 563;
_Scanner.SetFlyX(_Scanner.Config.MarkingOnTheFlyEncoderResolutionRotate, diameter);
//CIT는 원형 모터를 사용하면서 MOTF동작의 Coodinate는 X,Y로 하고 X만 사용 한다.
//그래서 endcoder resolution x, y에 어떤 값이든 넣어야 아래 함수에서 에러가 발생 하지 않음
_Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartEncoderPositionX);
_FlyOffsetX = _Scanner.Config.MarkingOntheFlyOffsetX;
_FlyOffsetY = _Scanner.Config.MarkingOntheFlyOffsetY;
} else {
// 1. Linear Track OTF 활성화 및 엔코더 초기화
_Scanner.SetFlyX(_Scanner.Config.MarkingOnTheFlyEncoderResolutionX);
// 2. 엔코더 특정 위치갈 때까지 대기
_Scanner.WaitForEncoderX(_Scanner.Config.MarkingStartEncoderPositionX);
_FlyOffsetX = _Scanner.Config.MarkingOntheFlyOffsetX;
_FlyOffsetY = _Scanner.Config.MarkingOntheFlyOffsetY;
}
} else if (_Scanner.Config.OnTheFlyCoordinate == eOntheFlyCoordinate.Rotate) {
Log.I(string.Format("MOTF 사용, Rotate 보상"));
// 1. 엔코더 값이 들어오면 바로 X(Rotary) Encoder 리셋
_Scanner.SetFlyRotate(_Scanner.Config.MarkingOnTheFlyEncoderResolutionRotate);
// 2. 일괄 회전.(OTW 세팅시, Encoder가 0부터 시작하므로)
foreach (IDrawObject obj in _InitParam.SelectedDrawObjectList) {
obj.Rotate((float)Rotation_Degree, new UnitPoint(_Scanner.Config.MarkingOnTheFlyRotateCenterX, _Scanner.Config.MarkingOnTheFlyRotateCenterY)); // +15도 회전
}
// 3. 원하는 용접 시작 각도 올 때까지 기다리기.
double weldingAngle = 0.0;
if (_SystemParameter.MachineType == eMachineName.CIT) {
weldingAngle = _Scanner.Config.MarkingRangeMinRotate;
} else {
weldingAngle = _Scanner.Config.MarkingRangeMinRotate + Rotation_Degree;
}
_Scanner.WaitForEncoderRotateX(weldingAngle); // -5도부터 용접시작하고 싶으면 10도 넣으면 됨
// _FlyOffsetX, _FlyOffsetY 값 설정 불필요, 0-0 이면 됨.
}
// 엔코더 기록(용접 시작)
_Scanner.StoreEncoder(0);
_Scanner.SaveAndRestartTimer(); // 이전 용접의 T/T을 가져가고 싶으면 여기서 Reset 하기 전에 GetTime으로 읽어가야 함
}
return SequenceAction.Ok();
}
private SequenceActionResult RecordSignalOn(SequenceContext context, string id, SequenceActionResult param)
{
if (_Scanner.Config.UseRecordSignal) {
_Scanner.SaveAndRestartTimer();
_Scanner.SetTrigger(_Scanner.Config.RecordPeriod,
_Scanner.Config.RecordSignalType1, _Scanner.Config.RecordSignalType2,
_Scanner.Config.RecordSignalType3, _Scanner.Config.RecordSignalType4);
}
return SequenceAction.Ok();
}
private SequenceActionResult LoadDrawObjectScannerParam(SequenceContext context, string id, SequenceActionResult param)
{
//객체에 대한 파라미터 Load 전 _LastX,Y를 현재 스캐너 위치로 받아옴
_Scanner.GetXYPos(out _LastX, out _LastY);
_ExternalCommandOffset.X = (float)_InitParam.CellMeasureCenterOffsetX;
_ExternalCommandOffset.Y = (float)_InitParam.CellMeasureCenterOffsetY;
int drawObjectCount = FuncLoadDrawObjectScannerParam(_InitParam.SelectedDrawObjectList);
return SequenceAction.Ok();
}
private int FuncLoadDrawObjectScannerParam(List<IDrawObject> objects)
{
int drawObjectCount = 0;
foreach (IDrawObject obj in objects) {
drawObjectCount += FuncLoadDrawObjectScannerParamSelected(obj);
}
Debug.WriteLine($"Scanner Space Check({_Scanner.GetListSpace()})");
return drawObjectCount;
}
private int LoadGroupScannerParam(Group obj)
{
int count = 0;
//서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
for (uint i = 0; i < obj.MarkingCount; i++) {
foreach (IDrawObject subObj in obj.SubObjects) { //Group객체내 포함된 DrawTools에 대한 용접 작업
count += FuncLoadDrawObjectScannerParamSelected(subObj);
}
}
return count;
}
private int FuncLoadDrawObjectScannerParamSelected(IDrawObject obj)
{
int count = 0;
if (obj.Enabled == true) {
if (obj is Line) {
LoadLineScannerParam((Line)obj);
count++;
} else if (obj is Dot) {
LoadDotScannerParam((Dot)obj);
count++;
} else if (obj is Spline) {
LoadSplineScannerParam((Spline)obj);
count++;
} else if (obj is SnowMan) {
LoadSnowManScannerParam((SnowMan)obj);
count++;
} else if (obj is Arc) {
LoadArcScannerParam((Arc)obj);
count++;
} else if (obj is Arc3Point) {
LoadArc3PScannerParam((Arc3Point)obj);
count++;
} else if (obj is ZigZag) {
LoadZigzagScannerParam((ZigZag)obj);
count++;
} else if (obj is Group) {
count += LoadGroupScannerParam((Group)obj);
}
}
return count;
}
private void SetScannerProcessCondition(ScannerParam param, out float percentLaser1, out float percentLaser2)
{
//TrumpLaser의 경우 SetLaserTiming 함수로 Gate 신호를 살려줘야 함
//우선 무조건 셋팅하는 것으로 쓰고 Trump_IO Class 만들면 _laser type이 Trump의 경우에만 셋팅해주는 것으로 변경하자
//LaserFrequency = 10, LaserPulseDuration = 0.1로 셋팅하면 출사 되는 것 확인_241025
if (_InitParam.NeedSetLaserTiming) {
_Scanner.SetLaserTiming((uint)param.LaserFrequency, (uint)(param.LaserPulseDuration * 1000), (uint)(param.LaserPulseDuration * 1000));
Log.D(string.Format($"[SetLaserTiming] LaserFrequency : {param.LaserFrequency}, LaserPulseDuration : {param.LaserPulseDuration}"));
}
// 1.레이저 ON/OFF delay 설정
_Scanner.SetLaesrDelay(param.LaserOnDelay, param.LaserOffDelay);
// 2.Mark delay 설정
_Scanner.SetScannerDelay(param.JumpDelay, param.MarkDelay, param.PolygonDelay);
// 3.DA(레이저 파워값) 설정
percentLaser1 = (float)_LaserPowerCalParam.CalibrateWattToPercent(param.LaserPower1, (uint)eAnalogPortOut.DA1);
percentLaser2 = (float)_LaserPowerCalParam.CalibrateWattToPercent(param.LaserPower2, (uint)eAnalogPortOut.DA2);
// AnalogPower는 안전하게 Jump전 0, Mark전 파워 셋팅해주는 것으로 한다.
//_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
//_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.점프 및 마크 속도 설정
_Scanner.SetJumpSpeed(param.JumpSpeed);
_Scanner.SetMarkSpeed(param.MarkSpeed);
}
private PowerProfilesList ReadPowerProfile(int powerProfileID)
{
//추후 경로 변경 필요
// TODO: ian0513.kim, DataPath 를 설정해서 경로를 빼주자
var json = File.ReadAllText("PowerProfileTest.json");
var root = JsonConvert.DeserializeObject<TopRootObject>(json);
PowerProfilesList powerProfile = new PowerProfilesList();
for (int i = 0; i < root.PowerProfilesLists[powerProfileID].PowerProfiles.Count; i++) {
PowerProfile profile = new PowerProfile();
profile.PowerProfileParams.Add(new PowerProfileParam());
profile.PowerProfileParams[0].Copy(root.PowerProfilesLists[powerProfileID].PowerProfiles[i].PowerProfileParams[0]);
powerProfile.PowerProfiles.Add(profile);
}
return powerProfile;
}
private void LoadLineScannerParam(Line obj)
{
//m_scannerToCameraReferenceOffsets은 SystemParameter에서 설정된 값, Cal하고나면 해당 Offset은 현재 필요없는거같은데 확인 필요, 필요시 Camera setting에 추가해주면됨
//m_externalCommandOffset은 AutoRun시 PLC에서 받아오는 offset임, 현재 LASA에서 BhAutoRun 클래스에 CCW라인에서만 사용하던데 실제 사용하는지 확인 필요
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1. 스캐너 가공 조건 설정
// TODO: ScannerCanvasUserCtrl 의 값이 제대로 가져와 지는지 확인 필요함. mainPage 로 접근해서 가져와야 할 것인데.
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
var laserLog = new ActualProcessUnit();
float percentLaser1 = 0.0f;
float percentLaser2 = 0.0f;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Line Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Line";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
laserLog.JumpDelay = param.JumpDelay;
laserLog.MarkDelay = param.MarkDelay;
laserLog.PolygonDelay = param.PolygonDelay;
laserLog.Power = param.LaserPower1;
laserLog.JumpSpeed = param.JumpSpeed;
laserLog.MarkSpeed = param.MarkSpeed;
// 2. 스캐너 가공 작업
// 2.1. Wobbel 관련 설정
if (param.WobbelEnable) {
// SnowMan 와블인 경우
if (param.SnowManEnable) {
int dx = 0;
int dy = 0;
double radian = (param.TiltingAngle - 90) * Math.PI / 180;
dx = (int)(Math.Cos(radian) * 100);
dy = (int)(Math.Sin(radian) * 100);
_Scanner.SetWobbelDirection(dx, dy);
_Scanner.SetWobbelDirection(0, 0);
// Wobbel 모드 설정
_Scanner.SetWobbelMode(true, 1, 1, 1, 2);
//Wobbel Vector 리스트 초기화
_Scanner.SetWobbelVector(0, 0, 0, 0);
List<double> dTrans = new List<double>();
List<double> dLong = new List<double>();
param.LoadWobbelVectorForHead(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.HeadPeriod, 0);
}
param.LoadWobbelVectorForBody(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.BodyPeriod, 0);
}
}
// Zigzag 와블인 경우
if (param.ZigzagWobbelEnable) {
// Wobbel 모드 설정
_Scanner.SetWobbelDirection(0, 0);
_Scanner.SetWobbelMode(true, 1, 1, 1, 2);
//Wobbel Vector 리스트 초기화
_Scanner.SetWobbelVector(0, 0, 0, 0);
List<double> dTrans = new List<double>();
List<double> dLong = new List<double>();
param.CreateWobbelVectorForZigzagPointList();
_Scanner.SetMarkSpeed(param.zigzagTotalSpeed);
param.LoadZigZagWobbelVector(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.m_zigzagPeriod[i + 1], 0);
}
}
// 일반적인 와블인 경우
if (!param.SnowManEnable && !param.ZigzagWobbelEnable) {
_Scanner.SetWobbelMode(param.WobbelEnable, param.WobbelTransversal,
param.WobbelLongditudinal, param.WobbelFrequency, (int)param.WobbelShape);
}
Log.I(string.Format($"[Line Weld] Item No({obj.No + 1}) Wobbel Setting : Use({param.WobbelEnable}), Transversal({param.WobbelTransversal}), Longitudinal({param.WobbelLongditudinal}), Frequency({param.WobbelFrequency}), WobbelShape({param.WobbelShape})"));
} else {
//_Scanner.SetWobbelDirection(0, 0);
//_Scanner.SetWobbelVector(0, 0, 0, 0);
//_Scanner.SetWobbelMode(false, param.WobbelTransversal, param.WobbelLongditudinal, param.WobbelFrequency, 0);
_Scanner.SetWobbelDirection(0, 0);
_Scanner.SetWobbelVector(0, 0, 0, 0);
_Scanner.SetWobbelMode(false, 0, 0, 0, 0);
}
#region 2.2. Ramping & PowerProfile 관련 설정
// 2.2. Ramping & PowerProfile 관련 설정
//if (param.UsePowerProfile) {
// PowerProfilesList powerProfile = ReadPowerProfile(param.PowerProfileID);
// // START- 시작위치 점프 관련 설정
// if (powerProfile.PowerProfiles.Count != 0) {
// float power = percentLaser1 * (float)powerProfile.PowerProfiles[0].PowerProfileParams[0].StartPowerPercent * 0.01f;
// //Param 명령을 주기 위해서는 Polygon Delay 를 0으로 한다.
// _Scanner.SetScannerDelay(param.JumpDelay, param.MarkDelay, 0);
// // 지그재그는 벡터 기반 컨트롤
// _Scanner.SetVectorControl((uint)eAutoControlParameter.AnalogOut1, (uint)eAutoControlParameter.AnalogOut1);
// if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.02 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.02) {
// //같은 위치인 경우 점프 딜레이 0으로 현재 위치(상대 위치 0,0)으로 점프 실행줘야 한다. --> Para함수는 Write_da 함수가 적용되지 않고, 마지막 Para 함수의 파워값으로 설정되기 때
// _Scanner.SetScannerDelay(0, param.MarkDelay, 0);
// _Scanner.ParaJumpRel(0, 0, power);
// } else {
// _Scanner.ParaJumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, power);
// }
// //첫번째 Profile Param
// double totalTime = powerProfile.PowerProfiles[0].PowerProfileParams[0].Time;
// double speed = param.ZigzagWobbelEnable ? param.zigzagTotalSpeed : param.MarkSpeed;
// double profileDistance = totalTime * speed * 0.001f;
// double x = profileDistance * (obj.P2.X - obj.P1.X) / obj.Distance + obj.P1.X;
// double y = profileDistance * (obj.P2.Y - obj.P1.Y) / obj.Distance + obj.P1.Y;
// power = percentLaser1 * (float)powerProfile.PowerProfiles[0].PowerProfileParams[0].EndPowerPercent * 0.01f;
// _Scanner.ParaMarkAbs(x + xOffset, y + yOffset, power);
// if (powerProfile.PowerProfiles.Count > 1) {
// for (int i = 1; i < powerProfile.PowerProfiles.Count; i++) {
// totalTime += powerProfile.PowerProfiles[i].PowerProfileParams[0].Time;
// profileDistance = totalTime * speed * 0.001f;
// if (profileDistance < obj.Distance) {
// power = percentLaser1 * (float)powerProfile.PowerProfiles[i].PowerProfileParams[0].StartPowerPercent * 0.01f;
// _Scanner.SetScannerDelay(0, param.MarkDelay, 0);
// _Scanner.ParaJumpRel(0, 0, power);
// x = profileDistance * (obj.P2.X - obj.P1.X) / obj.Distance + obj.P1.X;
// y = profileDistance * (obj.P2.Y - obj.P1.Y) / obj.Distance + obj.P1.Y;
// power = percentLaser1 * (float)powerProfile.PowerProfiles[i].PowerProfileParams[0].EndPowerPercent * 0.01f;
// _Scanner.ParaMarkAbs(x + xOffset, y + yOffset, power);
// } else {
// break;
// }
// }
// }
// if (profileDistance < obj.Distance) {
// _Scanner.ParaMarkAbs(obj.P2.X + xOffset, obj.P2.Y + yOffset, power);
// }
// } else {
// if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.02 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.02) {
// // 점프 안함
// } else {
// _Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
// }
// //Write_da 함수는 시작점으로 점프한 이후 Mark 명령 직전에 해준다.
// _Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
// _Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// _Scanner.MarkAbs(obj.PF2.X + xOffset, obj.PF2.Y + yOffset);
// }
// // END- 시작위치 점프 관련 설정
//} else {
// Wobble, PowerProfile을 사용하지 않는 일반적인 경우 이쪽으로 들어옴
#endregion
// START- Jump(Line Shot)
// @deprecated 굉장히 위험한 코드임. 충분히 검증하고 다시 코드 구현할 것. 첫번째 item의 경우 무조건 Jump 명령을 날려줘야 함.
// 안그러면 초기 위치를 0에서 시작 하기 때문에 위험함.
// RCC 검증을 위해 일단 주석 해제
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.02 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.02) {
// 점프 안함
} else {
// Jump 할 때는 Analog Out 값을 0으로 변경
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Line Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
//CSV Laser Log 추가
laserLog.JumpX = obj.PF1.X + xOffset;
laserLog.JumpY = obj.PF1.Y + yOffset;
}
// END- Jump(Line Shot)
// START- Mark
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0) && obj.MadebyRCC) {
// RCC Ramping 의 경우
// RCC 로 만들어진 object 는 경로정보가 여러개로 쪼개져 있기 때문에 기존의 ramping 구간을 사용하면 안된다.
// Ramping 에 대한 정보 자체를 RCC 인 경우에 한해 ratio 를 생성할때 같이 만들어줬다.
// ratio 는 0~1 의 값으로 되어 있다. 생성할때 interlock 처리했으나 여기서도 추가로 확인한다.
float powerratio = obj.RCC_PowerRatio;
if (powerratio < 0) {
powerratio = 0;
} else if (powerratio > 1) {
powerratio = 1;
}
float analogPercentLaserPower1 = percentLaser1 * powerratio; // center laser
float analogPercentLaserPower2 = percentLaser2 * powerratio; // ring laser
//_Scanner.SetVectorControl((uint)eAutoControlParameter.AnalogOut1, 0);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, analogPercentLaserPower1, eFunctionType.LIST);
// 기존 방식은 SetScannerProcessCondition 함수에서 center, ring 모두 ratio 100% 로 설정한다음, ramping 할때는 Ch1 만 power ratio 를 조정하고 있었다.
// 여기에서도 center만 비율 조정하려면 아래 줄을 주석하면된다. 주석처리하지 않으면 center , ring 모두 ramping 거리 비율로 power 조정된다.
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, analogPercentLaserPower2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.PF2.X + xOffset, obj.PF2.Y + yOffset);
Log.I(string.Format($"[RCC Subpath Line Weld] Mark(Ramping) ({obj.PF2.X + xOffset},{obj.PF2.Y + yOffset})"));
} else if ((param.RampingStartADist > 0 || param.RampingStartDist > 0 || param.RampingEndDist > 0) && ((param.RampingStartDist + param.RampingEndDist + param.RampingStartADist) <= 100.0)) {
double length = param.RampingInterval;
double FirstDistance = obj.Distance * (param.RampingStartADist / 100);
double StartDistance = obj.Distance * ((param.RampingStartDist) / 100);
double EndDistance = obj.Distance * (param.RampingEndDist / 100);
int firstCount = (int)(FirstDistance / length);
int risingCount = (int)(StartDistance / length);
int fallingCount = (int)(EndDistance / length);
int totalCount = (int)(obj.Distance / length);
Log.I($"Ramping count >> firstCount({firstCount}) / risingCount({risingCount}) / fallingCount({fallingCount}) / totalCount({totalCount})");
// Power1 A -> Start Power, B -> End Power
float firstPower_A1 = percentLaser1 * (float)param.RampingStartAPower1 / 100; // Ramping First Power 1
float firstPower_B1 = percentLaser1 * (float)param.RampingStartAPower2 / 100; // Ramping First Power 2
float startPower_A1 = percentLaser1 * (float)param.RampingStartPower1 / 100; // Ramping Start Power 1
float startPower_B1 = percentLaser1 * (float)param.RampingStartPower2 / 100; // Ramping Start Power 3
float endPower_A1 = percentLaser1 * (float)param.RampingEndPower1 / 100; // Ramping End Power 1
float endPower_B1 = percentLaser1 * (float)param.RampingEndPower2 / 100; // Ramping End Power 3
// Power2 A -> Start Power, B -> End Power
float firstPower_A2 = percentLaser2 * (float)param.RampingStartAPower1 / 100; // Ramping First Power 1
float firstPower_B2 = percentLaser2 * (float)param.RampingStartAPower2 / 100; // Ramping First Power 2
float startPower_A2 = percentLaser2 * (float)param.RampingStartPower1 / 100; // Ramping Start Power 1
float startPower_B2 = percentLaser2 * (float)param.RampingStartPower2 / 100; // Ramping Start Power 3
float endPower_A2 = percentLaser2 * (float)param.RampingEndPower1 / 100; // Ramping End Power 1
float endPower_B2 = percentLaser2 * (float)param.RampingEndPower2 / 100; // Ramping End Power 3
double dx = (obj.P2.X - obj.P1.X) / obj.Distance * length;
double dy = (obj.P2.Y - obj.P1.Y) / obj.Distance * length;
double curX = obj.P1.X;
double curY = obj.P1.Y;
Log.I($"[Line Weld] Ramping Condition No({obj.No + 1}), Line Length : {obj.Distance}" +
$"(Start_A:{param.RampingStartADist}%, Dist {FirstDistance}mm {(float)param.RampingStartAPower1}% -> {(float)param.RampingStartAPower2}%) " +
$"(Start_B:{param.RampingStartDist}%, Dist {StartDistance}mm {(float)param.RampingStartPower1}% -> {(float)param.RampingStartPower2}%) " +
$"(End:{param.RampingEndDist}%, Dist {EndDistance}mm {(float)param.RampingEndPower1}% -> {(float)param.RampingEndPower3}%)");
for (int iter = 0; iter < totalCount + 1; iter++) {
float power1 = percentLaser1;
float power2 = percentLaser2;
if (iter <= (risingCount + firstCount)) {
if (iter <= firstCount) {
if (iter == 0 || firstCount == 0) {
power1 = firstPower_A1;
power2 = firstPower_A2;
} else {
power1 = firstPower_A1 + (firstPower_B1 - firstPower_A1) * (iter / (float)firstCount);
power2 = firstPower_A2 + (firstPower_B2 - firstPower_A2) * (iter / (float)firstCount);
}
}
else if ((iter - firstCount) <= risingCount) {
if (risingCount == 0) {
power1 = startPower_B1;
power2 = startPower_B2;
}
else {
power1 = startPower_A1 + (startPower_B1 - startPower_A1) * ((iter - firstCount) / (float)risingCount);
power2 = startPower_A2 + (startPower_B2 - startPower_A2) * ((iter - firstCount) / (float)risingCount);
}
}
}
else if (iter >= totalCount - fallingCount) {
if (fallingCount == 0) {
power1 = endPower_A1;
power2 = endPower_A2;
}
else {
power1 = endPower_B1 + (endPower_A1 - endPower_B1) * (totalCount - iter) / (float)fallingCount;
power2 = endPower_B2 + (endPower_A2 - endPower_B2) * (totalCount - iter) / (float)fallingCount;
}
}
else {
power1 = startPower_B1;
power2 = startPower_B2;
}
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, power1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, power2, eFunctionType.LIST);
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
curX += dx;
curY += dy;
}
Log.I(string.Format($"[Line Weld] Mark(Ramping) ({obj.PF2.X},{obj.PF2.Y})"));
} else {
// Ramping 이 없는 경우
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.PF2.X + xOffset, obj.PF2.Y + yOffset);
//_Scanner.SetVectorControl((uint)eAutoControlParameter.NoUse, 0);
Log.I(string.Format($"[Line Weld] Mark ({obj.PF2.X + xOffset},{obj.PF2.Y + yOffset})"));
}
// END- Mark
//}
_LastX = obj.PF2.X + xOffset;
_LastY = obj.PF2.Y + yOffset;
Log.I(string.Format($"[Line Weld] Mark Start/End Point (Start Point : {obj.PF1.X},{obj.PF1.Y}, End Point : {obj.PF2.X},{obj.PF2.Y})"));
//CSV Laser Log 추가
laserLog.StartPointX = obj.PF1.X;
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
laserLog.HeadNo = Status.GetProcessingWeldingData().WeldingJigNo;
Status.GetProcessingWeldingData().APD.Add(laserLog);
}
private void LoadDotScannerParam(Dot obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1. 스캐너 가공 조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
float percentLaser1 = 0.0f;
float percentLaser2 = 0.0f;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Point Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
// 2. 스캐너 가공 작업
// 2.1. Jump(Point Shot)
// Jump 할 때는 Analog Out 값을 0으로 변경
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Point Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
// 2.2. Mark(Point Shot)
_Scanner.SetLaserTiming((uint)param.LaserFrequency, (uint)(param.LaserPulseDuration * 1000), (uint)(param.LaserPulseDuration * 1000));
// @deprecated 해당 코드는 사용에 주의가 필요 함.. 만약 사용시 원복할 필요가 있음..
// 이후 공정에 영향을 미치는 인자이므로
_Scanner.SetStandby(10, 0);
//_Scanner.SetScannerDelay(param.JumpDelay, 0, 0);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
//_Scanner.ParaMarkRel(0, 0, percentLaser1); // 위치 이동 없이 Power만 변경
// Laser On Time 만큼 레이저 On 실행
_Scanner.SetScannerDelay(param.JumpDelay, param.MarkDelay, param.PolygonDelay);
_Scanner.LaserOnList(param.SpotShotTime);
Log.I(string.Format($"[Point Weld] Mark ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
//Dot도 Last값 추가
_LastX = obj.PF1.X + xOffset;
_LastY = obj.PF1.Y + yOffset;
}
private void LoadSplineScannerParam(Spline obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
// Spline 의 경우 Wobbel 해제
_Scanner.SetWobbelMode(false, 0, 0, 0, 0);
Log.I(string.Format($"[Spline Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
// 1. Ramping을 위한 변수 설정
// totalCount를 전체 Count에서 Mark가 시작하는 구간으로 2를 빼준다.. 아래수식에서 헷갈리지 않기 위해서..
int totalCountP1 = obj.P1.Count - 2;
int totalCountP2 = obj.P2.Count - 2;
// P1과 P2의 Rising, FallingEdgeCount는 동일하다고 본다
double RampingStartDist = param.RampingStartDist;
double RampingEndDist = param.RampingEndDist;
double cumulativeDistanceForRising = 0;
double cumulativeDistanceForFalling = 0;
int risingEdgeCount = 0;
int fallingEdgeCount = 0;
for (int i = 0; i < obj.P1.Count - 1; i++) {
double segmentLength = HitUtil.Distance(obj.P1[i], obj.P1[i + 1]);
cumulativeDistanceForRising += segmentLength;
if (cumulativeDistanceForRising >= RampingStartDist) {
// Ramping 시작 지점을 찾았을 때, 해당 지점까지의 점 개수를 계산
risingEdgeCount = i + 1; // i는 이전 점까지의 거리를 포함하므로 i+1이 현재 점까지의 거리를 포함
Log.I(string.Format($"[Spline Weld] RisingEdgeCount : {risingEdgeCount}, cumulativeDistanceForRising : {cumulativeDistanceForRising})"));
break;
}
}
for (int i = obj.P1.Count - 2; i >= 0; i--) {
double segmentLength = HitUtil.Distance(obj.P1[i], obj.P1[i + 1]);
cumulativeDistanceForFalling += segmentLength;
if (cumulativeDistanceForFalling >= RampingEndDist) {
// Ramping End 지점을 찾았을 때, 해당 지점까지의 점 개수를 계산
fallingEdgeCount = obj.P1.Count - 1 - i;
Log.I(string.Format($"[Spline Weld] fallingEdgeCount : {fallingEdgeCount}, cumulativeDistanceForFalling : {cumulativeDistanceForFalling})"));
break;
}
}
#region Spline_Ramping
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0) && (risingEdgeCount + fallingEdgeCount < totalCountP1)) {
// 2. Rising/Falling Edge 시작파워 설정
//float startingPercentLaser1 = percentLaser1 * (float)param.RampingStartPower1 / 100 <= 100 ?
// percentLaser1 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_A1 = percentLaser1 * (float)param.RampingStartPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_B1 = percentLaser1 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower2 / 100 : 100;
float risingEdgePercentLaser1 = 0;
float startingPercentLaser2 = percentLaser2 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartPower2 / 100 : 100;
float risingEdgePercentLaser2 = 0;
//float endingPercentLaser1 = percentLaser1 * (float)param.RampingEndPower1 / 100 <= 100 ?
// percentLaser1 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_A1 = percentLaser1 * (float)param.RampingEndPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_B1 = percentLaser1 * (float)param.RampingEndPower3 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower3 / 100 : 100;
float fallingEdgePercentLaser1 = 0;
float endingPercentLaser2 = percentLaser2 * (float)param.RampingEndPower2 / 100 <= 100 ?
percentLaser2 * (float)param.RampingEndPower2 / 100 : 100;
float fallingEdgePercentLaser2 = 0;
switch (obj.DrawWay) {
case Spline.DrawWayType.OUT_OUT:
//P1 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[totalCountP1 + 1].X + xOffset, obj.P1[totalCountP1 + 1].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = totalCountP1; iter > totalCountP1 - risingEdgeCount; iter--) {
int currentStep = totalCountP1 - iter;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = totalCountP1 - risingEdgeCount; iter > fallingEdgeCount; iter--) {
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = fallingEdgeCount; iter >= 0; iter--) {
int currentStep = fallingEdgeCount - iter + 1;
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
_LastX = obj.P1[0].X + xOffset;
_LastY = obj.P1[0].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[totalCountP2 - 1].X + xOffset, obj.P2[totalCountP2 - 1].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = totalCountP2; iter > totalCountP2 - risingEdgeCount; iter--) {
int currentStep = totalCountP2 - iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = totalCountP2 - risingEdgeCount; iter > fallingEdgeCount; iter--) {
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = fallingEdgeCount; iter >= 0; iter--) {
int currentStep = fallingEdgeCount - iter + 1;
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
_LastX = obj.P2[0].X + xOffset;
_LastY = obj.P2[0].Y + yOffset;
}
break;
case Spline.DrawWayType.OUT_IN:
//P1 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[totalCountP1 + 1].X + xOffset, obj.P1[totalCountP1 + 1].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = totalCountP1; iter > totalCountP1 - risingEdgeCount; iter--) {
int currentStep = totalCountP1 - iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = totalCountP1 - risingEdgeCount; iter > fallingEdgeCount; iter--) {
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = fallingEdgeCount; iter >= 0; iter--) {
int currentStep = fallingEdgeCount - iter + 1;
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
_LastX = obj.P1[0].X + xOffset;
_LastY = obj.P1[0].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[0].X + xOffset, obj.P2[0].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = 1; iter < risingEdgeCount; iter++) {
int currentStep = iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount; iter < (totalCountP2 + 1) - fallingEdgeCount; iter++) {
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = (totalCountP2 + 1) - fallingEdgeCount; iter <= (totalCountP2 + 1); iter++) {
int currentStep = iter - ((totalCountP2 + 1) - fallingEdgeCount - 1);
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
_LastX = obj.P2[totalCountP2 + 1].X + xOffset;
_LastY = obj.P2[totalCountP2 + 1].Y + yOffset;
}
break;
case Spline.DrawWayType.IN_OUT:
//P1 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[0].X + xOffset, obj.P1[0].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = 1; iter < risingEdgeCount; iter++) {
int currentStep = iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount; iter < (totalCountP1 + 1) - fallingEdgeCount; iter++) {
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = (totalCountP1 + 1) - fallingEdgeCount; iter <= (totalCountP1 + 1); iter++) {
int currentStep = iter - ((totalCountP1 + 1) - fallingEdgeCount - 1);
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
_LastX = obj.P1[totalCountP1 + 1].X + xOffset;
_LastY = obj.P1[totalCountP1 + 1].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[totalCountP2 - 1].X + xOffset, obj.P2[totalCountP2 - 1].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = totalCountP2; iter > totalCountP2 - risingEdgeCount; iter--) {
int currentStep = totalCountP2 - iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = totalCountP2 - risingEdgeCount; iter > fallingEdgeCount; iter--) {
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = fallingEdgeCount; iter >= 0; iter--) {
int currentStep = fallingEdgeCount - iter + 1;
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
_LastX = obj.P2[0].X + xOffset;
_LastY = obj.P2[0].Y + yOffset;
}
break;
case Spline.DrawWayType.IN_IN:
//P1 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[0].X + xOffset, obj.P1[0].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = 1; iter < risingEdgeCount; iter++) {
int currentStep = iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount; iter < (totalCountP1 + 1) - fallingEdgeCount; iter++) {
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = (totalCountP1 + 1) - fallingEdgeCount; iter <= (totalCountP1 + 1); iter++) {
int currentStep = iter - ((totalCountP1 + 1) - fallingEdgeCount - 1);
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P1[iter].X + xOffset, obj.P1[iter].Y + yOffset);
}
_LastX = obj.P1[totalCountP1 + 1].X + xOffset;
_LastY = obj.P1[totalCountP1 + 1].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[0].X + xOffset, obj.P2[0].Y + yOffset, eFunctionType.LIST);
// 3. Rising Edge 구간
for (int iter = 1; iter < risingEdgeCount; iter++) {
int currentStep = iter;
//risingEdgePercentLaser1 = ((percentLaser1 - startingPercentLaser1) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser1;
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)risingEdgeCount * (float)(currentStep) + startingPercentLaser_A1;
risingEdgePercentLaser2 = ((percentLaser2 - startingPercentLaser2) / (float)risingEdgeCount) * (float)(currentStep) + startingPercentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 4.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 4.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount; iter < (totalCountP2 + 1) - fallingEdgeCount; iter++) {
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
// 5. Falling Edge 구간
for (int iter = (totalCountP2 + 1) - fallingEdgeCount; iter <= (totalCountP2 + 1); iter++) {
int currentStep = iter - ((totalCountP2 + 1) - fallingEdgeCount - 1);
//fallingEdgePercentLaser1 = ((endingPercentLaser1 - percentLaser1) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser1;
fallingEdgePercentLaser1 = ((endingPercentLaser_B1 - endingPercentLaser_A1) / (float)fallingEdgeCount) * (float)(currentStep) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = ((endingPercentLaser2 - percentLaser2) / (float)fallingEdgeCount) * (float)(currentStep) + percentLaser2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.P2[iter].X + xOffset, obj.P2[iter].Y + yOffset);
}
_LastX = obj.P2[totalCountP2 + 1].X + xOffset;
_LastY = obj.P2[totalCountP2 + 1].Y + yOffset;
}
break;
}
}
#endregion
else {
#region 2. Ramping 미사용 스캐너 가공
int pointCount = obj.P1.Count;
switch (obj.DrawWay) {
case Spline.DrawWayType.OUT_OUT:
//P1 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[pointCount - 1].X + xOffset, obj.P1[pointCount - 1].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = obj.P1.Count - 2; i >= 0; i--) {
_Scanner.MarkAbs(obj.P1[i].X + xOffset, obj.P1[i].Y + yOffset);
}
_LastX = obj.P1[0].X + xOffset;
_LastY = obj.P1[0].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[pointCount - 1].X + xOffset, obj.P2[pointCount - 1].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = obj.P2.Count - 2; i >= 0; i--) {
_Scanner.MarkAbs(obj.P2[i].X + xOffset, obj.P2[i].Y + yOffset);
}
_LastX = obj.P2[0].X + xOffset;
_LastY = obj.P2[0].Y + yOffset;
}
break;
case Spline.DrawWayType.OUT_IN:
//P1 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[pointCount - 1].X + xOffset, obj.P1[pointCount - 1].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = obj.P1.Count - 2; i >= 0; i--) {
_Scanner.MarkAbs(obj.P1[i].X + xOffset, obj.P1[i].Y + yOffset);
}
_LastX = obj.P1[0].X + xOffset;
_LastY = obj.P1[0].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[0].X + xOffset, obj.P2[0].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = 1; i < obj.P2.Count; i++) {
_Scanner.MarkAbs(obj.P2[i].X + xOffset, obj.P2[i].Y + yOffset);
}
_LastX = obj.P2[obj.P2.Count - 1].X + xOffset;
_LastY = obj.P2[obj.P2.Count - 1].Y + yOffset;
}
break;
case Spline.DrawWayType.IN_OUT:
//P1 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[0].X + xOffset, obj.P1[0].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = 1; i < obj.P1.Count; i++) {
_Scanner.MarkAbs(obj.P1[i].X + xOffset, obj.P1[i].Y + yOffset);
}
_LastX = obj.P1[obj.P1.Count - 1].X + xOffset;
_LastY = obj.P1[obj.P1.Count - 1].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 OUT에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[pointCount - 1].X + xOffset, obj.P2[pointCount - 1].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = obj.P2.Count - 2; i >= 0; i--) {
_Scanner.MarkAbs(obj.P2[i].X + xOffset, obj.P2[i].Y + yOffset);
}
_LastX = obj.P2[0].X + xOffset;
_LastY = obj.P2[0].Y + yOffset;
}
break;
case Spline.DrawWayType.IN_IN:
//P1 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P1[0].X + xOffset, obj.P1[0].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = 1; i < obj.P1.Count; i++) {
_Scanner.MarkAbs(obj.P1[i].X + xOffset, obj.P1[i].Y + yOffset);
}
_LastX = obj.P1[obj.P1.Count - 1].X + xOffset;
_LastY = obj.P1[obj.P1.Count - 1].Y + yOffset;
//tornado 의 경우 P2까지
if (obj.SplineTornado) {
//P2 IN에서 시작
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.P2[0].X + xOffset, obj.P2[0].Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = 1; i < obj.P2.Count; i++) {
_Scanner.MarkAbs(obj.P2[i].X + xOffset, obj.P2[i].Y + yOffset);
}
_LastX = obj.P2[obj.P2.Count - 1].X + xOffset;
_LastY = obj.P2[obj.P2.Count - 1].Y + yOffset;
}
break;
}
#endregion
}
Log.I(string.Format($"[Spline Weld] Draw Way : {obj.DrawWay}, Tornado 사용 : {obj.SplineTornado})"));
}
private void LoadSnowManScannerParam(SnowMan obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[SnowMan Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
// 2. 스캐너 가공 작업
// SnowMan 로드 시작
Math.Cos(obj.TiltingAngle * Math.PI / 180);
// Wobbel 각도 설정
_Scanner.SetWobbelDirection((int)(Math.Cos(obj.TiltingAngle * Math.PI / 180) * 100), (int)(Math.Cos(obj.TiltingAngle * Math.PI / 180) * 100));
// Wobbel 모드 설정
_Scanner.SetWobbelMode(true, 1, 1, 1, 2);
// Wobbel Vector 리스트 초기화
_Scanner.SetWobbelVector(0, 0, 0, 0); // period=0은 초기화
List<double> dTrans = new List<double>();
List<double> dLong = new List<double>();
// 확인 2025.03.06, SnowMan 을 사용하지 않기 때문에 해당 함수 확인 불필요 (LoadLineScannerParam() 에서는 param 으로 부터 설정을 하는데, 여기서는 obj 로 부터 설정)
obj.LoadWobbelVectorForHead(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)obj.HeadPeriod, 0);
}
obj.LoadWobbelVectorForBody(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)obj.BodyPeriod, 0);
}
//서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.PF2.X + xOffset, obj.PF2.Y + yOffset);
Log.I(string.Format($"[SnowMan Weld] Set Mark StartPoint/EndPoint(SP:{obj.PF1.X + xOffset},{obj.PF1.Y + yOffset},EP:{obj.PF2.X + xOffset},{obj.PF2.Y + yOffset})"));
// 확인 2025.03.06, SnowMan 을 사용하지 않기 때문에 저장 해 놓지 않음. (Line 에서 와블은 SnowMan 사용)
// 여기는 LastX, LastY 저장이 불필요
}
private void LoadArcScannerParam(Arc obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
var laserLog = new ActualProcessUnit();
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Arc Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Arc";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
laserLog.JumpDelay = param.JumpDelay;
laserLog.MarkDelay = param.MarkDelay;
laserLog.PolygonDelay = param.PolygonDelay;
laserLog.Power = param.LaserPower1;
laserLog.JumpSpeed = param.JumpSpeed;
laserLog.MarkSpeed = param.MarkSpeed;
// 2. 스캐너 가공 작업
// 2.1. Wobbel 관련 설정
// Snowman 사용 모드가 아닐 경우 Wobbel 모드 실행
if (!param.SnowManEnable) {
if (param.WobbelEnable) {
// Wobbel 각도 설정
_Scanner.SetWobbelDirection(0, 0);
// Wobbel 모드 설정
_Scanner.SetWobbelMode(param.WobbelEnable, param.WobbelTransversal, param.WobbelLongditudinal, param.WobbelFrequency, (int)param.WobbelShape);
Log.I(string.Format($"[Arc Weld] Item No({obj.No + 1}) Wobbel Setting : Use({param.WobbelEnable}), Transversal({param.WobbelTransversal}), Longitudinal({param.WobbelLongditudinal}), Frequency({param.WobbelFrequency}), WobbelShape({param.WobbelShape})"));
} else {
_Scanner.SetWobbelDirection(0, 0);
_Scanner.SetWobbelVector(0, 0, 0, 0);
_Scanner.SetWobbelMode(false, 0, 0, 0, 0);
}
//서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
//_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
// @deprecated 해당코드 검증 필요 !!
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.02 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.02) {
//점프 안함
} else {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Arc Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
}
} else {
int dx = 0;
int dy = 0;
double radian = (param.TiltingAngle - 90) * Math.PI / 180;
dx = (int)(Math.Cos(radian) * 100);
dy = (int)(Math.Sin(radian) * 100);
_Scanner.SetWobbelDirection(dx, dy);
// Wobbel 모드 설정
_Scanner.SetWobbelMode(true, 1, 1, 1, 2);
// Wobbel Vector 리스트 초기화
_Scanner.SetWobbelVector(0, 0, 0, 0); //period=0은 초기화
List<double> dTrans = new List<double>();
List<double> dLong = new List<double>();
param.LoadWobbelVectorForHead(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.HeadPeriod, 0);
}
param.LoadWobbelVectorForBody(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.BodyPeriod, 0);
}
// 서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
//_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.02 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.02) {
//점프 안함
} else {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Arc Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
}
}
// 2.2 Ramping 설정
double radius = Math.Sqrt((obj.CenterPointF.X - obj.PF1.X) * (obj.CenterPointF.X - obj.PF1.X) + (obj.CenterPointF.Y - obj.PF1.Y) * (obj.CenterPointF.Y - obj.PF1.Y));
double length = param.RampingInterval; // ArcRamping 간격 추가
double theta = 0;
// 2.2.0. Sweep 방향에 따른 회전 방향 설정
if (obj.Direction == Arc.eDirection.CCW) {
theta = (length / radius) * (180 / Math.PI);
}
if (obj.Direction == Arc.eDirection.CW) {
theta = (-length / radius) * (180 / Math.PI);
}
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0) && obj.MadebyRCC) {
// RCC 로 만들어진 object 는 경로정보가 여러개로 쪼개져 있기 때문에 기존의 ramping 구간을 사용하면 안된다.
// Ramping 에 대한 정보 자체를 RCC 인 경우에 한해 ratio 를 생성할때 같이 만들어줬다.
// ratio 는 0~1 의 값으로 되어 있다. 생성할때 interlock 처리했으나 여기서도 추가로 확인한다.
float powerratio = obj.RCC_PowerRatio;
if (powerratio < 0) {
powerratio = 0;
} else if (powerratio > 1) {
powerratio = 1;
}
float analogPercentLaserPower1 = percentLaser1 * powerratio; // center laser
float analogPercentLaserPower2 = percentLaser2 * powerratio; // ring laser
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, analogPercentLaserPower1, eFunctionType.LIST);
// 기존 방식은 SetScannerProcessCondition 함수에서 center, ring 모두 ratio 100% 로 설정한다음, ramping 할때는 Ch1 만 power ratio 를 조정하고 있었다.
// 여기에서도 center만 비율 조정하려면 아래 줄을 주석하면된다. 주석처리하지 않으면 center , ring 모두 ramping 거리 비율로 power 조정된다.
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, analogPercentLaserPower2, eFunctionType.LIST);
//Arc 는 현재 위치에서 시작, 중심점 기준으로 각도 만큼 회전
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, obj.SweepAngle);
} else if ((param.RampingStartDist > 0 || param.RampingEndDist > 0) && (param.RampingStartDist + param.RampingEndDist < radius * Math.Abs(obj.SweepAngle))) {
// 2.2.1.1. 데이터테이블 기준으로 파워 동작
//percentLaser1 = (float)_laserPowerCalParam.CalibrateWattToPercent(param.LaserPower1, (uint)eAnalogPortOut.DA1);
// 2.2.1.2. Ramping을 위한 변수 설정
double FirstAngle = obj.Length * (param.RampingStartADist / 100);
double StartAngle = obj.Length * ((param.RampingStartDist) / 100);
double EndAngle = obj.Length * (param.RampingEndDist / 100);
int firstEdgeCount = (int)(FirstAngle / length);
int risingEdgeCount = (int)(StartAngle / length);
int fallingEdgeCount = (int)(EndAngle / length);
//int totalCount = (int)(Math.Abs(obj.SweepAngle) / theta);
int totalCount = (int)(Math.Abs(obj.Length / theta));
// 2.2.2.1. Rising Edge 시작 파워 설정
float firstPercentLaser_A1 = percentLaser1 * (float)param.RampingStartAPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartAPower1 / 100 : 100;
float firstPercentLaser_B1 = percentLaser1 * (float)param.RampingStartAPower2 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartAPower2 / 100 : 100;
float firstPercentLaser_A2 = percentLaser2 * (float)param.RampingStartAPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartAPower1 / 100 : 100;
float firstPercentLaser_B2 = percentLaser2 * (float)param.RampingStartAPower2/ 100 <= 100 ?
percentLaser2 * (float)param.RampingStartAPower2/ 100 : 100;
float startingPercentLaser_A1 = percentLaser1 * (float)param.RampingStartPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_B1 = percentLaser1 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower2 / 100 : 100;
float startingPercentLaser_A2 = percentLaser2 * (float)param.RampingStartPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_B2 = percentLaser2 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartPower2 / 100 : 100;
float risingEdgePercentLaser1 = 0;
float risingEdgePercentLaser2 = 0;
Log.I($"[Arc Weld] Ramping Condition No({obj.No + 1}), Arc Length : {obj.Length}, Arc Angle : {obj.SweepAngle} " +
$"(Start_A:{param.RampingStartADist}%, Dist {FirstAngle}mm {(float)param.RampingStartAPower1}% -> {(float)param.RampingStartAPower2}%) " +
$"(Start_B:{param.RampingStartDist}%, Dist {StartAngle}mm {(float)param.RampingStartPower1}% -> {(float)param.RampingStartPower2}%) " +
$"(End:{param.RampingEndDist}%, Dist {EndAngle}mm {(float)param.RampingEndPower1}% -> {(float)param.RampingEndPower3}%)");
// 2.2.2.2. Rising Edge 구간 파워 및 위치 변경
for (int iter = 0; iter < risingEdgeCount + firstEdgeCount; iter++) {
// A1에서 B1으로 간다.
if (iter < firstEdgeCount - 1) {
risingEdgePercentLaser1 = (firstPercentLaser_B1 - firstPercentLaser_A1) / (float)(firstEdgeCount - 1) * (float)(iter) + firstPercentLaser_A1;
risingEdgePercentLaser2 = (firstPercentLaser_B2 - firstPercentLaser_A2) / (float)(firstEdgeCount - 1) * (float)(iter) + firstPercentLaser_A2;
} else {
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)(risingEdgeCount - 1) * (float)(iter - firstEdgeCount) + startingPercentLaser_A1;
risingEdgePercentLaser2 = (startingPercentLaser_B2 - startingPercentLaser_A2) / (float)(risingEdgeCount - 1) * (float)(iter - firstEdgeCount) + startingPercentLaser_A2;
}
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
// 2.2.3.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 2.2.3.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount+firstEdgeCount; iter < totalCount - fallingEdgeCount; iter++) {
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
// 2.2.4.1. Falling Edge 마지막 파워 설정
float endingPercentLaser_A1 = percentLaser1 * (float)param.RampingEndPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_B1 = percentLaser1 * (float)param.RampingEndPower3 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower3 / 100 : 100;
float fallingEdgePercentLaser1 = 0;
float endingPercentLaser_A2 = percentLaser2 * (float)param.RampingEndPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_B2 = percentLaser2 * (float)param.RampingEndPower3 / 100 <= 100 ?
percentLaser2 * (float)param.RampingEndPower3 / 100 : 100;
float fallingEdgePercentLaser2 = 0;
// 2.2.4.2. Falling Edge 구간 파워 및 위치 변경
for (int iter = totalCount - fallingEdgeCount; iter < totalCount; iter++) {
// Falling Edge 구간 시작하는 순간부터 Power 변경 원하므로 (iter - (totalCount - fallingEdgeCount - 1))
// B1에서 A1으로 간다.
fallingEdgePercentLaser1 = (endingPercentLaser_B1 - endingPercentLaser_A1) / (float)(fallingEdgeCount) * (float)(iter - (totalCount - fallingEdgeCount - 1)) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = (endingPercentLaser_B2 - endingPercentLaser_A2) / (float)(fallingEdgeCount) * (float)(iter - (totalCount - fallingEdgeCount - 1)) + endingPercentLaser_A2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
//// 2.2.5. 마지막 위치까지 이동해줌.
//double lastSweep = obj.SweepAngle - (totalCount * theta);
//_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, lastSweep);
} else {
//Arc 는 현재 위치에서 시작, 중심점 기준으로 각도 만큼 회전
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, obj.SweepAngle);
}
_LastX = obj.PF2.X + xOffset;
_LastY = obj.PF2.Y + yOffset;
//CSV Laser Log 추가
laserLog.StartPointX = obj.PF1.X;
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
Status.GetProcessingWeldingData().APD.Add(laserLog);
if (obj.MadebyRCC) {
Log.I(string.Format($"[RCC SubPath Arc Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
} else {
Log.I(string.Format($"[Arc Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
}
}
private void LoadArc3PScannerParam(Arc3Point obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
var laserLog = new ActualProcessUnit();
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Arc3P Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
//CSV Laser Log 추가
laserLog.Date = DateTime.Now;
laserLog.WeldType = "Arc3";
laserLog.WeldCondition = param.No + 1;
laserLog.LaserOnDelay = param.LaserOnDelay;
laserLog.LaserOffDelay = param.LaserOffDelay;
laserLog.JumpDelay = param.JumpDelay;
laserLog.MarkDelay = param.MarkDelay;
laserLog.PolygonDelay = param.PolygonDelay;
laserLog.Power = param.LaserPower1;
laserLog.JumpSpeed = param.JumpSpeed;
laserLog.MarkSpeed = param.MarkSpeed;
// 2. 스캐너 가공 작업
// 2.1. Wobbel 관련 설정
// Snowman 사용 모드가 아닐 경우 Wobbel 모드 실행
if (!param.SnowManEnable) {
if (param.WobbelEnable) {
// Wobbel 각도 설정
_Scanner.SetWobbelDirection(0, 0);
// Wobbel 모드 설정
_Scanner.SetWobbelMode(param.WobbelEnable, param.WobbelTransversal, param.WobbelLongditudinal, param.WobbelFrequency, (int)param.WobbelShape);
Log.I(string.Format($"[Arc3P Weld] Item No({obj.No + 1}) Wobbel Setting : Use({param.WobbelEnable}), Transversal({param.WobbelTransversal}), Longitudinal({param.WobbelLongditudinal}), Frequency({param.WobbelFrequency}), WobbelShape({param.WobbelShape})"));
} else {
_Scanner.SetWobbelDirection(0, 0);
_Scanner.SetWobbelVector(0, 0, 0, 0);
_Scanner.SetWobbelMode(false, 0, 0, 0, 0);
}
// 서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.01 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.01) {
//점프 안함
} else {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Arc3P Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
}
} else {
int dx = 0;
int dy = 0;
double radian = (param.TiltingAngle - 90) * Math.PI / 180;
dx = (int)(Math.Cos(radian) * 100);
dy = (int)(Math.Sin(radian) * 100);
_Scanner.SetWobbelDirection(dx, dy);
// Wobbel 모드 설정
_Scanner.SetWobbelMode(true, 1, 1, 1, 2);
//Wobbel Vector 리스트 초기화
_Scanner.SetWobbelVector(0, 0, 0, 0); //period=0은 초기화
List<double> dTrans = new List<double>();
List<double> dLong = new List<double>();
param.LoadWobbelVectorForHead(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.HeadPeriod, 0);
}
param.LoadWobbelVectorForBody(out dTrans, out dLong, (int)_Scanner.Config.ScaleXYFactor);
for (int i = 0; i < dTrans.Count; i++) {
_Scanner.SetWobbelVector(dTrans[i], dLong[i], (uint)param.BodyPeriod, 0);
}
// 서브객체인 경우에는 부모객체의 scanner parameter 조건으로 설정되어야 함
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.01 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.01) {
//점프 안함
} else {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
Log.I(string.Format($"[Arc3P Weld] Jump ({obj.PF1.X + xOffset},{obj.PF1.Y + yOffset})"));
}
}
// 2.2. Ramping 설정
double radius = Math.Sqrt((obj.CenterPointF.X - obj.PF1.X) * (obj.CenterPointF.X - obj.PF1.X) + (obj.CenterPointF.Y - obj.PF1.Y) * (obj.CenterPointF.Y - obj.PF1.Y));
double length = param.RampingInterval; // ArcRamping 간격 추가
double theta = 0;
// 2.2.0. Sweep 방향에 따른 회전 방향 설정
if (obj.Direction == Arc3Point.eDirection.CCW) {
theta = (length / radius) * (180 / Math.PI);
}
if (obj.Direction == Arc3Point.eDirection.CW) {
theta = (-length / radius) * (180 / Math.PI);
}
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0)
&& (param.RampingStartDist + param.RampingEndDist < radius * Math.Abs(obj.SweepAngle))) {
//&& !param.WobbelEnable && !param.SnowManEnable)
if (obj.MadebyRCC) {
// RCC 로 만들어진 object 는 경로정보가 여러개로 쪼개져 있기 때문에 기존의 ramping 구간을 사용하면 안된다.
// Ramping 에 대한 정보 자체를 RCC 인 경우에 한해 ratio 를 생성할때 같이 만들어줬다.
// ratio 는 0~1 의 값으로 되어 있다. 생성할때 interlock 처리했으나 여기서도 추가로 확인한다.
float powerratio = obj.RCC_PowerRatio;
if (powerratio < 0) {
powerratio = 0;
} else if (powerratio > 1) {
powerratio = 1;
}
float analogPercentLaserPower1 = percentLaser1 * powerratio; // center laser
float analogPercentLaserPower2 = percentLaser2 * powerratio; // ring laser
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, analogPercentLaserPower1, eFunctionType.LIST);
// 기존 방식은 SetScannerProcessCondition 함수에서 center, ring 모두 ratio 100% 로 설정한다음, ramping 할때는 Ch1 만 power ratio 를 조정하고 있었다.
// 여기에서도 center만 비율 조정하려면 아래 줄을 주석하면된다. 주석처리하지 않으면 center , ring 모두 ramping 거리 비율로 power 조정된다.
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, analogPercentLaserPower2, eFunctionType.LIST);
//Arc 는 현재 위치에서 시작, 중심점 기준으로 각도 만큼 회전
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, obj.SweepAngle);
} else {
// 2.2.1.2. Ramping을 위한 변수 설정
double FirstAngle = obj.SweepAngle * (param.RampingStartADist / 100);
double StartAngle = obj.SweepAngle * ((param.RampingStartDist) / 100);
double EndAngle = obj.SweepAngle * (param.RampingEndDist / 100);
int firstEdgeCount = (int)(FirstAngle / length);
int risingEdgeCount = (int)(StartAngle / length);
int fallingEdgeCount = (int)(EndAngle / length);
int totalCount = (int)Math.Abs(obj.SweepAngle / theta);
// 2.2.2.1. Rising Edge 시작 파워 설정
float firstPercentLaser_A1 = percentLaser1 * (float)param.RampingStartAPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartAPower1 / 100 : 100;
float firstPercentLaser_B1 = percentLaser1 * (float)param.RampingStartAPower2 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartAPower2 / 100 : 100;
float firstPercentLaser_A2 = percentLaser2 * (float)param.RampingStartAPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartAPower1 / 100 : 100;
float firstPercentLaser_B2 = percentLaser2 * (float)param.RampingStartAPower2 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartAPower2 / 100 : 100;
float startingPercentLaser_A1 = percentLaser1 * (float)param.RampingStartPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_B1 = percentLaser1 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser1 * (float)param.RampingStartPower2 / 100 : 100;
float startingPercentLaser_A2 = percentLaser2 * (float)param.RampingStartPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartPower1 / 100 : 100;
float startingPercentLaser_B2 = percentLaser2 * (float)param.RampingStartPower2 / 100 <= 100 ?
percentLaser2 * (float)param.RampingStartPower2 / 100 : 100;
float risingEdgePercentLaser1 = 0;
float risingEdgePercentLaser2 = 0;
Log.I($"[Arc3P Weld] Ramping Condition No({obj.No + 1}), Arc Angle : {obj.SweepAngle} " +
$"(Start_A:{param.RampingStartADist}%, Dist {FirstAngle}mm {(float)param.RampingStartAPower1}% -> {(float)param.RampingStartAPower2}%) " +
$"(Start_B:{param.RampingStartDist}%, Dist {StartAngle}mm {(float)param.RampingStartPower1}% -> {(float)param.RampingStartPower2}%) " +
$"(End:{param.RampingEndDist}%, Dist {EndAngle}mm {(float)param.RampingEndPower1}% -> {(float)param.RampingEndPower3}%)");
// 2.2.2.2. Rising Edge 구간 파워 및 위치 변경
for (int iter = 0; iter < risingEdgeCount + firstEdgeCount; iter++) {
// A1에서 B1으로 간다.
if (iter < firstEdgeCount - 1) {
risingEdgePercentLaser1 = (firstPercentLaser_B1 - firstPercentLaser_A1) / (float)(firstEdgeCount - 1) * (float)(iter) + firstPercentLaser_A1;
risingEdgePercentLaser2 = (firstPercentLaser_B2 - firstPercentLaser_A2) / (float)(firstEdgeCount - 1) * (float)(iter) + firstPercentLaser_A2;
} else {
risingEdgePercentLaser1 = (startingPercentLaser_B1 - startingPercentLaser_A1) / (float)(risingEdgeCount - 1) * (float)(iter - firstEdgeCount) + startingPercentLaser_A1;
risingEdgePercentLaser2 = (startingPercentLaser_B2 - startingPercentLaser_A2) / (float)(risingEdgeCount - 1) * (float)(iter - firstEdgeCount) + startingPercentLaser_A2;
}
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, risingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, risingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
// 2.2.3.1. Constant Range 파워 설정
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
// 2.2.3.2. Constant Range 구간 위치 변경
for (int iter = risingEdgeCount; iter < totalCount - fallingEdgeCount; iter++) {
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
// 2.2.4.1. Falling Edge 마지막 파워 설정
float endingPercentLaser_A1 = percentLaser1 * (float)param.RampingEndPower1 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_B1 = percentLaser1 * (float)param.RampingEndPower3 / 100 <= 100 ?
percentLaser1 * (float)param.RampingEndPower3 / 100 : 100;
float fallingEdgePercentLaser1 = 0;
float endingPercentLaser_A2 = percentLaser2 * (float)param.RampingEndPower1 / 100 <= 100 ?
percentLaser2 * (float)param.RampingEndPower1 / 100 : 100;
float endingPercentLaser_B2 = percentLaser2 * (float)param.RampingEndPower3 / 100 <= 100 ?
percentLaser2 * (float)param.RampingEndPower3 / 100 : 100;
float fallingEdgePercentLaser2 = 0;
// 2.2.4.2. Falling Edge 구간 파워 및 위치 변경
for (int iter = totalCount - fallingEdgeCount; iter < totalCount; iter++) {
fallingEdgePercentLaser1 = (endingPercentLaser_B1 - endingPercentLaser_A1) / (float)(fallingEdgeCount) * (float)(iter - (totalCount - fallingEdgeCount - 1)) + endingPercentLaser_A1;
fallingEdgePercentLaser2 = (endingPercentLaser_B2 - endingPercentLaser_A2) / (float)(fallingEdgeCount) * (float)(iter - (totalCount - fallingEdgeCount - 1)) + endingPercentLaser_A2;
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, fallingEdgePercentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, fallingEdgePercentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, theta);
}
// 2.2.5. 마지막 위치까지 이동해줌.
double lastSweep = obj.SweepAngle - (totalCount * theta);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, lastSweep);
}
} else {
//Arc 는 현재 위치에서 시작, 중심점 기준으로 각도 만큼 회전
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.ArcAbs(obj.CenterPointF.X + xOffset, obj.CenterPointF.Y + yOffset, obj.SweepAngle);
}
_LastX = obj.PF2.X + xOffset;
_LastY = obj.PF2.Y + yOffset;
//CSV Laser Log 추가
laserLog.StartPointX = obj.PF1.X;
laserLog.StartPointY = obj.PF1.Y;
laserLog.EndPointX = obj.PF2.X;
laserLog.EndPointY = obj.PF2.Y;
laserLog.LotID = Status.GetProcessingWeldingData().WorkLotId;
laserLog.CellID = Status.GetProcessingWeldingData().WorkCellId;
Status.GetProcessingWeldingData().APD.Add(laserLog);
if (obj.MadebyRCC) {
Log.I(string.Format($"[RCC SubPath Arc3P Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
} else {
Log.I(string.Format($"[Arc3P Weld] Arc center ({obj.CenterPointF.X + xOffset},{obj.CenterPointF.Y + yOffset}), Sweep angle : ({obj.SweepAngle})"));
}
}
private void LoadZigzagScannerParam(ZigZag obj)
{
// 카메라 오프셋 + Object의 Vision Offset + Fly 오프셋
double xOffset = _FlyOffsetX + _ExternalCommandOffset.X;
double yOffset = _FlyOffsetY + _ExternalCommandOffset.Y;
// 1.스캐너 가공조건 설정
ScannerParam param = ScannerCanvasUserCtrl.ScannerParams[obj.ScannerParamNo];
float percentLaser1 = 0;
float percentLaser2 = 0;
SetScannerProcessCondition(param, out percentLaser1, out percentLaser2);
Log.I(string.Format($"[Zigzag Weld] Weld Condition(No:{param.No + 1}) Setting(LaserOnDelay:{param.LaserOnDelay},LaserOffDelay:{param.LaserOffDelay},JumpDelay:{param.JumpDelay},MarkDelay:{param.MarkDelay},PolygonDelay:{param.PolygonDelay},Power:{param.LaserPower1},JumpSpeed:{param.JumpSpeed},MarkSpeed:{param.MarkSpeed})"));
// 1. 시작 위치로 점프
if (Math.Abs(_LastX - obj.PF1.X - xOffset) < 0.01 && Math.Abs(_LastY - obj.PF1.Y - yOffset) < 0.01) {
//점프 안함
} else {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.JumpAbs(obj.PF1.X + xOffset, obj.PF1.Y + yOffset, eFunctionType.LIST);
}
// 2. Ramping 설정
#region Ramping Zigzag
// === 전체 길이 및 파트 정보 계산 ===
if ((param.RampingStartDist > 0 || param.RampingEndDist > 0) && (param.RampingStartDist + param.RampingEndDist < obj.TotalLength)) {
var sw = Stopwatch.StartNew();
var totalLength = 0.0;
List<PartInfo> partInfoList = new List<PartInfo>();
for (int setIdx = 0; setIdx < obj.LineStartPoints.Count; setIdx++) {
// 1. 첫 번째 Line
{
double dx = obj.ArcStartPoints[setIdx].X - obj.LineStartPoints[setIdx].X;
double dy = obj.ArcStartPoints[setIdx].Y - obj.LineStartPoints[setIdx].Y;
double length = Math.Sqrt(dx * dx + dy * dy);
int count = (int)Math.Round(length / param.RampingInterval);
partInfoList.Add(new PartInfo {
IsArc = false,
Length = length,
Count = count,
DxPerStep = dx / count,
DyPerStep = dy / count,
ThetaPerStep = 0,
SetIndex = setIdx,
PartIndex = 0
});
totalLength += length;
}
// 2. Arc
{
double dxArc = obj.ArcCenterPoints[setIdx].X - obj.ArcStartPoints[setIdx].X;
double dyArc = obj.ArcCenterPoints[setIdx].Y - obj.ArcStartPoints[setIdx].Y;
double radius = Math.Sqrt(dxArc * dxArc + dyArc * dyArc);
float startAngle = (float)HitUtil.RadiansToDegrees(
HitUtil.LineAngleR(obj.ArcCenterPoints[setIdx], obj.ArcStartPoints[setIdx], 0));
float endAngle = (float)HitUtil.RadiansToDegrees(
HitUtil.LineAngleR(obj.ArcCenterPoints[setIdx], obj.ArcEndPoints[setIdx], 0));
// ZigZag DrawDirection이 Left이면 SweepAngle 계산을 CW -> CCW -> CW 순서, Right면 CCW -> CW -> CCW 순서로 해야함
float sweepAngle;
bool isEvenSet = (setIdx % 2 == 0);
if (obj.DrawDirection == ZigZag.eDrawDirection.Left) {
if (isEvenSet) {
// CW
sweepAngle = (-1) * Math.Abs(startAngle - endAngle);
if (sweepAngle < -180) sweepAngle = (360 + sweepAngle) * (-1);
} else {
// CCW
sweepAngle = Math.Abs(startAngle - endAngle);
if (sweepAngle > 180) sweepAngle = (360 - sweepAngle);
}
} else { // Right
if (isEvenSet) {
// CCW
sweepAngle = Math.Abs(startAngle - endAngle);
if (sweepAngle > 180) sweepAngle = (360 - sweepAngle);
} else {
// CW
sweepAngle = (-1) * Math.Abs(startAngle - endAngle);
if (sweepAngle < -180) sweepAngle = (360 + sweepAngle) * (-1);
}
}
double length = Math.Abs(sweepAngle) * (radius * Math.PI / 180);
int count = (int)Math.Round(length / param.RampingInterval);
double thetaPerStep = sweepAngle / count;
partInfoList.Add(new PartInfo {
IsArc = true,
Length = length,
Count = count,
DxPerStep = 0,
DyPerStep = 0,
ThetaPerStep = thetaPerStep,
SetIndex = setIdx,
PartIndex = 1
});
totalLength += length;
}
// 3. 두 번째 Line
{
double dx = obj.LineEndPoints[setIdx].X - obj.ArcEndPoints[setIdx].X;
double dy = obj.LineEndPoints[setIdx].Y - obj.ArcEndPoints[setIdx].Y;
double length = Math.Sqrt(dx * dx + dy * dy);
int count = (int)Math.Round(length / param.RampingInterval);
partInfoList.Add(new PartInfo {
IsArc = false,
Length = length,
Count = count,
DxPerStep = dx / count,
DyPerStep = dy / count,
ThetaPerStep = 0,
SetIndex = setIdx,
PartIndex = 2
});
totalLength += length;
}
}
// === StartRampingDist / EndRampingDist 인덱스 찾기 ===
bool useStart = param.RampingStartDist > 0;
bool useEnd = param.RampingEndDist > 0;
int startPartIndex = -1;
int endPartIndex = -1;
double cumulative = 0.0;
for (int i = 0; i < partInfoList.Count; i++) {
cumulative += partInfoList[i].Length;
if (useStart && startPartIndex == -1 && cumulative >= param.RampingStartDist) {
startPartIndex = i;
}
if (useEnd) {
if (endPartIndex == -1 && cumulative >= (totalLength - param.RampingEndDist)) {
endPartIndex = i;
break;
}
} else {
endPartIndex = partInfoList.Count;
}
}
// === Rising/Falling 전체 카운트 계산 ===
int risingCount = useStart ? (int)Math.Round(param.RampingStartDist / param.RampingInterval) : 0;
int fallingCount = useEnd ? (int)Math.Round(param.RampingEndDist / param.RampingInterval) : 0;
// === Rising 구간 ===
double curX = obj.P1.X;
double curY = obj.P1.Y;
if (useStart) {
int rampStep = 0;
int lastRampPartIdx = 0;
int lastRampCountInPart = 0;
// A -> Start Power, B -> End Power
float startingPercentLaser_A1 = Math.Min(percentLaser1 * (float)param.RampingStartPower1 / 100, 100);
float startingPercentLaser_B1 = Math.Min(percentLaser1 * (float)param.RampingStartPower2 / 100, 100);
// 2170은 Power를 사용하지 않는다.
float startingPercentLaser2 = 0; // Math.Min(percentLaser2 * (float)param.RampingStartPower2 / 100, 100);
for (int i = 0; i <= startPartIndex; i++) {
var part = partInfoList[i];
int repeatCount = part.Count;
for (int c = 0; c < repeatCount; c++) {
// power1은 startPower_A1 에서 startPower_B1 까지 변한다
float rampPercent1 = startingPercentLaser_A1 + (startingPercentLaser_B1 - startingPercentLaser_A1) * (rampStep / (float)risingCount);
float rampPercent2 = startingPercentLaser2 + (percentLaser2 - startingPercentLaser2) * (rampStep / (float)risingCount);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, rampPercent1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, rampPercent2, eFunctionType.LIST);
if (!part.IsArc) {
curX += part.DxPerStep;
curY += part.DyPerStep;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
_Scanner.ArcAbs(obj.ArcCenterPoints[part.SetIndex].X + xOffset,
obj.ArcCenterPoints[part.SetIndex].Y + yOffset,
part.ThetaPerStep);
if (c == repeatCount - 1) {
// Arc가 끝날 때 Line의 현재위치를 Arc 끝점으로 만들어줌
curX += (obj.ArcEndPoints[part.SetIndex].X - obj.ArcStartPoints[part.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[part.SetIndex].Y - obj.ArcStartPoints[part.SetIndex].Y) + yOffset;
}
}
rampStep++;
// 현재 PartIdx에서 Rising이 끝난 시점을 알고 있어야, Continuous 구간 시작 시 PartIdx가 동일한 파워로 마무리 될 수 있도록 함
lastRampPartIdx = i;
lastRampCountInPart = c + 1;
if (rampStep >= risingCount) {
// Arc에서 끝난 경우 curX,Y 좌표를 Arc의 끝점으로 업데이트 해줘야함
if (part.PartIndex % 3 == 1) {
curX += (obj.ArcEndPoints[part.SetIndex].X - obj.ArcStartPoints[part.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[part.SetIndex].Y - obj.ArcStartPoints[part.SetIndex].Y) + yOffset;
}
break;
}
}
if (rampStep >= risingCount) break;
}
// === Continuous 구간 ===
// 1. RisingEdge 남은 파트 처리
{
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
var part = partInfoList[lastRampPartIdx];
int remainingCount = part.Count - lastRampCountInPart;
if (!part.IsArc) {
curX += part.DxPerStep * remainingCount;
curY += part.DyPerStep * remainingCount;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
// Arc는 각도 누적
double totalTheta = part.ThetaPerStep * remainingCount;
_Scanner.ArcAbs(obj.ArcCenterPoints[part.SetIndex].X + xOffset,
obj.ArcCenterPoints[part.SetIndex].Y + yOffset,
totalTheta);
}
}
// Continuous 구간: EndRamping 시작 전까지
// EndRamping 미사용일 경우 endPartIndex가 partInfoList이기 때문에 해당 부분까지만 continuous 구간으로 생각해주면 됨
for (int i = lastRampPartIdx + 1; i < endPartIndex; i++) {
var part = partInfoList[i];
if (!part.IsArc) {
curX += part.DxPerStep * part.Count;
curY += part.DyPerStep * part.Count;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
// Arc는 각도 누적
double totalTheta = part.ThetaPerStep * part.Count;
_Scanner.ArcAbs(obj.ArcCenterPoints[part.SetIndex].X + xOffset,
obj.ArcCenterPoints[part.SetIndex].Y + yOffset,
totalTheta);
curX += (obj.ArcEndPoints[part.SetIndex].X - obj.ArcStartPoints[part.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[part.SetIndex].Y - obj.ArcStartPoints[part.SetIndex].Y) + yOffset;
}
}
} else {
// StartRamping 미사용 → 처음부터 Continuous
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
for (int i = 0; i < endPartIndex; i++) {
var part = partInfoList[i];
if (!part.IsArc) {
curX += part.DxPerStep * part.Count;
curY += part.DyPerStep * part.Count;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
_Scanner.ArcAbs(obj.ArcCenterPoints[part.SetIndex].X + xOffset,
obj.ArcCenterPoints[part.SetIndex].Y + yOffset,
part.ThetaPerStep * part.Count);
curX += (obj.ArcEndPoints[part.SetIndex].X - obj.ArcStartPoints[part.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[part.SetIndex].Y - obj.ArcStartPoints[part.SetIndex].Y) + yOffset;
}
}
}
if (useEnd) {
// Falling 구간 시작 전 해당 객체에서 앞에 남는 길이만큼은 continuos를 유지해줘야 함
PartInfo endPart = partInfoList[endPartIndex];
int remainCount = 0; // EndRamping 시작 전 Continuous 처리 스텝 수
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
{
double distFromEndStart = totalLength - param.RampingEndDist;
double partStartDist = 0.0;
for (int j = 0; j < endPartIndex; j++) {
partStartDist += partInfoList[j].Length;
}
double remainLength = distFromEndStart - partStartDist; // 해당 파트에서 Continuous로 처리할 길이
remainCount = (int)Math.Round(remainLength / param.RampingInterval);
if (!endPart.IsArc) {
curX += endPart.DxPerStep * remainCount;
curY += endPart.DyPerStep * remainCount;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
// Arc는 각도 누적
double totalTheta = endPart.ThetaPerStep * remainCount;
_Scanner.ArcAbs(obj.ArcCenterPoints[endPart.SetIndex].X + xOffset,
obj.ArcCenterPoints[endPart.SetIndex].Y + yOffset,
totalTheta);
}
}
// Falling 구간: remainCount 이후부터
int endRampStep = 0;
// A -> Start Power, B -> End Power
float endingPercentLaser_A1 = Math.Min(percentLaser1 * (float)param.RampingEndPower1 / 100, 100);
float endingPercentLaser_B1 = Math.Min(percentLaser1 * (float)param.RampingEndPower3 / 100, 100);
// 2170은 Power를 사용하지 않는다.
float endingPercentLaser2 = 0; // Math.Min(percentLaser2 * (float)param.RampingEndPower2 / 100, 100);
for (int c = remainCount; c < endPart.Count; c++) {
// power1은 endPower_A1 에서 endPower_B1 까지 변한다
float rampPercent1 = endingPercentLaser_A1 - (endingPercentLaser_B1 - endingPercentLaser_A1) * (endRampStep / (float)fallingCount);
float rampPercent2 = percentLaser2 - (percentLaser2 - endingPercentLaser2) * (endRampStep / (float)fallingCount);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, rampPercent1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, rampPercent2, eFunctionType.LIST);
if (!endPart.IsArc) {
curX += endPart.DxPerStep;
curY += endPart.DyPerStep;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
_Scanner.ArcAbs(obj.ArcCenterPoints[endPart.SetIndex].X + xOffset,
obj.ArcCenterPoints[endPart.SetIndex].Y + yOffset,
endPart.ThetaPerStep);
if (c == endPart.Count - 1) {
curX += (obj.ArcEndPoints[endPart.SetIndex].X - obj.ArcStartPoints[endPart.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[endPart.SetIndex].Y - obj.ArcStartPoints[endPart.SetIndex].Y) + yOffset;
}
}
endRampStep++;
}
// 이후 endPartIndex+1 부터 나머지 파트는 기존 Falling 로직 그대로
for (int i = endPartIndex + 1; i < partInfoList.Count; i++) {
var p = partInfoList[i];
for (int c = 0; c < p.Count; c++) {
float rampPercent1 = endingPercentLaser_A1 - (endingPercentLaser_A1 - endingPercentLaser_B1) * (endRampStep / (float)fallingCount);
float rampPercent2 = percentLaser2 - (percentLaser2 - endingPercentLaser2) * (endRampStep / (float)fallingCount);
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, rampPercent1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, rampPercent2, eFunctionType.LIST);
if (!p.IsArc) {
curX += p.DxPerStep;
curY += p.DyPerStep;
_Scanner.MarkAbs(curX + xOffset, curY + yOffset);
} else {
_Scanner.ArcAbs(obj.ArcCenterPoints[p.SetIndex].X + xOffset,
obj.ArcCenterPoints[p.SetIndex].Y + yOffset,
p.ThetaPerStep);
if (c == p.Count - 1) {
curX += (obj.ArcEndPoints[p.SetIndex].X - obj.ArcStartPoints[p.SetIndex].X) + xOffset;
curY += (obj.ArcEndPoints[p.SetIndex].Y - obj.ArcStartPoints[p.SetIndex].Y) + yOffset;
}
}
endRampStep++;
if (endRampStep >= fallingCount) break;
}
if (endRampStep >= fallingCount) break;
}
}
sw.Stop();
Log.I(string.Format($"[Zigzag Weld] Ramping UploadWeldingList time : {sw.ElapsedMilliseconds} msec"));
}
#endregion
else {
// 3. 지그재그 순서대로 마킹
for (int i = 0; i < obj.ZigZagCount; i++) {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, percentLaser1, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, percentLaser2, eFunctionType.LIST);
_Scanner.MarkAbs(obj.ArcStartPoints[i].X + xOffset, obj.ArcStartPoints[i].Y + yOffset);
float startAngle = (float)HitUtil.RadiansToDegrees(HitUtil.LineAngleR(obj.ArcCenterPoints[i], obj.ArcStartPoints[i], 0));
float endAngle = (float)HitUtil.RadiansToDegrees(HitUtil.LineAngleR(obj.ArcCenterPoints[i], obj.ArcEndPoints[i], 0));
float sweepAngle = 0;
if (i % 2 == 0) {
if (obj.DrawDirection == ZigZag.eDrawDirection.Left) {
sweepAngle = (-1) * Math.Abs(startAngle - endAngle);
if (sweepAngle < -180) {
sweepAngle = (360 + sweepAngle) * (-1);
}
_Scanner.ArcAbs(obj.ArcCenterPoints[i].X + xOffset, obj.ArcCenterPoints[i].Y + yOffset, sweepAngle);
} else {
sweepAngle = Math.Abs(startAngle - endAngle);
if (sweepAngle > 180) {
sweepAngle = (360 - sweepAngle);
}
_Scanner.ArcAbs(obj.ArcCenterPoints[i].X + xOffset, obj.ArcCenterPoints[i].Y + yOffset, sweepAngle);
}
} else {
if (obj.DrawDirection == ZigZag.eDrawDirection.Right) {
sweepAngle = (-1) * Math.Abs(startAngle - endAngle);
if (sweepAngle < -180) {
sweepAngle = (360 + sweepAngle) * (-1);
}
_Scanner.ArcAbs(obj.ArcCenterPoints[i].X + xOffset, obj.ArcCenterPoints[i].Y + yOffset, sweepAngle);
} else {
sweepAngle = Math.Abs(startAngle - endAngle);
if (sweepAngle > 180) {
sweepAngle = (360 - sweepAngle);
}
_Scanner.ArcAbs(obj.ArcCenterPoints[i].X + xOffset, obj.ArcCenterPoints[i].Y + yOffset, sweepAngle);
}
}
_Scanner.MarkAbs(obj.LineEndPoints[i].X + xOffset, obj.LineEndPoints[i].Y + yOffset);
}
}
_LastX = obj.LineEndPoints[obj.ZigZagCount - 1].X + xOffset;
_LastY = obj.LineEndPoints[obj.ZigZagCount - 1].Y + yOffset;
Log.I(string.Format($"[Zigzag Weld] Mark Start/End Point (Start Point : {obj.PF1.X + xOffset},{obj.PF1.Y + yOffset}, End Point : {obj.PF2.X + xOffset},{obj.PF2.Y + yOffset})"));
}
private SequenceActionResult MarkingOnTheFlyOff(SequenceContext context, string id, SequenceActionResult param)
{
_Scanner.StoreEncoder(1);
_Scanner.SaveAndRestartTimer();
if (_Scanner.Config.UseMarkingOnTheFly) {
if (_Scanner.Config.OnTheFlyCoordinate == eOntheFlyCoordinate.XY) {
// Linear OTW의 경우, 각 Draw Tool에서 좌표 오프셋을 해 주고 있음
} else if (_Scanner.Config.OnTheFlyCoordinate == eOntheFlyCoordinate.Rotate) {
// 여기서 원래 레시피로 돌려주기
foreach (IDrawObject obj in _InitParam.SelectedDrawObjectList) {
obj.Rotate((float)Rotation_Degree * -1, new UnitPoint(_Scanner.Config.MarkingOnTheFlyRotateCenterX, _Scanner.Config.MarkingOnTheFlyRotateCenterY)); // -15도 회전
}
}
// 추후, Parking Position 을 용접 시작 위치로 자동으로 계산되어 들어가게 개선 필요
_Scanner.Fly_Return(_Scanner.Config.MarkingOntheFlyParkingPositionX, _Scanner.Config.MarkingOntheFlyParkingPositionY);
// 엔코더 기록(용접 완료)
}
return SequenceAction.Ok();
}
private SequenceActionResult RecordSignalOff(SequenceContext context, string id, SequenceActionResult param)
{
if (_Scanner.Config.UseRecordSignal) {
_Scanner.SaveAndRestartTimer();
_Scanner.SetTrigger(0,
_Scanner.Config.RecordSignalType1, _Scanner.Config.RecordSignalType2,
_Scanner.Config.RecordSignalType3, _Scanner.Config.RecordSignalType4);
}
return SequenceAction.Ok();
}
private SequenceActionResult SaveResultData(SequenceContext context, string id, SequenceActionResult param)
{
// 결과 Data 를 업데이트 함.
context.PutValue(ContextKey.KEY_SEQUENCE_RESULT, true);
return SequenceAction.Ok();
}
private SequenceActionResult WriteCompletionPLCBit(SequenceContext context, string id, SequenceActionResult param)
{
// 완료 비트 write 하는 Node 추가 필요. format 유지, SeqScoutNormal 없음
Log.D("UploadWeldingList Sequence WriteCompletionPLCBit");
return SequenceAction.Ok();
}
private SequenceActionResult Complete(SequenceContext context, string id, SequenceActionResult param)
{
RestoreDeviceStatus();
Log.I("UploadWeldingList Complete");
_UploadSpanTime = DateTime.Now.Subtract(_UploadstartTime);
Log.D(string.Format($"[SeqUploadWeldingList] UploadWeldingList Complete Time : {_UploadSpanTime.TotalMilliseconds}"));
context.TryGetValue(ContextKey.KEY_SEQUENCE_RESULT, out bool seqResult);
SeqUploadWeldingList_OutParam result = new SeqUploadWeldingList_OutParam() { Success = seqResult };
if (seqResult == false) {
AlarmManager.Alarm(AlarmCode.ALARM_SEQUENCE_UploadWeldingList_ResultFailed);
}
Log.D("UploadWeldingList Sequence Complete");
return SequenceAction.Ok(result);
}
private bool RestoreDeviceStatus()
{
if (_InitParam.IncludeListStartEnd) {
_Scanner.WriteDA((uint)eAnalogPortOut.DA1, 0, eFunctionType.LIST);
_Scanner.WriteDA((uint)eAnalogPortOut.DA2, 0, eFunctionType.LIST);
_Scanner.SetEndList();
}
Log.I(string.Format($"SetEndList"));
return true;
}
private void OnForceError(SequenceContext arg1, string arg2)
{
Log.I("SequenceAction Ok : OnForceError");
}
private void OnStopped(SequenceContext obj)
{
Log.I("SequenceAction Ok : OnStopped");
}
}
public class SeqUploadWeldingList_InitParam : InitParam {
public bool NeedSetLaserTiming { get; set; } // eLaserType.Trumpf_IO, eLaserType.Coherent_Arm_IO ==> true
public bool IncludeListStartEnd { get; set; }
public double CellMeasureCenterOffsetX { get; set; }
public double CellMeasureCenterOffsetY { get; set; }
public List<IDrawObject> SelectedDrawObjectList { get; set; }
public eListNo WeldingScannerListNo { get; set; }
public double Rotation_Degree { get; set; }
}
public class SeqUploadWeldingList_OutParam : OutParam {
}
public class PartInfo {
public bool IsArc;
public double Length;
public int Count;
public double DxPerStep;
public double DyPerStep;
public double ThetaPerStep;
public int SetIndex; // 어떤 세트에 속하는지
public int PartIndex; // 세트 내 파트 인덱스 (0=Line1, 1=Arc, 2=Line2)
}
}