Add whatsapp feature

This commit is contained in:
Urtzi Alfaro
2025-11-13 16:01:08 +01:00
parent d7df2b0853
commit 9bc048d360
74 changed files with 9765 additions and 533 deletions

View File

@@ -528,50 +528,89 @@ class QualityCheck(Base):
}
class IoTProtocol(str, enum.Enum):
"""IoT protocol enumeration"""
REST_API = "rest_api"
OPC_UA = "opc_ua"
MQTT = "mqtt"
MODBUS = "modbus"
CUSTOM = "custom"
class IoTConnectionStatus(str, enum.Enum):
"""IoT connection status enumeration"""
CONNECTED = "connected"
DISCONNECTED = "disconnected"
ERROR = "error"
UNKNOWN = "unknown"
class Equipment(Base):
"""Equipment model for tracking production equipment"""
__tablename__ = "equipment"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Equipment identification
name = Column(String(255), nullable=False)
type = Column(SQLEnum(EquipmentType), nullable=False)
model = Column(String(100), nullable=True)
serial_number = Column(String(100), nullable=True)
location = Column(String(255), nullable=True)
manufacturer = Column(String(100), nullable=True)
firmware_version = Column(String(50), nullable=True)
# Status tracking
status = Column(SQLEnum(EquipmentStatus), nullable=False, default=EquipmentStatus.OPERATIONAL)
# Dates
install_date = Column(DateTime(timezone=True), nullable=True)
last_maintenance_date = Column(DateTime(timezone=True), nullable=True)
next_maintenance_date = Column(DateTime(timezone=True), nullable=True)
maintenance_interval_days = Column(Integer, nullable=True) # Maintenance interval in days
# Performance metrics
efficiency_percentage = Column(Float, nullable=True) # Current efficiency
uptime_percentage = Column(Float, nullable=True) # Overall equipment effectiveness
energy_usage_kwh = Column(Float, nullable=True) # Current energy usage
# Specifications
power_kw = Column(Float, nullable=True) # Power in kilowatts
capacity = Column(Float, nullable=True) # Capacity (units depend on equipment type)
weight_kg = Column(Float, nullable=True) # Weight in kilograms
# Temperature monitoring
current_temperature = Column(Float, nullable=True) # Current temperature reading
target_temperature = Column(Float, nullable=True) # Target temperature
# IoT Connectivity
iot_enabled = Column(Boolean, default=False, nullable=False)
iot_protocol = Column(String(50), nullable=True) # rest_api, opc_ua, mqtt, modbus, custom
iot_endpoint = Column(String(500), nullable=True) # URL or IP address
iot_port = Column(Integer, nullable=True) # Connection port
iot_credentials = Column(JSON, nullable=True) # Encrypted credentials (API keys, tokens, username/password)
iot_connection_status = Column(String(50), nullable=True) # connected, disconnected, error, unknown
iot_last_connected = Column(DateTime(timezone=True), nullable=True)
iot_config = Column(JSON, nullable=True) # Additional configuration (polling interval, specific endpoints, etc.)
# Real-time monitoring
supports_realtime = Column(Boolean, default=False, nullable=False)
poll_interval_seconds = Column(Integer, nullable=True) # How often to poll for data
# Sensor capabilities
temperature_zones = Column(Integer, nullable=True) # Number of temperature zones
supports_humidity = Column(Boolean, default=False, nullable=False)
supports_energy_monitoring = Column(Boolean, default=False, nullable=False)
supports_remote_control = Column(Boolean, default=False, nullable=False)
# Status
is_active = Column(Boolean, default=True)
# Notes
notes = Column(Text, nullable=True)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
@@ -586,6 +625,8 @@ class Equipment(Base):
"model": self.model,
"serial_number": self.serial_number,
"location": self.location,
"manufacturer": self.manufacturer,
"firmware_version": self.firmware_version,
"status": self.status.value if self.status else None,
"install_date": self.install_date.isoformat() if self.install_date else None,
"last_maintenance_date": self.last_maintenance_date.isoformat() if self.last_maintenance_date else None,
@@ -599,6 +640,19 @@ class Equipment(Base):
"weight_kg": self.weight_kg,
"current_temperature": self.current_temperature,
"target_temperature": self.target_temperature,
"iot_enabled": self.iot_enabled,
"iot_protocol": self.iot_protocol,
"iot_endpoint": self.iot_endpoint,
"iot_port": self.iot_port,
"iot_connection_status": self.iot_connection_status,
"iot_last_connected": self.iot_last_connected.isoformat() if self.iot_last_connected else None,
"iot_config": self.iot_config,
"supports_realtime": self.supports_realtime,
"poll_interval_seconds": self.poll_interval_seconds,
"temperature_zones": self.temperature_zones,
"supports_humidity": self.supports_humidity,
"supports_energy_monitoring": self.supports_energy_monitoring,
"supports_remote_control": self.supports_remote_control,
"is_active": self.is_active,
"notes": self.notes,
"created_at": self.created_at.isoformat() if self.created_at else None,
@@ -606,3 +660,216 @@ class Equipment(Base):
}
class EquipmentSensorReading(Base):
"""Equipment sensor reading model for time-series IoT data"""
__tablename__ = "equipment_sensor_readings"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
equipment_id = Column(UUID(as_uuid=True), nullable=False, index=True)
batch_id = Column(UUID(as_uuid=True), nullable=True, index=True)
# Timestamp
reading_time = Column(DateTime(timezone=True), nullable=False, index=True)
# Temperature readings (support multiple zones)
temperature = Column(Float, nullable=True)
temperature_zones = Column(JSON, nullable=True) # {"zone1": 180, "zone2": 200, "zone3": 185}
target_temperature = Column(Float, nullable=True)
# Humidity
humidity = Column(Float, nullable=True)
target_humidity = Column(Float, nullable=True)
# Energy monitoring
energy_consumption_kwh = Column(Float, nullable=True)
power_current_kw = Column(Float, nullable=True)
# Equipment status
operational_status = Column(String(50), nullable=True) # running, idle, warming_up, cooling_down
cycle_stage = Column(String(100), nullable=True) # preheating, baking, cooling
cycle_progress_percentage = Column(Float, nullable=True)
time_remaining_minutes = Column(Integer, nullable=True)
# Process parameters
motor_speed_rpm = Column(Float, nullable=True)
door_status = Column(String(20), nullable=True) # open, closed
steam_level = Column(Float, nullable=True)
# Quality indicators
product_weight_kg = Column(Float, nullable=True)
moisture_content = Column(Float, nullable=True)
# Additional sensor data (flexible JSON for manufacturer-specific metrics)
additional_sensors = Column(JSON, nullable=True)
# Data quality
data_quality_score = Column(Float, nullable=True)
is_anomaly = Column(Boolean, default=False, nullable=False)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"equipment_id": str(self.equipment_id),
"batch_id": str(self.batch_id) if self.batch_id else None,
"reading_time": self.reading_time.isoformat() if self.reading_time else None,
"temperature": self.temperature,
"temperature_zones": self.temperature_zones,
"target_temperature": self.target_temperature,
"humidity": self.humidity,
"target_humidity": self.target_humidity,
"energy_consumption_kwh": self.energy_consumption_kwh,
"power_current_kw": self.power_current_kw,
"operational_status": self.operational_status,
"cycle_stage": self.cycle_stage,
"cycle_progress_percentage": self.cycle_progress_percentage,
"time_remaining_minutes": self.time_remaining_minutes,
"motor_speed_rpm": self.motor_speed_rpm,
"door_status": self.door_status,
"steam_level": self.steam_level,
"product_weight_kg": self.product_weight_kg,
"moisture_content": self.moisture_content,
"additional_sensors": self.additional_sensors,
"data_quality_score": self.data_quality_score,
"is_anomaly": self.is_anomaly,
"created_at": self.created_at.isoformat() if self.created_at else None,
}
class EquipmentConnectionLog(Base):
"""Equipment connection log for tracking IoT connectivity"""
__tablename__ = "equipment_connection_logs"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
equipment_id = Column(UUID(as_uuid=True), nullable=False, index=True)
# Connection event
event_type = Column(String(50), nullable=False) # connected, disconnected, error, timeout
event_time = Column(DateTime(timezone=True), nullable=False, index=True)
# Connection details
connection_status = Column(String(50), nullable=False)
protocol_used = Column(String(50), nullable=True)
endpoint = Column(String(500), nullable=True)
# Error tracking
error_message = Column(Text, nullable=True)
error_code = Column(String(50), nullable=True)
# Performance metrics
response_time_ms = Column(Integer, nullable=True)
data_points_received = Column(Integer, nullable=True)
# Additional details
additional_data = Column(JSON, nullable=True)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"equipment_id": str(self.equipment_id),
"event_type": self.event_type,
"event_time": self.event_time.isoformat() if self.event_time else None,
"connection_status": self.connection_status,
"protocol_used": self.protocol_used,
"endpoint": self.endpoint,
"error_message": self.error_message,
"error_code": self.error_code,
"response_time_ms": self.response_time_ms,
"data_points_received": self.data_points_received,
"additional_data": self.additional_data,
"created_at": self.created_at.isoformat() if self.created_at else None,
}
class EquipmentIoTAlert(Base):
"""Equipment IoT alert model for real-time equipment alerts"""
__tablename__ = "equipment_iot_alerts"
# Primary identification
id = Column(UUID(as_uuid=True), primary_key=True, default=uuid.uuid4)
tenant_id = Column(UUID(as_uuid=True), nullable=False, index=True)
equipment_id = Column(UUID(as_uuid=True), nullable=False, index=True)
batch_id = Column(UUID(as_uuid=True), nullable=True, index=True)
# Alert information
alert_type = Column(String(50), nullable=False) # temperature_deviation, connection_lost, equipment_error
severity = Column(String(20), nullable=False) # info, warning, critical
alert_time = Column(DateTime(timezone=True), nullable=False, index=True)
# Alert details
title = Column(String(255), nullable=False)
message = Column(Text, nullable=False)
sensor_reading_id = Column(UUID(as_uuid=True), nullable=True)
# Threshold information
threshold_value = Column(Float, nullable=True)
actual_value = Column(Float, nullable=True)
deviation_percentage = Column(Float, nullable=True)
# Status tracking
is_active = Column(Boolean, default=True, nullable=False)
is_acknowledged = Column(Boolean, default=False, nullable=False)
acknowledged_by = Column(UUID(as_uuid=True), nullable=True)
acknowledged_at = Column(DateTime(timezone=True), nullable=True)
is_resolved = Column(Boolean, default=False, nullable=False)
resolved_by = Column(UUID(as_uuid=True), nullable=True)
resolved_at = Column(DateTime(timezone=True), nullable=True)
resolution_notes = Column(Text, nullable=True)
# Automated response
auto_resolved = Column(Boolean, default=False, nullable=False)
corrective_action_taken = Column(String(255), nullable=True)
# Additional data
additional_data = Column(JSON, nullable=True)
# Timestamps
created_at = Column(DateTime(timezone=True), server_default=func.now())
updated_at = Column(DateTime(timezone=True), server_default=func.now(), onupdate=func.now())
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary following shared pattern"""
return {
"id": str(self.id),
"tenant_id": str(self.tenant_id),
"equipment_id": str(self.equipment_id),
"batch_id": str(self.batch_id) if self.batch_id else None,
"alert_type": self.alert_type,
"severity": self.severity,
"alert_time": self.alert_time.isoformat() if self.alert_time else None,
"title": self.title,
"message": self.message,
"sensor_reading_id": str(self.sensor_reading_id) if self.sensor_reading_id else None,
"threshold_value": self.threshold_value,
"actual_value": self.actual_value,
"deviation_percentage": self.deviation_percentage,
"is_active": self.is_active,
"is_acknowledged": self.is_acknowledged,
"acknowledged_by": str(self.acknowledged_by) if self.acknowledged_by else None,
"acknowledged_at": self.acknowledged_at.isoformat() if self.acknowledged_at else None,
"is_resolved": self.is_resolved,
"resolved_by": str(self.resolved_by) if self.resolved_by else None,
"resolved_at": self.resolved_at.isoformat() if self.resolved_at else None,
"resolution_notes": self.resolution_notes,
"auto_resolved": self.auto_resolved,
"corrective_action_taken": self.corrective_action_taken,
"additional_data": self.additional_data,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None,
}