======List Motion======
ListMotion 예제 소스 페이지입니다.
\\
====다단속도====
\\
===example===
\\
{{:platform:ethercat:6_manual:user:listmotion_test.png|}}
\\
===C#===
private void btnTest_Click(object sender, EventArgs e)
{
// 하나의 축에 대해 위치별로 속도를 변경하여 속도 프로파일을 생성하는 예제
// 가속구간과 감속 구간을 세구간으로 나누어 다른 속도 패턴으로 이송 (세단계 가속, 세단계 감속)
// 에러처리는 생략.
// 리스트모션 맵인덱스,
// 최대 8개까지 지원 됨 (0~7)
// 0~31번 축 중에서 리스트 모션에 참여하는 축의 Mask,
// 1,2,3 번 참여시 axisMask1 = 14
uint axisMask1 = 0;
// 32~63번 축 중에서 리스트 모션에 참여하는 축의 Mask,
uint axisMask2 = 0;
int speedMode = (int)ec.EEcmSpeedMode.ecmSMODE_TRAPE;
int stepID = 0;
if (axisList.Count() < 31)
{
axisMask1 = (uint)(1 << axisID);
axisMask2 = 0;
}
else
{
axisMask1 = 0;
axisMask2 = (uint)(0x01 << (axisID - 32));
}
lmMapIndex = 0;
double initSpeed = 0;
double endSpeed = 0;
double workSpeed = 0;
double accel = 0;
double decel = 0;
// 리스트 모션 기능을 시작한다.
// 이후 실행되는 명령은 리스트모션 테이블에 등록되며, ecmLmCtl_Run() 함수 실행시 순차 실행된다.
ec.ecmLmCtl_Begin(netID, lmMapIndex, axisMask1, axisMask2, ref errorCode);
// lmMapIndex 에 해당하는 리스트모션 테이블에 등록되어 있는 모든 스텝을 제거한다.
ec.ecmLmCtl_ClearQue(netID, lmMapIndex, ref errorCode);
// 다음 속도 패턴으로 이어지고 가속만 존재하는 경우.
initSpeed = 0;
endSpeed = 20000;
accel = 10000;
decel = 0; // 감속이 없으므로 decel = 0
workSpeed = endSpeed; // 감속이 없는 경우 workSpeed와 endSpeed는 같다.
ec.ecmSxCfg_SetSpeedPatt(netID, axisID, speedMode, initSpeed, endSpeed, workSpeed, accel, decel, ref errorCode);
// 실행되는 명령에 ID 를 부여한다.
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
// 이송 예약
ec.ecmSxMot_MoveStart(netID, axisID, 20000, ref errorCode);
// 다음 속도 패턴으로 이어지고 가속과 정속만 존재하는 경우
initSpeed = endSpeed; // 이전속도 패턴에서 이어지므로 이전 속도 패턴의 endSpeed가 InitSpeed가 된다.
endSpeed = 40000; //
accel = 20000;
decel = 0;
workSpeed = endSpeed; // 감속이 없는 경우 workSpeed와 endSpeed는 같다.
ec.ecmSxCfg_SetSpeedPatt(netID, axisID, speedMode, initSpeed, endSpeed, workSpeed, accel, decel, ref errorCode);
// 이송 예약
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmSxMot_MoveStart(netID, axisID, 50000, ref errorCode);
// 다음 속도 패턴으로 이어지고 가속과 정속, 감속이 존재하는 경우
initSpeed = endSpeed; // 이전속도 패턴에서 이어지므로 이전 속도 패턴의 endSpeed가 InitSpeed가 된다.
endSpeed = 40000; //
accel = 10000;
decel = 10000;
workSpeed = 50000;
ec.ecmSxCfg_SetSpeedPatt(netID, axisID, speedMode, initSpeed, endSpeed, workSpeed, accel, decel, ref errorCode);
// 이송 예약
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmSxMot_MoveStart(netID, axisID, 100000, ref errorCode);
// 가속 없이 정속 이송 후, 감속하는 경우
initSpeed = endSpeed; // 이전속도 패턴에서 이어지므로 이전 속도 패턴의 endSpeed가 InitSpeed가 된다.
endSpeed = 20000; //
accel = 0; // 가속이 없으므로 accel = 0
decel = 20000;
workSpeed = initSpeed; // 이전속도 패턴에서 이어지며, workSpeed 부터 시작하므로, workSpeed = initSpeed 가 된다.
ec.ecmSxCfg_SetSpeedPatt(netID, axisID, speedMode, initSpeed, endSpeed, workSpeed, accel, decel, ref errorCode);
// 이송 예약
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmSxMot_MoveStart(netID, axisID, 70000, ref errorCode);
// 감속 후 이송완료
initSpeed = endSpeed; // 이전속도 패턴에서 이어지므로 이전 속도 패턴의 endSpeed가 InitSpeed가 된다.
endSpeed = 0; //
accel = 0; // 가속이 없으므로 accel = 0
decel = 10000;
workSpeed = initSpeed;
ec.ecmSxCfg_SetSpeedPatt(netID, axisID, speedMode, initSpeed, endSpeed, workSpeed, accel, decel, ref errorCode);
// 이송 예약
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID, ref errorCode);
ec.ecmSxMot_MoveStart(netID, axisID, 20000, ref errorCode);
// 등록된 명령 실행 시작
ec.ecmLmCtl_Run(netID, lmMapIndex, ref errorCode);
int runStepCount = 0, runStepID = 0, runStepState = 0;
int timeLimit = 100000;
Stopwatch sw = new Stopwatch();
sw.Start();
bool isSuccess = false;
// 이송 시간이 timeLimit 보다 크면 에러처리. 본 예제에서는 생략한다.
Task.Factory.StartNew(() =>
{
while (sw.ElapsedMilliseconds < timeLimit)
{
// 현재 실행되고 있는 스텝에 대한 정보를 확인한다.
ec.ecmLmSt_GetRunStepInfo(netID, lmMapIndex, ref runStepCount, ref runStepID, ref runStepState, ref errorCode);
// runStepID : 현재 실행되고 있는 StepID
// runStepState : 현재 실행되고 있는 Step의 상태 (Ready, Busy, Paused, Completed)
// 현재 실행되고 있는 StepID가 마지막 등록된 StepID와 같고, 실행 상태가 Complete 이면 리스토 모션 종료로 판단
// StepCount로 비교하거나 ecmLmSt_GetRemStepCount 를 이용하여 RemStep 등으로 비교해도 된다.
if (runStepID == stepID && runStepState == (int)ec.EEcmLmCmdItemSts.ecmLM_CMDITEM_STS_COMPLETED)
{
isSuccess = true;
break;
}
// 진행상황 처리
// lblRunStepCount.BeginInvoke(new Action(() => lblRunStepCount.Text = runStepCount.ToString()));
// lblRunStepID.BeginInvoke(new Action(() => lblRunStepID.Text = runStepID.ToString()));
// lblRunStepState.BeginInvoke(new Action(() => lblRunStepState.Text = ((ec.EEcmLmCmdItemSts)runStepState).ToString()));
Thread.Sleep(10);
}
if (!isSuccess)
{
// 에러처리
}
//리스토 모션 종료. 이후 이송 명령은 즉시 실행 됨
ec.ecmLmCtl_End(netID, lmMapIndex, ref errorCode);
});
if (!isSuccess)
{
// 에러처리
}
}
\\
====연속 보간이송====
\\
===example===
\\
{{:platform:ethercat:6_manual:user:listmotion_test_2.png|}}
\\
===C#===
private void btnTest2_Click(object sender, EventArgs e)
{
// 말풍선 그리기 예제
// 에러처리는 생략.
// 리스트모션 맵인덱스,
// 최대 8개까지 지원 됨 (0~7)
// 보간 제어를 위한 맵인덱스
lmMapIndex = 0;
int ixMapIndex = 0;
// 0~31번 축 중에서 리스트 모션에 참여하는 축의 Mask,
// 1,2,3 번 참여시 axisMask1 = 14
uint axisMask1 = 0;
// 32~63번 축 중에서 리스트 모션에 참여하는 축의 Mask,
uint axisMask2 = 0;
int axisX = axisList[cbxAxisX.SelectedIndex];
int axisY = axisList[cbxAxisY.SelectedIndex];
// axisX < 32 & axisY < 32 로 간주
axisMask1 = (uint)((1 << axisX) + (1 << axisY));
// lmMapIndex 에 해당하는 리스트모션 테이블에 등록되어 있는 모든 스텝을 제거한다.
ec.ecmLmCtl_ClearQue(netID, lmMapIndex, ref errorCode);
// 리스트 모션 기능을 시작한다.
// 이후 실행되는 명령은 리스트모션 테이블에 등록되며, ecmLmCtl_Run() 함수 실행시 순차 실행된다.
ec.ecmLmCtl_Begin(netID, lmMapIndex, axisMask1, axisMask2, ref errorCode);
// 보간맵 설정을 위한 axisList
int[] ixAxisList = new int[2]{axisX, axisY};
// 보간맵 설정
ec.ecmIxCfg_MapAxes(netID, ixMapIndex, 2, ixAxisList, ref errorCode);
int speedType = 1; //VectorSpeed;
int speedMode = (int)ec.EEcmSpeedMode.ecmSMODE_TRAPE;
// 첫번째 이송의 속도 설정 (가속만 설정)
// endSpeed = workSpeed. decel = 0;
ec.ecmIxCfg_SetSpeedPatt(netID, ixMapIndex, speedType, speedMode, 0, 10000, 10000, 100000, 0, ref errorCode);
int stepID = 0;
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 0, 0 }, ref errorCode);
// 두번째 ~ 마지막 두번째 이송까지의 속도 설정 (정속만 설정)
// initSpeed, endSpeed = workSpeed. accel, decel = 0;
ec.ecmIxCfg_SetSpeedPatt(netID, ixMapIndex, speedType, speedMode, 0, 10000, 10000, 100000, 0, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 20000, 20000 }, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 50000, 20000 }, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_ArcAng_A(netID, lmMapIndex, 50000, 30000, 90, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 60000, 50000 }, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_ArcAng_A(netID, lmMapIndex, 50000, 50000, 90, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { -10000, 60000 }, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_ArcAng_A(netID, lmMapIndex, -10000, 50000, 90, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { -20000, 30000 }, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_ArcAng_A(netID, lmMapIndex, -10000, 30000, 90, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID++, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 10000, 20000 }, ref errorCode);
// 마지막 이송 속도 설정 (감속만 설정)
// initSpeed = workSpeed. accel, endSpeed = 0;
ec.ecmIxCfg_SetSpeedPatt(netID, ixMapIndex, speedType, speedMode, 0, 10000, 10000, 100000, 0, ref errorCode);
ec.ecmLmCfg_SetStepId(netID, lmMapIndex, stepID, ref errorCode);
ec.ecmIxMot_LineTo(netID, lmMapIndex, new double[] { 0, 0 }, ref errorCode);
// 등록된 명령 실행 시작
ec.ecmLmCtl_Run(netID, lmMapIndex, ref errorCode);
int runStepCount = 0, runStepID = 0, runStepState = 0;
const int timeLimit = 10000;
Stopwatch sw = new Stopwatch();
sw.Start();
bool isSuccess = false;
// 이송 시간이 timeLimit 보다 크면 에러처리. 본 예제에서는 생략한다.
// 이송 시간이 timeLimit 보다 크면 에러처리. 본 예제에서는 생략한다.
Task.Factory.StartNew(() =>
{
while (sw.ElapsedMilliseconds < timeLimit)
{
// 현재 실행되고 있는 스텝에 대한 정보를 확인한다.
ec.ecmLmSt_GetRunStepInfo(netID, lmMapIndex, ref runStepCount, ref runStepID, ref runStepState, ref errorCode);
// runStepID : 현재 실행되고 있는 StepID
// runStepState : 현재 실행되고 있는 Step의 상태 (Ready, Busy, Paused, Completed)
// 현재 실행되고 있는 StepID가 마지막 등록된 StepID와 같고, 실행 상태가 Complete 이면 리스토 모션 종료로 판단
// StepCount로 비교하거나 ecmLmSt_GetRemStepCount 를 이용하여 RemStep 등으로 비교해도 된다.
if (runStepCount == stepID && runStepState == (int)ec.EEcmLmCmdItemSts.ecmLM_CMDITEM_STS_COMPLETED)
{
isSuccess = true;
break;
}
lblRunStepCount.BeginInvoke(new Action(() => lblRunStepCount.Text = runStepCount.ToString()));
lblRunStepID.BeginInvoke(new Action(() => lblRunStepID.Text = runStepID.ToString()));
lblRunStepState.BeginInvoke(new Action(() => lblRunStepState.Text = ((ec.EEcmLmCmdItemSts)runStepState).ToString()));
Thread.Sleep(10);
}
if (!isSuccess)
{
// 에러처리
}
//리스토 모션 종료. 이후 이송 명령은 즉시 실행 됨
ec.ecmLmCtl_End(netID, lmMapIndex, ref errorCode);
});
if (!isSuccess)
{
// 에러처리
}
}