251 lines
7.0 KiB
Python
251 lines
7.0 KiB
Python
"""
|
|
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()
|