팁스비전에서 제공해준 예시 C++ 코드
//M_AUX_IO4에 대한 사용 설정
MdigControl(m_MilDigitizer, M_IO_MODE + M_AUX_IO4, M_OUTPUT);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_STATE + M_AUX_IO4, M_ENABLE);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_ACTIVATION + M_AUX_IO4, M_EDGE_RISING);
//M_AUX_IO5에 대한 사용 설정
MdigControl(m_MilDigitizer, M_IO_MODE + M_AUX_IO5, M_OUTPUT);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_STATE + M_AUX_IO5, M_ENABLE);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_ACTIVATION + M_AUX_IO5, M_EDGE_RISING);
//M_AUX_IO4 ON 상태 변경 OK
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT4, M_ON);
//M_AUX_IO5 ON 상태 변경 NG
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_ON);
//아래는 M_AUX_IO4, M_AUX_IO5 상태 읽어 오는 코드
MIL_INT UserBitState4;
MIL_INT UserBitState5;
MdigInquire(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, &UserBitState4);
MdigInquire(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, &UserBitState5);
if (UserBitState4 == M_OFF)// Specifies that the specified bit is set to off.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT4, M_OFF);
}
else if (UserBitState4 == M_ON)// Specifies that the specified bit is set to on.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT4, M_OFF);
}
if (UserBitState5 == M_OFF)// Specifies that the specified bit is set to off.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_OFF);
}
else if (UserBitState5 == M_ON)// Specifies that the specified bit is set to on.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_OFF);
}
그런데 여기서 우리는 실제로는 OK를 default state으로 사용할 거라서 IO4는 사용할 필요 없이 IO5만 사용하면 된다고 함. 그 대신 edge rising에 반응하기 때문에, 다시 신호를 낮춰주는 작업이 필요하긴 한듯
M_AUX_IO5만 남긴 버전
//M_AUX_IO5에 대한 사용 설정
MdigControl(m_MilDigitizer, M_IO_MODE + M_AUX_IO5, M_OUTPUT);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_STATE + M_AUX_IO5, M_ENABLE);
MdigControl(m_MilDigitizer, M_IO_INTERRUPT_ACTIVATION + M_AUX_IO5, M_EDGE_RISING);
//M_AUX_IO5 ON 상태 변경 NG
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_ON);
//아래는 M_AUX_IO5 상태 읽어 오는 코드
MIL_INT UserBitState5;
if (UserBitState5 == M_OFF)// Specifies that the specified bit is set to off.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_OFF);
}
else if (UserBitState5 == M_ON)// Specifies that the specified bit is set to on.
{
MdigControl(m_MilDigitizer, M_USER_BIT_STATE + M_USER_BIT5, M_OFF);
}
저장 코드와 합친 버전
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Rapixo CXP + 라인스캔 카메라 (고정 N_LINES + 연속 저장 버전) + Aux IO 5 Toggle
- Aux IO 0: 입력 (Trigger) -> 카메라 촬영
- Aux IO 5: 출력 (Output) -> 이미지 저장 시마다 상태 토글 (ON/OFF 반복)
- 펄스당 1라인씩 스캔되고, 총 N_LINES 라인이 쌓이면 2D 이미지(한 프레임)로 저장
- 이 동작을 무한 반복하면서 파일을 계속 저장 (Ctrl+C 로 중단)
"""
import os
import sys
import time
import mil as MIL
# ============================
# 1. 사용자 설정값
# ============================
N_LINES = 1600 # 이번 스캔에서 쌓을 라인 수 (최종 이미지의 높이)
BITS_PER_PIXEL = 24 # 일반적인 단색 카메라면 8
BITS_PER_CHANNEL = 8
DCF_NAME_STR = "CoaxPress_currentstate.dcf" # 사용할 DCF 이름
# 기본 출력 파일 이름 (프레임 번호 붙여서 저장됨)
OUTPUT_PATH = "images/linescan_result.tif"
AUX_INDEX_TRIGGER = 0 # 트리거 입력용 Aux IO 인덱스 (0 -> M_AUX_IO0)
AUX_INDEX_OUTPUT = 5 # 상태 출력용 Aux IO 인덱스 (5 -> M_AUX_IO5)
# 카메라 GenICam 트리거 파라미터
TRIGGER_SELECTOR_VALUE = "LineStart"
TRIGGER_MODE_VALUE = "On"
TRIGGER_SOURCE_VALUE = "LinkTrigger0"
ENABLE_AUX_POLL_DEBUG = False
# MIL.MappControl(MIL.M_DEFAULT, MIL.M_ERROR, MIL.M_PRINT_DISABLE) # 에러 메시지 제거하기 위함이었으나 오히려 이게 에러 발생
# ============================
# 2. 유틸 함수들
# ============================
def poll_aux_io(MilDig, aux_index: int = 0, duration_sec: int = 5):
aux_const = MIL.M_AUX_IO0 + aux_index
last_state = None
t0 = time.time()
print(f"[INFO] Polling AUX IO{aux_index} status for {duration_sec} sec...")
while time.time() - t0 < duration_sec:
state = MIL.MdigInquire(MilDig, MIL.M_IO_STATUS + aux_const, None)
if state != last_state:
print(f"[DBG] AUX IO{aux_index} state changed: {last_state} -> {state}")
last_state = state
time.sleep(0.001) # 1ms
def configure_camera_trigger_genicam(MilDig):
print("[INFO] Configuring camera GenICam trigger features...")
def set_str_feature(name: str, value: str):
try:
MIL.MdigControlFeature(
MilDig,
MIL.M_FEATURE_VALUE, # ControlType
MIL.MIL_TEXT(name), # FeatureName
MIL.M_TYPE_STRING, # UserVarType
MIL.MIL_TEXT(value), # UserVarPtr
)
print(f" - {name} = {value}")
except Exception as e:
print(f"[WARN] Failed to set feature {name} -> {value}: {e}")
set_str_feature("AcquisitionMode", "Continuous")
set_str_feature("TriggerSelector", TRIGGER_SELECTOR_VALUE)
set_str_feature("TriggerMode", TRIGGER_MODE_VALUE)
set_str_feature("TriggerSource", TRIGGER_SOURCE_VALUE)
def configure_digitizer_tl_trigger(MilDig, aux_index: int = 0):
"""
Capture Works 'Digital I/O control' 설정을 코드로 옮긴 것.
"""
print(f"[INFO] Configuring digitizer TL Trigger from Aux IO{aux_index}...")
aux_const = MIL.M_AUX_IO0 + aux_index
# 0) Grab 모드를 비동기/트리거 기반으로 설정
try:
MIL.MdigControl(MilDig, MIL.M_GRAB_MODE, MIL.M_SYNCHRONOUS)
print(" - M_GRAB_MODE = M_SYNCHRONOUS")
except Exception as e:
print("[WARN] M_GRAB_MODE 설정 실패(무시 가능):", e)
# 1) Aux IO n을 입력 모드로 설정
try:
MIL.MdigControl(
MilDig,
MIL.M_IO_MODE + aux_const,
MIL.M_INPUT,
)
print(f" - IO mode: Aux IO{aux_index} -> INPUT")
except Exception as e:
print(f"[WARN] Failed to set IO mode of Aux IO{aux_index} to INPUT: {e}")
# 2) TL Trigger source를 Aux IO n으로 설정
try:
tl_trigger_const = MIL.M_TL_TRIGGER
MIL.MdigControl(
MilDig,
MIL.M_IO_SOURCE + tl_trigger_const,
aux_const,
)
print(f" - IO source: TL Trigger <- Aux IO{aux_index}")
except Exception as e:
print(f"[WARN] Failed to set IO source of TL Trigger from Aux IO{aux_index}: {e}")
tl_trigger_const = MIL.M_TL_TRIGGER
# 3) Interrupt activation: Any edge
try:
MIL.MdigControl(
MilDig,
MIL.M_IO_INTERRUPT_ACTIVATION + tl_trigger_const,
MIL.M_ANY_EDGE,
)
print(" - IO interrupt activation: Any edge")
except Exception as e:
print(f"[WARN] Failed to set IO interrupt activation (Any edge): {e}")
# 4) IO interrupt state: Disable
try:
MIL.MdigControl(
MilDig,
MIL.M_IO_INTERRUPT_STATE + tl_trigger_const,
MIL.M_DISABLE,
)
print(" - IO interrupt state: DISABLE")
except Exception as e:
print(f"[WARN] Failed to set IO interrupt state (DISABLE): {e}")
try:
MIL.MdigControl(
MilDig,
MIL.M_SOURCE_SIZE_Y,
N_LINES,
)
print(f" - Set frame height {N_LINES}")
except Exception as e:
print(f"[WARN] Failed to set frame height: {e}")
def configure_aux_io5_output(MilDig):
"""
Aux IO 5번을 Output 모드로 설정하고, 요청하신 인터럽트 설정을 적용합니다.
"""
print(f"[INFO] Configuring Aux IO{AUX_INDEX_OUTPUT} as OUTPUT...")
aux_out_const = MIL.M_AUX_IO0 + AUX_INDEX_OUTPUT
try:
# 1. Mode 설정: Output
MIL.MdigControl(MilDig, MIL.M_IO_MODE + aux_out_const, MIL.M_OUTPUT)
print(f" - Aux IO{AUX_INDEX_OUTPUT} Mode: OUTPUT")
# 2. Interrupt State: Enable (요청사항)
MIL.MdigControl(MilDig, MIL.M_IO_INTERRUPT_STATE + aux_out_const, MIL.M_ENABLE)
print(f" - Aux IO{AUX_INDEX_OUTPUT} Interrupt State: ENABLE")
# 3. Interrupt Activation: Rising Edge (요청사항)
MIL.MdigControl(MilDig, MIL.M_IO_INTERRUPT_ACTIVATION + aux_out_const, MIL.M_EDGE_RISING)
print(f" - Aux IO{AUX_INDEX_OUTPUT} Interrupt Activation: RISING EDGE")
# 4. 초기 상태: OFF
MIL.MdigControl(MilDig, MIL.M_IO_STATE + aux_out_const, MIL.M_OFF)
print(f" - Aux IO{AUX_INDEX_OUTPUT} Initial State: OFF")
except Exception as e:
print(f"[ERROR] Failed to configure Aux IO{AUX_INDEX_OUTPUT}: {e}")
# ============================
# 3. 메인 함수
# ============================
def main():
print("[INFO] Line-scan capture start (continuous mode with IO toggle)")
MilApp = MIL.M_NULL
MilSys = MIL.M_NULL
MilDig = MIL.M_NULL
MilDest = MIL.M_NULL
try:
# ----------------------------
# 3-1) MIL Application / System / Digitizer 할당
# ----------------------------
MilApp = MIL.MappAlloc(MIL.MIL_TEXT("M_DEFAULT"), MIL.M_DEFAULT, None)
MilSys = MIL.MsysAlloc(MilApp, MIL.M_SYSTEM_DEFAULT, MIL.M_DEFAULT, MIL.M_DEFAULT, None)
DCF_NAME = MIL.MIL_TEXT(DCF_NAME_STR)
MilDig = MIL.MdigAlloc(MilSys, MIL.M_DEFAULT, DCF_NAME, MIL.M_DEFAULT, None)
print(f"[INFO] Digitizer allocated with DCF = {DCF_NAME_STR!r}")
# (옵션) Grab timeout 설정 (10초)
if hasattr(MIL, "M_GRAB_TIMEOUT"):
MIL.MdigControl(MilDig, MIL.M_GRAB_TIMEOUT, 10000)
# ----------------------------
# 3-2) 트리거 및 IO 설정
# ----------------------------
# A. 카메라 및 입력 트리거(Aux 0) 설정
configure_camera_trigger_genicam(MilDig)
configure_digitizer_tl_trigger(MilDig, AUX_INDEX_TRIGGER)
# B. 출력 IO(Aux 5) 설정 (추가된 기능)
configure_aux_io5_output(MilDig)
if ENABLE_AUX_POLL_DEBUG:
poll_aux_io(MilDig, AUX_INDEX_TRIGGER, duration_sec=5)
# ----------------------------
# 3-3) 이미지 사이즈 확인 및 버퍼 할당
# ----------------------------
line_width = MIL.MdigInquire(MilDig, MIL.M_SIZE_X, None)
frame_height = MIL.MdigInquire(MilDig, MIL.M_SIZE_Y, None)
num_bands = MIL.MdigInquire(MilDig, MIL.M_SIZE_BAND, None)
print(f"[INFO] SIZE_X = {line_width}, SIZE_Y = {frame_height}")
MilDest = MIL.MbufAllocColor(
MilSys,
num_bands,
line_width,
frame_height,
BITS_PER_CHANNEL + MIL.M_UNSIGNED,
MIL.M_IMAGE | MIL.M_GRAB,
None
)
# ----------------------------
# 3-4) 연속 Grab 루프
# ----------------------------
root, ext = os.path.splitext(OUTPUT_PATH)
if not ext:
ext = ".tif"
try:
MIL.MdigControl(MilDig, MIL.M_GRAB_STOP, MIL.M_DEFAULT)
time.sleep(0.1)
except:
pass
print("[INFO] Ready to grab frames continuously.")
print(f"[INFO] Trigger: Aux IO{AUX_INDEX_TRIGGER}, Toggle Output: Aux IO{AUX_INDEX_OUTPUT}")
print("[INFO] Press Ctrl+C to stop.\\n")
frame_idx = 0
# Aux IO 5번의 현재 상태 추적용 변수 (초기값 OFF)
current_io5_state = MIL.M_OFF
while True:
frame_idx += 1
print(f"[INFO] ===== Frame {frame_idx} =====")
print(f"[INFO] Waiting for triggers on Aux IO{AUX_INDEX_TRIGGER}...")
# 1. 이미지 그랩 (Aux 0 입력 대기)
try:
MIL.MdigGrab(MilDig, MilDest)
except Exception as e:
print(f"[ERROR] MdigGrab failed at frame {frame_idx}: {e}")
break
# 2. 이미지 저장
out_path = f"{root}_{frame_idx:05d}{ext}"
try:
if os.path.exists(out_path):
os.remove(out_path)
MIL.MbufExport(MIL.MIL_TEXT(out_path), MIL.M_TIFF, MilDest)
print(f"[INFO] Saved: {out_path}")
except Exception as e:
print(f"[ERROR] Failed to save frame {frame_idx}: {e}")
break
# 3. Aux IO 5 상태 토글 (ON <-> OFF)
try:
# 현재 상태의 반대값 계산
next_state = MIL.M_ON if current_io5_state == MIL.M_OFF else MIL.M_OFF
# IO 상태 적용 (M_IO_STATE를 사용하여 출력 제어)
MIL.MdigControl(MilDig, MIL.M_IO_STATE + MIL.M_AUX_IO5, next_state)
# 상태 변수 업데이트 및 로그 출력
current_io5_state = next_state
state_str = "ON" if current_io5_state == MIL.M_ON else "OFF"
print(f"[INFO] Aux IO{AUX_INDEX_OUTPUT} Toggled -> {state_str}")
except Exception as e:
print(f"[ERROR] Failed to toggle Aux IO{AUX_INDEX_OUTPUT}: {e}")
# IO 에러가 나더라도 루프를 계속 돌지 말지 결정 (여기선 로그만 찍고 계속 진행)
except KeyboardInterrupt:
print("\\n[INFO] KeyboardInterrupt received. Stopping...")
except Exception as e:
print("[ERROR] Exception:", e)
finally:
print("[INFO] Freeing MIL resources...")
# 진행 중인 그랩 중단 (혹시 모를 비동기 그랩)
try:
if MilDig not in (None, MIL.M_NULL):
MIL.MdigHalt(MilDig)
print("[INFO] MdigHalt called.")
except Exception as e:
print(f"[WARN] MdigHalt failed or not needed: {e}")
# Digitizer 먼저 free
try:
if MilDig not in (None, MIL.M_NULL):
MIL.MdigFree(MilDig)
print("[INFO] Freed MilDig.")
MilDig = MIL.M_NULL
except Exception as e:
print(f"[WARN] Failed to free MilDig: {e}")
# 그 다음 이미지 버퍼 free
try:
if MilDest not in (None, MIL.M_NULL):
MIL.MbufFree(MilDest)
print("[INFO] Freed MilDest.")
MilDest = MIL.M_NULL
except Exception as e:
print(f"[WARN] Failed to free MilDest: {e}")
# System / App free
try:
if MilSys not in (None, MIL.M_NULL):
MIL.MsysFree(MilSys)
print("[INFO] Freed MilSys.")
MilSys = MIL.M_NULL
except Exception as e:
print(f"[WARN] Failed to free MilSys: {e}")
try:
if MilApp not in (None, MIL.M_NULL):
MIL.MappFree(MilApp)
print("[INFO] Freed MilApp.")
MilApp = MIL.M_NULL
except Exception as e:
print(f"[WARN] Failed to free MilApp: {e}")
print("[INFO] Done.")
if __name__ == "__main__":
if len(sys.argv) == 2:
try:
N_LINES = int(sys.argv[1])
print(f"[INFO] N_LINES overridden: {N_LINES}")
except:
pass
main()