FUXA SCADA + OpenPLC + 3가지 산업 프로토콜(Modbus/OPC-UA/S7Comm) 통합한 볼 부상 시뮬레이션 학습 프로젝트. Docker Compose 13 컨테이너 + Machbase Neo 히스토리안.
  • Python 69.4%
  • JavaScript 25%
  • Smalltalk 4.1%
  • Dockerfile 1.5%
Find a file
2026-03-17 10:58:15 +09:00
docs README에 Machbase 테이블/대시보드 스크린샷 추가 2026-03-17 10:52:21 +09:00
fuxa FUXA 스크립트에서 mqtt 절대 경로로 require 수정 2026-03-17 10:35:13 +09:00
openplc S7Comm 쓰기 수정: DBWrite 사용, 읽기/쓰기 태그 분리, README 추가 2026-03-11 17:15:55 +09:00
simulator Phase 3 완료: OPC-UA 3~4호기 FUXA 연동 2026-03-11 11:13:03 +09:00
.gitignore .gitignore에 .mcp.json 추가 2026-03-12 18:30:05 +09:00
CLAUDE.md MQTT 표기를 HTTP POST로 수정 (실제 구현 반영) 2026-03-12 18:32:23 +09:00
DEVELOPMENT-PLAN.md README, DEVELOPMENT-PLAN 문서 업데이트 (히스토리안 MQTT 전환 반영) 2026-03-17 10:23:21 +09:00
docker-compose.yml MQTT 표기를 HTTP POST로 수정 (실제 구현 반영) 2026-03-12 18:32:23 +09:00
README.md ball-levitation-sim 링크수정 2026-03-17 10:58:15 +09:00

Ball Levitation SCADA

볼 부상 시뮬레이션 6대 설비를 오픈소스 SCADA(FUXA)로 통합 감시/제어하는 프로젝트.

3종 산업용 프로토콜(Modbus TCP, OPC-UA, S7Comm)을 활용하여 docker compose up 한 줄로 13개 컨테이너가 구동된다.

아키텍처

block-beta
    columns 6

    block:SCADA["SCADA 인프라"]:6
        columns 1
        FUXA["fuxa - FUXA 웹 HMI :18881"]
    end

    space:6

    block:GM["Modbus TCP"]:2
        columns 2
        PLC1["plc-1"] PLC2["plc-2"]
        space space
        SIM1["sim-1"] SIM2["sim-2"]
    end
    block:GO["OPC-UA"]:2
        columns 2
        PLC3["plc-3"] PLC4["plc-4"]
        space space
        SIM3["sim-3"] SIM4["sim-4"]
    end
    block:GS["S7Comm"]:2
        columns 2
        PLC5["plc-5"] PLC6["plc-6"]
        space space
        SIM5["sim-5"] SIM6["sim-6"]
    end

    FUXA -- "Modbus TCP" --> GM
    FUXA -- "OPC-UA" --> GO
    FUXA -- "S7Comm" --> GS
    PLC1 <--> SIM1
    PLC2 <--> SIM2
    PLC3 <--> SIM3
    PLC4 <--> SIM4
    PLC5 <--> SIM5
    PLC6 <--> SIM6

각 PLC는 PID 제어 프로그램(IEC 61131-3 ST)을 실행하며, 시뮬레이터가 볼 부상 물리 모델을 계산하여 Modbus TCP로 PLC와 통신한다.

빠른 시작

최초 실행 (1회)

# 공용 네트워크 생성
docker network create infra-net

# 히스토리안 인프라 기동 (Machbase Neo + Grafana)
cd ../infra && docker compose up -d && cd ../ball-levitation-scada

# Machbase 스키마 생성 (Web UI → http://localhost:5654 → SQL 탭)
# CREATE TAG TABLE SCADA_TAGS (
#     name VARCHAR(100) PRIMARY KEY,
#     time DATETIME BASETIME,
#     value DOUBLE SUMMARIZED
# ) WITH ROLLUP EXTENSION;
# CREATE RETENTION ret_7d_1h DURATION 7 DAY INTERVAL 1 HOUR;
# ALTER TABLE SCADA_TAGS ADD RETENTION ret_7d_1h;

SCADA 스택 실행

# 전체 시스템 기동 (13개 컨테이너)
docker compose up -d --build

# PLC 초기화 대기 (약 30초)
# 시뮬레이터가 PLC 상태를 확인 후 자동으로 프로그램 업로드/실행

# FUXA 장치/태그 등록
uv run --with requests python fuxa/setup_devices.py

# HMI 화면 + 서버 스크립트 구성 (히스토리안 전송 포함)
uv run --with requests python fuxa/setup_views.py

# FUXA 재시작 (장치 연결 활성화)
docker compose restart fuxa

클린 재실행 (SCADA 스택만 초기화)

Machbase 데이터는 유지하면서 SCADA 컨테이너만 완전히 재설치할 때 사용한다.

# SCADA 컨테이너 + 볼륨 삭제 (infra는 건드리지 않음)
docker compose down -v

# 이미지 재빌드 및 기동
docker compose up -d --build

# FUXA 설정 재등록
uv run --with requests python fuxa/setup_devices.py
uv run --with requests python fuxa/setup_views.py
docker compose restart fuxa

컨테이너 구성

컨테이너 역할 이미지 호스트 포트
fuxa SCADA HMI scada-fuxa (패치 적용) 18881
plc-1, plc-2 Modbus TCP PLC openplc-runtime -
plc-3, plc-4 OPC-UA PLC openplc-runtime 4841, 4842
plc-5, plc-6 S7Comm PLC openplc-runtime 1021, 1022
sim-1 ~ sim-6 물리 시뮬레이터 Python (커스텀) -

HMI 화면

개요

6대 설비 상태를 프로토콜별 3열로 배치. PV/SP/MV 값과 프로그레스 바로 한눈에 상태 확인.

개요 화면

조정 + 트렌드

호기당 1행(총 6행) 구성. 좌측에 PV/MV/SP 실시간 차트, 우측에 PID 파라미터(SP/KP/KI/KD) 입력 위젯.

  • 차트 Y축: 0~1000 고정 범위
  • 차트 X축: 최근 5분 롤링 버퍼
  • 데이터 수집: 250ms 주기 (초당 4회)
  • 목표높이 랜덤변경: 우측 상단 토글 스위치 활성화 시, 1분 간격으로 6대 설비의 SP를 200~800 랜덤 정수로 자동 변경 (FUXA 서버 스크립트로 동작, 브라우저 종료 후에도 유지)

조정+트렌드 화면

알람

PV 값 기반 상태 표시 테이블. NORMAL/RISING/HIGH/LOW/VERY HIGH 5단계 색상 코딩.

알람 화면

태그 설계

호기당 6개 태그, 총 36개:

태그 설명 방향
BALL_xx.PV 볼 위치 (0~1000) 읽기
BALL_xx.MV 바람세기 (0~1000) 읽기
BALL_xx.SP 설정값 읽기/쓰기
BALL_xx.KP 비례 게인 읽기/쓰기
BALL_xx.KI 적분 게인 읽기/쓰기
BALL_xx.KD 미분 게인 읽기/쓰기

히스토리안 (Machbase Neo)

별도 Compose(infra/docker-compose.yml)로 운영되는 Machbase Neo에 태그 데이터를 저장한다.

  • 데이터 흐름: FUXA 서버 스크립트 → MQTT Publish → Machbase Neo Tag Table
  • 전송 방식: MQTT v5 append 모드 (포트 5653), 지속 연결로 HTTP보다 오버헤드 적음
  • 샘플링: 250ms 간격, 1초 배치(144행) 전송
  • 테이블: SCADA_TAGS (36개 태그, WITH ROLLUP EXTENSION)
  • 보관: 7일 원시 데이터, 초/분/시 자동 롤업
  • 대시보드: Machbase Neo 내장 대시보드 (호기당 PV/SP/MV 차트)

SCADA_TAGS 테이블

36개 태그 시리즈, 초/분/시 롤업, 7일 retention 정책이 적용된 Tag Table.

SCADA_TAGS 테이블

Machbase 대시보드

6대 설비의 PV/SP/MV를 실시간으로 표시. 최근 5분, 5초 자동 새로고침.

Machbase 대시보드

프로토콜별 설정

프로토콜 호기 PLC 플러그인 FUXA 연결
Modbus TCP 1~2 기본 내장 ModbusTCP, HR 10~15
OPC-UA 3~4 opcua (4840) OPCUA, ns=2;i=1~6
S7Comm 5~6 s7comm (102) SiemensS7, DB20.DBW20~30

디렉토리 구조

ball-levitation-scada/
  docker-compose.yml        # 13개 컨테이너 정의
  openplc/
    pid_control.st          # PLC PID 제어 프로그램 (IEC 61131-3 ST)
    opcua.json              # OPC-UA 플러그인 설정
    s7comm_config.json      # S7Comm 플러그인 설정
    plugins-default.conf    # OpenPLC 플러그인 활성화 설정
  simulator/
    simulator.py            # 볼 부상 물리 시뮬레이터
    Dockerfile
  fuxa/
    Dockerfile              # FUXA 패치 (node-snap7, mqtt.js, DNS resolve)
    s7-index-patched.js     # S7 드라이버 호스트네임 지원 패치
    setup_devices.py        # 장치/태그 자동 등록 스크립트
    setup_views.py          # HMI 화면 자동 구성 스크립트 (FuxaServer 내부 장치, 서버 스크립트 포함)
    random_sp.py            # SP 랜덤 변경 CLI 스크립트 (독립 실행용)
    FUXA-API.md             # FUXA REST API 참조 문서
  docs/
    phase3-opcua-implementation.md
    phase4-s7comm-implementation.md
    phase5-hmi-implementation.md
    phase5-chart-configuration.md
    phase5-htmlinput-debug.md
    phase6-historian-implementation.md
    mqtt-historian-research.md      # Machbase MQTT 브로커 + FUXA 전환 조사
    s7comm-write-investigation.md

기술 스택

  • SCADA: FUXA (오픈소스, Node.js)
  • PLC: OpenPLC Runtime (IEC 61131-3)
  • 시뮬레이터: Python (pymodbus)
  • 컨테이너: Docker Compose
  • 차트 엔진: uPlot (FUXA 내장)

FUXA 패치 사항

FUXA 공식 이미지에 다음 패치를 적용하여 사용한다 (fuxa/Dockerfile):

  1. node-snap7 설치: S7Comm 프로토콜 지원 (기본 미포함)
  2. mqtt.js 설치: Machbase Neo MQTT 브로커로 히스토리안 데이터 전송 (기본 미포함)
  3. S7 드라이버 DNS resolve: snap7이 호스트네임을 지원하지 않아 IP 변환 패치 적용
  4. null-safe 가드: setDeviceConnectionStatus TypeError 방지
  5. S7 DBWrite 패치: WriteMultiVars가 DB 영역에서 데이터 미전송하는 버그 우회 (setValue에서 DBWrite 사용)

관련 프로젝트

  • ball-levitation-sim - PLC 1대 + 시뮬레이터 기반 프로젝트 (이 프로젝트의 원형)