[장비 pc제어]시퀀스 짜보기 (스레드 타이머, switch-case)

미우·2022년 1월 3일
1

장비제어

목록 보기
2/2
post-thumbnail

-반도체 장비 제어, 자동화 장비 제어의 꽃인 시퀀스 짜기-
내일 직원에게 시퀀스 짜는 방법을 알려주기 위해서 우선 글로 써보기로 하였습니다. 원래 퇴근하고 바로 쓰려했는데 밥먹고 유튜브 좀 보고 방청소 하다보니 어느새 11시... 자기 전 졸음을 참고 적어봅니다.
아래 예제 소스는 c#이지만 원리를 알면 어느 언어에든 적용이 가능하다고 생각합니다.

(프로젝트를 만드는 과정은 생략합니다.)

시퀀스를 짜는데는 여러 방법이 있겠지만 주로 쓰이는 스레드 타이머를 사용한 방식을 소개하겠습니다.

한 줄로 표현하자면,

[스레드 타이머를 초기화한다]-[타이머를 실행한다]-[타이머 내부에 switch-case문을 사용하여 시퀀스를 작성한다.]

1. 타이머 초기화

스레드 타이머를 사용할 것이기 때문에
1) using namespace로써 System.Threading.Tasks를 정의해줍니다.
2) 전역 변수로 오늘의 주인공 timer와 switch-case문에서 event id로 사용될 변수를 선언해줍니다.
3) 타이머 초기화 함수를 작성한다. (Init_Timer)

  • new System.Threading.Timer(함수명)
    함수명에는 지정된 간격으로 실행될 callback 함수명을 넣어줍니다. 저는 함수명을 OnTimer로 정했습니다.
  • 타이머를 시작하고 종료하기 위해 다른 함수에서도 컨트롤 할 수 있도록 위에서 전역변수로 선언한 timer에 넣어주었습니다.

4) 타이머를 초기화해주기 위하여 클래스 생성자에서 타이머 초기화 함수를 호출한다. (원하는 타이밍이 있음 그 곳에서 호출해도 되며, 굳이 함수형식으로 만들지 않고 바로 3)의 내용을 넣어줘도 됩니다.)

(아래 소스 참고)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks; // 필수, 스레드 타이머를 사용하기 위해
namespace NanoConnectProgram
{
    class Preprocessing_Catridge
    {
    	System.Threading.Timer timer; 
        int mn_eventID = 0; 
        public Preprocessing_Catridge()
        {
            Init_Timer(); // 타이머 초기화 함수
        }
        private void Init_Timer()
        {
        // OnTimer는 아래에서 실제 사용될 타이머 함수 이름입니다.
            timer = new System.Threading.Timer(OnTimer); 
        }
        

2. 타이머 실행(과 종료)

1) SetTimer함수로 타이머를 시작시킬 예정이므로, 타이머(시퀀스)가 시작되면 어느 case로 진입할지 결정할 event id와 얼마의 주기로 실행시킬지를 결정할 period 변수를 인자로 받도록 함수를 작성합니다.

2) 인자로 받은 event id는 전역변수로 선언된 mn_eventID에 넣어주고, period 인자는 timer.Change(0, period)에 넣어서 사용합니다.

  • Timer.Change 메서드는 타이머 시작시간 및 호출 사이의 간격을 변경합니다. (ms단위)
  • 즉, 0초 후에 period의 간격으로 timer를 실행한다는 의미입니다.
  • 여기서 말하는 timer는 위에 초기화 단계에서 콜백 함수(OnTimer)가 담긴 타이머 변수이죠? 다시 말하자면 0초 후에 period 간격으로 OnTimer함수가 실행됩니다.

3) KillTimer는 Timer를 종료시킬때 호출될 함수입니다. 시작시킬때와 동일하게 Change메서드를 사용하며, 인자로 무한대를 뜻하는 상수를 넣어줍니다.)

public void SetTimer(int event_id, int period)
{
	mn_eventID = event_id;
	timer.Change(0, period);
}
public void KillTimer()
{
      timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite);
}

3. 콜백함수에 switch-case문으로 시퀀스 작성!

1) 이제 위에서 SetTimer가 실행되면 설정된 주기에 맞춰 OnTimer가 호출됩니다.

  • 그말인 즉슨 SetTimer(10, 100)으로 실행되었다면, 100ms주기로 OnTimer가 호출될 것이고 case 10부터 실행이 됩니다.
  • case 10에 작성된 내용이 실행이 되고, mn_eventID에 20을 넣어주고 끝났습니다.
  • 다음 100ms에 호출된 OnTimer는 switch문 case 20부터 호출이 될 것이고, case 10에서 실행된 내용이 잘 완료됐는지 체크하는 첫번째 if문이 참이될때까지 100ms 주기로 OnTimer가 호출되면 첫번째 if문까지만 실행이 될 것입니다.
  • 그리고 첫번째 if문이 참이되면 if문 내부의 내용이 실행되고, 만약 비정상 동작/상태가 감지되면 mn_eventID에 1000을 넣어주고 바로 case문을 종료시켜 다음 호출때 case 1000으로 이동시키게 합니다.
  • case 1000에서는 타이머 종료 함수가 선언되어 있으므로, 타이머가 종료되겠습니다.
  • 정상일 경우 두번째 if문을 통과하여 다음 동작이 실행되며 mn_eventID에 30이 입력되어 다음 주기에 OnTimer가 실행되면 case 30이 실행되는 흐름이 되겠습니다.
private void OnTimer(Object state)
{
   switch(mn_eventID)
   {
        case 10:
        	// 동작시킬 함수 작성
        	IO_PC_FUNCTION(ID, PC_FUNCTION.VALVE1_OPEN);
        	mn_eventID = 20;
        	break;
        case 20:
                if(동작 완료 체크 또는 센서의 상태 확인)
        	{
                  if(비정상 동작 또는 비정상 상태 확인)
                  {
                  	// 에러 로그 작성
                      Console.WriteLine("case 20 stop");
                      mn_eventID = 1000; // 타이머 종료
                      break;
                  }
                  // 동작시킬 함수 작성
                  IO_PC_FUNCTION(ID, PC_FUNCTION.REACTION_CHMABER_NP);
                  mn_eventID = 30;
            	}
            	break;
        .
        .
        .
        case 1000:
                KillTimer();
                break;
        

간단하죠?

  • 타이머만 잘 세팅해준다면 장비 동작 시퀀스에 맞게 switch-case문을 얼마나 효율적으로 짤것인지만 고민해주면 됩니다.
  • 타이머 내부에서도 타이머 실행 주기 변경이 가능하므로, 장비 시퀀스에 맞게 다음 호출 주기가 길어져야 한다 싶으면 아래와 같이 case문 내에 넣어주면 됩니다.
    timer.Change(1000,1000);
    -> 1000ms 후 부터 1000ms 주기로 OnTimer호출하겠다.
  • 주의할 점은 timer.Change(0,1000)으로 쓸경우 바로 OnTimer가 재호출되므로 첫번째 인자에 현재까지의 주기를 넣거나 상황에 맞게 적당한 숫자를 넣어주는 것을 권장합니다.
  • case문의 숫자의 경우 상수로 만들어줘서 구별하기 쉽게 변수로 만들어도 좋습니다. (후에 짧게 올려보겠습니다.)
  • case문의 숫자에 의미를 담아도 좋습니다. 10계열은 get동작, 20계열은 put동작
  • case문의 숫자를 21, 22, 23 이렇게 바짝 붙어서 처음에 작성하시면 21과 22사이에 필요한 동작이 있을 경우 전체적으로 뒤에 따라오는 숫자를 손봐야 하므로 처음부터 20, 30, 40 이렇게 가거나 더 여유롭게 200, 300, 400 이렇게 가는것을 추천드립니다.

이상으로 시퀀스 짜는 기법 중 하나인 [스레드 타이머 + switch-case문] 방식을 소개해드렸습니다.

profile
장비 엔지니어에서 일본 테스트 엔지니어로

0개의 댓글