py-alaska 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- py_alaska/SmBlock.py +263 -0
- py_alaska/__init__.py +63 -0
- py_alaska/div_logo.png +0 -0
- py_alaska/gconfig.py +1241 -0
- py_alaska/imi_camera.py +391 -0
- py_alaska/tab_camera.py +730 -0
- py_alaska/task_manager.py +661 -0
- py_alaska/task_monitor.py +1533 -0
- py_alaska/task_performance.py +550 -0
- py_alaska/task_signal.py +238 -0
- py_alaska-0.1.0.dist-info/METADATA +263 -0
- py_alaska-0.1.0.dist-info/RECORD +15 -0
- py_alaska-0.1.0.dist-info/WHEEL +5 -0
- py_alaska-0.1.0.dist-info/licenses/LICENSE +21 -0
- py_alaska-0.1.0.dist-info/top_level.txt +1 -0
py_alaska/imi_camera.py
ADDED
|
@@ -0,0 +1,391 @@
|
|
|
1
|
+
"""IMI 카메라 제어 클래스 (Neptune API)"""
|
|
2
|
+
|
|
3
|
+
from ast import Global
|
|
4
|
+
import ctypes
|
|
5
|
+
from email.mime import image
|
|
6
|
+
import logging
|
|
7
|
+
import multiprocessing
|
|
8
|
+
import os
|
|
9
|
+
import sys
|
|
10
|
+
import time
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
from typing import Optional
|
|
13
|
+
|
|
14
|
+
import cv2
|
|
15
|
+
import numpy as np
|
|
16
|
+
from PySide6.QtCore import QObject, Signal
|
|
17
|
+
from PySide6.QtGui import QImage, QPixmap
|
|
18
|
+
|
|
19
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1])) # Dynamic2 폴더
|
|
20
|
+
|
|
21
|
+
from camera.SmBlock import SmBlock
|
|
22
|
+
from utils.config_log import logger as log
|
|
23
|
+
from ConfigReader import GlobalConfig, banner
|
|
24
|
+
from camera.Neptune_API import *
|
|
25
|
+
from threading import Thread
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class CameraStatus(Thread, QObject):
|
|
29
|
+
image_received = Signal(np.ndarray)
|
|
30
|
+
status_changed = Signal(str)
|
|
31
|
+
def __init__(self):
|
|
32
|
+
QObject.__init__(self)
|
|
33
|
+
Thread.__init__(self)
|
|
34
|
+
self.running = True
|
|
35
|
+
self.smblock = SmBlock(name="SmBlock", shape=(2048,2448, 3), maxsize=100, create=True, lock=multiprocessing.Lock())
|
|
36
|
+
self.rx_image = multiprocessing.Queue()
|
|
37
|
+
self.rx_cmdq = multiprocessing.Queue()
|
|
38
|
+
self.is_connected = False
|
|
39
|
+
self.count_invalid = 0
|
|
40
|
+
self.count_rx = 0
|
|
41
|
+
|
|
42
|
+
def set_value(self, key, value):
|
|
43
|
+
print(f"Set {key} = {value}")
|
|
44
|
+
|
|
45
|
+
def put_cmd(self, cmd):
|
|
46
|
+
self.rx_cmdq.put(cmd)
|
|
47
|
+
|
|
48
|
+
def put_image(self, rx_frame, pImage):
|
|
49
|
+
#print(f"put_image called self.count_rx={self.count_rx} count_invalid={self.count_invalid}")
|
|
50
|
+
if rx_frame is None:
|
|
51
|
+
self.rx_image.put(None)
|
|
52
|
+
return
|
|
53
|
+
image = (cv2.cvtColor(rx_frame.reshape(pImage.uiHeight, pImage.uiWidth), cv2.COLOR_GRAY2BGR)
|
|
54
|
+
if pImage.uiBitDepth != 8
|
|
55
|
+
else cv2.cvtColor(rx_frame.reshape(pImage.uiHeight, pImage.uiWidth, -1), 63))
|
|
56
|
+
index = self.smblock.malloc2(image)
|
|
57
|
+
self.rx_image.put((index, self.count_rx))
|
|
58
|
+
|
|
59
|
+
def put_cmd(self, cmd):
|
|
60
|
+
self.rx_cmdq.put(cmd)
|
|
61
|
+
|
|
62
|
+
def run(self):
|
|
63
|
+
self.running = True
|
|
64
|
+
reason = "normal_exit"
|
|
65
|
+
while self.running:
|
|
66
|
+
if not self.rx_image.empty():
|
|
67
|
+
image_id, frame_id = self.rx_image.get()
|
|
68
|
+
if image_id is None:
|
|
69
|
+
reason = "image_none"
|
|
70
|
+
break
|
|
71
|
+
#print(f"Received index:{image_id} rx={self.count_rx} invalid={self.count_invalid} is_connected = {self.is_connected}")
|
|
72
|
+
image = self.smblock.get(image_id)
|
|
73
|
+
self.image_received.emit(image.copy()) # 빨리 Frame들어오면 문제가 생김
|
|
74
|
+
self.smblock.mfree(image_id)
|
|
75
|
+
if not self.rx_cmdq.empty():
|
|
76
|
+
cmd = self.rx_cmdq.get()
|
|
77
|
+
banner(f"[CAM.SIGANL] <{cmd}> received")
|
|
78
|
+
if cmd == "close":
|
|
79
|
+
self.running = False
|
|
80
|
+
self.is_connected = False
|
|
81
|
+
reason = "cmd_close"
|
|
82
|
+
break
|
|
83
|
+
if cmd == "connected":
|
|
84
|
+
reason = "cmd_connected"
|
|
85
|
+
self.reopen()
|
|
86
|
+
self.is_connected = True
|
|
87
|
+
self.status_changed.emit("connected")
|
|
88
|
+
if cmd == "disconnected":
|
|
89
|
+
reason = "cmd_disconnected"
|
|
90
|
+
self.is_connected = False
|
|
91
|
+
self.status_changed.emit("disconnected")
|
|
92
|
+
time.sleep(0.01)
|
|
93
|
+
banner(f"** CameraSignal thread stopped. reason: {reason} **")
|
|
94
|
+
|
|
95
|
+
@staticmethod
|
|
96
|
+
def numpy_to_pixmap( image: np.ndarray) -> QPixmap:
|
|
97
|
+
if image.ndim == 2:
|
|
98
|
+
height, width = image.shape
|
|
99
|
+
q_image = QImage(image.data, width, height, width, QImage.Format_Grayscale8)
|
|
100
|
+
else:
|
|
101
|
+
height, width, channels = image.shape
|
|
102
|
+
format_map = {3: QImage.Format_BGR888, 4: QImage.Format_RGBA8888}
|
|
103
|
+
if channels not in format_map:
|
|
104
|
+
raise ValueError(f"Unsupported channel number: {channels}")
|
|
105
|
+
q_image = QImage(image.data, width, height, channels * width, format_map[channels])
|
|
106
|
+
|
|
107
|
+
return QPixmap.fromImage(q_image)
|
|
108
|
+
|
|
109
|
+
class imi_camera(CameraStatus):
|
|
110
|
+
"""IMI 카메라 제어 클래스"""
|
|
111
|
+
|
|
112
|
+
DriverInit = False
|
|
113
|
+
HEARTBEAT_TIME = 500
|
|
114
|
+
DEFAULT_MAC = "192.168.1.100"
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def __init__(self):
|
|
118
|
+
super().__init__()
|
|
119
|
+
self.handle = ctypes.c_void_p(0)
|
|
120
|
+
self.mac = self.DEFAULT_MAC
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
log.info("[ImiCamera] 인스턴스 생성")
|
|
124
|
+
|
|
125
|
+
# self.flow_thread = CameraSignal(camera=self)
|
|
126
|
+
|
|
127
|
+
@staticmethod
|
|
128
|
+
def get_mac_list() -> list[str]:
|
|
129
|
+
"""연결된 카메라의 MAC 주소 목록 반환"""
|
|
130
|
+
numberOfCamera = ctypes.c_uint32(0)
|
|
131
|
+
err = ntcGetCameraCount(ctypes.pointer(numberOfCamera))
|
|
132
|
+
# banner(f"get_mac_list: err= {err}, count= {self.numberOfCamera.value}")
|
|
133
|
+
if err != ENeptuneError.NEPTUNE_ERR_Success.value or numberOfCamera.value == 0:
|
|
134
|
+
log.error(f"[get_mac_list] 카메라를 찾을 수 없음 / 관리자권한, 케이블점검 err = {err} count= {numberOfCamera.value}")
|
|
135
|
+
return []
|
|
136
|
+
|
|
137
|
+
cam_count = numberOfCamera.value
|
|
138
|
+
info = (NEPTUNE_CAM_INFO * cam_count)()
|
|
139
|
+
err = ntcGetCameraInfo(info, cam_count)
|
|
140
|
+
|
|
141
|
+
if err != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
142
|
+
log.error(f"[get_mac_list] ntcGetCameraInfo 실패: err={err}")
|
|
143
|
+
return []
|
|
144
|
+
|
|
145
|
+
mac_list = [info[i].strMAC.decode('utf-8') for i in range(cam_count)]
|
|
146
|
+
log.info(f"[get_mac_list] 카메라 {cam_count}대 발견: {mac_list}")
|
|
147
|
+
return mac_list
|
|
148
|
+
|
|
149
|
+
def close(self) -> None:
|
|
150
|
+
banner(f"[CAM.CLOSE] {self.is_connected}")
|
|
151
|
+
if not self.is_connected:
|
|
152
|
+
return
|
|
153
|
+
try:
|
|
154
|
+
self.put_cmd("close")
|
|
155
|
+
ntcClose(self.handle)
|
|
156
|
+
count = 0
|
|
157
|
+
while self.rx_image.qsize() > 0:
|
|
158
|
+
image_id,frame_id = self.rx_image.get()
|
|
159
|
+
self.smblock.mfree(image_id)
|
|
160
|
+
count = count + 1
|
|
161
|
+
banner(f"CAMEAR. CLOSE -------------------------------droped frames: {count}")
|
|
162
|
+
self.put_cmd("disconnected")
|
|
163
|
+
except Exception as e:
|
|
164
|
+
log.exception(f"[ImiCamera] 종료 중 오류: {e}")
|
|
165
|
+
self.put_cmd("disconnected")
|
|
166
|
+
|
|
167
|
+
def open(self) -> bool:
|
|
168
|
+
"""카메라 연결"""
|
|
169
|
+
banner("CAMEAR. OPEN ---------------------------------")
|
|
170
|
+
if self.is_connected:
|
|
171
|
+
log.warning("[ImiCamera] 이미 연결되어 있음")
|
|
172
|
+
return True
|
|
173
|
+
|
|
174
|
+
# 드라이버 초기화
|
|
175
|
+
if not imi_camera.DriverInit:
|
|
176
|
+
if ntcInit() != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
177
|
+
log.error("[ImiCamera] 드라이버 초기화 실패")
|
|
178
|
+
return False
|
|
179
|
+
imi_camera.DriverInit = True
|
|
180
|
+
log.info("[ImiCamera] 드라이버 초기화 완료")
|
|
181
|
+
banner(f"connect: DriverInit= {imi_camera.DriverInit} success")
|
|
182
|
+
|
|
183
|
+
# 기존 핸들 정리
|
|
184
|
+
if self.handle:
|
|
185
|
+
ntcClose(self.handle)
|
|
186
|
+
self.handle = ctypes.c_void_p(0)
|
|
187
|
+
|
|
188
|
+
# 카메라 검색 및 연결
|
|
189
|
+
mac_list = imi_camera.get_mac_list()
|
|
190
|
+
if not mac_list:
|
|
191
|
+
log.error("[ImiCamera] 사용 가능한 카메라가 없음")
|
|
192
|
+
return False
|
|
193
|
+
self.mac = mac_list[0]
|
|
194
|
+
self.reopen()
|
|
195
|
+
|
|
196
|
+
# if ntcOpen(bytes(self.mac, encoding='utf-8'), ctypes.byref(self.handle)) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
197
|
+
# log.error(f"[ImiCamera] ntcOpen 실패: {self.mac}")
|
|
198
|
+
# return False
|
|
199
|
+
|
|
200
|
+
# if ntcSetHeartbeatTime(self.handle, self.HEARTBEAT_TIME) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
201
|
+
# log.error("[ImiCamera] ntcSetHeartbeatTime 실패")
|
|
202
|
+
# return False
|
|
203
|
+
|
|
204
|
+
# self.set_trigger_mode(False) # 처음 패킷막기
|
|
205
|
+
|
|
206
|
+
# # 콜백 설정
|
|
207
|
+
# self.callback_func = ctypes.CFUNCTYPE(None, NEPTUNE_IMAGE, ctypes.c_void_p)(self.RecvFrameCallBack)
|
|
208
|
+
# ntcSetFrameCallback(self.handle, self.callback_func, self.handle)
|
|
209
|
+
|
|
210
|
+
# self.callback_drop_func = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self.RecvFrameDropCallBack)
|
|
211
|
+
# ntcSetFrameDropCallback(self.handle, self.callback_drop_func, self.handle)
|
|
212
|
+
|
|
213
|
+
# self.callback_device_check_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p)(self.RecvDeviceCheckCallBack)
|
|
214
|
+
# ntcSetDeviceCheckCallback(self.callback_device_check_func, self.handle)
|
|
215
|
+
|
|
216
|
+
# # Acquisition 초기화
|
|
217
|
+
# for i, state in enumerate([False, True, False, True]):
|
|
218
|
+
# ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value if state else ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
219
|
+
|
|
220
|
+
self.thread = Thread(target=self.run, daemon=True)
|
|
221
|
+
self.thread.start()
|
|
222
|
+
self.put_cmd("connected")
|
|
223
|
+
log.info(f"[ImiCamera] 카메라 연결 성공: {self.mac}")
|
|
224
|
+
return True
|
|
225
|
+
|
|
226
|
+
def reopen(self):
|
|
227
|
+
banner("CAMEAR. RE-OPEN ---------------------------------")
|
|
228
|
+
|
|
229
|
+
if ntcOpen(bytes(self.mac, encoding='utf-8'), ctypes.byref(self.handle)) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
230
|
+
log.error(f"[ImiCamera] ntcOpen 실패: {self.mac}")
|
|
231
|
+
return False
|
|
232
|
+
|
|
233
|
+
if ntcSetHeartbeatTime(self.handle, self.HEARTBEAT_TIME) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
234
|
+
log.error("[ImiCamera] ntcSetHeartbeatTime 실패")
|
|
235
|
+
return False
|
|
236
|
+
|
|
237
|
+
nExposeure = GlobalConfig.instance().getConfigValue("camera.exposure", 15000)
|
|
238
|
+
exposure = ctypes.c_uint32(nExposeure)
|
|
239
|
+
if ntcSetExposureTime(self.handle, exposure.value) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
240
|
+
log.error(f"[ImiCamera] ntcSetExposureTime 실패")
|
|
241
|
+
return False
|
|
242
|
+
|
|
243
|
+
self.set_trigger_mode(False) # 처음 패킷막기
|
|
244
|
+
|
|
245
|
+
# 콜백 설정
|
|
246
|
+
self.callback_func = ctypes.CFUNCTYPE(None, NEPTUNE_IMAGE, ctypes.c_void_p)(self.RecvFrameCallBack)
|
|
247
|
+
ntcSetFrameCallback(self.handle, self.callback_func, self.handle)
|
|
248
|
+
|
|
249
|
+
self.callback_drop_func = ctypes.CFUNCTYPE(None, ctypes.c_void_p)(self.RecvFrameDropCallBack)
|
|
250
|
+
ntcSetFrameDropCallback(self.handle, self.callback_drop_func, self.handle)
|
|
251
|
+
|
|
252
|
+
self.callback_device_check_func = ctypes.CFUNCTYPE(None, ctypes.c_int, ctypes.c_void_p)(self.RecvDeviceCheckCallBack)
|
|
253
|
+
ntcSetDeviceCheckCallback(self.callback_device_check_func, self.handle)
|
|
254
|
+
|
|
255
|
+
# Acquisition 초기화
|
|
256
|
+
for i, state in enumerate([False, True, False, True]):
|
|
257
|
+
ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value if state else ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
258
|
+
|
|
259
|
+
def set_frame_rate(self, frame_rate: float) -> bool:
|
|
260
|
+
emFPS = ctypes.c_uint32(0)
|
|
261
|
+
dbFPS = ctypes.c_double(frame_rate)
|
|
262
|
+
if ntcSetFrameRate(self.handle, emFPS.value, dbFPS.value) != ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
263
|
+
log.error(f"[ImiCamera] ntcSetFrameRate 실패")
|
|
264
|
+
return False
|
|
265
|
+
|
|
266
|
+
def set_trigger_mode(self, enabled: bool) -> bool:
|
|
267
|
+
self.count_invalid = 0
|
|
268
|
+
self.count_rx = 0
|
|
269
|
+
st_trigger = NEPTUNE_TRIGGER()
|
|
270
|
+
st_trigger.Source = ENeptuneTriggerSource.NEPTUNE_TRIGGER_SOURCE_LINE1.value
|
|
271
|
+
st_trigger.Mode = ENeptuneTriggerMode.NEPTUNE_TRIGGER_MODE_0.value
|
|
272
|
+
st_trigger.Polarity = ENeptunePolarity.NEPTUNE_POLARITY_FALLINGEDGE.value
|
|
273
|
+
if enabled == True:
|
|
274
|
+
st_trigger.OnOff = ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value
|
|
275
|
+
self.set_frame_rate(300.0)
|
|
276
|
+
else :
|
|
277
|
+
st_trigger.OnOff = ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value
|
|
278
|
+
self.set_frame_rate(5.0)
|
|
279
|
+
err = ntcSetTrigger(self.handle, st_trigger)
|
|
280
|
+
if err == ENeptuneError.NEPTUNE_ERR_Success.value:
|
|
281
|
+
self.get_trigger_mode()
|
|
282
|
+
return True
|
|
283
|
+
else:
|
|
284
|
+
banner(f"ntcSetTrigger err={err}")
|
|
285
|
+
return False
|
|
286
|
+
|
|
287
|
+
def get_trigger_mode(self):
|
|
288
|
+
""" 카메라의 트리거 모드 정보 GET """
|
|
289
|
+
neptune_trigger = NEPTUNE_TRIGGER()
|
|
290
|
+
err = ntcGetTrigger(self.handle,ctypes.pointer(neptune_trigger))
|
|
291
|
+
|
|
292
|
+
if err != 0:
|
|
293
|
+
return (False, None)
|
|
294
|
+
else :
|
|
295
|
+
# 성공한 경우 값을 출력
|
|
296
|
+
print(f"Trigger OnOff : {neptune_trigger.OnOff}"
|
|
297
|
+
f" Mode : {neptune_trigger.Mode}"
|
|
298
|
+
f" Polarity : {neptune_trigger.Polarity}"
|
|
299
|
+
f"Trigger nParam : {neptune_trigger.nParam}")
|
|
300
|
+
|
|
301
|
+
if neptune_trigger.OnOff == ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value:
|
|
302
|
+
return (True, True)
|
|
303
|
+
else :
|
|
304
|
+
return (True, False)
|
|
305
|
+
|
|
306
|
+
def set_exposure(self, exposure: int) -> bool:
|
|
307
|
+
banner(f"set_exposure: {exposure}")
|
|
308
|
+
GlobalConfig.instance().setConfigValue("camera.exposure", exposure)
|
|
309
|
+
pui_micro_sec = ctypes.c_uint32(exposure)
|
|
310
|
+
err = ntcSetExposureTime(self.handle, pui_micro_sec)
|
|
311
|
+
if err != 0:
|
|
312
|
+
return False
|
|
313
|
+
return True
|
|
314
|
+
|
|
315
|
+
def get_exposure(self) -> int:
|
|
316
|
+
"""카메라 노출값 반환 (config에서)"""
|
|
317
|
+
return GlobalConfig.instance().getConfigValue("camera.exposure", 15000)
|
|
318
|
+
|
|
319
|
+
def clear_image(self):
|
|
320
|
+
for i, state in enumerate([False, True, False, True]):
|
|
321
|
+
ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value if state else ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
322
|
+
|
|
323
|
+
def stop_stream(self):
|
|
324
|
+
ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
325
|
+
|
|
326
|
+
def start_stream(self):
|
|
327
|
+
for i, state in enumerate([False, True, False, True]):
|
|
328
|
+
ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value if state else ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def RecvDeviceCheckCallBack(self, state: int, pContext: Optional[ctypes.c_void_p] = None) -> None:
|
|
333
|
+
log.warning(f"[ImiCamera] 디바이스 {'추가' if state == 0 else '제거'}")
|
|
334
|
+
if state == 0:
|
|
335
|
+
self.put_cmd("connected")
|
|
336
|
+
for s in [False, True, False, True]:
|
|
337
|
+
ntcSetAcquisition(self.handle, ENeptuneBoolean.NEPTUNE_BOOL_TRUE.value if s else ENeptuneBoolean.NEPTUNE_BOOL_FALSE.value)
|
|
338
|
+
else:
|
|
339
|
+
self.put_cmd("disconnected")
|
|
340
|
+
return None
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def RecvFrameCallBack(self, pImage: NEPTUNE_IMAGE, pContext: Optional[ctypes.c_void_p] = None) -> Optional[np.ndarray]:
|
|
345
|
+
try:
|
|
346
|
+
self.count_rx += 1
|
|
347
|
+
|
|
348
|
+
rx_frame = np.frombuffer(pImage.pData[0:pImage.uiSize], dtype=np.uint8)
|
|
349
|
+
self.put_image(rx_frame, pImage)
|
|
350
|
+
except Exception as e:
|
|
351
|
+
log.exception(f"[ImiCamera] 프레임 처리 오류: {e}")
|
|
352
|
+
return None
|
|
353
|
+
|
|
354
|
+
def RecvFrameDropCallBack(self, pContext: Optional[ctypes.c_void_p] = None) -> None:
|
|
355
|
+
self.count_invalid += 1
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
|
|
359
|
+
def main() -> int:
|
|
360
|
+
"""테스트 메인 함수"""
|
|
361
|
+
logging.basicConfig(
|
|
362
|
+
level=logging.INFO,
|
|
363
|
+
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
camera = imi_camera()
|
|
367
|
+
try:
|
|
368
|
+
if not camera.open():
|
|
369
|
+
log.error("카메라 연결 실패")
|
|
370
|
+
return 1
|
|
371
|
+
camera.set_trigger_mode(True)
|
|
372
|
+
time.sleep(20)
|
|
373
|
+
camera.close()
|
|
374
|
+
log.info(f"최종 수신된 프레임 수: {camera.count_rx}")
|
|
375
|
+
return 0
|
|
376
|
+
|
|
377
|
+
except KeyboardInterrupt:
|
|
378
|
+
log.info("사용자 중단 (Ctrl+C)")
|
|
379
|
+
return 0
|
|
380
|
+
except Exception as e:
|
|
381
|
+
log.error(f"예외 발생: {e}")
|
|
382
|
+
import traceback
|
|
383
|
+
traceback.print_exc()
|
|
384
|
+
return 1
|
|
385
|
+
finally:
|
|
386
|
+
camera.close()
|
|
387
|
+
log.info("프로그램 종료")
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
if __name__ == "__main__":
|
|
391
|
+
exit(main())
|