Download controller.py from Asah-ML-Copilot-A25-CS047/Predictive-Machine: direct link, hf CLI and curl.
- Browser
- Download file 10.7 kB
-
https://huggingface.co/spaces/Asah-ML-Copilot-A25-CS047/Predictive-Machine/resolve/main/controller.py
- Command line
-
hf download hf://spaces/Asah-ML-Copilot-A25-CS047/Predictive-Machine/controller.py
-
curl -L -o controller.py https://huggingface.co/spaces/Asah-ML-Copilot-A25-CS047/Predictive-Machine/resolve/main/controller.py
10.7 kB
| import logging | |
| from model import Model | |
| from utils import ( | |
| calculate_risk_score, | |
| convert_cyclical_to_original, | |
| create_timestamp_from_predictions, | |
| get_failure_severity, | |
| get_failure_type_name, | |
| get_risk_level, | |
| prepare_prediction_data, | |
| prepare_sensor_data_for_anomaly, | |
| ) | |
| import numpy as np | |
| from datetime import datetime, timedelta | |
| logger = logging.getLogger(__name__) | |
| class Controller: | |
| def __init__(self, database_sensor): | |
| self.__sensor = database_sensor | |
| self.__model = Model() | |
| logger.info("Controller initialized with Model") | |
| def set_sensor_data(self, sensor): | |
| self.__sensor = sensor | |
| if isinstance(sensor, list): | |
| logger.debug(f"Sensor data updated with {len(sensor)} readings") | |
| else: | |
| logger.debug(f"Sensor data updated for machine_id: {sensor.get('machine_id')}") | |
| # Binary method | |
| def predict_binary(self): | |
| if self.__sensor is None: | |
| logger.warning("Binary prediction attempted with no sensor data") | |
| return { | |
| "success": False, | |
| "error": "No sensor data available from database.", | |
| } | |
| # Handle both list and dict cases | |
| if isinstance(self.__sensor, dict) and self.__sensor.get("message") == "Data already predicted": | |
| logger.info(f"Skipping binary prediction - data already predicted for UDI: {self.__sensor.get('udi')}") | |
| return { | |
| "success": False, | |
| "error": "Data already predicted", | |
| } | |
| if ( | |
| self.__model.model_binary is None | |
| or self.__model.preprocessor_anomaly is None | |
| ): | |
| logger.error("Binary model or preprocessor not loaded - cannot perform prediction") | |
| return { | |
| "success": False, | |
| "error": "Binary model or preprocessor not loaded.", | |
| } | |
| try: | |
| # For binary, we always use single sensor data (dict) | |
| sensor_dict = self.__sensor if isinstance(self.__sensor, dict) else self.__sensor[0] | |
| X_scaled = prepare_sensor_data_for_anomaly( | |
| sensor_dict, self.__model.preprocessor_anomaly | |
| ) | |
| if X_scaled is None: | |
| logger.error("Failed to prepare sensor data for binary prediction") | |
| return {"success": False, "error": "Failed to prepare sensor data."} | |
| if hasattr(self.__model.model_binary, "predict_proba"): | |
| probabilities = self.__model.model_binary.predict_proba(X_scaled)[0] | |
| confidence_normal = float(probabilities[0]) | |
| confidence_error = float(probabilities[1]) | |
| is_error = int(confidence_error > 0.5) | |
| confidence = confidence_error if is_error else confidence_normal | |
| else: | |
| prediction = self.__model.model_binary.predict(X_scaled) | |
| is_error = int(prediction[0]) | |
| confidence = 1.0 if is_error else 0.0 | |
| risk_score = confidence * 100 if is_error else (1 - confidence) * 100 | |
| logger.info(f"Binary prediction complete - Failure: {bool(is_error)}, Confidence: {confidence:.2f}, Risk: {risk_score:.2f}") | |
| result = { | |
| "success": True, | |
| "failure_predicted": bool(is_error), | |
| "confidence": float(f"{confidence}"), | |
| "risk_score": float(f"{risk_score}"), | |
| } | |
| return result | |
| except Exception as e: | |
| logger.error(f"Exception in binary prediction: {str(e)}") | |
| return { | |
| "success": False, | |
| "message": f"Failed to predict binary data: {str(e)}" | |
| } | |
| # Classification Method | |
| def predict_classification(self): | |
| if self.__sensor is None: | |
| logger.warning("Classification prediction attempted with no sensor data") | |
| return { | |
| "success": False, | |
| "error": "No sensor data available from database.", | |
| } | |
| # Handle both list and dict cases | |
| if isinstance(self.__sensor, dict) and self.__sensor.get("message") == "Data already predicted": | |
| logger.info(f"Skipping classification prediction - data already predicted for UDI: {self.__sensor.get('udi')}") | |
| return { | |
| "success": False, | |
| "error": "Data already predicted", | |
| } | |
| if ( | |
| self.__model.model_multiclass is None | |
| or self.__model.preprocessor_anomaly is None | |
| ): | |
| logger.error("Multiclass model or preprocessor not loaded - cannot perform prediction") | |
| return {"success": False, "error": "Model or scalers not loaded."} | |
| try: | |
| # For classification, we always use single sensor data (dict) | |
| sensor_dict = self.__sensor if isinstance(self.__sensor, dict) else self.__sensor[0] | |
| X_scaled = prepare_sensor_data_for_anomaly( | |
| sensor_dict, self.__model.preprocessor_anomaly | |
| ) | |
| if X_scaled is None: | |
| logger.error("Failed to prepare sensor data for classification prediction") | |
| return {"success": False, "error": "Failed to prepare sensor data."} | |
| prediction = self.__model.model_multiclass.predict(X_scaled) | |
| if hasattr(self.__model.model_multiclass, "predict_proba"): | |
| probabilities = self.__model.model_multiclass.predict_proba(X_scaled)[0] | |
| failure_index = int(probabilities.argmax()) | |
| confidence = float(probabilities[failure_index]) | |
| all_probs = { | |
| get_failure_type_name(i): float(prob) | |
| for i, prob in enumerate(probabilities) | |
| } | |
| else: | |
| failure_index = int(prediction[0]) | |
| confidence = 1.0 | |
| all_probs = None | |
| failure_type = get_failure_type_name(failure_index) | |
| severity = get_failure_severity(failure_index) | |
| risk_score = calculate_risk_score(confidence, severity) | |
| risk_level = get_risk_level(risk_score) | |
| logger.info(f"Classification prediction complete - Failure Type: {failure_type}, Confidence: {confidence:.2f}, Risk Level: {risk_level}, Risk Score: {risk_score:.2f}") | |
| return { | |
| "success": True, | |
| "failure_type": failure_type, | |
| "confidence": float(f"{confidence}"), | |
| "risk_score": float(f"{risk_score}"), | |
| "risk_level": risk_level, | |
| "all_probabilities": all_probs, | |
| } | |
| except Exception as e: | |
| logger.error(f"Exception in classification prediction: {str(e)}") | |
| return { | |
| "success": False, | |
| "message": f"Failed to predict classification data: {str(e)}" | |
| } | |
| # Time series method | |
| def predict_time_series(self): | |
| # 1. Validation Checks | |
| if self.__sensor is None: | |
| logger.warning("Time series prediction attempted with no sensor data") | |
| return {"success": False, "error": "No sensor data available."} | |
| if isinstance(self.__sensor, dict) and self.__sensor.get("message") == "Data already predicted": | |
| logger.info(f"Skipping time series - data already predicted for UDI: {self.__sensor.get('udi')}") | |
| return {"success": False, "error": "Data already predicted"} | |
| if self.__model.model_lstm is None or self.__model.scaler_lstm is None: | |
| logger.error("LSTM model or scaler_x not loaded") | |
| return {"success": False, "error": "Model or scalers not loaded."} | |
| try: | |
| sensor_data = self.__sensor if isinstance(self.__sensor, list) else [self.__sensor] | |
| input_data = [] | |
| for row in sensor_data: | |
| input_data.append([ | |
| row.get("air_temp"), | |
| row.get("process_temp"), | |
| row.get("rotational_speed"), | |
| row.get("torque"), | |
| row.get("tool_wear", 0) | |
| ]) | |
| input_array = np.array(input_data) | |
| if len(input_array) < 30: | |
| missing = 30 - len(input_array) | |
| padding = np.tile(input_array[0], (missing, 1)) | |
| input_array = np.vstack([padding, input_array]) | |
| elif len(input_array) > 30: | |
| input_array = input_array[-30:] | |
| input_scaled = self.__model.scaler_lstm.transform(input_array) | |
| input_reshaped = input_scaled.reshape(1, 30, 5) | |
| health_score = float(self.__model.model_lstm.predict(input_reshaped, verbose=0)[0][0]) | |
| MAX_LIFE_DAYS = 7.0 | |
| MAX_LIFE_MINUTES = MAX_LIFE_DAYS * 24 * 60 | |
| rul_minutes_left = health_score * MAX_LIFE_MINUTES | |
| days_remaining = health_score * MAX_LIFE_DAYS | |
| now_str = sensor_data[0].get("timestamp") | |
| if isinstance(now_str, str): | |
| now = datetime.fromisoformat(now_str.replace('Z', '+00:00')) | |
| else: | |
| now = datetime.now() | |
| failure_date = now + timedelta(minutes=rul_minutes_left) | |
| if days_remaining < 1.0: | |
| status = "Critical" | |
| elif days_remaining < 3.0: | |
| status = "Warning" | |
| else: | |
| status = "Good" | |
| logger.info(f"Health: {health_score*100:.1f}% -> {days_remaining:.2f} Days Left") | |
| return { | |
| "success": True, | |
| "predictions": { | |
| "health_score": round(health_score, 4), # e.g., 0.95 | |
| "health_percentage": round(health_score * 100, 2), # e.g., 95.0% | |
| "rul_minutes": round(rul_minutes_left, 2), | |
| "days_remaining": round(days_remaining, 2), | |
| "predicted_failure_date": failure_date.strftime('%Y-%m-%d %H:%M:%S'), | |
| "status": status | |
| }, | |
| "input_timestamp": str(now_str), | |
| } | |
| except Exception as e: | |
| logger.error(f"Exception in time series prediction: {str(e)}") | |
| return { | |
| "success": False, | |
| "message": f"Failed to predict time-series data: {str(e)}" | |
| } |