mriusero commited on
Commit
4d9a2f5
·
1 Parent(s): 7528ee2
src/production/flow.py CHANGED
@@ -1,29 +1,19 @@
1
- import os
2
  import time
3
- import json
4
  import random
5
  import numpy as np
6
  import pandas as pd
7
  from datetime import datetime, timedelta
8
 
9
  from .downtime import machine_errors
10
- from .processing import process
11
 
12
- PRODUCTION = False
13
- PROD_STATE = {
14
- "current_time": None,
15
- "part_id": None,
16
- "data": None
17
- }
18
-
19
- def synthetic_data():
20
  """
21
  Generate synthetic production data for a manufacturing process.
22
  """
23
- global PROD_STATE
24
- data = PROD_STATE["data"] if PROD_STATE["data"] else []
25
- current_time = PROD_STATE["current_time"] if PROD_STATE["current_time"] else datetime.now()
26
- part_id = PROD_STATE["part_id"] if PROD_STATE["part_id"] else 1
27
  non_compliance_rates = {
28
  1: 0.05,
29
  2: 0.10,
@@ -32,7 +22,7 @@ def synthetic_data():
32
  }
33
 
34
  for _ in range(1000):
35
- if not PRODUCTION:
36
  break
37
 
38
  if random.random() < 0.2:
@@ -40,7 +30,7 @@ def synthetic_data():
40
  error = machine_errors[error_key]
41
  downtime = error["downtime"]
42
 
43
- data.append({
44
  "timestamp": current_time.strftime("%Y-%m-%d %H:%M:%S"),
45
  "event": "Machine Error",
46
  "error_code": error_key,
@@ -61,7 +51,7 @@ def synthetic_data():
61
 
62
  compliance = 'OK' if (0.3 <= position <= 0.5) and (0.2 <= orientation <= 0.6) else 'NOK'
63
 
64
- data.append({
65
  "part_id": part_id,
66
  "timestamp": current_time.strftime("%Y-%m-%d %H:%M:%S"),
67
  "position": round(position, 4),
@@ -76,18 +66,11 @@ def synthetic_data():
76
 
77
  current_time += timedelta(seconds=1)
78
 
79
- PROD_STATE["data"] = data
80
- PROD_STATE["current_time"] = current_time
81
- PROD_STATE["part_id"] = part_id
82
-
83
- return data
84
 
85
- def compile(data):
86
- """
87
- Update production data in real-time.
88
- """
89
  raw_data = []
90
- for row in data:
91
  raw_data.append({
92
  "Part ID": row.get("part_id", "N/A"),
93
  "Timestamp": row.get("timestamp", "N/A"),
@@ -101,42 +84,5 @@ def compile(data):
101
  "Downtime Start": row.get("downtime_start", "N/A"),
102
  "Downtime End": row.get("downtime_end", "N/A")
103
  })
104
- return pd.DataFrame(raw_data)
105
-
106
-
107
- def play_fn():
108
- """
109
- Start the production simulation and generate synthetic data.
110
- """
111
- print("=== STARTING PRODUCTION ===")
112
- global PRODUCTION
113
- PRODUCTION = True
114
- while PRODUCTION:
115
- data = synthetic_data()
116
- raw_data = compile(data)
117
- tools_dfs, machine_json = process(raw_data)
118
- yield [tools_dfs[key] for key in tools_dfs.keys()] + [machine_json]
119
 
120
-
121
- def stop_fn():
122
- """
123
- Pause the production simulation.
124
- """
125
- print("--- PAUSE ---")
126
- global PRODUCTION
127
- PRODUCTION = False
128
-
129
-
130
- def reset_fn():
131
- """
132
- Reset the production state and clear the data.
133
- """
134
- os.system('clear')
135
- print("=== RESET DONE ===")
136
- global PRODUCTION, PROD_STATE
137
- PRODUCTION = False
138
- PROD_STATE = {
139
- "current_time": None,
140
- "part_id": None,
141
- "data": None
142
- }
 
 
1
  import time
 
2
  import random
3
  import numpy as np
4
  import pandas as pd
5
  from datetime import datetime, timedelta
6
 
7
  from .downtime import machine_errors
 
8
 
9
+ def generate_data(state):
 
 
 
 
 
 
 
10
  """
11
  Generate synthetic production data for a manufacturing process.
12
  """
13
+ current_time = state["current_time"] if state["current_time"] else datetime.now()
14
+ part_id = state["part_id"] if state["part_id"] else 0
15
+ if 'raw' not in state['data']:
16
+ state['data']['raw'] = []
17
  non_compliance_rates = {
18
  1: 0.05,
19
  2: 0.10,
 
22
  }
23
 
24
  for _ in range(1000):
25
+ if not state["running"]:
26
  break
27
 
28
  if random.random() < 0.2:
 
30
  error = machine_errors[error_key]
31
  downtime = error["downtime"]
32
 
33
+ state['data']['raw'].append({
34
  "timestamp": current_time.strftime("%Y-%m-%d %H:%M:%S"),
35
  "event": "Machine Error",
36
  "error_code": error_key,
 
51
 
52
  compliance = 'OK' if (0.3 <= position <= 0.5) and (0.2 <= orientation <= 0.6) else 'NOK'
53
 
54
+ state['data']['raw'].append({
55
  "part_id": part_id,
56
  "timestamp": current_time.strftime("%Y-%m-%d %H:%M:%S"),
57
  "position": round(position, 4),
 
66
 
67
  current_time += timedelta(seconds=1)
68
 
69
+ state["current_time"] = current_time
70
+ state["part_id"] = part_id
 
 
 
71
 
 
 
 
 
72
  raw_data = []
73
+ for row in state['data']['raw']:
74
  raw_data.append({
75
  "Part ID": row.get("part_id", "N/A"),
76
  "Timestamp": row.get("timestamp", "N/A"),
 
84
  "Downtime Start": row.get("downtime_start", "N/A"),
85
  "Downtime End": row.get("downtime_end", "N/A")
86
  })
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
87
 
88
+ state['data']['raw_df'] = pd.DataFrame(raw_data)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
src/ui/dashboard.py CHANGED
@@ -1,42 +1,98 @@
 
 
1
  import gradio as gr
2
- import pandas as pd
3
-
4
- from src.production.flow import play_fn, stop_fn, reset_fn
5
-
6
- def dashboard_ui():
7
- with gr.Tab("Dashboard"):
8
- with gr.Row():
9
- with gr.Blocks():
10
- play = gr.Button("▶️ Play")
11
- stop = gr.Button("⏸️ Pause")
12
- reset = gr.Button("🔄 Reset")
13
-
14
- with gr.Row():
15
- with gr.Column(scale=3):
16
- json_output = {}
17
- json_component = gr.JSON(label="Machine JSON", value=json_output, visible=True)
18
-
19
- df_outputs = {
20
- "DataFrame 1": pd.DataFrame(),
21
- "DataFrame 2": pd.DataFrame(),
22
- "DataFrame 3": pd.DataFrame(),
23
- "DataFrame 4": pd.DataFrame(),
24
- "DataFrame 5": pd.DataFrame(),
25
- }
26
- df_components = [gr.DataFrame(label=df_name, visible=False) for df_name in df_outputs.keys()]
27
-
28
- play.click(
29
- fn=play_fn,
30
- inputs=None,
31
- outputs=df_components + [json_component]
32
- )
33
- stop.click(
34
- fn=stop_fn,
35
- inputs=None,
36
- outputs=None
37
- )
38
- reset.click(
39
- fn=reset_fn,
40
- inputs=None,
41
- outputs=None
42
- )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import time
2
+ import json
3
  import gradio as gr
4
+
5
+ from src.production.flow import generate_data#, compile
6
+ from src.production.metrics.tools import get_tools_metrics
7
+ from src.production.metrics.machine import get_machine_metrics
8
+
9
+ def dashboard_ui(state):
10
+
11
+ def update_dashboard(state):
12
+ if state['running']:
13
+ generate_data(state)
14
+
15
+ raw_data = state['data']['raw_df']
16
+ print(raw_data)
17
+
18
+
19
+ tools_dfs = get_tools_metrics(raw_data)
20
+ tools_dfs = {tool: df for tool, df in tools_dfs.items() if not df.empty}
21
+
22
+ for tool, df in tools_dfs.items():
23
+ print(tool)
24
+ print(df.columns.tolist())
25
+ print("\n")
26
+
27
+ machine_results = get_machine_metrics(raw_data)
28
+ machine_json = json.dumps(machine_results, indent=4)
29
+ print(machine_json)
30
+
31
+ return state
32
+
33
+
34
+
35
+
36
+
37
+ timer = gr.Timer(0.1)
38
+ timer.tick(
39
+ fn=update_dashboard,
40
+ inputs=state,
41
+ outputs=state
42
+ )
43
+
44
+
45
+
46
+ # # ---------------- TOOLS METRICS DISPLAY ----------------
47
+ # tools_count = 4
48
+ # visualizers = [ToolMetricsDisplay() for _ in range(tools_count)]
49
+ # for id, display in enumerate(visualizers, start=1):
50
+ # with gr.Row():
51
+ # df = datalist.value['data'][id - 1][0]
52
+ # header = f"Tool {id}"
53
+ # html_content = f"""
54
+ # <div style="display: flex; align-items: center; justify-content: flex-start; width: 100%;">
55
+ # <div style="flex: 0 0 2%; border-top: 1px solid white;"></div>
56
+ # <h2 style="flex: 0 0 auto; margin: 0 10px;">{header}</h2>
57
+ # <div style="flex: 1; border-top: 1px solid white;"></div>
58
+ # </div>
59
+ # """
60
+ # gr.HTML(html_content)
61
+ # with gr.Row():
62
+ # with gr.Column(scale=1):
63
+ # gr.Markdown("### `Position`")
64
+ # with gr.Group():
65
+ # with gr.Row(height=250):
66
+ # pos_normal_plot = gr.Plot(display.normal_curve(df=df, cote='pos'))
67
+ # with gr.Row(height=150):
68
+ # pos_cp_gauge = gr.Plot(display.gauge(df=df, type='cp', cote='pos'))
69
+ # pos_cpk_gauge = gr.Plot(display.gauge(df=df, type='cpk', cote='pos'))
70
+ #
71
+ # with gr.Column(scale=1):
72
+ # gr.Markdown("### `Orientation`")
73
+ # with gr.Group():
74
+ # with gr.Row(height=250):
75
+ # ori_normal_plot = gr.Plot(display.normal_curve(df=df, cote='ori'))
76
+ # with gr.Row(height=150):
77
+ # ori_cp_gauge = gr.Plot(display.gauge(df=df, type='cp', cote='ori'))
78
+ # ori_cpk_gauge = gr.Plot(display.gauge(df=df, type='cpk', cote='ori'))
79
+ #
80
+ # with gr.Column(scale=2):
81
+ # gr.Markdown("### `Control card`")
82
+ # with gr.Row(height=400):
83
+ # control_plot = gr.Plot(display.control_graph(df=df))
84
+ #
85
+ # timer = gr.Timer(1)
86
+ # timer.tick(
87
+ # lambda : [
88
+ # display.normal_curve(df=df, cote='pos'),
89
+ # display.gauge(df=df, type='cp', cote='pos'),
90
+ # display.gauge(df=df, type='cpk', cote='pos'),
91
+ # display.normal_curve(df=df, cote='ori'),
92
+ # display.gauge(df=df, type='cp', cote='ori'),
93
+ # display.gauge(df=df, type='cpk', cote='ori'),
94
+ # display.control_graph(df=df)
95
+ # ],
96
+ # outputs=[pos_normal_plot, pos_cp_gauge, pos_cpk_gauge, ori_normal_plot, ori_cp_gauge, ori_cpk_gauge,
97
+ # control_plot]
98
+ # )
src/ui/graphs/__init__.py ADDED
File without changes
src/ui/graphs/tools_graphs.py ADDED
@@ -0,0 +1,194 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import numpy as np
2
+ from scipy.stats import norm
3
+ import pandas as pd
4
+ import plotly.graph_objects as go
5
+ import gradio as gr
6
+
7
+
8
+ class ToolMetricsDisplay:
9
+ def __init__(self):
10
+ self.df = None
11
+ self.pos_color = '#2CFCFF'
12
+ self.ori_color = '#ff8508'
13
+
14
+ @staticmethod
15
+ def gauge(df, type=None, cote=None):
16
+ if df is None or df.empty:
17
+ fig = go.Figure()
18
+ fig.update_layout(
19
+ template='plotly_dark',
20
+ width=200,
21
+ height=170,
22
+ margin=dict(l=30, r=50, t=30, b=0),
23
+ annotations=[dict(
24
+ text="No data available",
25
+ showarrow=False,
26
+ font=dict(size=16, color="white")
27
+ )]
28
+ )
29
+ return fig
30
+
31
+ column = f"{cote}_rolling_{type}"
32
+ idx = df['Timestamp'].idxmax()
33
+ value = df.loc[idx, column]
34
+ fig = go.Figure(go.Indicator(
35
+ mode="gauge+number",
36
+ value=value,
37
+ number={'font': {'color': 'white', 'size': 20}},
38
+ domain={'x': [0, 1], 'y': [0, 1]},
39
+ title={'text': type, 'font': {'color': 'white', 'size': 16}},
40
+ gauge={'axis': {'range': [0, 3]},
41
+ 'bar': {'color': 'black', 'thickness': 0.4},
42
+ 'steps': [
43
+ {'range': [0, 1.33], 'color': "red"},
44
+ {'range': [1.33, 2], 'color': "yellow"},
45
+ {'range': [2, 3], 'color': "green"}],
46
+ 'threshold': {
47
+ 'line': {'color': 'black', 'width': 3},
48
+ 'thickness': 0.8,
49
+ 'value': value}}
50
+ ))
51
+ fig.update_layout(
52
+ template='plotly_dark',
53
+ width=200,
54
+ height=170,
55
+ margin=dict(l=30, r=50, t=30, b=0),
56
+ )
57
+ return fig
58
+
59
+ def control_graph(self, df):
60
+ if df is None or df.empty:
61
+ fig = go.Figure()
62
+ fig.update_layout(
63
+ template='plotly_dark',
64
+ xaxis_title='Timestamp',
65
+ yaxis_title='Valeurs',
66
+ showlegend=True,
67
+ width=720,
68
+ height=400,
69
+ margin=dict(l=70, r=10, t=30, b=70),
70
+ annotations=[dict(
71
+ text="No data available",
72
+ showarrow=False,
73
+ font=dict(size=20, color="white")
74
+ )]
75
+ )
76
+ return fig
77
+
78
+ fig = go.Figure()
79
+ fig.add_trace(go.Scatter(x=df['Timestamp'], y=[0.3] * len(df), mode='lines',
80
+ line=dict(dash='dot', color=self.pos_color, width=1), name='lsl pos'))
81
+ fig.add_trace(go.Scatter(x=df['Timestamp'], y=[0.5] * len(df), mode='lines',
82
+ line=dict(dash='dot', color=self.pos_color, width=1), name='usl pos'))
83
+ fig.add_trace(go.Scatter(x=df['Timestamp'], y=[0.2] * len(df), mode='lines',
84
+ line=dict(dash='dot', color=self.ori_color, width=1), name='lsl ori'))
85
+ fig.add_trace(go.Scatter(x=df['Timestamp'], y=[0.6] * len(df), mode='lines',
86
+ line=dict(dash='dot', color=self.ori_color, width=1), name='usl ori'))
87
+ fig.add_trace(
88
+ go.Scatter(x=df['Timestamp'], y=df['Position'], mode='lines+markers', line=dict(color=self.pos_color),
89
+ name='pos'))
90
+ fig.add_trace(
91
+ go.Scatter(x=df['Timestamp'], y=df['Orientation'], mode='lines+markers', line=dict(color=self.ori_color),
92
+ name='ori'))
93
+ fig.update_layout(
94
+ template='plotly_dark',
95
+ xaxis_title='Timestamp',
96
+ yaxis_title='Valeurs',
97
+ showlegend=True,
98
+ width=720,
99
+ height=400,
100
+ margin=dict(l=70, r=10, t=30, b=70)
101
+ )
102
+ return fig
103
+
104
+ def normal_curve(self, df, cote=None):
105
+ if df is None or df.empty:
106
+ fig = go.Figure()
107
+ fig.update_layout(
108
+ template='plotly_dark',
109
+ showlegend=False,
110
+ width=370,
111
+ height=250,
112
+ margin=dict(l=40, r=40, t=40, b=40),
113
+ annotations=[dict(
114
+ text="No data available",
115
+ showarrow=False,
116
+ font=dict(size=16, color="white")
117
+ )]
118
+ )
119
+ return fig
120
+
121
+ if cote == 'pos':
122
+ color = self.pos_color
123
+ else:
124
+ color = self.ori_color
125
+ mu_column = f"{cote}_rolling_mean"
126
+ std_column = f"{cote}_rolling_std"
127
+ idx = df['Timestamp'].idxmax()
128
+ mu, std = df.loc[idx, [mu_column, std_column]]
129
+ x = np.linspace(mu - 3 * std, mu + 3 * std, 100)
130
+ y = norm.pdf(x, mu, std)
131
+ fig = go.Figure()
132
+ fig.add_trace(go.Scatter(x=x, y=y, mode='lines', name='Normal Curve', line=dict(color=color)))
133
+ fig.add_shape(type="line", x0=0.6, y0=0, x1=0.6, y1=max(y), line=dict(color="red", width=1, dash="dot"),
134
+ name='usl')
135
+ fig.add_shape(type="line", x0=0.2, y0=0, x1=0.2, y1=max(y), line=dict(color="red", width=1, dash="dot"),
136
+ name='lsl')
137
+ fig.update_layout(
138
+ template='plotly_dark',
139
+ showlegend=False,
140
+ width=370,
141
+ height=250,
142
+ margin=dict(l=40, r=40, t=40, b=40)
143
+ )
144
+ return fig
145
+
146
+ #def tool_block(self, df, id=1):
147
+ # header = f"Tool {id}"
148
+ # html_content = f"""
149
+ # <div style="display: flex; align-items: center; justify-content: flex-start; width: 100%;">
150
+ # <div style="flex: 0 0 2%; border-top: 1px solid white;"></div>
151
+ # <h2 style="flex: 0 0 auto; margin: 0 10px;">{header}</h2>
152
+ # <div style="flex: 1; border-top: 1px solid white;"></div>
153
+ # </div>
154
+ # """
155
+ # gr.HTML(html_content)
156
+ #
157
+ # with gr.Row():
158
+ # with gr.Column(scale=1):
159
+ # gr.Markdown("### `Position`")
160
+ # with gr.Group():
161
+ # with gr.Row(height=250):
162
+ # pos_normal_plot = gr.Plot(self.normal_curve(df=df, cote='pos'))
163
+ # with gr.Row(height=150):
164
+ # pos_cp_gauge = gr.Plot(self.gauge(df=df, type='cp', cote='pos'))
165
+ # pos_cpk_gauge = gr.Plot(self.gauge(df=df, type='cpk', cote='pos'))
166
+ #
167
+ # with gr.Column(scale=1):
168
+ # gr.Markdown("### `Orientation`")
169
+ # with gr.Group():
170
+ # with gr.Row(height=250):
171
+ # ori_normal_plot = gr.Plot(self.normal_curve(df=df, cote='ori'))
172
+ # with gr.Row(height=150):
173
+ # ori_cp_gauge = gr.Plot(self.gauge(df=df, type='cp', cote='ori'))
174
+ # ori_cpk_gauge = gr.Plot(self.gauge(df=df, type='cpk', cote='ori'))
175
+ #
176
+ # with gr.Column(scale=2):
177
+ # gr.Markdown("### `Control card`")
178
+ # with gr.Row(height=400):
179
+ # control_plot = gr.Plot(self.control_graph(df=df))
180
+
181
+ #timer = gr.Timer(1)
182
+ #
183
+ #timer.tick(
184
+ # lambda: [
185
+ # self.update_normal_curve(cote='pos'),
186
+ # self.update_gauge(type='cp', cote='pos'),
187
+ # self.update_gauge(type='cpk', cote='pos'),
188
+ # self.update_normal_curve(cote='ori'),
189
+ # self.update_gauge(type='cp', cote='ori'),
190
+ # self.update_gauge(type='cpk', cote='ori'),
191
+ # self.update_control_graph()
192
+ # ],
193
+ # outputs=[pos_normal_plot, pos_cp_gauge, pos_cpk_gauge, ori_normal_plot, ori_cp_gauge, ori_cpk_gauge, control_plot]
194
+ #)