Upload 3 files
Browse files- app.py +191 -0
- requirements.txt +46 -0
- voltage_monitor_.py +239 -0
app.py
ADDED
@@ -0,0 +1,191 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import streamlit as st
|
2 |
+
import pandas as pd
|
3 |
+
import time
|
4 |
+
from datetime import datetime
|
5 |
+
import plotly.express as px
|
6 |
+
import plotly.graph_objects as go
|
7 |
+
from voltage_monitor_ import WeatherSimulator, VoltageMonitor, VoltageStabilizer, NotificationSystem
|
8 |
+
|
9 |
+
# Page configuration
|
10 |
+
st.set_page_config(
|
11 |
+
page_title="Voltage Monitoring System",
|
12 |
+
page_icon="⚡",
|
13 |
+
layout="wide"
|
14 |
+
)
|
15 |
+
|
16 |
+
# Initialize session state
|
17 |
+
if 'historical_data' not in st.session_state:
|
18 |
+
st.session_state.historical_data = []
|
19 |
+
if 'alerts' not in st.session_state:
|
20 |
+
st.session_state.alerts = []
|
21 |
+
if 'is_monitoring' not in st.session_state:
|
22 |
+
st.session_state.is_monitoring = False
|
23 |
+
|
24 |
+
# Initialize system components
|
25 |
+
weather_sim = WeatherSimulator()
|
26 |
+
voltage_monitor = VoltageMonitor()
|
27 |
+
notification_system = NotificationSystem()
|
28 |
+
stabilizer = VoltageStabilizer()
|
29 |
+
|
30 |
+
# Function to add alert to session state
|
31 |
+
def add_alert(alert):
|
32 |
+
st.session_state.alerts.append(alert)
|
33 |
+
|
34 |
+
# Function to clear all alerts
|
35 |
+
def clear_alerts():
|
36 |
+
st.session_state.alerts = []
|
37 |
+
|
38 |
+
# Title and description
|
39 |
+
st.title("⚡ Real-time Voltage Monitoring System")
|
40 |
+
st.markdown("""
|
41 |
+
This dashboard provides real-time monitoring of voltage levels with weather impact consideration.
|
42 |
+
Monitor voltage fluctuations, weather conditions, and system notifications in one place.
|
43 |
+
""")
|
44 |
+
|
45 |
+
# Alerts Section - Outside the monitoring loop to persist
|
46 |
+
if st.session_state.alerts:
|
47 |
+
st.sidebar.divider()
|
48 |
+
st.sidebar.subheader("🚨 Alert History")
|
49 |
+
col_alerts, col_clear = st.sidebar.columns([3, 1])
|
50 |
+
with col_clear:
|
51 |
+
if st.button("Clear Alerts"):
|
52 |
+
clear_alerts()
|
53 |
+
|
54 |
+
for alert in st.session_state.alerts:
|
55 |
+
severity_color = "red" if alert.get('severity') == "Severe" else "orange"
|
56 |
+
st.sidebar.markdown(
|
57 |
+
f"""
|
58 |
+
<div style='padding: 10px; border-left: 3px solid {severity_color}; margin: 5px 0;'>
|
59 |
+
<small>{alert['timestamp']}</small><br>
|
60 |
+
<strong style='color: {severity_color};'>{alert['severity']} Alert</strong><br>
|
61 |
+
Voltage: {alert['voltage']}V<br>
|
62 |
+
Weather: {alert['weather']}
|
63 |
+
</div>
|
64 |
+
""",
|
65 |
+
unsafe_allow_html=True
|
66 |
+
)
|
67 |
+
|
68 |
+
# Sidebar controls
|
69 |
+
with st.sidebar:
|
70 |
+
st.header("Control Panel")
|
71 |
+
|
72 |
+
if not st.session_state.is_monitoring:
|
73 |
+
if st.button("Start Monitoring", type="primary"):
|
74 |
+
st.session_state.is_monitoring = True
|
75 |
+
else:
|
76 |
+
if st.button("Stop Monitoring", type="secondary"):
|
77 |
+
st.session_state.is_monitoring = False
|
78 |
+
|
79 |
+
st.divider()
|
80 |
+
st.subheader("System Settings")
|
81 |
+
nominal_voltage = st.slider("Nominal Voltage", 200.0, 250.0, 230.0, 0.5)
|
82 |
+
update_interval = st.slider("Update Interval (seconds)", 1, 10, 2)
|
83 |
+
|
84 |
+
# Create three columns for metrics
|
85 |
+
col1, col2, col3 = st.columns(3)
|
86 |
+
|
87 |
+
# Main monitoring loop
|
88 |
+
if st.session_state.is_monitoring:
|
89 |
+
placeholder = st.empty()
|
90 |
+
|
91 |
+
with placeholder.container():
|
92 |
+
# Get current readings
|
93 |
+
weather_data = weather_sim.get_current_weather()
|
94 |
+
current_voltage = voltage_monitor.measure_voltage(weather_data['impact_factor'])
|
95 |
+
voltage_analysis = voltage_monitor.analyze_voltage(current_voltage)
|
96 |
+
|
97 |
+
# Update historical data
|
98 |
+
st.session_state.historical_data.append({
|
99 |
+
'timestamp': datetime.now(),
|
100 |
+
'voltage': voltage_analysis['voltage'],
|
101 |
+
'weather': weather_data['condition'],
|
102 |
+
'status': voltage_analysis['status'],
|
103 |
+
'severity': voltage_analysis['severity'],
|
104 |
+
'impact_factor': weather_data['impact_factor']
|
105 |
+
})
|
106 |
+
|
107 |
+
# Add alert if status is not normal
|
108 |
+
if voltage_analysis['status'] != "Normal":
|
109 |
+
add_alert({
|
110 |
+
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
111 |
+
'voltage': voltage_analysis['voltage'],
|
112 |
+
'weather': weather_data['condition'],
|
113 |
+
'status': voltage_analysis['status'],
|
114 |
+
'severity': voltage_analysis['severity']
|
115 |
+
})
|
116 |
+
|
117 |
+
# Display current metrics
|
118 |
+
with col1:
|
119 |
+
st.metric(
|
120 |
+
label="Current Voltage",
|
121 |
+
value=f"{voltage_analysis['voltage']}V",
|
122 |
+
delta=f"{voltage_analysis['deviation_percentage']}%",
|
123 |
+
delta_color="inverse"
|
124 |
+
)
|
125 |
+
|
126 |
+
with col2:
|
127 |
+
st.metric(
|
128 |
+
label="Weather Condition",
|
129 |
+
value=weather_data['condition'],
|
130 |
+
delta=f"Impact: {weather_data['impact_factor']:.2f}"
|
131 |
+
)
|
132 |
+
|
133 |
+
with col3:
|
134 |
+
status_color = "normal" if voltage_analysis['status'] == "Normal" else "inverse"
|
135 |
+
st.metric(
|
136 |
+
label="System Status",
|
137 |
+
value=voltage_analysis['status'],
|
138 |
+
delta=voltage_analysis['severity'],
|
139 |
+
delta_color=status_color
|
140 |
+
)
|
141 |
+
|
142 |
+
# Create DataFrame from historical data
|
143 |
+
df = pd.DataFrame(st.session_state.historical_data)
|
144 |
+
|
145 |
+
# Display charts
|
146 |
+
if not df.empty:
|
147 |
+
# Voltage trend chart
|
148 |
+
fig_voltage = px.line(
|
149 |
+
df,
|
150 |
+
x='timestamp',
|
151 |
+
y='voltage',
|
152 |
+
title='Voltage Trend Over Time'
|
153 |
+
)
|
154 |
+
fig_voltage.add_hline(y=nominal_voltage, line_dash="dash", line_color="green", annotation_text="Nominal")
|
155 |
+
st.plotly_chart(fig_voltage, use_container_width=True)
|
156 |
+
|
157 |
+
# Weather impact chart
|
158 |
+
col_weather, col_status = st.columns(2)
|
159 |
+
|
160 |
+
with col_weather:
|
161 |
+
weather_counts = df['weather'].value_counts()
|
162 |
+
fig_weather = px.pie(
|
163 |
+
values=weather_counts.values,
|
164 |
+
names=weather_counts.index,
|
165 |
+
title='Weather Conditions Distribution'
|
166 |
+
)
|
167 |
+
st.plotly_chart(fig_weather)
|
168 |
+
|
169 |
+
with col_status:
|
170 |
+
status_counts = df['status'].value_counts()
|
171 |
+
fig_status = px.bar(
|
172 |
+
x=status_counts.index,
|
173 |
+
y=status_counts.values,
|
174 |
+
title='Voltage Status Distribution',
|
175 |
+
labels={'x': 'Status', 'y': 'Count'}
|
176 |
+
)
|
177 |
+
st.plotly_chart(fig_status)
|
178 |
+
|
179 |
+
# Display historical data table
|
180 |
+
if st.session_state.historical_data:
|
181 |
+
st.divider()
|
182 |
+
st.subheader("Historical Data")
|
183 |
+
df_display = pd.DataFrame(st.session_state.historical_data).tail(50)
|
184 |
+
st.dataframe(df_display, use_container_width=True)
|
185 |
+
|
186 |
+
# Add a small delay before next update
|
187 |
+
time.sleep(update_interval)
|
188 |
+
st.rerun()
|
189 |
+
|
190 |
+
else:
|
191 |
+
st.info("Click 'Start Monitoring' in the sidebar to begin voltage monitoring.")
|
requirements.txt
ADDED
@@ -0,0 +1,46 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
altair==5.5.0
|
2 |
+
attrs==24.3.0
|
3 |
+
blinker==1.9.0
|
4 |
+
cachetools==5.5.0
|
5 |
+
certifi==2024.12.14
|
6 |
+
charset-normalizer==3.4.1
|
7 |
+
click==8.1.8
|
8 |
+
gitdb==4.0.12
|
9 |
+
GitPython==3.1.44
|
10 |
+
idna==3.10
|
11 |
+
Jinja2==3.1.5
|
12 |
+
joblib==1.4.2
|
13 |
+
jsonschema==4.23.0
|
14 |
+
jsonschema-specifications==2024.10.1
|
15 |
+
markdown-it-py==3.0.0
|
16 |
+
MarkupSafe==3.0.2
|
17 |
+
mdurl==0.1.2
|
18 |
+
narwhals==1.21.1
|
19 |
+
numpy==2.2.1
|
20 |
+
packaging==24.2
|
21 |
+
pandas==2.2.3
|
22 |
+
pillow==11.1.0
|
23 |
+
plotly==5.24.1
|
24 |
+
protobuf==5.29.3
|
25 |
+
pyarrow==18.1.0
|
26 |
+
pydeck==0.9.1
|
27 |
+
Pygments==2.19.1
|
28 |
+
python-dateutil==2.9.0.post0
|
29 |
+
pytz==2024.2
|
30 |
+
referencing==0.35.1
|
31 |
+
requests==2.32.3
|
32 |
+
rich==13.9.4
|
33 |
+
rpds-py==0.22.3
|
34 |
+
scikit-learn==1.6.0
|
35 |
+
scipy==1.15.0
|
36 |
+
six==1.17.0
|
37 |
+
smmap==5.0.2
|
38 |
+
streamlit==1.41.1
|
39 |
+
tenacity==9.0.0
|
40 |
+
threadpoolctl==3.5.0
|
41 |
+
toml==0.10.2
|
42 |
+
tornado==6.4.2
|
43 |
+
typing_extensions==4.12.2
|
44 |
+
tzdata==2024.2
|
45 |
+
urllib3==2.3.0
|
46 |
+
watchdog==6.0.0
|
voltage_monitor_.py
ADDED
@@ -0,0 +1,239 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import numpy as np
|
2 |
+
import pandas as pd
|
3 |
+
from datetime import datetime, timedelta
|
4 |
+
import time
|
5 |
+
from typing import Dict, List, Tuple
|
6 |
+
import random
|
7 |
+
import json
|
8 |
+
from sklearn.linear_model import LinearRegression
|
9 |
+
|
10 |
+
class WeatherSimulator:
|
11 |
+
"""Simulates weather conditions with more dramatic impact on voltage"""
|
12 |
+
|
13 |
+
def __init__(self):
|
14 |
+
self.weather_conditions = ['Clear', 'Rain', 'Storm', 'Heavy Storm']
|
15 |
+
# Increased impact factors for more dramatic effect
|
16 |
+
self.weather_impact = {
|
17 |
+
'Clear': 1.0,
|
18 |
+
'Rain': 0.85, # More impact
|
19 |
+
'Storm': 0.65, # Significant impact
|
20 |
+
'Heavy Storm': 0.45 # Severe impact
|
21 |
+
}
|
22 |
+
# Sudden weather change probability
|
23 |
+
self.sudden_change_prob = 0.1
|
24 |
+
|
25 |
+
def get_current_weather(self) -> Dict:
|
26 |
+
"""Simulates current weather with possible sudden changes"""
|
27 |
+
# Sudden severe weather chance
|
28 |
+
if random.random() < self.sudden_change_prob:
|
29 |
+
condition = 'Heavy Storm'
|
30 |
+
print("\n⚠️ SUDDEN SEVERE WEATHER EVENT DETECTED! ⚠️")
|
31 |
+
else:
|
32 |
+
weights = [0.5, 0.3, 0.15, 0.05]
|
33 |
+
condition = np.random.choice(self.weather_conditions, p=weights)
|
34 |
+
|
35 |
+
return {
|
36 |
+
'condition': condition,
|
37 |
+
'impact_factor': self.weather_impact[condition],
|
38 |
+
'temperature': round(random.uniform(15, 35), 1),
|
39 |
+
'humidity': round(random.uniform(30, 90), 1),
|
40 |
+
'wind_speed': round(random.uniform(0, 50), 1)
|
41 |
+
}
|
42 |
+
|
43 |
+
class VoltageMonitor:
|
44 |
+
"""Enhanced voltage monitoring with more sensitive thresholds"""
|
45 |
+
|
46 |
+
def __init__(self, nominal_voltage: float = 230.0, tolerance: float = 0.1):
|
47 |
+
self.nominal_voltage = nominal_voltage
|
48 |
+
self.tolerance = tolerance
|
49 |
+
self.min_voltage = nominal_voltage * (1 - tolerance)
|
50 |
+
self.max_voltage = nominal_voltage * (1 + tolerance)
|
51 |
+
|
52 |
+
def measure_voltage(self, weather_impact: float) -> float:
|
53 |
+
"""Simulates voltage measurement with more dramatic weather impact"""
|
54 |
+
# Increased base variation
|
55 |
+
base_variation = random.uniform(-10, 10)
|
56 |
+
|
57 |
+
# More dramatic weather impact
|
58 |
+
weather_variation = (1 - weather_impact) * 40 # Doubled impact
|
59 |
+
|
60 |
+
# Add sudden spikes during bad weather
|
61 |
+
if weather_impact < 0.7: # During storms
|
62 |
+
spike_chance = random.random()
|
63 |
+
if spike_chance < 0.3: # 30% chance of spike
|
64 |
+
weather_variation *= random.uniform(1.5, 2.0)
|
65 |
+
print("\n⚡ VOLTAGE SPIKE DETECTED! ⚡")
|
66 |
+
|
67 |
+
measured_voltage = self.nominal_voltage + base_variation + weather_variation
|
68 |
+
return round(measured_voltage, 2)
|
69 |
+
|
70 |
+
def analyze_voltage(self, voltage: float) -> Dict:
|
71 |
+
"""Analyzes voltage with detailed status information"""
|
72 |
+
if voltage < self.min_voltage:
|
73 |
+
status = "Low"
|
74 |
+
severity = "Severe" if voltage < self.min_voltage * 0.9 else "Moderate"
|
75 |
+
elif voltage > self.max_voltage:
|
76 |
+
status = "High"
|
77 |
+
severity = "Severe" if voltage > self.max_voltage * 1.1 else "Moderate"
|
78 |
+
else:
|
79 |
+
status = "Normal"
|
80 |
+
severity = "Normal"
|
81 |
+
|
82 |
+
return {
|
83 |
+
'voltage': voltage,
|
84 |
+
'status': status,
|
85 |
+
'severity': severity,
|
86 |
+
'deviation': round(abs(voltage - self.nominal_voltage), 2),
|
87 |
+
'deviation_percentage': round((abs(voltage - self.nominal_voltage) / self.nominal_voltage) * 100, 2),
|
88 |
+
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
89 |
+
}
|
90 |
+
|
91 |
+
class VoltageStabilizer:
|
92 |
+
"""Enhanced voltage stabilization system"""
|
93 |
+
|
94 |
+
def __init__(self):
|
95 |
+
self.correction_factor = 1.0
|
96 |
+
self.correction_history = []
|
97 |
+
|
98 |
+
def calculate_correction(self, voltage: float, nominal_voltage: float) -> float:
|
99 |
+
"""Calculates required correction with improved accuracy"""
|
100 |
+
base_correction = nominal_voltage / voltage if voltage != 0 else 1.0
|
101 |
+
|
102 |
+
# Add fine-tuning based on deviation
|
103 |
+
deviation = abs(voltage - nominal_voltage)
|
104 |
+
if deviation > 20: # Large deviation
|
105 |
+
fine_tune = 0.05 # Additional 5% correction
|
106 |
+
base_correction *= (1 + fine_tune if voltage < nominal_voltage else 1 - fine_tune)
|
107 |
+
|
108 |
+
return base_correction
|
109 |
+
|
110 |
+
def apply_correction(self, voltage: float, weather_impact: float) -> float:
|
111 |
+
"""Applies voltage correction with weather consideration"""
|
112 |
+
correction = self.calculate_correction(voltage, 230.0)
|
113 |
+
corrected_voltage = round(voltage * correction, 2)
|
114 |
+
|
115 |
+
self.correction_history.append({
|
116 |
+
'timestamp': datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
|
117 |
+
'original_voltage': voltage,
|
118 |
+
'corrected_voltage': corrected_voltage,
|
119 |
+
'correction_factor': round(correction, 4)
|
120 |
+
})
|
121 |
+
|
122 |
+
return corrected_voltage
|
123 |
+
|
124 |
+
class NotificationSystem:
|
125 |
+
"""Enhanced notification system with detailed alerts"""
|
126 |
+
|
127 |
+
def __init__(self):
|
128 |
+
self.alert_history = []
|
129 |
+
self.notification_count = 0
|
130 |
+
|
131 |
+
def check_and_notify(self, voltage_data: Dict, weather_data: Dict) -> None:
|
132 |
+
"""Checks conditions and generates detailed notifications"""
|
133 |
+
message = None
|
134 |
+
|
135 |
+
if voltage_data['status'] != "Normal":
|
136 |
+
message = self._generate_alert_message(voltage_data, weather_data)
|
137 |
+
self.alert_history.append({
|
138 |
+
'timestamp': voltage_data['timestamp'],
|
139 |
+
'message': message,
|
140 |
+
'voltage': voltage_data['voltage'],
|
141 |
+
'weather': weather_data['condition'],
|
142 |
+
'severity': voltage_data['severity']
|
143 |
+
})
|
144 |
+
self.notification_count += 1
|
145 |
+
|
146 |
+
if message:
|
147 |
+
self._send_notification(message)
|
148 |
+
|
149 |
+
def _generate_alert_message(self, voltage_data: Dict, weather_data: Dict) -> str:
|
150 |
+
"""Generates detailed alert message with severity and recommendations"""
|
151 |
+
severity_symbol = "🔴" if voltage_data['severity'] == "Severe" else "🟡"
|
152 |
+
|
153 |
+
message = (
|
154 |
+
f"{severity_symbol} VOLTAGE ALERT {severity_symbol}\n"
|
155 |
+
f"Status: {voltage_data['status']} ({voltage_data['severity']})\n"
|
156 |
+
f"Current Voltage: {voltage_data['voltage']}V\n"
|
157 |
+
f"Deviation: {voltage_data['deviation']}V ({voltage_data['deviation_percentage']}%)\n"
|
158 |
+
f"Weather Condition: {weather_data['condition']}\n"
|
159 |
+
f"Time: {voltage_data['timestamp']}\n"
|
160 |
+
f"Recommended Action: "
|
161 |
+
)
|
162 |
+
|
163 |
+
if voltage_data['severity'] == "Severe":
|
164 |
+
message += "Immediate voltage stabilization required!"
|
165 |
+
else:
|
166 |
+
message += "Monitor situation closely and prepare stabilization if needed."
|
167 |
+
|
168 |
+
return message
|
169 |
+
|
170 |
+
def _send_notification(self, message: str) -> None:
|
171 |
+
"""Sends formatted notification"""
|
172 |
+
print("\n" + "=" * 60)
|
173 |
+
print("🚨 NOTIFICATION ALERT 🚨")
|
174 |
+
print(message)
|
175 |
+
print("=" * 60 + "\n")
|
176 |
+
|
177 |
+
def main():
|
178 |
+
"""Main simulation loop with enhanced visualization"""
|
179 |
+
print("Starting Enhanced Weather-based Voltage Monitoring System...")
|
180 |
+
print("Monitoring for voltage fluctuations and weather impacts...\n")
|
181 |
+
|
182 |
+
weather_sim = WeatherSimulator()
|
183 |
+
voltage_monitor = VoltageMonitor()
|
184 |
+
notification_system = NotificationSystem()
|
185 |
+
stabilizer = VoltageStabilizer()
|
186 |
+
|
187 |
+
historical_data = []
|
188 |
+
|
189 |
+
try:
|
190 |
+
while True:
|
191 |
+
# Get weather and measure voltage
|
192 |
+
weather_data = weather_sim.get_current_weather()
|
193 |
+
current_voltage = voltage_monitor.measure_voltage(weather_data['impact_factor'])
|
194 |
+
|
195 |
+
# Analyze voltage
|
196 |
+
voltage_analysis = voltage_monitor.analyze_voltage(current_voltage)
|
197 |
+
|
198 |
+
# Store historical data
|
199 |
+
historical_data.append({
|
200 |
+
'timestamp': voltage_analysis['timestamp'],
|
201 |
+
'voltage': voltage_analysis['voltage'],
|
202 |
+
'weather': weather_data['condition'],
|
203 |
+
'status': voltage_analysis['status'],
|
204 |
+
'severity': voltage_analysis['severity']
|
205 |
+
})
|
206 |
+
|
207 |
+
# Check for notifications
|
208 |
+
notification_system.check_and_notify(voltage_analysis, weather_data)
|
209 |
+
|
210 |
+
# Apply correction if needed
|
211 |
+
if voltage_analysis['status'] != "Normal":
|
212 |
+
corrected_voltage = stabilizer.apply_correction(
|
213 |
+
current_voltage,
|
214 |
+
weather_data['impact_factor']
|
215 |
+
)
|
216 |
+
print(f"⚡ VOLTAGE CORRECTION APPLIED:")
|
217 |
+
print(f"Original: {current_voltage}V → Corrected: {corrected_voltage}V")
|
218 |
+
|
219 |
+
# Display current status
|
220 |
+
print(f"\nTime: {voltage_analysis['timestamp']}")
|
221 |
+
print(f"Weather Condition: {weather_data['condition']} "
|
222 |
+
f"(Impact Factor: {weather_data['impact_factor']:.2f})")
|
223 |
+
print(f"Voltage Status: {voltage_analysis['status']} "
|
224 |
+
f"({voltage_analysis['severity']})")
|
225 |
+
print(f"Current Voltage: {voltage_analysis['voltage']}V "
|
226 |
+
f"(Deviation: {voltage_analysis['deviation_percentage']}%)")
|
227 |
+
|
228 |
+
print("-" * 60)
|
229 |
+
time.sleep(2)
|
230 |
+
|
231 |
+
except KeyboardInterrupt:
|
232 |
+
print("\nSimulation stopped by user")
|
233 |
+
df = pd.DataFrame(historical_data)
|
234 |
+
df.to_csv('voltage_monitoring_history.csv', index=False)
|
235 |
+
print(f"Total alerts generated: {notification_system.notification_count}")
|
236 |
+
print("Historical data saved to voltage_monitoring_history.csv")
|
237 |
+
|
238 |
+
if __name__ == "__main__":
|
239 |
+
main()
|