mriusero commited on
Commit
e916724
·
1 Parent(s): a555792

feat: add general graphs

Browse files
src/production/flow.py CHANGED
@@ -88,7 +88,7 @@ async def generate_data(state):
88
  elif not new_row.empty and not new_row.isna().all().all():
89
  state['data']['raw_df'] = new_row.copy()
90
 
91
- print(f"- part {part_id} data generated")
92
  part_id += 1
93
  await asyncio.sleep(0.2)
94
 
 
88
  elif not new_row.empty and not new_row.isna().all().all():
89
  state['data']['raw_df'] = new_row.copy()
90
 
91
+ #print(f"- part {part_id} data generated")
92
  part_id += 1
93
  await asyncio.sleep(0.2)
94
 
src/ui/dashboard.py CHANGED
@@ -7,6 +7,7 @@ from src.production.flow import generate_data
7
  from src.production.metrics.tools import tools_metrics
8
  from src.production.metrics.machine import machine_metrics, fetch_issues
9
  from src.ui.graphs.tools_graphs import ToolMetricsDisplay
 
10
 
11
  MAX_ROWS = 1000
12
  TOOLS_COUNT = 2
@@ -20,28 +21,52 @@ async def dataflow(state):
20
  Main function that updates data if necessary.
21
  Avoids processing if the raw data hasn't changed.
22
  """
 
23
  state.setdefault('data', {}).setdefault('tools', {})
 
 
 
 
 
24
  state['data'].setdefault('issues', {})
 
25
 
 
26
  if state.get('running'):
27
  if 'gen_task' not in state or state['gen_task'] is None or state['gen_task'].done():
28
  state['gen_task'] = asyncio.create_task(generate_data(state))
29
 
30
  raw_data = state['data'].get('raw_df', pd.DataFrame())
31
- if raw_data.empty:
32
- return [pd.DataFrame()] * TOOLS_COUNT
33
 
 
 
 
 
 
 
 
 
 
 
34
  if len(raw_data) > MAX_ROWS:
35
  raw_data = raw_data.tail(MAX_ROWS)
36
 
 
37
  current_hash = hash_dataframe(raw_data)
38
  if state.get('last_hash') == current_hash:
39
  return [
40
  pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
41
  for i in range(1, TOOLS_COUNT+1)
 
 
 
 
 
 
42
  ]
43
  state['last_hash'] = current_hash
44
 
 
45
  tools_data = await tools_metrics(raw_data)
46
  tools_data = {tool: df for tool, df in tools_data.items() if not df.empty}
47
  for tool, df in tools_data.items():
@@ -53,58 +78,107 @@ async def dataflow(state):
53
  issues = await fetch_issues(raw_data)
54
  state['data']['issues'] = issues
55
 
56
- return [
57
- pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
58
- for i in range(1, TOOLS_COUNT+1)
59
- ]
 
 
 
 
 
 
 
 
60
 
61
- def update_display_and_plots(df, display):
62
- """
63
- Uses an existing instance of ToolMetricsDisplay to generate plots.
64
- """
65
- return [
66
- display.normal_curve(df, cote='pos'),
67
- display.gauge(df, type='cp', cote='pos'),
68
- display.gauge(df, type='cpk', cote='pos'),
69
- display.normal_curve(df, cote='ori'),
70
- display.gauge(df, type='cp', cote='ori'),
71
- display.gauge(df, type='cpk', cote='ori'),
72
- display.control_graph(df),
73
- ]
74
-
75
- def init_displays_and_blocks(n=TOOLS_COUNT):
76
  """
77
- Initializes the graphical objects (ToolMetricsDisplay) and their associated blocks.
 
 
 
 
78
  """
79
  displays = []
80
- blocks = []
 
 
 
 
 
 
 
 
 
 
 
 
 
81
  for i in range(1, n + 1):
82
  display = ToolMetricsDisplay()
83
  displays.append(display)
84
- blocks.extend(display.tool_block(df=pd.DataFrame(), id=i))
85
- return displays, blocks
 
 
86
 
87
  async def on_tick(state, displays):
88
  """
89
- Tick function called periodically: updates plots only if data has changed.
90
- Uses a lock to prevent concurrent execution.
 
 
 
 
 
91
  """
92
  async with state.setdefault('lock', asyncio.Lock()):
93
- dfs = await dataflow(state)
94
- all_plots = []
95
- for df, display in zip(dfs, displays):
96
- plots = update_display_and_plots(df, display)
97
- all_plots.extend(plots)
98
- return all_plots + [state]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
 
100
  def dashboard_ui(state):
101
  """
102
  Creates the Gradio interface and sets a refresh every second.
 
 
 
103
  """
104
- displays, initial_plots = init_displays_and_blocks()
 
105
  timer = gr.Timer(1.0)
106
  timer.tick(
107
  fn=partial(on_tick, displays=displays),
108
  inputs=[state],
109
- outputs=initial_plots + [state]
110
  )
 
7
  from src.production.metrics.tools import tools_metrics
8
  from src.production.metrics.machine import machine_metrics, fetch_issues
9
  from src.ui.graphs.tools_graphs import ToolMetricsDisplay
10
+ from src.ui.graphs.general_graphs import GeneralMetricsDisplay
11
 
12
  MAX_ROWS = 1000
13
  TOOLS_COUNT = 2
 
21
  Main function that updates data if necessary.
22
  Avoids processing if the raw data hasn't changed.
23
  """
24
+ # Initialize state
25
  state.setdefault('data', {}).setdefault('tools', {})
26
+ state['data']['tools'].setdefault('all', pd.DataFrame())
27
+
28
+ for i in range(1, TOOLS_COUNT + 1):
29
+ state['data']['tools'].setdefault(f'tool_{i}', pd.DataFrame())
30
+
31
  state['data'].setdefault('issues', {})
32
+ state.setdefault('efficiency', {})
33
 
34
+ # Check running state
35
  if state.get('running'):
36
  if 'gen_task' not in state or state['gen_task'] is None or state['gen_task'].done():
37
  state['gen_task'] = asyncio.create_task(generate_data(state))
38
 
39
  raw_data = state['data'].get('raw_df', pd.DataFrame())
 
 
40
 
41
+ # Cold start
42
+ if raw_data.empty:
43
+ return (
44
+ [pd.DataFrame()] * TOOLS_COUNT + # outils
45
+ [pd.DataFrame()] + # all
46
+ [pd.DataFrame()] + # issues
47
+ [{}] # efficiency
48
+ )
49
+
50
+ # Limit MAX_ROWS
51
  if len(raw_data) > MAX_ROWS:
52
  raw_data = raw_data.tail(MAX_ROWS)
53
 
54
+ # Check if data has changed
55
  current_hash = hash_dataframe(raw_data)
56
  if state.get('last_hash') == current_hash:
57
  return [
58
  pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
59
  for i in range(1, TOOLS_COUNT+1)
60
+ ] + [
61
+ pd.DataFrame(state['data']['tools'].get('all', pd.DataFrame()))
62
+ ] + [
63
+ pd.DataFrame(state['data']['issues'])
64
+ ] + [
65
+ state['efficiency']
66
  ]
67
  state['last_hash'] = current_hash
68
 
69
+ # Process data
70
  tools_data = await tools_metrics(raw_data)
71
  tools_data = {tool: df for tool, df in tools_data.items() if not df.empty}
72
  for tool, df in tools_data.items():
 
78
  issues = await fetch_issues(raw_data)
79
  state['data']['issues'] = issues
80
 
81
+ return (
82
+ [
83
+ pd.DataFrame(state['data']['tools'].get(f'tool_{i}', pd.DataFrame()))
84
+ for i in range(1, TOOLS_COUNT + 1)
85
+ ] + [
86
+ pd.DataFrame(state['data']['tools'].get('all', pd.DataFrame()))
87
+ ] + [
88
+ pd.DataFrame(state['data']['issues'])
89
+ ] + [
90
+ state['efficiency']
91
+ ]
92
+ )
93
 
94
+
95
+ def init_components(n=TOOLS_COUNT):
 
 
 
 
 
 
 
 
 
 
 
 
 
96
  """
97
+ Initializes the graphical objects (ToolMetricsDisplay and GeneralMetricsDisplay)
98
+ and returns:
99
+ - displays: list of display objects [GeneralMetricsDisplay, ToolMetricsDisplay1, ToolMetricsDisplay2, ...]
100
+ - tool_plots: list of tool-related Gradio components
101
+ - general_plots: list of general-related Gradio components
102
  """
103
  displays = []
104
+ tool_plots = []
105
+ general_plots = []
106
+
107
+ # General metrics display and its plots
108
+ main_display = GeneralMetricsDisplay()
109
+ displays.append(main_display)
110
+ general_plots.extend(
111
+ main_display.block(
112
+ all_tools_df=pd.DataFrame(),
113
+ issues_df=pd.DataFrame(),
114
+ efficiency_data={}
115
+ )
116
+ )
117
+ # Tool metrics displays and their plots
118
  for i in range(1, n + 1):
119
  display = ToolMetricsDisplay()
120
  displays.append(display)
121
+ tool_plots.extend(display.tool_block(df=pd.DataFrame(), id=i))
122
+
123
+ return displays, tool_plots, general_plots
124
+
125
 
126
  async def on_tick(state, displays):
127
  """
128
+ Tick function called periodically to update plots if data has changed.
129
+
130
+ Handles:
131
+ - Tool-specific plots (tool_1, tool_2, ..., tool_n)
132
+ - General plots (all tools, issues, efficiency)
133
+
134
+ Returns two lists of plots separately for tools and general metrics, plus state.
135
  """
136
  async with state.setdefault('lock', asyncio.Lock()):
137
+
138
+ data = await dataflow(state)
139
+ tool_dfs = data[:-3] # all individual tool DataFrames
140
+ all_tools_df = data[-3] # 'all' tools DataFrame
141
+ issues_df = data[-2] # issues DataFrame
142
+ efficiency_data = data[-1] # efficiency dict
143
+
144
+ # Update general plots
145
+ general_plots = []
146
+ general_display = displays[0]
147
+ general_plots.extend(
148
+ general_display.update(
149
+ all_tools_df=all_tools_df,
150
+ issues_df=issues_df,
151
+ efficiency_data=efficiency_data
152
+ )
153
+ )
154
+ # Update tool-specific plots
155
+ tool_plots = []
156
+ for df, display in zip(tool_dfs, displays[1:]):
157
+ tool_plots.extend(
158
+ [
159
+ display.normal_curve(df, cote='pos'),
160
+ display.gauge(df, type='cp', cote='pos'),
161
+ display.gauge(df, type='cpk', cote='pos'),
162
+ display.normal_curve(df, cote='ori'),
163
+ display.gauge(df, type='cp', cote='ori'),
164
+ display.gauge(df, type='cpk', cote='ori'),
165
+ display.control_graph(df),
166
+ ]
167
+ )
168
+ return tool_plots + general_plots + [state]
169
 
170
  def dashboard_ui(state):
171
  """
172
  Creates the Gradio interface and sets a refresh every second.
173
+
174
+ The outputs are separated into two groups for tools and general metrics to
175
+ preserve layout order and grouping.
176
  """
177
+ displays, tool_plots, general_plots = init_components()
178
+
179
  timer = gr.Timer(1.0)
180
  timer.tick(
181
  fn=partial(on_tick, displays=displays),
182
  inputs=[state],
183
+ outputs=tool_plots + general_plots + [state]
184
  )
src/ui/graphs/general_graphs.py ADDED
@@ -0,0 +1,18 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import gradio as gr
2
+ import plotly.express as px
3
+
4
+ class GeneralMetricsDisplay:
5
+ def __init__(self):
6
+ self.plot_all_tools = gr.Plot()
7
+ self.plot_issues = gr.Plot()
8
+ self.plot_efficiency = gr.Plot()
9
+
10
+ def block(self, all_tools_df, issues_df, efficiency_data):
11
+ return [self.plot_all_tools, self.plot_issues, self.plot_efficiency]
12
+
13
+ def update(self, all_tools_df, issues_df, efficiency_data):
14
+ #fig_all = px.scatter(tools_df, x="Position", y="Orientation", color="Compliance", title="All Tools Summary")
15
+ #fig_issues = px.histogram(issues_df, x="Error Description", title="Error Types Distribution")
16
+ #fig_eff = px.bar(x=list(efficiency_data.keys()), y=list(efficiency_data.values()), title="Machine Efficiency")
17
+ #return [fig_all, fig_issues, fig_eff]
18
+ return self.plot_all_tools, self.plot_issues, self.plot_efficiency