import mysql.connector from database.connection import get_db_connection import logging import json # 설비 추가 def add_equipment_to_db(equip_data): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: # DB 테이블에 만약 equipment_type 컬럼이 추가되었는지 파악 # 없을 경우 기본값을 채워 안전하게 동작 sql = """ INSERT INTO equipment (name, yarn_bobbin_count, dryer_type, power_consumption, max_cutting_speed, max_feed_speed) VALUES (%s, %s, %s, %s, %s, %s) """ cursor.execute(sql, ( equip_data.get('name'), equip_data.get('yarn_bobbin_count'), equip_data.get('dryer_type'), equip_data.get('power_consumption'), equip_data.get('max_cutting_speed'), equip_data.get('max_feed_speed') )) conn.commit() new_equipment_id = cursor.lastrowid if new_equipment_id: init_status_sql = "INSERT INTO machine_status (equipment_id, device_state, sync_state) VALUES (%s, %s, %s)" cursor.execute(init_status_sql, (new_equipment_id, "OFFLINE", "IDLE")) conn.commit() return new_equipment_id except mysql.connector.Error as err: logging.error(f"Error adding equipment: {err}") return None # 설비 수정 def update_equipment_in_db(equip_data): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: sql = """ UPDATE equipment SET name = %s, yarn_bobbin_count = %s, dryer_type = %s, power_consumption = %s, max_cutting_speed = %s, max_feed_speed = %s WHERE id = %s """ cursor.execute(sql, ( equip_data.get('name'), equip_data.get('yarn_bobbin_count'), equip_data.get('dryer_type'), equip_data.get('power_consumption'), equip_data.get('max_cutting_speed'), equip_data.get('max_feed_speed'), equip_data.get('id') )) conn.commit() return True except mysql.connector.Error as err: logging.error(f"Error updating equipment: {err}") return False # 설비 삭제 def delete_equipment_from_db(equipment_id): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: cursor.execute("DELETE FROM machine_status WHERE equipment_id = %s", (equipment_id,)) cursor.execute("DELETE FROM plan_assignments WHERE equipment_id = %s", (equipment_id,)) cursor.execute("DELETE FROM equipment WHERE id = %s", (equipment_id,)) conn.commit() return True except mysql.connector.Error as err: logging.error(f"Error deleting equipment: {err}") return False # 모든 설비 목록 조회 def get_all_equipment_from_db(): # --- DEBUG LOG --- print("[DB DEBUG] get_all_equipment_from_db function starts.") # ----------------- try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: # machine_status 테이블과 JOIN하여 실시간 장치 상태(device_state)를 함께 조회 sql = """ SELECT e.*, ms.device_state FROM equipment e LEFT JOIN machine_status ms ON e.id = ms.equipment_id """ cursor.execute(sql) results = cursor.fetchall() # --- DEBUG LOG --- print(f"[DB DEBUG] Query executed. Raw rows fetched: {len(results)}") # ----------------- for r in results: # device_state가 'ONLINE'이면 is_online을 True로 설정 r['is_online'] = (r.get('device_state') == 'ONLINE') if 'equipment_type' not in r or not r['equipment_type']: r['equipment_type'] = 'CC_MC_TYPE_A' # 프론트엔드가 카드를 렌더링할 때 요구하는 필수 필드 보완 (방어 코드) if 'type' not in r or not r['type']: r['type'] = 'cutter' if 'ip_address' not in r or not r['ip_address']: r['ip_address'] = '127.0.0.1' if 'line_prefix' not in r or not r['line_prefix']: r['line_prefix'] = 'line1' return results except mysql.connector.Error as err: logging.error(f"Error getting all equipment: {err}") # --- DEBUG LOG --- print(f"[DB DEBUG] Error fetching equipment: {err}") # ----------------- return [] # 특정 설비 상세 정보 조회 def get_equipment_details_from_db(equipment_id): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: cursor.execute("SELECT * FROM equipment WHERE id = %s", (equipment_id,)) equipment_data = cursor.fetchone() if not equipment_data: return None if 'equipment_type' not in equipment_data: equipment_data['equipment_type'] = 'CC_MC_TYPE_A' return { "equipment": equipment_data } except mysql.connector.Error as err: logging.error(f"Error getting equipment details: {err}") return None # 실시간 설비 상태 DB에 저장/업데이트 (UPSERT) def save_to_db(data): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: sql = """ INSERT INTO machine_status ( equipment_id, timestamp, device_state, operation_mode, yarn_sensors, dryer_states, temperatures, pump_states, cutting_motor_is_running, cutting_motor_value, transfer_motor_is_running, transfer_motor_value, fan_state, door_sensors, water_level_sensor ) VALUES ( %(equipment_id)s, %(timestamp)s, %(device_state)s, %(operation_mode)s, %(yarn_sensors)s, %(dryer_states)s, %(temperatures)s, %(pump_states)s, %(cutting_motor_is_running)s, %(cutting_motor_value)s, %(transfer_motor_is_running)s, %(transfer_motor_value)s, %(fan_state)s, %(door_sensors)s, %(water_level_sensor)s ) ON DUPLICATE KEY UPDATE timestamp = VALUES(timestamp), device_state = VALUES(device_state), operation_mode = VALUES(operation_mode), yarn_sensors = VALUES(yarn_sensors), dryer_states = VALUES(dryer_states), temperatures = VALUES(temperatures), pump_states = VALUES(pump_states), cutting_motor_is_running = VALUES(cutting_motor_is_running), cutting_motor_value = VALUES(cutting_motor_value), transfer_motor_is_running = VALUES(transfer_motor_is_running), transfer_motor_value = VALUES(transfer_motor_value), fan_state = VALUES(fan_state), door_sensors = VALUES(door_sensors), water_level_sensor = VALUES(water_level_sensor) """ params = data.copy() for key, value in params.items(): if isinstance(value, list) or isinstance(value, dict): params[key] = json.dumps(value) cursor.execute(sql, params) conn.commit() logging.info(f"Successfully saved status for equipment_id: {data['equipment_id']}") return True except mysql.connector.Error as err: logging.error(f"DB Error in save_to_db: {err}") return False except Exception as e: logging.error(f"An unexpected error occurred in save_to_db: {e}") return False # 모든 설비의 현재 상태를 설비 정보와 JOIN하여 조회 def get_all_machine_statuses_joined(): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: sql = """ SELECT e.id as equipment_id, e.name, ms.timestamp, ms.device_state, ms.d_read_g0, ms.d_read_g1, ms.d_read_g2 FROM equipment e LEFT JOIN machine_status ms ON e.id = ms.equipment_id ORDER BY e.id """ cursor.execute(sql) results = cursor.fetchall() for r in results: if 'equipment_type' not in r: r['equipment_type'] = 'CC_MC_TYPE_A' return results except mysql.connector.Error as err: logging.error(f"Error in get_all_machine_statuses_joined: {err}") return [] # 특정 설비의 상세 상태를 설비 정보와 JOIN하여 조회 def get_machine_status_detail_joined(equipment_id): try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: sql = """ SELECT e.*, ms.timestamp, ms.device_state, ms.sync_state, ms.m_read_raw, ms.m_write_raw, ms.d_read_g0, ms.d_read_g1, ms.d_read_g2, ms.d_read_g3, ms.d_read_g4, ms.d_read_g5, ms.d_read_g6, ms.d_read_g7, ms.d_read_g8, ms.d_read_g9, ms.d_write_g0, ms.d_write_g1, ms.d_write_g2, ms.d_write_g3 FROM equipment e LEFT JOIN machine_status ms ON e.id = ms.equipment_id WHERE e.id = %s """ cursor.execute(sql, (equipment_id,)) result = cursor.fetchone() if result: if 'equipment_type' not in result: result['equipment_type'] = 'CC_MC_TYPE_A' # JSON 컬럼 자동 파싱 처리 for key in ["m_read_raw", "m_write_raw"]: val = result.get(key) if isinstance(val, str): try: result[key] = json.loads(val) except json.JSONDecodeError: logging.warning(f"Could not decode JSON for key {key}: {val}") return result except mysql.connector.Error as err: logging.error(f"Error in get_machine_status_detail_joined: {err}") return None def update_m_write_in_db(equipment_id: int, payload: list): """M영역 쓰기 설정값을 machine_status의 m_write_raw JSON에 병합 업데이트합니다.""" try: with get_db_connection() as conn: with conn.cursor(dictionary=True) as cursor: # 기존 데이터 가져오기 cursor.execute("SELECT m_write_raw FROM machine_status WHERE equipment_id = %s", (equipment_id,)) row = cursor.fetchone() m_write = {} if row and row.get("m_write_raw"): try: m_write = json.loads(row["m_write_raw"]) except: pass # 새로운 값 병합 for item in payload: if len(item) >= 2: bit_id = str(item[0]) val = int(item[1]) m_write[bit_id] = val # 업데이트 수행 sql = "UPDATE machine_status SET m_write_raw = %s WHERE equipment_id = %s" cursor.execute(sql, (json.dumps(m_write), equipment_id)) conn.commit() return True except Exception as e: logging.error(f"Error in update_m_write_in_db: {e}") return False def update_d_write_in_db(equipment_id: int, payload: list): """D영역 쓰기 설정값을 machine_status의 d_write_g0~g3 개별 컬럼에 업데이트합니다.""" try: with get_db_connection() as conn: with conn.cursor() as cursor: for item in payload: if len(item) >= 2: group_id = int(item[0]) val = float(item[1]) if 0 <= group_id <= 3: sql = f"UPDATE machine_status SET d_write_g{group_id} = %s WHERE equipment_id = %s" cursor.execute(sql, (val, equipment_id)) conn.commit() return True except Exception as e: logging.error(f"Error in update_d_write_in_db: {e}") return False def save_control_command(equipment_id: int, command_id: str, cmd_type: str, requested_value: list, status: str): """제어 명령 요청을 생성하여 control_commands 테이블에 기록합니다.""" try: with get_db_connection() as conn: with conn.cursor() as cursor: sql = """ INSERT INTO control_commands (equipment_id, command_id, type, requested_value, command_status) VALUES (%s, %s, %s, %s, %s) """ cursor.execute(sql, (equipment_id, command_id, cmd_type, json.dumps(requested_value), status)) conn.commit() return True except Exception as e: logging.error(f"Error in save_control_command: {e}") return False def update_control_command_status(command_id: str, status: str, applied_value: list = None): """수신된 ACK 신호나 타임아웃에 기반하여 제어 명령 상태를 업데이트합니다.""" try: with get_db_connection() as conn: with conn.cursor() as cursor: if applied_value is not None: sql = """ UPDATE control_commands SET command_status = %s, applied_value = %s, applied_at = CURRENT_TIMESTAMP WHERE command_id = %s """ cursor.execute(sql, (status, json.dumps(applied_value), command_id)) elif status == "PLC_APPLIED": sql = """ UPDATE control_commands SET command_status = %s, applied_value = requested_value, applied_at = CURRENT_TIMESTAMP WHERE command_id = %s """ cursor.execute(sql, (status, command_id)) elif status in ("FAILED", "TIMEOUT"): sql = """ UPDATE control_commands SET command_status = %s, applied_at = CURRENT_TIMESTAMP WHERE command_id = %s """ cursor.execute(sql, (status, command_id)) else: cursor.execute( "UPDATE control_commands SET command_status = %s WHERE command_id = %s", (status, command_id) ) conn.commit() return True except Exception as e: logging.error(f"Error in update_control_command_status: {e}") return False