DSatishchandra commited on
Commit
35973e5
·
verified ·
1 Parent(s): b74770b

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +188 -55
app.py CHANGED
@@ -10,7 +10,7 @@ SITES = {
10
  "Hyderabad": [17.385044, 78.486671],
11
  "Gadwal": [16.2351, 77.8052],
12
  "Kurnool": [15.8281, 78.0373],
13
- "Ballari": [12.9716, 77.5946]
14
  }
15
 
16
  # ---- Helper Functions ----
@@ -32,11 +32,28 @@ def simulate_pole(pole_id, site_name):
32
  vibration = round(random.uniform(0, 5), 2)
33
  camera_status = random.choice(['Online', 'Offline'])
34
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  alert_level = 'Green'
36
- if tilt_angle > 30 or vibration > 3:
37
- alert_level = 'Yellow'
38
- if tilt_angle > 40 or vibration > 4.5:
39
- alert_level = 'Red'
 
40
 
41
  health_score = max(0, 100 - (tilt_angle + vibration * 10))
42
  timestamp = datetime.now() - timedelta(hours=random.randint(0, 6))
@@ -56,66 +73,182 @@ def simulate_pole(pole_id, site_name):
56
  'Camera Status': camera_status,
57
  'Health Score': round(health_score, 2),
58
  'Alert Level': alert_level,
 
59
  'Last Checked': timestamp.strftime('%Y-%m-%d %H:%M:%S')
60
  }
61
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
62
  # ---- Streamlit UI ----
63
  st.set_page_config(page_title="Smart Pole Monitoring", layout="wide")
64
  st.title("🌍 Smart Renewable Pole Monitoring - Multi-Site")
65
 
66
- selected_site = st.text_input("Enter site to view (Hyderabad, Gadwal, Kurnool, Bangalore):", "Hyderabad")
67
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
68
  if selected_site in SITES:
69
- with st.spinner(f"Simulating poles at {selected_site}..."):
70
- poles_data = [simulate_pole(i + 1, site) for site in SITES for i in range(POLES_PER_SITE)]
71
  df = pd.DataFrame(poles_data)
72
  site_df = df[df['Site'] == selected_site]
73
 
74
- # Summary Metrics
75
- col1, col2, col3 = st.columns(3)
76
- col1.metric("Total Poles", site_df.shape[0])
77
- col2.metric("Red Alerts", site_df[site_df['Alert Level'] == 'Red'].shape[0])
78
- col3.metric("Power Insufficiencies", site_df[site_df['Power Status'] == 'Insufficient'].shape[0])
79
-
80
- # Table View
81
- st.subheader(f"📋 Pole Data Table for {selected_site}")
82
- with st.expander("Filter Options"):
83
- alert_filter = st.multiselect("Alert Level", options=site_df['Alert Level'].unique(), default=site_df['Alert Level'].unique())
84
- camera_filter = st.multiselect("Camera Status", options=site_df['Camera Status'].unique(), default=site_df['Camera Status'].unique())
85
-
86
- filtered_df = site_df[(site_df['Alert Level'].isin(alert_filter)) & (site_df['Camera Status'].isin(camera_filter))]
87
- st.dataframe(filtered_df, use_container_width=True)
88
-
89
- # Charts
90
- st.subheader("📊 Energy Generation Comparison")
91
- st.bar_chart(site_df[['Solar (kWh)', 'Wind (kWh)']].mean())
92
-
93
- st.subheader("📈 Tilt vs. Vibration")
94
- st.scatter_chart(site_df[['Tilt Angle (°)', 'Vibration (g)']])
95
-
96
- # Map with Red Alerts
97
- st.subheader("📍 Red Alert Pole Locations")
98
- red_df = site_df[site_df['Alert Level'] == 'Red']
99
- if not red_df.empty:
100
- st.pydeck_chart(pdk.Deck(
101
- initial_view_state=pdk.ViewState(
102
- latitude=SITES[selected_site][0],
103
- longitude=SITES[selected_site][1],
104
- zoom=12,
105
- pitch=50
106
- ),
107
- layers=[
108
- pdk.Layer(
109
- 'ScatterplotLayer',
110
- data=red_df,
111
- get_position='[Longitude, Latitude]',
112
- get_color='[255, 0, 0, 160]',
113
- get_radius=100,
114
- )
115
- ]
116
- ))
117
- else:
118
- st.info("No red alerts at this time.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
119
 
120
  else:
121
- st.warning("Invalid site. Please enter one of: Hyderabad, Gadwal, Kurnool, Ballari")
 
10
  "Hyderabad": [17.385044, 78.486671],
11
  "Gadwal": [16.2351, 77.8052],
12
  "Kurnool": [15.8281, 78.0373],
13
+ "Ballari": [15.1394, 76.9214] # Updated coordinates for Ballari
14
  }
15
 
16
  # ---- Helper Functions ----
 
32
  vibration = round(random.uniform(0, 5), 2)
33
  camera_status = random.choice(['Online', 'Offline'])
34
 
35
+ # Anomaly detection
36
+ anomalies = []
37
+ if solar_kwh < 4.0:
38
+ anomalies.append("Low Solar Output")
39
+ if wind_kwh < 0.7:
40
+ anomalies.append("Low Wind Output")
41
+ if tilt_angle > 30:
42
+ anomalies.append("Pole Tilt Risk")
43
+ if vibration > 3:
44
+ anomalies.append("Vibration Alert")
45
+ if camera_status == 'Offline':
46
+ anomalies.append("Camera Offline")
47
+ if power_status == 'Insufficient':
48
+ anomalies.append("Power Insufficient")
49
+
50
+ # Alert level logic
51
  alert_level = 'Green'
52
+ if anomalies:
53
+ if tilt_angle > 40 or vibration > 4.5 or len(anomalies) > 1:
54
+ alert_level = 'Red'
55
+ else:
56
+ alert_level = 'Yellow'
57
 
58
  health_score = max(0, 100 - (tilt_angle + vibration * 10))
59
  timestamp = datetime.now() - timedelta(hours=random.randint(0, 6))
 
73
  'Camera Status': camera_status,
74
  'Health Score': round(health_score, 2),
75
  'Alert Level': alert_level,
76
+ 'Anomalies': ';'.join(anomalies) if anomalies else 'None',
77
  'Last Checked': timestamp.strftime('%Y-%m-%d %H:%M:%S')
78
  }
79
 
80
+ # ---- Custom CSS for Advanced UI ----
81
+ st.markdown("""
82
+ <style>
83
+ .stApp {
84
+ background-color: #f5f7fa;
85
+ }
86
+ .metric-card {
87
+ background-color: white;
88
+ padding: 15px;
89
+ border-radius: 10px;
90
+ box-shadow: 0 4px 8px rgba(0,0,0,0.1);
91
+ text-align: center;
92
+ }
93
+ .red-alert {
94
+ background-color: #ffe6e6;
95
+ color: #d32f2f;
96
+ padding: 10px;
97
+ border-radius: 5px;
98
+ font-weight: bold;
99
+ }
100
+ .sidebar .sidebar-content {
101
+ background-color: #ffffff;
102
+ border-right: 1px solid #e0e0e0;
103
+ }
104
+ h1, h2, h3 {
105
+ color: #1a237e;
106
+ }
107
+ </style>
108
+ """, unsafe_allow_html=True)
109
+
110
  # ---- Streamlit UI ----
111
  st.set_page_config(page_title="Smart Pole Monitoring", layout="wide")
112
  st.title("🌍 Smart Renewable Pole Monitoring - Multi-Site")
113
 
114
+ # Sidebar with enhanced controls
115
+ with st.sidebar:
116
+ st.header("🛠️ Control Panel")
117
+ with st.expander("Site Selection", expanded=True):
118
+ selected_site = st.selectbox(
119
+ "Select Site",
120
+ options=list(SITES.keys()),
121
+ index=0,
122
+ help="Choose a site to monitor poles."
123
+ )
124
+ with st.expander("Simulation Settings"):
125
+ num_poles = st.slider("Number of Poles per Site", 5, 50, POLES_PER_SITE)
126
+ simulate_faults = st.checkbox("Simulate Faults", value=True)
127
+ with st.expander("Filters"):
128
+ alert_filter = st.multiselect(
129
+ "Alert Level",
130
+ options=['Green', 'Yellow', 'Red'],
131
+ default=['Green', 'Yellow', 'Red']
132
+ )
133
+ camera_filter = st.multiselect(
134
+ "Camera Status",
135
+ options=['Online', 'Offline'],
136
+ default=['Online', 'Offline']
137
+ )
138
+
139
+ # Simulate data
140
  if selected_site in SITES:
141
+ with st.spinner(f"Simulating {num_poles} poles at {selected_site}..."):
142
+ poles_data = [simulate_pole(i + 1, site) for site in SITES for i in range(num_poles)]
143
  df = pd.DataFrame(poles_data)
144
  site_df = df[df['Site'] == selected_site]
145
 
146
+ # Tabs for different views
147
+ tab1, tab2, tab3, tab4 = st.tabs(["📊 Dashboard", "📋 Data Table", "📈 Charts", "📍 Map"])
148
+
149
+ with tab1:
150
+ # Dashboard with metrics
151
+ st.subheader("System Overview")
152
+ red_alerts_count = site_df[site_df['Alert Level'] == 'Red'].shape[0]
153
+ if red_alerts_count > 0:
154
+ st.markdown(f"<div class='red-alert'>🚨 {red_alerts_count} Red Alerts Detected! Immediate Action Required!</div>", unsafe_allow_html=True)
155
+ else:
156
+ st.success(" No Red Alerts Detected")
157
+
158
+ col1, col2, col3, col4 = st.columns(4)
159
+ with col1:
160
+ st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
161
+ st.metric("Total Poles", site_df.shape[0])
162
+ st.markdown("</div>", unsafe_allow_html=True)
163
+ with col2:
164
+ st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
165
+ st.metric("Red Alerts", red_alerts_count, delta_color="inverse")
166
+ st.markdown("</div>", unsafe_allow_html=True)
167
+ with col3:
168
+ st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
169
+ st.metric("Power Insufficiencies", site_df[site_df['Power Status'] == 'Insufficient'].shape[0])
170
+ st.markdown("</div>", unsafe_allow_html=True)
171
+ with col4:
172
+ st.markdown("<div class='metric-card'>", unsafe_allow_html=True)
173
+ st.metric("Average Health Score", round(site_df['Health Score'].mean(), 2))
174
+ st.markdown("</div>", unsafe_allow_html=True)
175
+
176
+ # Red Alerts Summary
177
+ red_df = site_df[site_df['Alert Level'] == 'Red']
178
+ if not red_df.empty:
179
+ st.subheader("🚨 Critical Red Alerts")
180
+ st.dataframe(
181
+ red_df[['Pole ID', 'Anomalies', 'Tilt Angle (°)', 'Vibration (g)', 'Power Status', 'Camera Status', 'Health Score', 'Last Checked']],
182
+ use_container_width=True
183
+ )
184
+ csv = red_df.to_csv(index=False)
185
+ st.download_button(
186
+ label="Download Red Alerts CSV",
187
+ data=csv,
188
+ file_name=f"{selected_site}_red_alerts.csv",
189
+ mime="text/csv"
190
+ )
191
+
192
+ with tab2:
193
+ # Filtered Data Table
194
+ st.subheader(f"Pole Data for {selected_site}")
195
+ filtered_df = site_df[
196
+ (site_df['Alert Level'].isin(alert_filter)) &
197
+ (site_df['Camera Status'].isin(camera_filter))
198
+ ]
199
+
200
+ # Conditional formatting for red alerts
201
+ def highlight_red_alerts(row):
202
+ return ['background-color: #ffe6e6' if row['Alert Level'] == 'Red' else '' for _ in row]
203
+
204
+ st.dataframe(
205
+ filtered_df[['Pole ID', 'Anomalies', 'Solar (kWh)', 'Wind (kWh)', 'Power Status', 'Tilt Angle (°)', 'Vibration (g)', 'Camera Status', 'Health Score', 'Alert Level', 'Last Checked']].style.apply(highlight_red_alerts, axis=1),
206
+ use_container_width=True
207
+ )
208
+
209
+ with tab3:
210
+ # Charts
211
+ st.subheader("Energy Generation Comparison")
212
+ st.bar_chart(site_df[['Solar (kWh)', 'Wind (kWh)']].mean())
213
+
214
+ st.subheader("Tilt vs. Vibration")
215
+ scatter_data = site_df[['Tilt Angle (°)', 'Vibration (g)', 'Alert Level']].copy()
216
+ scatter_data['color'] = scatter_data['Alert Level'].map({
217
+ 'Green': '[0, 255, 0, 160]',
218
+ 'Yellow': '[255, 255, 0, 160]',
219
+ 'Red': '[255, 0, 0, 160]'
220
+ })
221
+ st.scatter_chart(scatter_data[['Tilt Angle (°)', 'Vibration (g)']])
222
+
223
+ with tab4:
224
+ # Map with Red Alerts
225
+ st.subheader("Red Alert Pole Locations")
226
+ if not red_df.empty:
227
+ st.pydeck_chart(pdk.Deck(
228
+ initial_view_state=pdk.ViewState(
229
+ latitude=SITES[selected_site][0],
230
+ longitude=SITES[selected_site][1],
231
+ zoom=12,
232
+ pitch=50
233
+ ),
234
+ layers=[
235
+ pdk.Layer(
236
+ 'ScatterplotLayer',
237
+ data=red_df,
238
+ get_position='[Longitude, Latitude]',
239
+ get_color='[255, 0, 0, 160]',
240
+ get_radius=100,
241
+ pickable=True,
242
+ auto_highlight=True
243
+ )
244
+ ],
245
+ tooltip={
246
+ "html": "<b>Pole ID:</b> {Pole ID}<br><b>Anomalies:</b> {Anomalies}<br><b>Tilt:</b> {Tilt Angle (°)}°<br><b>Vibration:</b> {Vibration (g)}g<br><b>Health Score:</b> {Health Score}",
247
+ "style": {"backgroundColor": "white", "color": "#333"}
248
+ }
249
+ ))
250
+ else:
251
+ st.info("No red alerts at this time.")
252
 
253
  else:
254
+ st.error("Invalid site. Please enter one of: Hyderabad, Gadwal, Kurnool, Ballari")