mriusero commited on
Commit
f5f591a
·
1 Parent(s): 0156020

core: data shape

Browse files
app.py CHANGED
@@ -1,4 +1,5 @@
1
  import gradio as gr
 
2
 
3
  from src.ui import sidebar_ui, dashboard_ui
4
  from src.ui.session import session_state
@@ -12,10 +13,10 @@ custom_theme = gr.themes.Base(
12
 
13
  STATE = {
14
  "running": False,
15
- "current_time": None,
16
- "part_id": None,
 
17
  "data": {},
18
- "efficiency": {},
19
  }
20
 
21
  with gr.Blocks(theme=custom_theme) as demo:
 
1
  import gradio as gr
2
+ from datetime import datetime
3
 
4
  from src.ui import sidebar_ui, dashboard_ui
5
  from src.ui.session import session_state
 
13
 
14
  STATE = {
15
  "running": False,
16
+ "date": datetime.now(),
17
+ "part_id": 0,
18
+ "status": {},
19
  "data": {},
 
20
  }
21
 
22
  with gr.Blocks(theme=custom_theme) as demo:
src/production/flow.py CHANGED
@@ -13,7 +13,7 @@ async def generate_data(state):
13
  """
14
  Generate synthetic production data for a manufacturing process.
15
  """
16
- current_time = state["current_time"] if state["current_time"] else datetime.now()
17
  part_id = state["part_id"] if state["part_id"] else 0
18
 
19
  non_compliance_rates = {
@@ -95,5 +95,5 @@ async def generate_data(state):
95
 
96
  current_time += timedelta(seconds=1)
97
 
98
- state["current_time"] = current_time
99
  state["part_id"] = part_id
 
13
  """
14
  Generate synthetic production data for a manufacturing process.
15
  """
16
+ current_time = state["date"] if state["date"] else datetime.now()
17
  part_id = state["part_id"] if state["part_id"] else 0
18
 
19
  non_compliance_rates = {
 
95
 
96
  current_time += timedelta(seconds=1)
97
 
98
+ state["date"] = current_time
99
  state["part_id"] = part_id
src/production/metrics/machine.py CHANGED
@@ -35,6 +35,14 @@ async def machine_metrics(raw_data):
35
  mtbf = operating_time / downtime_count if downtime_count > 0 else pd.Timedelta(0)
36
  mttr = unplanned_stop_time / downtime_count if downtime_count > 0 else pd.Timedelta(0)
37
 
 
 
 
 
 
 
 
 
38
  return {
39
  "opening_time": str(opening_time),
40
  "required_time": str(required_time),
@@ -42,10 +50,11 @@ async def machine_metrics(raw_data):
42
  "operating_time": str(operating_time),
43
  "net_time": str(net_time),
44
  "useful_time": str(useful_time),
45
- "quality_rate": quality_rate,
46
- "operating_rate": operating_rate,
47
- "availability_rate": availability_rate,
48
- "OEE": OEE,
 
49
  "MTBF": str(mtbf),
50
  "MTTR": str(mttr)
51
  }
 
35
  mtbf = operating_time / downtime_count if downtime_count > 0 else pd.Timedelta(0)
36
  mttr = unplanned_stop_time / downtime_count if downtime_count > 0 else pd.Timedelta(0)
37
 
38
+ # Quality rate per tool ID
39
+ quality_by_tool = {}
40
+ for tool_id in [1, 2]:
41
+ tool_df = df[df["Tool ID"] == tool_id]
42
+ total = len(tool_df)
43
+ ok_count = (tool_df["Compliance"] == "OK").sum()
44
+ quality_by_tool[f"quality_rate_tool_{tool_id}"] = round(((ok_count / total) * 100), 2) if total > 0 else 0
45
+
46
  return {
47
  "opening_time": str(opening_time),
48
  "required_time": str(required_time),
 
50
  "operating_time": str(operating_time),
51
  "net_time": str(net_time),
52
  "useful_time": str(useful_time),
53
+ "quality_rate": round(quality_rate, 2),
54
+ **quality_by_tool,
55
+ "operating_rate": round(operating_rate, 2),
56
+ "availability_rate": round(availability_rate, 2),
57
+ "OEE": round(OEE, 2),
58
  "MTBF": str(mtbf),
59
  "MTTR": str(mttr)
60
  }
src/ui/dashboard.py CHANGED
@@ -31,7 +31,7 @@ async def dataflow(state):
31
  state['data']['tools'].setdefault(f'tool_{i}', pd.DataFrame())
32
 
33
  state['data'].setdefault('issues', {})
34
- state.setdefault('efficiency', {})
35
 
36
  # Check running state
37
  if state.get('running'):
@@ -64,7 +64,7 @@ async def dataflow(state):
64
  ] + [
65
  pd.DataFrame(state['data']['issues'])
66
  ] + [
67
- state['efficiency']
68
  ]
69
  state['last_hash'] = current_hash
70
 
@@ -74,12 +74,38 @@ async def dataflow(state):
74
  for tool, df in tools_data.items():
75
  state['data']['tools'][tool] = df
76
 
 
77
  machine_data = await machine_metrics(raw_data)
78
- state['efficiency'] = machine_data
79
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
80
  issues = await fetch_issues(raw_data)
81
  state['data']['issues'] = issues
82
 
 
83
  return (
84
  [
85
  pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
@@ -89,7 +115,7 @@ async def dataflow(state):
89
  ] + [
90
  pd.DataFrame(state['data']['issues'])
91
  ] + [
92
- state['efficiency']
93
  ]
94
  )
95
 
@@ -108,20 +134,18 @@ def init_components(n=TOOLS_COUNT):
108
  tool_plots = []
109
  general_plots = []
110
 
111
- # Tool metrics displays and their plots
112
- for i in range(1, n + 1):
113
  display = ToolMetricsDisplay()
114
  displays.append(display)
115
  tool_plots.extend(display.tool_block(df=pd.DataFrame(), id=i))
116
 
117
- # General metrics display and its plots
118
- main_display = GeneralMetricsDisplay()
119
  displays.append(main_display)
120
  general_plots.extend(
121
  main_display.general_block(
122
  all_tools_df=pd.DataFrame(),
123
  issues_df=pd.DataFrame(),
124
- efficiency_data={}
125
  )
126
  )
127
  return displays, tool_plots, general_plots
@@ -141,18 +165,16 @@ async def on_tick(state, displays):
141
  tool_dfs = data[:-3] # all individual tool DataFrames
142
  all_tools_df = data[-3] # 'all' tools DataFrame
143
  issues_df = data[-2] # issues DataFrame
144
- efficiency_data = data[-1] # efficiency dict
145
 
146
- # General plots
147
- general_display = displays[-1]
148
  general_plots = general_display.refresh(
149
  all_tools_df=all_tools_df,
150
  issues_df=issues_df,
151
- efficiency_data=efficiency_data
152
  )
153
 
154
- # Tool-specific plots
155
- tool_plots = []
156
  for df, display in zip(tool_dfs, displays[:-1]):
157
  tool_plots.extend(display.refresh(df=df))
158
 
 
31
  state['data']['tools'].setdefault(f'tool_{i}', pd.DataFrame())
32
 
33
  state['data'].setdefault('issues', {})
34
+ state.setdefault('status', {})
35
 
36
  # Check running state
37
  if state.get('running'):
 
64
  ] + [
65
  pd.DataFrame(state['data']['issues'])
66
  ] + [
67
+ state['status']
68
  ]
69
  state['last_hash'] = current_hash
70
 
 
74
  for tool, df in tools_data.items():
75
  state['data']['tools'][tool] = df
76
 
77
+ # Get machine metrics
78
  machine_data = await machine_metrics(raw_data)
79
+ state['status'] = machine_data
80
 
81
+ # Get tools stats
82
+ for tool in ['tool_1', 'tool_2', 'all']:
83
+ df = state['data']['tools'].get(tool, pd.DataFrame())
84
+ if df.empty or 'Timestamp' not in df.columns:
85
+ continue
86
+
87
+ df = df.copy()
88
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce')
89
+ df.dropna(subset=['Timestamp'], inplace=True)
90
+
91
+ if df.empty:
92
+ continue
93
+
94
+ idx = df['Timestamp'].idxmax()
95
+
96
+ for cote in ['pos', 'ori']:
97
+ for metric_type in ['cp', 'cpk']:
98
+ column = f"{cote}_rolling_{metric_type}"
99
+ if column in df.columns:
100
+ value = df.at[idx, column]
101
+ key = f"{tool}_{metric_type}_{cote}"
102
+ state['status'][key] = round(value, 4)
103
+
104
+ # Get issues
105
  issues = await fetch_issues(raw_data)
106
  state['data']['issues'] = issues
107
 
108
+ # Update situation
109
  return (
110
  [
111
  pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
 
115
  ] + [
116
  pd.DataFrame(state['data']['issues'])
117
  ] + [
118
+ state['status']
119
  ]
120
  )
121
 
 
134
  tool_plots = []
135
  general_plots = []
136
 
137
+ for i in range(1, n + 1): # Tool metrics displays
 
138
  display = ToolMetricsDisplay()
139
  displays.append(display)
140
  tool_plots.extend(display.tool_block(df=pd.DataFrame(), id=i))
141
 
142
+ main_display = GeneralMetricsDisplay() # General metrics display
 
143
  displays.append(main_display)
144
  general_plots.extend(
145
  main_display.general_block(
146
  all_tools_df=pd.DataFrame(),
147
  issues_df=pd.DataFrame(),
148
+ status={}
149
  )
150
  )
151
  return displays, tool_plots, general_plots
 
165
  tool_dfs = data[:-3] # all individual tool DataFrames
166
  all_tools_df = data[-3] # 'all' tools DataFrame
167
  issues_df = data[-2] # issues DataFrame
168
+ status = data[-1] # status dict
169
 
170
+ general_display = displays[-1] # General plots
 
171
  general_plots = general_display.refresh(
172
  all_tools_df=all_tools_df,
173
  issues_df=issues_df,
174
+ status=status
175
  )
176
 
177
+ tool_plots = [] # Tool-specific plots
 
178
  for df, display in zip(tool_dfs, displays[:-1]):
179
  tool_plots.extend(display.refresh(df=df))
180
 
src/ui/graphs/general_graphs.py CHANGED
@@ -209,7 +209,7 @@ class GeneralMetricsDisplay:
209
  )
210
  return fig
211
 
212
- def general_block(self, all_tools_df, issues_df, efficiency_data):
213
  header = f"Metrics Summary"
214
  html_content = f"""
215
  <div style="display: flex; align-items: center; justify-content: flex-start; width: 100%;">
@@ -225,24 +225,24 @@ class GeneralMetricsDisplay:
225
  total_count = gr.Plot(
226
  self.kpi_value(
227
  value=self.get_max_part_id(all_tools_df),
228
- title="Total Count"
229
  )
230
  )
231
  total_time = gr.Plot(
232
  self.kpi_value(
233
- value=efficiency_data.get("opening_time", "0 days 00:00:00"),
234
  title="Total Time"
235
  )
236
  )
237
  mtbf_plot = gr.Plot(
238
  self.kpi_value(
239
- value=efficiency_data.get("MTBF", "0 days 00:00:00"),
240
  title="MTBF"
241
  )
242
  )
243
  mttr_plot = gr.Plot(
244
  self.kpi_value(
245
- value=efficiency_data.get("MTTR", "0 days 00:00:00"),
246
  title="MTTR"
247
  )
248
  )
@@ -252,21 +252,21 @@ class GeneralMetricsDisplay:
252
  with gr.Row(height=150):
253
  oee_plot = gr.Plot(
254
  self.kpi_rate(
255
- percentage=efficiency_data.get('OEE', 0),
256
  title="OEE"
257
  )
258
  )
259
  with gr.Row(height=150):
260
  quality_rate_plot = gr.Plot(
261
  self.kpi_rate(
262
- percentage=efficiency_data.get("quality_rate", 0),
263
  title="Quality Rate"
264
  )
265
  )
266
  with gr.Row(height=150):
267
  availability_plot = gr.Plot(
268
  self.kpi_rate(
269
- percentage=efficiency_data.get("availability_rate", 0),
270
  title="Availability"
271
  )
272
  )
@@ -284,14 +284,14 @@ class GeneralMetricsDisplay:
284
  ]
285
  return self.plots
286
 
287
- def refresh(self, all_tools_df, issues_df, efficiency_data):
288
  return [
289
- self.kpi_value(value=self.get_max_part_id(all_tools_df), title="Total Count"),
290
- self.kpi_value(value=efficiency_data.get("opening_time", "0 days 00:00:00"), title="Total Time"),
291
- self.kpi_rate(percentage=efficiency_data.get('OEE', 0), title="OEE"),
292
- self.kpi_rate(percentage=efficiency_data.get("quality_rate", 0), title="Quality Rate"),
293
- self.kpi_rate(percentage=efficiency_data.get("availability_rate", 0), title="Availability"),
294
- self.kpi_value(value=efficiency_data.get("MTBF", "0 days 00:00:00"), title="MTBF"),
295
- self.kpi_value(value=efficiency_data.get("MTTR", "0 days 00:00:00"), title="MTTR"),
296
  self.pareto(issues_df, error_col='Error Code')
297
  ]
 
209
  )
210
  return fig
211
 
212
+ def general_block(self, all_tools_df, issues_df, status):
213
  header = f"Metrics Summary"
214
  html_content = f"""
215
  <div style="display: flex; align-items: center; justify-content: flex-start; width: 100%;">
 
225
  total_count = gr.Plot(
226
  self.kpi_value(
227
  value=self.get_max_part_id(all_tools_df),
228
+ title="Total Count (parts)"
229
  )
230
  )
231
  total_time = gr.Plot(
232
  self.kpi_value(
233
+ value=status.get("opening_time", "0 days 00:00:00"),
234
  title="Total Time"
235
  )
236
  )
237
  mtbf_plot = gr.Plot(
238
  self.kpi_value(
239
+ value=status.get("MTBF", "0 days 00:00:00"),
240
  title="MTBF"
241
  )
242
  )
243
  mttr_plot = gr.Plot(
244
  self.kpi_value(
245
+ value=status.get("MTTR", "0 days 00:00:00"),
246
  title="MTTR"
247
  )
248
  )
 
252
  with gr.Row(height=150):
253
  oee_plot = gr.Plot(
254
  self.kpi_rate(
255
+ percentage=status.get('OEE', 0),
256
  title="OEE"
257
  )
258
  )
259
  with gr.Row(height=150):
260
  quality_rate_plot = gr.Plot(
261
  self.kpi_rate(
262
+ percentage=status.get("quality_rate", 0),
263
  title="Quality Rate"
264
  )
265
  )
266
  with gr.Row(height=150):
267
  availability_plot = gr.Plot(
268
  self.kpi_rate(
269
+ percentage=status.get("availability_rate", 0),
270
  title="Availability"
271
  )
272
  )
 
284
  ]
285
  return self.plots
286
 
287
+ def refresh(self, all_tools_df, issues_df, status):
288
  return [
289
+ self.kpi_value(value=self.get_max_part_id(all_tools_df), title="Total Count (parts)"),
290
+ self.kpi_value(value=status.get("opening_time", "0 days 00:00:00"), title="Total Time"),
291
+ self.kpi_rate(percentage=status.get('OEE', 0), title="OEE"),
292
+ self.kpi_rate(percentage=status.get("quality_rate", 0), title="Quality Rate"),
293
+ self.kpi_rate(percentage=status.get("availability_rate", 0), title="Availability"),
294
+ self.kpi_value(value=status.get("MTBF", "0 days 00:00:00"), title="MTBF"),
295
+ self.kpi_value(value=status.get("MTTR", "0 days 00:00:00"), title="MTTR"),
296
  self.pareto(issues_df, error_col='Error Code')
297
  ]