@@ -33,7 +33,8 @@ def show():
3333 - device details (A bar chart of traffic volume in the last 60 seconds, and a table of network flows)
3434 """
3535 device_mac_address = show_device_list ()
36-
36+ # The logic for displaying the API message has been moved into this fragment,
37+ display_api_status ()
3738 if not device_mac_address :
3839 st .warning ("No device selected. Please select a device to view details." )
3940 return
@@ -42,50 +43,26 @@ def show():
4243 show_device_details (device_mac_address )
4344
4445
45- def reset_labeling_state ():
46- """Resets all state variables related to a labeling session."""
47- common .config_set ('labeling_in_progress' , False )
48- st .session_state ['countdown' ] = False
49- st .session_state ['start_time' ] = None
50- st .session_state ['end_time' ] = None
51- st .session_state ['show_labeling_setup' ] = False
52-
53-
54- def _collect_packets_callback ():
55- """
56- Executed when 'Start' is clicked. Sets the flag to trigger the countdown
57- and packet start logic in the main function.
58- """
59- logger .info ("[Packets] Start button clicked, initiating countdown and add to Label Deque..." )
60- st .session_state ['countdown' ] = True
61-
62- labeling_event = {
63- "prolific_id" : common .config_get ('prolific_id' , '' ),
64- "device_name" : st .session_state ['device_name' ],
65- "activity_label" : st .session_state ['activity_label' ],
66- "mac_address" : st .session_state ['mac_address' ],
67- }
68- _labeling_event_deque .append (labeling_event )
69- # st.rerun() will be triggered by Streamlit after the callback completes
70-
71-
72- def _send_packets_callback ():
46+ @st .fragment (run_every = 10 )
47+ def display_api_status ():
7348 """
74- Executed when the 'Labeling Complete' button is clicked.
75- Handles stopping packet collection and sending data to the server.
49+ Continuously checks the global 'api_message' configuration variable
50+ and displays the corresponding Streamlit notification (success, error, or warning).
51+ This fragment runs every 10 seconds, ensuring the user sees updates from the
52+ background thread (label_thread) in real-time.
7653 """
77- if not common .config_get ('labeling_in_progress' , default = False ):
78- common .config_set ('api_message' , "warning|No labeling session in progress. Please start a labeling session first." )
79- return
80-
81- logger .info ("[Packets] Collect the end time and prep for packet collection." )
82- if len (_labeling_event_deque ) == 0 :
83- logger .warning ("[Packets] No labeling event found in the queue when trying to set end time." )
84- else :
85- _labeling_event_deque [- 1 ]['end_time' ] = int (time .time ())
86- st .session_state ['end_time' ] = _labeling_event_deque [- 1 ]['end_time' ]
54+ # Use st.empty() to ensure the message appears in the same place and is cleared on next run
55+ status_placeholder = st .empty ()
56+ api_message = common .config_get ('api_message' , default = '' )
8757
88- reset_labeling_state ()
58+ if len (api_message ) != 0 :
59+ msg_type , msg = api_message .split ('|' , 1 )
60+ if msg_type == 'success' :
61+ status_placeholder .success (f"✅ { msg } " )
62+ elif msg_type == 'error' :
63+ status_placeholder .error (f"❌ { msg } " )
64+ elif msg_type == 'warning' :
65+ status_placeholder .warning (f"⚠️ { msg } " )
8966
9067
9168def label_thread ():
@@ -101,7 +78,6 @@ def label_thread():
10178 logger .info ("[Packets] Will check if there are labeled packets to send..." )
10279 # 1. Check if labeling session has ended
10380 if len (_labeling_event_deque ) == 0 :
104- common .config_set ('api_message' , "success| The packet collector thread is idle." )
10581 logger .info ("[Packets] There is no labeling event ready. Not sending packets." )
10682 continue
10783 else :
@@ -110,14 +86,12 @@ def label_thread():
11086 # Make sure end time is NOT a none
11187 end_time = _labeling_event_deque [0 ].get ('end_time' , None )
11288 if end_time is None :
113- common .config_set ('api_message' , "success| The packet collector thread is idle." )
11489 logger .info ("[Packets] End time hasn't been set yet, The labeling session is still ongoing. Not sending packets yet." )
11590 continue
11691
11792 # If the labeling session is still ongoing, skip sending
11893 if time .time () <= end_time :
11994 logger .info ("[Packets] Labeling session not complete yet. The end time is still in the future." )
120- common .config_set ('api_message' , "warning| The packet collector thread is waiting for the labeling session to complete." )
12195 continue
12296
12397 # 2. Process and empty the queue
@@ -158,8 +132,9 @@ def label_thread():
158132 timeout = 10
159133 )
160134 if response .status_code == 200 :
161- logger .info (f"[Packets] { time .strftime ('%Y-%m-%d %H:%M:%S' )} - All packets sent successfully." )
162- common .config_set ('api_message' , f"success| Labeled packets successfully sent to the server. \n { label_data } " )
135+ logger .info (f"[Packets] { time .strftime ('%Y-%m-%d %H:%M:%S' )} - All packets sent successfully. \n { label_data } " )
136+ display_message = f"Labeled packets successfully | { label_data } "
137+ common .config_set ('api_message' , f"success|{ display_message } " )
163138 else :
164139 logger .info (f"[Packets] { time .strftime ('%Y-%m-%d %H:%M:%S' )} - API Failed, packets NOT sent!." )
165140 common .config_set ('api_message' , f"error|Failed to send labeled packets. Server status: { response .status_code } . { len (pending_packet_list )} Packets were not sent." )
@@ -173,6 +148,60 @@ def label_thread():
173148 pending_packet_list .clear ()
174149
175150
151+ def reset_labeling_state ():
152+ """
153+ Resets all state variables related to a labeling session.
154+ """
155+ st .session_state ['countdown' ] = False
156+ st .session_state ['show_labeling_setup' ] = False
157+ st .session_state ['start_time' ] = None
158+ st .session_state ['end_time' ] = None
159+ st .session_state ['device_name' ] = None
160+ st .session_state ['activity_label' ] = None
161+
162+
163+ def _collect_packets_callback ():
164+ """
165+ Executed when 'Start' is clicked. Sets the flag to trigger the countdown
166+ and packet start logic in the main function.
167+ """
168+ logger .info ("[Packets] Start button clicked, initiating countdown and add to Label Deque..." )
169+ st .session_state ['countdown' ] = True
170+
171+ labeling_event = {
172+ "prolific_id" : common .config_get ('prolific_id' , '' ),
173+ "device_name" : st .session_state ['device_name' ],
174+ "activity_label" : st .session_state ['activity_label' ],
175+ "mac_address" : st .session_state ['mac_address' ],
176+ }
177+ _labeling_event_deque .append (labeling_event )
178+
179+
180+ def _send_packets_callback ():
181+ """
182+ Executed when the 'Labeling Complete' button is clicked.
183+ Handles stopping packet collection and sending data to the server.
184+ """
185+ if not common .config_get ('labeling_in_progress' , default = False ):
186+ common .config_set ('api_message' , "warning|No labeling session in progress. Please start a labeling session first." )
187+ return
188+
189+ if common .config_get ('packet_count' , 0 ) == 0 :
190+ st .warning ("[Packets] No packets were captured for labeling." )
191+ logger .warning ("[Packets] No packets were captured for labeling." )
192+ return
193+
194+ logger .info ("[Packets] Collect the end time and prep for packet collection." )
195+ if len (_labeling_event_deque ) == 0 :
196+ logger .warning ("[Packets] No labeling event found in the queue when trying to set end time." )
197+ else :
198+ _labeling_event_deque [- 1 ]['end_time' ] = int (time .time ())
199+ st .session_state ['end_time' ] = _labeling_event_deque [- 1 ]['end_time' ]
200+ common .config_set ('labeling_in_progress' , False )
201+ common .config_set ('packet_count' , 0 )
202+ reset_labeling_state ()
203+
204+
176205def update_device_inspected_status (mac_address : str ):
177206 """
178207 Manually update to inspected status so that all the packets can be collected for the MAC Address.
@@ -191,28 +220,6 @@ def update_device_inspected_status(mac_address: str):
191220 common .config_set (device_inspected_config_key , True )
192221
193222
194- @st .fragment (run_every = 10 )
195- def display_api_status ():
196- """
197- Continuously checks the global 'api_message' configuration variable
198- and displays the corresponding Streamlit notification (success, error, or warning).
199- This fragment runs every 10 seconds, ensuring the user sees updates from the
200- background thread (label_thread) in real-time.
201- """
202- # Use st.empty() to ensure the message appears in the same place and is cleared on next run
203- status_placeholder = st .empty ()
204- api_message = common .config_get ('api_message' , default = '' )
205-
206- if len (api_message ) != 0 :
207- msg_type , msg = api_message .split ('|' , 1 )
208- if msg_type == 'success' :
209- status_placeholder .success (f"✅ { msg } " )
210- elif msg_type == 'error' :
211- status_placeholder .error (f"❌ { msg } " )
212- elif msg_type == 'warning' :
213- status_placeholder .warning (f"⚠️ { msg } " )
214-
215-
216223def label_activity_workflow (mac_address : str ):
217224 """
218225 Manages the interactive, state-driven workflow for labeling network activity in Streamlit.
@@ -252,12 +259,12 @@ def label_activity_workflow(mac_address: str):
252259 help = "Click to start labeling an activity for this device. This will reset any previous labeling state."
253260 ):
254261 if len (_labeling_event_deque ) > 0 :
255- st .warning ("A previous labeling session is still active. Try again in 15 seconds when the previous session should have been sent." )
262+ st .warning ("A previous labeling session is still active. Try again in 15 seconds when the previous session's packets should have been sent." )
256263 return
257264 update_device_inspected_status (mac_address )
258- reset_labeling_state ()
259265 # Keep labeling_in_progress=True until the very end, controlled by config_get/set
260266 common .config_set ('labeling_in_progress' , True )
267+ common .config_set ('api_message' , '' )
261268 st .session_state ['show_labeling_setup' ] = True
262269 st .rerun () # Rerun to show setup menu
263270
@@ -322,9 +329,6 @@ def label_activity_workflow(mac_address: str):
322329 help = "Click to stop collecting packets and send the labeled packets to NYU mLab."
323330 )
324331
325- # The logic for displaying the API message has been moved into this fragment,
326- display_api_status ()
327-
328332 # --- 4. Countdown Logic (Blocking, happens between Start and Collection) ---
329333 if st .session_state ['countdown' ]:
330334 countdown_placeholder = st .empty ()
@@ -377,6 +381,9 @@ def save_labeled_activity_packets(pkt):
377381 if mac_address and (pkt [sc .Ether ].src == mac_address or pkt [sc .Ether ].dst == mac_address ):
378382 # We also check if the timestamp is correct
379383 _labeled_activity_packet_queue .put (pkt )
384+ packet_count = common .config_get ('packet_count' , 0 )
385+ packet_count += 1
386+ common .config_set ('packet_count' , packet_count )
380387
381388
382389@st .cache_data (show_spinner = False )
@@ -523,6 +530,8 @@ def show_device_list():
523530 st .warning ("No devices found. Please inspect a device first." )
524531 st .stop ()
525532
533+ is_labeling_active = common .config_get ('labeling_in_progress' , default = False )
534+
526535 # Show a dropdown to select a device; each option shows both the device's MAC address and IP address
527536 device_options = [f"{ common .get_device_custom_name (device ['mac_address' ])} - { device ['ip_address' ]} - { device ['mac_address' ]} " for device in device_list ]
528537 device_options .insert (0 , "(Select a device)" ) # Add a placeholder option
@@ -550,8 +559,13 @@ def _selected_device_changed_callback():
550559 device_options ,
551560 index = selected_index ,
552561 key = 'selected_device' ,
553- on_change = _selected_device_changed_callback
562+ on_change = _selected_device_changed_callback ,
563+ disabled = is_labeling_active
554564 )
555565
566+ if is_labeling_active :
567+ st .warning (
568+ "⚠️ **Device selection is locked** while a labeling session is in progress." )
569+
556570 return device_mac_address
557571
0 commit comments