Add files via upload
This commit is contained in:
1 parent
875dc44682
commit
e3bb87d4e7
15 files changed
+3623
-1
No files matched your search
@@ -1 +1,59 @@
|
|||||||
# Smartfactory
|
<img width="1260" height="891" alt="Image" src="https://github.com/user-attachments/assets/600a07b7-64cd-4822-b122-ef01c0ea5cd6" />
|
||||||
|
|
||||||
|
# Intel Geti + AI QC Conveyor
|
||||||
|
인텔 Geti + 인공지능 QC 컨베이어
|
||||||
|
|
||||||
|
|
||||||
|
## 프로젝트 소개
|
||||||
|
인건비 절감을 위한 생산 공장 컨베이어 자동화 장치 입니다.
|
||||||
|
<br>
|
||||||
|
|
||||||
|
## 개발 기간
|
||||||
|
* 25.09.24 - 25.10.22
|
||||||
|
|
||||||
|
### 맴버구성
|
||||||
|
| 이름 | 담당 |
|
||||||
|
|------|------|
|
||||||
|
|서채건|PM, GUI 구성, 코드 통합|
|
||||||
|
|김동현|코드 통합, PPT제작, 발표|
|
||||||
|
|김선곤|GUI 구성, DB 읽기 쓰기 로직|
|
||||||
|
|박서정|데이터셋 학습 및 검증, 스티커 유무 감별|
|
||||||
|
|조민재|데이터셋 학습 및 검증, 오염 비율 계산|
|
||||||
|
|
||||||
|
### 개발 환경
|
||||||
|
- `Intel Geti`
|
||||||
|
- `Python 3.12.3`
|
||||||
|
- `Arduino Mega`
|
||||||
|
- `MariaDB 10`
|
||||||
|
- `Tkinter`
|
||||||
|
|
||||||
|
## 주요 기능
|
||||||
|
#### 모델 1 작동
|
||||||
|
- Pink 병뚜껑 QC 스티커 유무, 오염 유무
|
||||||
|
- No 스티커 or 오염 30% 초과 or Purple 병뚜껑 -> 완전불량
|
||||||
|
- 스티커 and 오염 0% 초과 30% 이하 -> 부분불량
|
||||||
|
- 스티커 and 오염 0% -> 정상
|
||||||
|
|
||||||
|
#### 모델 2 작동
|
||||||
|
- Purple 병뚜껑 QC 스티커 유무, 오염 유무
|
||||||
|
- No 스티커 or 오염 30% 초과 or Pink 병뚜껑 -> 완전불량
|
||||||
|
- 스티커 and 오염 0% 초과 30% 이하 -> 부분불량
|
||||||
|
- 스티커 and 오염 0% -> 정상
|
||||||
|
|
||||||
|
#### 관리자 모드
|
||||||
|
- 수동 컨베이어 제어
|
||||||
|
- 수동 엑츄에이터 조작
|
||||||
|
|
||||||
|
## 파일 구조
|
||||||
|
```
|
||||||
|
team2/
|
||||||
|
├─ iotdemo/
|
||||||
|
│ ├─ __init__.py
|
||||||
|
│ ├─ debounce.py
|
||||||
|
│ ├─ factory_controller.py
|
||||||
|
│ ├─ pins.py
|
||||||
|
│ ├─ pyduino.py
|
||||||
|
│ └─ pyft232.py
|
||||||
|
├─ MODEL_FILE
|
||||||
|
└─ run.py
|
||||||
|
```
|
||||||
@@ -0,0 +1,148 @@
|
|||||||
|
/* Packet Processing */
|
||||||
|
#define PACKET_START (0x55)
|
||||||
|
#define PACKET_END (0xaa)
|
||||||
|
|
||||||
|
#define PACKET_ID_INTERRUPT (0x00)
|
||||||
|
#define PACKET_ID_MAX (199)
|
||||||
|
#define PACKET_ID_ERROR (0xff)
|
||||||
|
|
||||||
|
static byte packet[5] = { PACKET_START, 0xff, 0xff, 0xff, PACKET_END };
|
||||||
|
|
||||||
|
enum pin_functions
|
||||||
|
{
|
||||||
|
BEACON_RED = 2,
|
||||||
|
BEACON_ORANGE,
|
||||||
|
BEACON_GREEN,
|
||||||
|
BEACON_BUZZER,
|
||||||
|
|
||||||
|
ACTUATOR_1, // 6
|
||||||
|
ACTUATOR_2,
|
||||||
|
CONVEYOR_EN,
|
||||||
|
CONVEYOR_PWM,
|
||||||
|
|
||||||
|
START_BUTTON, // 10
|
||||||
|
STOP_BUTTON,
|
||||||
|
PHOTOELECTRIC_SENSOR_1,
|
||||||
|
PHOTOELECTRIC_SENSOR_2,
|
||||||
|
|
||||||
|
LED // 14
|
||||||
|
};
|
||||||
|
|
||||||
|
#define SEND_PACKET(pin, value) \
|
||||||
|
do \
|
||||||
|
{ \
|
||||||
|
packet[2] = (pin); \
|
||||||
|
packet[3] = (value); \
|
||||||
|
Serial.write(packet, sizeof(packet)); \
|
||||||
|
} while (0)
|
||||||
|
|
||||||
|
/* Utility Functions */
|
||||||
|
#define INIT_OUTPUT_PAD(pad, val) \
|
||||||
|
pinMode((pad), OUTPUT); \
|
||||||
|
digitalWrite((pad), (val))
|
||||||
|
|
||||||
|
#define INIT_PWM_PAD(pad, val) \
|
||||||
|
pinMode((pad), OUTPUT); \
|
||||||
|
analogWrite((pad), (val))
|
||||||
|
|
||||||
|
#define CHECK(expr) \
|
||||||
|
if ((expr)) \
|
||||||
|
{ \
|
||||||
|
SEND_PACKET(0xff, 0xff); \
|
||||||
|
return; \
|
||||||
|
}
|
||||||
|
|
||||||
|
/* System Initialization */
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
/* Initialize Outputs */
|
||||||
|
INIT_OUTPUT_PAD(BEACON_RED, HIGH);
|
||||||
|
INIT_OUTPUT_PAD(BEACON_ORANGE, HIGH);
|
||||||
|
INIT_OUTPUT_PAD(BEACON_GREEN, HIGH);
|
||||||
|
|
||||||
|
INIT_OUTPUT_PAD(BEACON_BUZZER, HIGH);
|
||||||
|
|
||||||
|
INIT_OUTPUT_PAD(ACTUATOR_1, HIGH);
|
||||||
|
INIT_OUTPUT_PAD(ACTUATOR_2, HIGH);
|
||||||
|
|
||||||
|
INIT_OUTPUT_PAD(CONVEYOR_EN, HIGH);
|
||||||
|
INIT_PWM_PAD(CONVEYOR_PWM, 0);
|
||||||
|
|
||||||
|
INIT_OUTPUT_PAD(LED, HIGH);
|
||||||
|
|
||||||
|
/* Initialize Inputs */
|
||||||
|
pinMode(START_BUTTON, INPUT_PULLUP);
|
||||||
|
pinMode(STOP_BUTTON, INPUT_PULLUP);
|
||||||
|
pinMode(PHOTOELECTRIC_SENSOR_1, INPUT_PULLUP);
|
||||||
|
pinMode(PHOTOELECTRIC_SENSOR_2, INPUT_PULLUP);
|
||||||
|
|
||||||
|
/* Prepare UART port - 115200 baud rate */
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial)
|
||||||
|
;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Set to 0xff to report initial state */
|
||||||
|
static byte inputStatus[4] = { 0xff, 0xff, 0xff, 0xff };
|
||||||
|
|
||||||
|
static inline compareAndReport(byte pin)
|
||||||
|
{
|
||||||
|
static byte idx;
|
||||||
|
static byte value;
|
||||||
|
|
||||||
|
idx = pin - START_BUTTON;
|
||||||
|
value = digitalRead(pin);
|
||||||
|
if (inputStatus[idx] == value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
inputStatus[idx] = value;
|
||||||
|
SEND_PACKET(pin, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline void polling(void)
|
||||||
|
{
|
||||||
|
packet[1] = PACKET_ID_INTERRUPT;
|
||||||
|
|
||||||
|
compareAndReport(START_BUTTON);
|
||||||
|
compareAndReport(STOP_BUTTON);
|
||||||
|
compareAndReport(PHOTOELECTRIC_SENSOR_1);
|
||||||
|
compareAndReport(PHOTOELECTRIC_SENSOR_2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Actual Main loop */
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
polling();
|
||||||
|
|
||||||
|
/* Packet Processing */
|
||||||
|
if (Serial.available() < 5)
|
||||||
|
return;
|
||||||
|
|
||||||
|
static byte packetId;
|
||||||
|
static byte pin;
|
||||||
|
static byte value;
|
||||||
|
|
||||||
|
packet[1] = PACKET_ID_ERROR;
|
||||||
|
|
||||||
|
CHECK(Serial.read() != PACKET_START);
|
||||||
|
|
||||||
|
packetId = Serial.read();
|
||||||
|
CHECK(packetId == 0 || packetId > PACKET_ID_MAX);
|
||||||
|
|
||||||
|
pin = Serial.read();
|
||||||
|
CHECK(pin < BEACON_RED || pin > LED);
|
||||||
|
|
||||||
|
value = Serial.read();
|
||||||
|
|
||||||
|
CHECK(Serial.read() != PACKET_END);
|
||||||
|
|
||||||
|
/* Run command */
|
||||||
|
packet[1] = packetId;
|
||||||
|
|
||||||
|
if (pin == CONVEYOR_PWM)
|
||||||
|
analogWrite(pin, value);
|
||||||
|
else
|
||||||
|
digitalWrite(pin, !!value);
|
||||||
|
|
||||||
|
SEND_PACKET(pin, value);
|
||||||
|
}
|
||||||
BIN
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 705 KiB |
Binary file not shown.
@@ -0,0 +1,10 @@
|
|||||||
|
"""
|
||||||
|
Smart Factory controller APIs
|
||||||
|
"""
|
||||||
|
|
||||||
|
from .factory_controller import FactoryController
|
||||||
|
from .pins import Inputs, Outputs
|
||||||
|
from .pyduino import PyDuino
|
||||||
|
from .pyft232 import PyFt232
|
||||||
|
|
||||||
|
__all__ = ('FactoryController', 'Inputs', 'Outputs', 'PyDuino', 'PyFt232')
|
||||||
@@ -0,0 +1,164 @@
|
|||||||
|
"""
|
||||||
|
Robust debounce decorator for callbacks like GPIO interrupts.
|
||||||
|
|
||||||
|
Features:
|
||||||
|
- Leading / trailing edge control
|
||||||
|
- Optional max_wait (force-fire) to avoid indefinite deferral
|
||||||
|
- Per-key debounce (e.g., per-pin) for shared handlers
|
||||||
|
- Thread-safe (uses Lock), time.monotonic() for steady-time
|
||||||
|
- Drop-in compatible with previous debounce(wait_sec) usage
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
import time
|
||||||
|
import threading
|
||||||
|
from functools import wraps
|
||||||
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
|
|
||||||
|
def debounce(
|
||||||
|
wait_sec: float,
|
||||||
|
*,
|
||||||
|
leading: bool = False,
|
||||||
|
trailing: bool = True,
|
||||||
|
max_wait: Optional[float] = None,
|
||||||
|
key: Optional[Callable[..., Any]] = None,
|
||||||
|
clock: Callable[[], float] = time.monotonic,
|
||||||
|
):
|
||||||
|
"""
|
||||||
|
Debounce a function so that it’s not called too frequently.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
wait_sec: Quiet period length. Calls within this window are coalesced.
|
||||||
|
leading: If True, call immediately on the first call in a burst.
|
||||||
|
trailing: If True, call after the burst “settles” for wait_sec.
|
||||||
|
max_wait: Force a call if this many seconds have elapsed since the
|
||||||
|
last *invocation* (helps for steady streams).
|
||||||
|
key: Per-key debounce. A function that maps (*args, **kwargs) to a
|
||||||
|
hashable key (e.g., lambda pin, *_: pin). If None, a single
|
||||||
|
global bucket is used.
|
||||||
|
clock: Time source; default is time.monotonic().
|
||||||
|
|
||||||
|
Notes:
|
||||||
|
- If both leading and trailing are True:
|
||||||
|
* first call in a burst fires immediately,
|
||||||
|
* further calls delay a trailing fire until wait_sec after the last call,
|
||||||
|
* but a trailing fire is skipped if there were no calls after the leading fire.
|
||||||
|
- If only leading is True => at most one call per burst, immediate.
|
||||||
|
- If only trailing is True => only the last call args are used.
|
||||||
|
- If max_wait is set, a call is forced at most every max_wait seconds.
|
||||||
|
"""
|
||||||
|
if not leading and not trailing:
|
||||||
|
raise ValueError("At least one of leading or trailing must be True.")
|
||||||
|
|
||||||
|
class _Bucket:
|
||||||
|
__slots__ = (
|
||||||
|
"lock", "timer", "last_invoke", "last_call",
|
||||||
|
"pending", "pending_args", "pending_kwargs",
|
||||||
|
)
|
||||||
|
def __init__(self):
|
||||||
|
self.lock = threading.Lock()
|
||||||
|
self.timer: Optional[threading.Timer] = None
|
||||||
|
self.last_invoke: Optional[float] = None # last actual function call time
|
||||||
|
self.last_call: Optional[float] = None # last wrapper call time
|
||||||
|
self.pending: bool = False
|
||||||
|
self.pending_args = ()
|
||||||
|
self.pending_kwargs = {}
|
||||||
|
|
||||||
|
buckets = {}
|
||||||
|
buckets_lock = threading.Lock()
|
||||||
|
|
||||||
|
def _get_bucket(k: Any) -> _Bucket:
|
||||||
|
with buckets_lock:
|
||||||
|
b = buckets.get(k)
|
||||||
|
if b is None:
|
||||||
|
b = _Bucket()
|
||||||
|
buckets[k] = b
|
||||||
|
return b
|
||||||
|
|
||||||
|
def decorator(fn: Callable):
|
||||||
|
@wraps(fn)
|
||||||
|
def wrapper(*args, **kwargs):
|
||||||
|
# derive bucket key
|
||||||
|
k = key(*args, **kwargs) if key else None
|
||||||
|
b = _get_bucket(k)
|
||||||
|
now = clock()
|
||||||
|
|
||||||
|
def _invoke_locked():
|
||||||
|
"""Call the function with the latest pending args/kwargs.
|
||||||
|
b.lock must be held by caller."""
|
||||||
|
b.last_invoke = clock()
|
||||||
|
b.pending = False
|
||||||
|
call_args = b.pending_args
|
||||||
|
call_kwargs = b.pending_kwargs
|
||||||
|
# release the lock during the user call to avoid deadlocks
|
||||||
|
b.lock.release()
|
||||||
|
try:
|
||||||
|
return fn(*call_args, **call_kwargs)
|
||||||
|
finally:
|
||||||
|
b.lock.acquire()
|
||||||
|
|
||||||
|
def _schedule_trailing():
|
||||||
|
"""(Re)schedule trailing call timer from 'now'."""
|
||||||
|
if b.timer:
|
||||||
|
b.timer.cancel()
|
||||||
|
b.timer = threading.Timer(wait_sec, _trailing_fire)
|
||||||
|
b.timer.daemon = True
|
||||||
|
b.timer.start()
|
||||||
|
|
||||||
|
def _trailing_fire():
|
||||||
|
with b.lock:
|
||||||
|
# fire only if something is pending
|
||||||
|
if not b.pending:
|
||||||
|
return
|
||||||
|
# if max_wait is set and we haven't exceeded it yet AND there
|
||||||
|
# were no new calls, still trailing should run because timer fired
|
||||||
|
_invoke_locked()
|
||||||
|
# after trailing fire, clear timer
|
||||||
|
t = b.timer
|
||||||
|
b.timer = None
|
||||||
|
if t:
|
||||||
|
try:
|
||||||
|
t.cancel()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
with b.lock:
|
||||||
|
b.last_call = now
|
||||||
|
b.pending = True
|
||||||
|
b.pending_args = args
|
||||||
|
b.pending_kwargs = kwargs
|
||||||
|
|
||||||
|
call_now = False
|
||||||
|
|
||||||
|
# Decide leading fire
|
||||||
|
if leading:
|
||||||
|
if b.last_invoke is None:
|
||||||
|
# first ever
|
||||||
|
call_now = True
|
||||||
|
else:
|
||||||
|
# if enough time has passed since last_invoke -> new burst
|
||||||
|
if (now - b.last_invoke) >= wait_sec and (b.timer is None):
|
||||||
|
call_now = True
|
||||||
|
|
||||||
|
# Decide max_wait forced fire (never conflict with call_now)
|
||||||
|
if (not call_now) and max_wait is not None and b.last_invoke is not None:
|
||||||
|
if (now - b.last_invoke) >= max_wait:
|
||||||
|
call_now = True
|
||||||
|
|
||||||
|
result = None
|
||||||
|
if call_now:
|
||||||
|
result = _invoke_locked()
|
||||||
|
# after a leading/max_wait fire, we may still want trailing if more
|
||||||
|
# calls keep arriving; re-arm the trailing timer if trailing is True
|
||||||
|
if trailing:
|
||||||
|
_schedule_trailing()
|
||||||
|
else:
|
||||||
|
# no immediate call; manage trailing
|
||||||
|
if trailing:
|
||||||
|
_schedule_trailing()
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
return wrapper
|
||||||
|
return decorator
|
||||||
@@ -0,0 +1,250 @@
|
|||||||
|
"""
|
||||||
|
Smart Factory HW module controller
|
||||||
|
"""
|
||||||
|
|
||||||
|
from logging import getLogger
|
||||||
|
from os import listdir
|
||||||
|
from sys import platform
|
||||||
|
from time import sleep
|
||||||
|
from typing import Optional, Union
|
||||||
|
|
||||||
|
from .debounce import debounce
|
||||||
|
from .pins import Inputs, Outputs
|
||||||
|
from .pyduino import PyDuino
|
||||||
|
from .pyft232 import PyFt232
|
||||||
|
|
||||||
|
__all__ = ('FactoryController', )
|
||||||
|
|
||||||
|
|
||||||
|
class FactoryController:
|
||||||
|
"""
|
||||||
|
Factory HW controller class
|
||||||
|
"""
|
||||||
|
|
||||||
|
DEV_ON = False
|
||||||
|
DEV_OFF = True
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
port: Optional[Union[str, int]] = None,
|
||||||
|
*,
|
||||||
|
debug: bool = True):
|
||||||
|
self.debug = debug
|
||||||
|
if self.debug:
|
||||||
|
self.logger = getLogger('CONTROLLER')
|
||||||
|
|
||||||
|
self.__force_stop = False
|
||||||
|
|
||||||
|
self.__device = None
|
||||||
|
self.__device_name = None
|
||||||
|
|
||||||
|
# open device
|
||||||
|
self.port = self.__detect_serial(port if port is not None else -1)
|
||||||
|
if self.port is not None and 'ttyUSB' in self.port:
|
||||||
|
try:
|
||||||
|
self.__device = PyFt232(self.port, debug=debug)
|
||||||
|
self.__device_name = 'ft232'
|
||||||
|
except Exception:
|
||||||
|
self.__device = None
|
||||||
|
else:
|
||||||
|
try:
|
||||||
|
self.__device = PyDuino(self.port, debug=debug)
|
||||||
|
self.__device_name = 'arduino'
|
||||||
|
except Exception:
|
||||||
|
self.__device = None
|
||||||
|
|
||||||
|
# Arduino beacon indicator
|
||||||
|
self.red = False
|
||||||
|
self.orange = False
|
||||||
|
self.green = False
|
||||||
|
|
||||||
|
self.led = False
|
||||||
|
|
||||||
|
self.defect_sensor_status = False
|
||||||
|
self.color_sensor_status = False
|
||||||
|
|
||||||
|
# interrupt handler
|
||||||
|
if not self.is_dummy:
|
||||||
|
self.__device.watch(Inputs.START_BUTTON,
|
||||||
|
self.__button_interrupt)
|
||||||
|
self.__device.watch(Inputs.STOP_BUTTON,
|
||||||
|
self.__button_interrupt)
|
||||||
|
|
||||||
|
self.__device.watch(Inputs.PHOTOELECTRIC_SENSOR_1,
|
||||||
|
self.__sensor_interrupt)
|
||||||
|
self.__device.watch(Inputs.PHOTOELECTRIC_SENSOR_2,
|
||||||
|
self.__sensor_interrupt)
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info(
|
||||||
|
f'use {"Dummy" if self.is_dummy else "Arduino"} Controller')
|
||||||
|
|
||||||
|
def __detect_serial(self, port):
|
||||||
|
if isinstance(port, str):
|
||||||
|
return port
|
||||||
|
|
||||||
|
if not isinstance(port, int):
|
||||||
|
raise RuntimeError(
|
||||||
|
f'Invalid port argument type: {port} - {type(port)}')
|
||||||
|
|
||||||
|
if platform in {'win32', 'cygwin'}:
|
||||||
|
return f'COM{port}'
|
||||||
|
|
||||||
|
if not platform.startswith('linux'):
|
||||||
|
raise RuntimeError('Not supported OS')
|
||||||
|
|
||||||
|
if port == -1:
|
||||||
|
for path in listdir("/dev"):
|
||||||
|
if path.startswith("ttyACM"):
|
||||||
|
return f"/dev/{path}"
|
||||||
|
else:
|
||||||
|
return f'/dev/ttyACM{port}'
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
if not self.is_dummy:
|
||||||
|
self.system_start()
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
if not self.is_dummy:
|
||||||
|
self.system_stop()
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __set(self, pin, value):
|
||||||
|
if self.is_dummy or self.__force_stop:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__device.set(pin, value)
|
||||||
|
|
||||||
|
def __get(self, pin):
|
||||||
|
if self.is_dummy:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return self.__device.get(pin)
|
||||||
|
|
||||||
|
def __led(self, pin, on):
|
||||||
|
if self.__device_name == 'arduino':
|
||||||
|
self.__set(
|
||||||
|
pin,
|
||||||
|
FactoryController.DEV_ON if on else FactoryController.DEV_OFF)
|
||||||
|
|
||||||
|
def __actuator(self, actuator_id):
|
||||||
|
if self.__device_name == 'arduino':
|
||||||
|
self.__set(actuator_id, FactoryController.DEV_ON)
|
||||||
|
sleep(0.1)
|
||||||
|
self.__set(actuator_id, FactoryController.DEV_OFF)
|
||||||
|
elif self.__device_name == 'ft232':
|
||||||
|
self.__set(PyFt232.PKT_CMD_DETECTION, actuator_id)
|
||||||
|
sleep(0.5)
|
||||||
|
self.__set(PyFt232.PKT_CMD_DETECTION, PyFt232.PKT_CMD_DETECTION_0)
|
||||||
|
|
||||||
|
@debounce(0.1)
|
||||||
|
def __button_interrupt(self, pin: int, value: int, *_):
|
||||||
|
if value != 0 or self.__force_stop:
|
||||||
|
return
|
||||||
|
|
||||||
|
if pin == Inputs.START_BUTTON:
|
||||||
|
self.system_start()
|
||||||
|
|
||||||
|
elif pin == Inputs.STOP_BUTTON:
|
||||||
|
self.system_stop()
|
||||||
|
|
||||||
|
@debounce(0.3)
|
||||||
|
def __sensor_interrupt(self, pin: int, value: int, *_):
|
||||||
|
status = value == 0
|
||||||
|
|
||||||
|
if pin == Inputs.PHOTOELECTRIC_SENSOR_1:
|
||||||
|
self.defect_sensor_status = status
|
||||||
|
|
||||||
|
elif pin == Inputs.PHOTOELECTRIC_SENSOR_2:
|
||||||
|
self.color_sensor_status = status
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# Public properties
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_dummy(self):
|
||||||
|
return self.__device is None
|
||||||
|
|
||||||
|
@property
|
||||||
|
def red(self):
|
||||||
|
return bool(self.__get(Outputs.BEACON_RED))
|
||||||
|
|
||||||
|
@red.setter
|
||||||
|
def red(self, on):
|
||||||
|
self.__led(Outputs.BEACON_RED, on)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def orange(self):
|
||||||
|
return bool(self.__get(Outputs.BEACON_ORANGE))
|
||||||
|
|
||||||
|
@orange.setter
|
||||||
|
def orange(self, on):
|
||||||
|
self.__led(Outputs.BEACON_ORANGE, on)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def green(self):
|
||||||
|
return bool(self.__get(Outputs.BEACON_GREEN))
|
||||||
|
|
||||||
|
@green.setter
|
||||||
|
def green(self, on):
|
||||||
|
self.__led(Outputs.BEACON_GREEN, on)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def led(self):
|
||||||
|
return bool(self.__get(Outputs.LED))
|
||||||
|
|
||||||
|
@led.setter
|
||||||
|
def led(self, on):
|
||||||
|
self.__led(Outputs.LED, on)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def conveyor(self):
|
||||||
|
return bool(self.__get(Outputs.CONVEYOR_EN))
|
||||||
|
|
||||||
|
@conveyor.setter
|
||||||
|
def conveyor(self, on):
|
||||||
|
if on:
|
||||||
|
self.__set(Outputs.CONVEYOR_EN, FactoryController.DEV_ON)
|
||||||
|
self.__set(Outputs.CONVEYOR_PWM, 255)
|
||||||
|
else:
|
||||||
|
self.__set(Outputs.CONVEYOR_PWM, 0)
|
||||||
|
self.__set(Outputs.CONVEYOR_EN, FactoryController.DEV_OFF)
|
||||||
|
|
||||||
|
def push_actuator(self, num):
|
||||||
|
if self.__device_name == 'arduino':
|
||||||
|
if num == 1:
|
||||||
|
self.__actuator(Outputs.ACTUATOR_1)
|
||||||
|
else:
|
||||||
|
self.__actuator(Outputs.ACTUATOR_2)
|
||||||
|
elif self.__device_name == 'ft232':
|
||||||
|
self.__actuator(num)
|
||||||
|
|
||||||
|
###########################################################################
|
||||||
|
# Public methods
|
||||||
|
|
||||||
|
def system_start(self) -> None:
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info("Start System")
|
||||||
|
|
||||||
|
self.conveyor = False
|
||||||
|
|
||||||
|
def system_stop(self) -> None:
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info("Stop System")
|
||||||
|
|
||||||
|
self.conveyor = True
|
||||||
|
|
||||||
|
def close(self):
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info("Shutdown System")
|
||||||
|
|
||||||
|
self.__force_stop = True
|
||||||
|
if self.is_dummy:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__device.close()
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
"""
|
||||||
|
Input / Output Pin map for the smart factory demo kit
|
||||||
|
"""
|
||||||
|
from enum import IntEnum
|
||||||
|
|
||||||
|
|
||||||
|
class Outputs(IntEnum):
|
||||||
|
BEACON_RED = 2
|
||||||
|
BEACON_ORANGE = 3
|
||||||
|
BEACON_GREEN = 4
|
||||||
|
|
||||||
|
BEACON_BUZZER = 5
|
||||||
|
|
||||||
|
ACTUATOR_1 = 6
|
||||||
|
ACTUATOR_2 = 7
|
||||||
|
|
||||||
|
CONVEYOR_EN = 8
|
||||||
|
CONVEYOR_PWM = 9
|
||||||
|
|
||||||
|
LED = 14
|
||||||
|
|
||||||
|
|
||||||
|
class Inputs(IntEnum):
|
||||||
|
START_BUTTON = 10
|
||||||
|
STOP_BUTTON = 11
|
||||||
|
PHOTOELECTRIC_SENSOR_1 = 12
|
||||||
|
PHOTOELECTRIC_SENSOR_2 = 13
|
||||||
@@ -0,0 +1,294 @@
|
|||||||
|
"""
|
||||||
|
Raw level arduino GPIO controll module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from logging import getLogger
|
||||||
|
from queue import Queue
|
||||||
|
from threading import Condition, Lock, Thread
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
from serial import Serial
|
||||||
|
|
||||||
|
__all__ = ('PyDuino', )
|
||||||
|
|
||||||
|
|
||||||
|
def _dummy(*_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PyDuino:
|
||||||
|
"""
|
||||||
|
Raw level arduino GPIO controll class
|
||||||
|
"""
|
||||||
|
|
||||||
|
PACKET_START = 0x55
|
||||||
|
PACKET_END = 0xaa
|
||||||
|
|
||||||
|
PACKET_ID_INTERRUPT = 0
|
||||||
|
PACKET_ID_MAX = 199
|
||||||
|
PACKET_ID_ERROR = 0xff
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
port: str,
|
||||||
|
baudrate: int = 115200,
|
||||||
|
*,
|
||||||
|
debug: bool = False):
|
||||||
|
self.debug = debug
|
||||||
|
if self.debug:
|
||||||
|
self.logger = getLogger('PYDUINO')
|
||||||
|
self.logger.info("serial port %s (%d)", port, baudrate)
|
||||||
|
|
||||||
|
self.__arduino = None
|
||||||
|
self.__stop_requested = False # to support graceful exit
|
||||||
|
self.__force_stop = False
|
||||||
|
self.__watcher = {}
|
||||||
|
self.__values = [1] * 70 # Pin status
|
||||||
|
for idx in (0, 1):
|
||||||
|
self.__values[idx] = 0xff # NC
|
||||||
|
|
||||||
|
# init arduio
|
||||||
|
arduino = Serial(port=port, baudrate=baudrate, timeout=1)
|
||||||
|
arduino.reset_input_buffer()
|
||||||
|
arduino.reset_output_buffer()
|
||||||
|
self.__arduino = arduino
|
||||||
|
|
||||||
|
self.__init_cv = Condition()
|
||||||
|
self.__init_input_pins = (10, 11, 12, 13)
|
||||||
|
self.__init_count = 0
|
||||||
|
|
||||||
|
self.__rx = Thread(target=self.__receiver)
|
||||||
|
self.__rx.name = 'Arduino Rx'
|
||||||
|
self.__rx.start()
|
||||||
|
|
||||||
|
self.__init()
|
||||||
|
|
||||||
|
self.__tx_lock = Lock()
|
||||||
|
self.__tx_cv = Condition()
|
||||||
|
self.__tx_queue = Queue()
|
||||||
|
self.__tx_packet_id = 1
|
||||||
|
self.__tx = Thread(target=self.__transmitter)
|
||||||
|
self.__tx.name = 'Arduino Tx'
|
||||||
|
self.__tx.start()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __wait_for_input_status(self, pin: int, value: int, _):
|
||||||
|
self.__values[pin] = value
|
||||||
|
|
||||||
|
with self.__init_cv:
|
||||||
|
self.__init_count += 1
|
||||||
|
if self.__init_count == len(self.__init_input_pins):
|
||||||
|
self.__init_cv.notify()
|
||||||
|
|
||||||
|
def __init(self):
|
||||||
|
with self.__init_cv:
|
||||||
|
self.__init_cv.wait()
|
||||||
|
|
||||||
|
for idx in self.__init_input_pins:
|
||||||
|
self.watch(idx)
|
||||||
|
|
||||||
|
# pyduino.py - inside class PyDuino
|
||||||
|
|
||||||
|
def __transmitter(self):
|
||||||
|
arduino = self.__arduino
|
||||||
|
|
||||||
|
while True:
|
||||||
|
packet = self.__tx_queue.get()
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info(packet)
|
||||||
|
|
||||||
|
pin, value, packet_id = packet
|
||||||
|
self.__tx_queue.task_done()
|
||||||
|
if pin is None:
|
||||||
|
break # End of Tx
|
||||||
|
|
||||||
|
pin = int(pin) & 0xff
|
||||||
|
value = int(value) & 0xff
|
||||||
|
|
||||||
|
try:
|
||||||
|
arduino.write(bytes([
|
||||||
|
PyDuino.PACKET_START, packet_id, pin, value,
|
||||||
|
PyDuino.PACKET_END
|
||||||
|
]))
|
||||||
|
except Exception as e:
|
||||||
|
# 치명 오류 시: 강제 정지하고 Tx 종료
|
||||||
|
try:
|
||||||
|
from serial import SerialException
|
||||||
|
except Exception:
|
||||||
|
SerialException = Exception
|
||||||
|
if isinstance(e, (SerialException, OSError)):
|
||||||
|
if self.debug:
|
||||||
|
try: self.logger.warning("Tx write failed / serial down: %s", e)
|
||||||
|
except Exception: pass
|
||||||
|
self.__force_stop = True
|
||||||
|
# set()에서 wait중일 수 있으니 깨워준다
|
||||||
|
try:
|
||||||
|
with self.__tx_cv:
|
||||||
|
self.__tx_cv.notify_all()
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
break
|
||||||
|
# 기타 오류는 약간 쉬고 재시도(선택)
|
||||||
|
import time; time.sleep(0.02)
|
||||||
|
|
||||||
|
# Rx Thread
|
||||||
|
def __receiver(self):
|
||||||
|
import time
|
||||||
|
# 초기 입력핀 감시 등록(기존 로직 유지)
|
||||||
|
for idx in self.__init_input_pins:
|
||||||
|
self.__values[idx] = 0xff
|
||||||
|
self.watch(idx, self.__wait_for_input_status)
|
||||||
|
|
||||||
|
while self.__force_stop is False:
|
||||||
|
try:
|
||||||
|
packet = self.__receive_packet()
|
||||||
|
except Exception as e:
|
||||||
|
# 시리얼 분리/중복접근 등 치명 오류 → 1회 로깅 후 조용히 종료
|
||||||
|
try:
|
||||||
|
from serial import SerialException
|
||||||
|
except Exception:
|
||||||
|
SerialException = Exception
|
||||||
|
if isinstance(e, (SerialException, OSError)):
|
||||||
|
if self.debug:
|
||||||
|
try: self.logger.warning("Serial disconnected/busy: %s", e)
|
||||||
|
except Exception: pass
|
||||||
|
# 상태 플래그(옵션)
|
||||||
|
try: self._connected = False
|
||||||
|
except Exception: pass
|
||||||
|
break
|
||||||
|
# 기타 오류는 소프트 리트라이
|
||||||
|
time.sleep(0.02)
|
||||||
|
continue
|
||||||
|
|
||||||
|
if packet is None:
|
||||||
|
# 타임아웃/빈 읽기 → 살짝 쉬고 계속
|
||||||
|
time.sleep(0.02)
|
||||||
|
continue
|
||||||
|
|
||||||
|
# 정상 패킷 처리
|
||||||
|
pin, value, packet_id = packet
|
||||||
|
self.__values[pin] = value
|
||||||
|
|
||||||
|
if packet_id == PyDuino.PACKET_ID_INTERRUPT:
|
||||||
|
self.__watcher.get(pin, _dummy)(*packet)
|
||||||
|
else:
|
||||||
|
with self.__tx_cv:
|
||||||
|
self.__tx_cv.notify()
|
||||||
|
|
||||||
|
def __receive_packet(self):
|
||||||
|
arduino = self.__arduino
|
||||||
|
|
||||||
|
# 안전 타임아웃 보정(0/None이면 짧게 설정)
|
||||||
|
try:
|
||||||
|
if getattr(arduino, "timeout", None) in (None, 0):
|
||||||
|
arduino.timeout = 0.2
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
# 첫 바이트 읽기
|
||||||
|
try:
|
||||||
|
data = arduino.read()
|
||||||
|
except Exception:
|
||||||
|
# 상위에서 예외 처리
|
||||||
|
raise
|
||||||
|
if not data or data[0] != PyDuino.PACKET_START:
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 나머지 4바이트 조립(타임아웃이면 None 반환)
|
||||||
|
packet = []
|
||||||
|
while len(packet) != 4:
|
||||||
|
try:
|
||||||
|
data = arduino.read()
|
||||||
|
except Exception:
|
||||||
|
raise
|
||||||
|
if not data:
|
||||||
|
return None # Timeout → 상위 루프가 부드럽게 스킵
|
||||||
|
packet.append(data[0])
|
||||||
|
|
||||||
|
packet_id, pin, value, packet_end = packet
|
||||||
|
if packet_id < 0 or packet_id > PyDuino.PACKET_ID_ERROR:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if pin < 0 or pin > len(self.__values):
|
||||||
|
return None
|
||||||
|
|
||||||
|
if packet_end != PyDuino.PACKET_END:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return (pin, value, packet_id)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""
|
||||||
|
Close existing arduino connection
|
||||||
|
"""
|
||||||
|
if self.__arduino is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__stop_requested = True
|
||||||
|
if self.__force_stop:
|
||||||
|
return
|
||||||
|
|
||||||
|
# post end of tx marker
|
||||||
|
self.__tx_queue.put((None, None, None))
|
||||||
|
self.__tx.join()
|
||||||
|
|
||||||
|
# Stop Rx
|
||||||
|
self.__force_stop = True
|
||||||
|
self.__rx.join()
|
||||||
|
|
||||||
|
# close serial
|
||||||
|
self.__arduino.close()
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info("stopped")
|
||||||
|
|
||||||
|
# pyduino.py - inside class PyDuino
|
||||||
|
|
||||||
|
def set(self, pin: int, value: int) -> bool:
|
||||||
|
"""
|
||||||
|
Set arduino pin with given value
|
||||||
|
"""
|
||||||
|
if self.__stop_requested or self.__force_stop:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with self.__tx_lock:
|
||||||
|
packet_id = self.__tx_packet_id
|
||||||
|
|
||||||
|
ok = False
|
||||||
|
with self.__tx_cv:
|
||||||
|
self.__tx_queue.put((pin, value, packet_id))
|
||||||
|
# wait()에 타임아웃 추가 (예: 0.5s)
|
||||||
|
self.__tx_cv.wait(timeout=0.5)
|
||||||
|
ok = not self.__force_stop # Rx/Tx가 죽었으면 False
|
||||||
|
|
||||||
|
self.__tx_packet_id += 1
|
||||||
|
if self.__tx_packet_id == PyDuino.PACKET_ID_MAX:
|
||||||
|
self.__tx_packet_id = 1
|
||||||
|
|
||||||
|
return ok
|
||||||
|
|
||||||
|
def get(self, pin: int) -> int:
|
||||||
|
"""
|
||||||
|
Get current arduino pin value from proxy
|
||||||
|
"""
|
||||||
|
return self.__values[pin]
|
||||||
|
|
||||||
|
def watch(
|
||||||
|
self,
|
||||||
|
pin: int,
|
||||||
|
callback: Optional[Callable[[int, int, int],
|
||||||
|
None]] = None) -> None:
|
||||||
|
"""
|
||||||
|
Set callback function for given pin
|
||||||
|
"""
|
||||||
|
if callback is None:
|
||||||
|
callback = _dummy
|
||||||
|
|
||||||
|
self.__watcher[pin] = callback
|
||||||
@@ -0,0 +1,213 @@
|
|||||||
|
"""
|
||||||
|
Raw level FT232 controll module
|
||||||
|
"""
|
||||||
|
|
||||||
|
from logging import getLogger
|
||||||
|
from queue import Queue
|
||||||
|
from threading import Condition, Lock, Thread
|
||||||
|
from typing import Callable, Optional
|
||||||
|
|
||||||
|
from serial import Serial
|
||||||
|
|
||||||
|
__all__ = ('PyFt232', )
|
||||||
|
|
||||||
|
|
||||||
|
def _dummy(*_):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
class PyFt232:
|
||||||
|
"""
|
||||||
|
Raw level FT232 controll class
|
||||||
|
"""
|
||||||
|
|
||||||
|
PKT_START = 0xaa
|
||||||
|
PKT_END = 0xaa
|
||||||
|
|
||||||
|
PKT_CMD_ACK = 0x00
|
||||||
|
|
||||||
|
PKT_CMD_START = 0x01
|
||||||
|
PKT_CMD_START_STOP = 0x00
|
||||||
|
PKT_CMD_START_START = 0x01
|
||||||
|
|
||||||
|
PKT_CMD_LOADING = 0x02
|
||||||
|
PKT_CMD_LOADING_STOP = 0x00
|
||||||
|
PKT_CMD_LOADING_LOAD = 0x01
|
||||||
|
|
||||||
|
PKT_CMD_DETECTION = 0x03
|
||||||
|
PKT_CMD_DETECTION_0 = 0x00
|
||||||
|
PKT_CMD_DETECTION_1 = 0x01
|
||||||
|
PKT_CMD_DETECTION_2 = 0x02
|
||||||
|
PKT_CMD_DETECTION_3 = 0x03
|
||||||
|
|
||||||
|
PKT_CMD_SPEED = 0x04
|
||||||
|
PKT_CMD_SPEED_STOP = 0x00
|
||||||
|
PKT_CMD_SPEED_UP = 0x01
|
||||||
|
PKT_CMD_SPEED_DOWN = 0x02
|
||||||
|
|
||||||
|
#PKT_CMD_AUTO_LOADING = 0x05
|
||||||
|
#PKT_CMD_VERSION = 0x64
|
||||||
|
|
||||||
|
PKT_ID_MAX = 199
|
||||||
|
PKT_ID_ERROR = 0xff
|
||||||
|
|
||||||
|
def __init__(self,
|
||||||
|
port: str = '/dev/ttyUSB0',
|
||||||
|
baudrate: int = 19200,
|
||||||
|
*,
|
||||||
|
debug: bool = False):
|
||||||
|
self.debug = debug
|
||||||
|
if self.debug:
|
||||||
|
self.logger = getLogger('PYFT232')
|
||||||
|
self.logger.info("serial port %s (%d)", port, baudrate)
|
||||||
|
|
||||||
|
self.__ft232 = None
|
||||||
|
self.__stop_requested = False # to support graceful exit
|
||||||
|
self.__force_stop = False
|
||||||
|
self.__values = [1] * 14 # Pin status
|
||||||
|
for idx in (0, 1):
|
||||||
|
self.__values[idx] = 0xff # NC
|
||||||
|
|
||||||
|
# init arduio
|
||||||
|
ft232 = Serial(port=port, baudrate=baudrate, timeout=1)
|
||||||
|
ft232.reset_input_buffer()
|
||||||
|
ft232.reset_output_buffer()
|
||||||
|
self.__ft232 = ft232
|
||||||
|
|
||||||
|
self.__rx = Thread(target=self.__receiver)
|
||||||
|
self.__rx.name = 'Arduino Rx'
|
||||||
|
self.__rx.start()
|
||||||
|
|
||||||
|
self.__init()
|
||||||
|
|
||||||
|
self.__tx_lock = Lock()
|
||||||
|
self.__tx_cv = Condition()
|
||||||
|
self.__tx_queue = Queue()
|
||||||
|
self.__tx = Thread(target=self.__transmitter)
|
||||||
|
self.__tx.name = 'Arduino Tx'
|
||||||
|
self.__tx.start()
|
||||||
|
|
||||||
|
def __del__(self):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def __checksum(self, length: int, cmd: int, data: list):
|
||||||
|
return ~((length + cmd + sum(data)) & 0xff) & 0xff
|
||||||
|
|
||||||
|
def __init(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
# Tx Thread
|
||||||
|
def __transmitter(self):
|
||||||
|
ft232 = self.__ft232
|
||||||
|
|
||||||
|
while True:
|
||||||
|
packet = self.__tx_queue.get()
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info(packet)
|
||||||
|
|
||||||
|
if len(packet) != 2:
|
||||||
|
break
|
||||||
|
|
||||||
|
cmd, data = packet
|
||||||
|
self.__tx_queue.task_done()
|
||||||
|
if cmd is None:
|
||||||
|
break # End of Tx
|
||||||
|
|
||||||
|
ft232.write(
|
||||||
|
bytes([
|
||||||
|
PyFt232.PKT_START, 0x05, cmd, data, self.__checksum(0x05, cmd, [data])
|
||||||
|
]))
|
||||||
|
|
||||||
|
# Rx Thread
|
||||||
|
def __receiver(self):
|
||||||
|
while self.__force_stop is False:
|
||||||
|
packet = self.__receive_packet()
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info(packet)
|
||||||
|
if packet is None:
|
||||||
|
continue
|
||||||
|
|
||||||
|
# TODO : What need to be done?
|
||||||
|
cmd, data = packet
|
||||||
|
|
||||||
|
# run callback
|
||||||
|
with self.__tx_cv:
|
||||||
|
self.__tx_cv.notify()
|
||||||
|
|
||||||
|
def __receive_packet(self):
|
||||||
|
ft232 = self.__ft232
|
||||||
|
|
||||||
|
data = ft232.read()
|
||||||
|
if len(data) == 0 or data[0] != PyFt232.PKT_START:
|
||||||
|
return None
|
||||||
|
|
||||||
|
packet = []
|
||||||
|
while len(packet) != 5:
|
||||||
|
data = ft232.read()
|
||||||
|
if len(data) == 0:
|
||||||
|
return None # Timeout
|
||||||
|
|
||||||
|
packet.append(data[0])
|
||||||
|
|
||||||
|
length, cmd, data, dataerr, checksum = packet
|
||||||
|
if length != 6:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if cmd != 0x00 or dataerr != 0x00:
|
||||||
|
return None
|
||||||
|
|
||||||
|
if self.__checksum(length, cmd, [data, dataerr]) != checksum:
|
||||||
|
return None
|
||||||
|
|
||||||
|
return (cmd, data)
|
||||||
|
|
||||||
|
def __enter__(self):
|
||||||
|
return self
|
||||||
|
|
||||||
|
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||||
|
self.close()
|
||||||
|
|
||||||
|
def close(self) -> None:
|
||||||
|
"""
|
||||||
|
Close existing ft232 connection
|
||||||
|
"""
|
||||||
|
if self.__ft232 is None:
|
||||||
|
return
|
||||||
|
|
||||||
|
self.__stop_requested = True
|
||||||
|
if self.__force_stop:
|
||||||
|
return
|
||||||
|
|
||||||
|
# post end of tx marker
|
||||||
|
self.__tx_queue.put((None, None, None))
|
||||||
|
self.__tx.join()
|
||||||
|
|
||||||
|
# Stop Rx
|
||||||
|
self.__force_stop = True
|
||||||
|
self.__rx.join()
|
||||||
|
|
||||||
|
# close serial
|
||||||
|
self.__ft232.close()
|
||||||
|
|
||||||
|
if self.debug:
|
||||||
|
self.logger.info("stopped")
|
||||||
|
|
||||||
|
def set(self, cmd: int, value: int) -> bool:
|
||||||
|
"""
|
||||||
|
Set ft232 pin with given value
|
||||||
|
"""
|
||||||
|
if self.__stop_requested:
|
||||||
|
return False
|
||||||
|
|
||||||
|
with self.__tx_lock:
|
||||||
|
with self.__tx_cv:
|
||||||
|
self.__tx_queue.put((cmd, value))
|
||||||
|
self.__tx_cv.wait()
|
||||||
|
|
||||||
|
return True
|
||||||
|
|
||||||
|
def get(self, pin: int) -> int:
|
||||||
|
"""
|
||||||
|
Get current ft232 pin value from proxy
|
||||||
|
"""
|
||||||
|
return self.__values[pin]
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
pymysql
|
||||||
|
opencv-python
|
||||||
|
numpy
|
||||||
|
onnxruntime
|
||||||
|
tk
|
||||||
|
pillow
|
||||||
Reference in new issue
Block a user