[C#] 시리얼 통신 시, 수신 이벤트가 두번 이상으로 들어올 경우


이거 때문에 하루종일…

현장 실습에서 젤 어이가 없으면서 젤 당황스러웠던게 바로 시리얼 통신이다. 산업 현장에서 측정장비나 생산장비들이 가지는 정보를 PC나 다른 장비로 전달하기 위해서 시리얼 통신이 많이 사용되는데, 사실 시리얼 통신이라는것을 이때 처음 본 것이다. 기껏 본거야 안드로이드 폰에 펌웨어 올릴 때 가상 COM포트로 데이터 전송하는것 정도? 현장 실습이 반이상 지나서 시리얼 통신을 주구장창 만저본 결과, 뭐 정말 별 것도 아니었지만. 컴퓨터를 처음 만질때 부터 USB에 키보드와 마우스를 꼽았던 나로써는 솔직히 이게 뭔지… 조차 몰랐으니까.


처음 봤으니 당연히 이걸 어떻게 다뤄야 하는지에 대한 감 조차 오지 않았다. 근데 하라고 하니 일단은 해야지. 그래서 처음에는 진짜 아무것도 모르고 시리얼 통신 예제를 복붙하다시피 해서 요구사항대로 진행했었는데, 정말 얘기치 않은 문제가 발생하는것이다. 열심히 예제와 실제 통신하는것을 찾아본 결과 개념 자체는 어느정도 이해가 됬는데, 정작 프로그램에 적용해보니 뭔가 이상한 것이다.

결국 하루종일 그 문제에만 몰두했다. 이게 도대체 뭔지조차 감이 오지 않았고, 당장 시리얼 통신을 시물레이션할 수 있다는 것도 알지 못했으니 결국 실물로 측정기같은게 있어야 계속 진행할 수 있었는데 그걸 전혀 몰랐다. 그래서 원인을 사실 전혀 알지 못했었다. 뭔가 이상해서 안되긴 하는데 뭐가 문제인지를 전혀 몰랐다.


시물레이션을 해보니 알게 되었는데…

결국 시리얼 통신에 대해 깊이 알아보게 되었고, 시물레이션이 가능한 가상 시리얼 통신 시물레이터도 알게 되었고, 결과적으로 쓰는 방법 자체를 완벽히 알게 되었다. 그래서 테스트를 해 보니…

수신되는 데이터가 짤려 들어오는것이다


여러 C# 시리얼 통신 예제들은 수신시 이벤트가 발생하도록 되어 있다. 이 시리얼 통신의 수신 이벤트가 발생하면 수신 버퍼에서 데이터를 가져와 처리를 하게 된다. 문제는 이 이벤트가 한번 데이터 송신시 한번 되는것이 아닌, 두세번에 걸쳐 나눠 들어오는 것이다. 간단한 예를 들어보면…

동해물과 백두산이 마르고 닳도록 하느님이 보우하사 우리나라 만세
무궁화 삼천리 화려강산 대한사람 대한으로 길이 보전하세

라는 문자열 데이터를 시리얼 통신으로 받으면


동해물과 백두산이 마르고 닳도록 하느님이 보우하
사 우리나라 만세
무궁화 삼천리 화려강산 대한사람 대
한으로 길이 보전하세

라고 세번의 이벤트가 발생되고 이걸 합쳐야 원래의 데이터로 들어오는것이다. USB to RS232 통신이여서 그런건지, 아니면 C#에서 제공하는 시리얼 통신 예제의 문제인지, 아니면 송신하는 쪽의 문제인건지, 통신 설정의 문제인지는 정확히 알 수 없지만. 여튼 데이터가 나눠진다. 나눠지는 횟수나 데이터가 잘려지는 위치는 그때그때 랜덤하며, 이게 결국 비트로 들어오는 데이터다보니 합치는것도 잘 합쳐야 데이터가 깨지지 않는다. 그리고 아무리 잘 들어와도 조금씩 데이터가 깨지는건 덤


문제는, 내가 필요로하는건 측정 장비에서 송신하는 데이터를 모두 받은 다음 아스키 코드로 번역하여 문자열로 만들고, 완성된 문자열에서 특정 값들을 가져오는것. 가져와서 DB에 쏴주고 특정 값은 HTTP POST로 보내줘야 한다.

그래서 어떻게 할까 고민을 했었는데, 결과적으로는 야매스러운 방법이긴 했지만, 완성을 하였다.


데이터 수신을 받아 처음과 끝으로 구분

내가 한 방법은 이 소제목과 같다. 측정 장비는 정규화된 문자열을 보내준다. 그중에서 값 부분만 측정한 값을 채워서 보내주는 것이다. 예를 들어 전자저울 같으면,

''''''''''''''''''''
측정 정보
''''''''''''''''''''
측정 모델       어쩌구
측정 시간       저쩌구 s
측정 타입       어쩌구 type

''''''''''''''''''''
측정 결과
''''''''''''''''''''
최종 무개       저쩌구 kg

''''''''''''''''''''
측정 종료
''''''''''''''''''''

이런식으로 되어 있다는 것. 물론 원래 전자저울은 그냥 00.00KG. 딱 한줄 찍어 보내준다. 이건 그냥 예시를 위해…


그러니까 여기서 시작은 “측정 정보” 라는 문자열로, 끝은 “측정 종료” 라는 문자열로 구분할 수 있다는 것. 시리얼 통신을 쭈욱 받아 문자열로 계속 쌓으면서 시작 문자열이 확인이 되면 그 앞은 다 버리고, 그 뒤부터 다시 차곡차곡 쌓는다. 쌓다가 “측정 종료” 라는 문자열을 확인하게 되면 그 앞은 필요한 곳으로 전송하던지 저장하고 있던지 하고, 그 뒤에 들어오는 문자열은 버리는것.

좋은 솔루션이라고는 할 수 없다 하지만, 별다른 해결 방법이 안보이는데 뭐… 정확히는 RS232 통신 쪽 설정 문제인거 같은데, 나로써는 도저히 해결을 할 수 없어 보여 이런 방법이라도 적용해본거지.


코드 및 설명

앞에서 이미 다 설명했으니 관련된 부분의 코드만 간략하게 기록한다. 아무래도 현재 돌아가고 있는 프로그램의 코드고 내 실력이 형편없어 보기 난해할 순 있지만. 그래도 나는 이런식으로 해결했다는 의미로 남기는 것.

int offset = 0;
string startStr;
string endStr;


private void sPort_DataReceived(object sender, SerialDataReceivedEventArgs e)
        {
            byte[] rxBuffer;

            if (offset == 0)
            {
                rxBuffer = new byte[4096];
            }

            string rxString = "";
            int intRecSize = sPort.BytesToRead;

            if (intRecSize != 0)
            {
                sPort.Read(rxBuffer, offset, intRecSize);
                offset += intRecSize;

                for (int iTemp = 0; iTemp < offset; iTemp++)
                {
                    rxString += Convert.ToChar(rxBuffer[iTemp]);    
                }

                if (rxString.Contains(startStr))
                {
                    rxString = rxString.Substring(rxString.IndexOf(startStr));
                    if (rxString.Contains(endStr))
                    {
                        program.dataReceived(rxString, "Receive");
                        offset = 0;
                    }
                }
            }
        }

시리얼 통신 예제 자체는 이미 인터넷에 많이 돌아다니고 있다. 다만 이 부분이 해결된 시리얼 통신 예제는 보이지가 않더라. 그러니까 이런 경우가 흔하지는 않다는 거고… 흔하지 않다는건 잘 해 두기만 한다면 이런 문제는 없다는 거겠지. 그래서, 시리얼 통신 자체의 부분은 생략한다. 해당 메서드는 시리얼 통신 예제에서 시리얼 데이터가 수신될 때 발생하는 이벤트 메서드이니 쉽게 찾을 수 있을 것이다.


여기에서 startStr과 endStr 변수는 통신의 시작과 종료에 정해진 문자열을 지정하면 된다. 코드를 간단히 보면 알겠지만 시리얼 통신으로 받는 족족 문자열로 변환한 후 시작 문자열과 종료 문자열을 검사해서 시작 문자열이 있으면 그 이전은 잘라내고, 시작 문자열과 종료 문자열 둘다 있으면, 특별한 작업을 하던지 다른 클래스로 보내던지(여기서는

program.dataReceived(rxString, "Receive");

라는 부분으로 program 클래스에 완성 문자열을 보내게 된다.) 하면 되고, 시작과 끝으로 셋트가 맞춰지면 offset을 0으로 하여 문자열을 초기화 하고 다음 시리얼 통신의 수신 대기를 하게 된다.

이렇게 해서 일단 필요하다고 하는 현장 두곳에다가 넣어놨는데, 지금까지 클래임 없이 조용한걸 보면 아마도 잘 작동하는듯.

Minny_

,