|
@@ -0,0 +1,109 @@
|
|
|
|
|
+from dataclasses import dataclass
|
|
|
|
|
+from enum import Enum
|
|
|
|
|
+from typing import List, Tuple
|
|
|
|
|
+from .config import ConfigParser, SlotConfig
|
|
|
|
|
+import logging
|
|
|
|
|
+
|
|
|
|
|
+logger = logging.getLogger(__name__)
|
|
|
|
|
+
|
|
|
|
|
+class CellStatus(Enum):
|
|
|
|
|
+ WAITING = "waiting"
|
|
|
|
|
+ MEASURING = "measuring"
|
|
|
|
|
+ COMPLETED = "completed"
|
|
|
|
|
+ FAILED = "failed"
|
|
|
|
|
+
|
|
|
|
|
+@dataclass
|
|
|
|
|
+class Cell:
|
|
|
|
|
+ id: str
|
|
|
|
|
+ status: CellStatus
|
|
|
|
|
+ measurement_slot: int = None
|
|
|
|
|
+ capacity: float = None
|
|
|
|
|
+
|
|
|
|
|
+class DropoffGrade:
|
|
|
|
|
+ def __init__(self, name: str, x: float, y: float, z: float):
|
|
|
|
|
+ self.name = name
|
|
|
|
|
+ self.position = (x, y, z)
|
|
|
|
|
+
|
|
|
|
|
+class RobotController:
|
|
|
|
|
+ def __init__(self):
|
|
|
|
|
+ self.config = ConfigParser()
|
|
|
|
|
+ self.devices = self.config.get_devices()
|
|
|
|
|
+ self.feeder = self.config.get_feeder()
|
|
|
|
|
+ self.dropoff_grades = self.config.get_dropoff_grades()
|
|
|
|
|
+ self.rejected_grade = self.dropoff_grades['rejected']
|
|
|
|
|
+ self.system_settings = self.config.get_system_settings()
|
|
|
|
|
+
|
|
|
|
|
+ # Initialize with configured values
|
|
|
|
|
+ self.total_slots = sum(len(device.slots) for device in self.devices)
|
|
|
|
|
+ self.work_queue: List[SlotConfig] = []
|
|
|
|
|
+
|
|
|
|
|
+ self.gripper_occupied = False
|
|
|
|
|
+
|
|
|
|
|
+ async def pick_cell_from_feeder(self):
|
|
|
|
|
+ # Implementation for picking cell from feeder
|
|
|
|
|
+ logger.info("Picking cell from feeder")
|
|
|
|
|
+ self.gripper_occupied = True
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+ async def read_cell_id(self):
|
|
|
|
|
+ # Use vision system to read data matrix and return cell ID
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ def get_next_free_slot(self):
|
|
|
|
|
+ for device in self.devices:
|
|
|
|
|
+ for slot in device.slots:
|
|
|
|
|
+ if not slot.occupied:
|
|
|
|
|
+ logger.info(f"Next free slot found at position {slot.position}")
|
|
|
|
|
+ return slot
|
|
|
|
|
+
|
|
|
|
|
+ logger.error("No free slots available")
|
|
|
|
|
+ return None
|
|
|
|
|
+
|
|
|
|
|
+ async def insert_cell_to_next_available(self, cell: Cell):
|
|
|
|
|
+ if not self.gripper_occupied:
|
|
|
|
|
+ logger.error("Gripper not occupied")
|
|
|
|
|
+ return
|
|
|
|
|
+ slot = self.get_next_free_slot()
|
|
|
|
|
+ if slot:
|
|
|
|
|
+ await self.insert_cell_to_slot(cell, slot)
|
|
|
|
|
+ else:
|
|
|
|
|
+ logger.error("No free slots available")
|
|
|
|
|
+
|
|
|
|
|
+ async def insert_cell_to_slot(self, cell: Cell, slot: SlotConfig):
|
|
|
|
|
+ if slot.occupied:
|
|
|
|
|
+ logger.error(f"Slot {slot.id} is already occupied")
|
|
|
|
|
+ return
|
|
|
|
|
+ if not self.gripper_occupied:
|
|
|
|
|
+ logger.error("Gripper not occupied")
|
|
|
|
|
+ return
|
|
|
|
|
+ slot.occupied = True
|
|
|
|
|
+ slot.cell_id = cell
|
|
|
|
|
+ logger.info(f"Cell {cell.id} inserted to slot at position {slot.position}")
|
|
|
|
|
+ # Move to slot and insert cell
|
|
|
|
|
+ pass
|
|
|
|
|
+
|
|
|
|
|
+ async def collect_cell_from_slot(self, slot: SlotConfig):
|
|
|
|
|
+ if self.gripper_occupied:
|
|
|
|
|
+ logger.error("Gripper already occupied")
|
|
|
|
|
+ return
|
|
|
|
|
+ # Collect cell from measurement slot
|
|
|
|
|
+ slot.occupied = False
|
|
|
|
|
+ slot.cell_id = None
|
|
|
|
|
+ logger.info(f"Cell {slot.cell_id} collected from slot at position {slot.position}")
|
|
|
|
|
+ return slot.cell_id
|
|
|
|
|
+
|
|
|
|
|
+ async def sort_cell(self, cell: Cell):
|
|
|
|
|
+ if cell.status == CellStatus.FAILED:
|
|
|
|
|
+ await self.dropoff_cell(cell.measurement_slot, self.rejected_grade)
|
|
|
|
|
+ logger.info(f"Cell {cell.id} sorted to rejected grade")
|
|
|
|
|
+ return
|
|
|
|
|
+ for name, grade in self.dropoff_grades.items():
|
|
|
|
|
+ if cell.capacity >= grade.capacity_threshold:
|
|
|
|
|
+ await self.dropoff_cell(cell, grade)
|
|
|
|
|
+ logger.info(f"Cell {cell.id} sorted to grade {name}")
|
|
|
|
|
+ return
|
|
|
|
|
+ logger.error(f"No suitable grade found for cell {cell.id} with capacity {cell.capacity}")
|
|
|
|
|
+
|
|
|
|
|
+ async def dropoff_cell(self, dropoff_grade: DropoffGrade):
|
|
|
|
|
+ # Drop collected cell at position
|
|
|
|
|
+ pass
|