롯데 주류공장 원격검침 프로젝트 회고

Essay

롯데 주류공장 원격검침 프로젝트는 반복적이고 시간이 많이드는 업무를 소프트웨어로 개선하는 프로젝트입니다. 한국공대 SEP 협동조합에서 진행하였습니다.

한달에 한번 반복적인 업무

한 달에 한 번 각 공장으로 출장을 가서 계측기들을 검침하는 업무가 있었습니다. 업무 자체는 단순했습니다. 공장에 방문해 각 계측기를 조작해 값을 확인하고 기록하면 됐습니다.

하지만 매달 반복해야 한다는 점이 문제였습니다. 출장에 필요한 교통비와 이동 시간은 물론이고 검침을 위해 담당자가 직접 공장까지 이동해야 했습니다. 고객의 요구사항은 명확하고 심플했습니다.

계측기 검침을 사무실에서 하고싶다.

만약 이걸 소프트웨어로 개선한다면 매달 공장까지 이동하지 않아도 되고 출장에 드는 시간과 비용도 줄일 수 있습니다. 단순히 반복되는 업무 하나를 자동화하는 것만으로도 꽤 큰 효과를 만들어낼 수 있습니다.

처음해보지만 꽤 재밌어보였습니다.

구조는 이렇게 해볼까?

가장 먼저 데이터를 수집해야 할 계측기부터 조사했습니다. 확인해보니 계측기들은 RS485 통신을 지원하고 Modbus RTU 프로토콜을 통해 데이터를 읽을 수 있었습니다.

그렇다면 계측기와 통신하면서 데이터를 수집할 장치가 현장에 하나 필요했습니다. 대학 시절 라즈베리파이를 사용해본 경험이 있었기 때문에 라즈베리파이에 하이박스를 이용해 하우징을 추가하고 이를 현장에 설치하는 방식으로 구성하기로 했습니다.

구조는 대략 다음과 같이 생각했습니다.

---
config:
  layout: elk
---
flowchart TB
    SERVER["Server"]
    MQ["Rabbit MQ"]

    subgraph gunsan["군산"]
        MIDDLE_1["미들웨어"]
        G1_METER_1["열량계 1"]
        G1_METER_2["전력량계 1"]

        MIDDLE_1 -. "Modbus RTU" .-> G1_METER_1
        MIDDLE_1 -. "Modbus RTU" .-> G1_METER_2
    end

    subgraph opo["오포"]
        MIDDLE_2["미들웨어"]
        G2_METER_1["열량계 1"]
        G2_METER_2["열량계 2"]
        G2_METER_3["열량계 3"]
        G2_METER_4["열량계 4"]

        MIDDLE_2 -. "Modbus RTU" .-> G2_METER_1
        MIDDLE_2 -. "Modbus RTU" .-> G2_METER_2
        MIDDLE_2 -. "Modbus RTU" .-> G2_METER_3
        MIDDLE_2 -. "Modbus RTU" .-> G2_METER_4
    end

    subgraph ansung["안성"]
        MIDDLE_3["미들웨어"]
        G3_METER_1["열량계"]

        MIDDLE_3 -. "Modbus RTU" .-> G3_METER_1
    end

    MIDDLE_1 --> MQ
    MIDDLE_2 --> MQ
    MIDDLE_3 --> MQ

    MQ --> SERVER
    UI -- "HTTP" --> SERVER
    SERVER --> DB1["Maria DB"]
    SERVER --> DB2["Influx DB"]

그런데 한 가지 문제가 있었습니다. 드물지만 서버에서 현장의 계측기로 명령을 내려야 하는 경우가 있었습니다.

처음에는 미들웨어가 주기적으로 서버를 polling 하고 명령이 있을경우 수행하는 방식도 생각했습니다. 하지만 이 방식은 정해진 인터벌마다 서버를 확인해야 하기 때문에 서버에서 명령을 내리더라도 다음 폴링 시점까지 기다려야 합니다. 무엇보다 서버에서 미들웨어로 명령을 보내는 경우는 데이터 수집에 비해 빈도가 낮았습니다.

그래서 단순한 폴링 구조보다는 메시지 큐를 하나 두는 것이 좋겠다고 판단했습니다.

미들웨어와 서버 사이에 RabbitMQ를 두고 미들웨어가 데이터를 수집하면 메시지를 전달하고 서버에서 현장으로 명령을 내려야 할 때도 메시지를 통해 전달하도록 구성했습니다.

현장에 인터넷이 없네?

그동안 프로젝트를 진행하면서 네트워크 자체를 크게 고민해본 적은 많지 않았습니다. 대부분 센터에는 이미 구축되어 있었고 필요한 환경도 갖춰져 있었기 때문입니다.

하지만 이번 프로젝트는 달랐습니다. 계측기와 미들웨어를 설치해야 하는 현장에 인터넷망이 없었습니다.

미들웨어에서 계측기 데이터를 수집하는 것까지는 해결했지만 이 데이터를 서버까지 전달하려면 결국 외부 네트워크가 필요했습니다.

방법을 찾아보니 LTE Router라는 제품이 있었고 통신사에서 IoT 용도로 사용할 수 있는 요금제도 제공하고 있었습니다. 결국 LTE Router를 통해 현장에서 인터넷에 연결하고 미들웨어가 이 네트워크를 이용해 서버와 통신하는 방식으로 결정했습니다.

예상하지 못했던 통신비라는 비용이 추가되어 조금 아쉽기는 했습니다. 하지만 별도의 유선 인터넷을 현장까지 구축하는 것은 현실적으로 어려웠습니다. LTE Router를 사용하는 것이 가장 간단하고 현실적인 방법이라고 판단했습니다.

돈으로 해결할 수 있으면 그 방법이 가장 쉽다…

옛날 선배가 했던말이 떠올랐습니다.

이렇게 해서 계측기부터 서버까지 데이터를 전달할 수 있는 기본적인 통신 구조가 만들어졌습니다.

Modbus

Modbus 프로토콜은 이번 프로젝트에서 계측기를 조사하면서 처음 알게 되었습니다. 스펙을 살펴보니 평소 다루던 PLC 통신과 비슷한 부분이 많았고 프레임이 단순하였습니다. 물론 한 번에 읽을 수 있는 레지스터 수의 제한이 좀 작아 대량의 메모리를 읽고 처리해야하는 물류 자동화 환경에서는 적합하지 않겠다는 생각도 들었습니다. 공식 문서를 참고하면서 Kotlin으로 직접 Frame Generator를 만들고 생성한 프레임을 Serial 통신으로 전송해 계측기와 통신하는 방식으로 테스트했습니다.

당시 제가 조사했을 때는 오픈소스 Modbus 라이브러리 대부분이 프로토콜과 전송을 함께 묶어 구성하고 있었습니다. 아마 사용 편의성을 위한 설계였겠지만 개인적으로는 프로토콜과 전송 계층은 분리하는 것이 더 유연하다고 생각했습니다.

예를 들어 Modbus TCP를 사용하면서 소켓 통신에 Netty를 적용하고 싶다면 전송 계층까지 포함된 라이브러리를 사용하는 순간 라이브러리가 제공하는 방식에 맞춰야 합니다. 반대로 Modbus의 Frame 생성과 파싱만 제공한다면 전송 방식은 애플리케이션에서 자유롭게 선택할 수 있습니다.

그래서 이번 프로젝트에서는 Modbus 자체의 Frame을 생성하고 파싱하는 부분만 직접 구현하고 실제 바이트의 송수신은 별도의 Serial 통신 계층에서 담당하도록 분리했습니다.

마치며

처음에는 “한 달에 한 번 현장에 가지 않으면 된다”는 단순한 문제였지만 그 하나를 해결하기 위해 통신, 네트워크, 메시징, 서버까지 고민하게 됐었습니다.

라즈베리파이를 패키징하며 오랜만에 대학 시절의 기억도 떠올랐습니다. 현장에 별도의 PC를 두기에는 부담스럽지만 간단한 데이터 수집과 통신이 필요한 경우라면 라즈베리파이도 충분히 좋은 선택지가 될 수 있다고 생각합니다.

반복되는 작은 업무 하나에서 출발해 여러 이슈를 하나씩 해결해 나갔습니다. 과거 선배는 시켜서 코드만 짜는 코더가 되면 안 된다고 했었습니다. 저도 그 말에 공감합니다. 현재 환경과 자원, 제약을 이해하고 그 안에서 좋은 선택을 찾아가는 과정이 개발자라고 생각합니다.

작은 경험이었지만 뜻깊었고 성취감도 느낄 수 있던 프로젝트였습니다.