Add whatsapp feature
This commit is contained in:
@@ -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,
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user