2424import numpy as np
2525
2626from pulsecatcher import pulsecatcher
27- from dash import dash_table
27+ from dash import dash_table , html
2828from scipy .signal import find_peaks , peak_widths
2929from collections import defaultdict
3030from datetime import datetime
@@ -62,33 +62,6 @@ def normalise_pulse(average):
6262 normalised = [int (n - mean ) for n in average ]
6363 return normalised
6464
65- def get_serial_device_information ():
66- try :
67- with shproto .dispatcher .command_lock :
68- shproto .dispatcher .command = "-inf"
69- time .sleep (0.2 )
70- device_info = shproto .dispatcher .inf_str
71- time .sleep (0.2 )
72- shproto .dispatcher .inf_str = ""
73- time .sleep (0.2 )
74- return device_info if device_info else "No response from device"
75- except Exception as e :
76- logger .error (f"Error retrieving device information: { e } " )
77- return "Error retrieving device information"
78-
79- def parse_device_info (info_string ):
80- components = info_string .split ()
81- settings = {}
82- for i in range (0 , len (components ) - 1 , 2 ):
83- key = components [i ]
84- value = components [i + 1 ]
85- if value .replace ('.' , '' , 1 ).isdigit () and value .count ('.' ) < 2 :
86- converted_value = float (value ) if '.' in value else int (value )
87- else :
88- converted_value = value
89- settings [key ] = converted_value
90- return settings
91-
9265# Normalized pulse samples less normalized shape samples squared summed and rooted
9366def distortion (normalised , shape ):
9467 product = [(x - y ) ** 2 for x , y in zip (shape , normalised )]
@@ -765,57 +738,173 @@ def fetch_json(file_id):
765738 logger .error (f"Error fetching JSON: { e } \n " )
766739 return None
767740
768- def execute_serial_command (input_cmd ):
769- with shproto .dispatcher .command_lock :
770- shproto .dispatcher .command = input_cmd
771- logger .info (f"Sending command { input_cmd } to device\n " )
741+ def get_serial_device_information ():
742+ try :
743+ with shproto .dispatcher .command_lock :
744+ shproto .dispatcher .command = "-inf"
745+
746+ time .sleep (0.4 )
747+
748+ dev_info = shproto .dispatcher .inf_str
749+ shproto .dispatcher .inf_str = ""
750+
751+ return dev_info if dev_info else "No response from device"
752+
753+ except Exception as e :
754+ logger .error (f"Error retrieving device information: { e } " )
755+ return "Error retrieving device information"
756+
757+
758+
759+
760+ def parse_device_info (info_string ):
761+
762+ tokens = info_string .split ()
763+ settings = {}
764+ i = 0
765+ n = len (tokens )
766+
767+ while i < n :
768+ # 1) key is always one token
769+ key = tokens [i ]
770+ i += 1
771+ if i >= n :
772+ break
773+
774+ # 2) if the next token starts a bracketed list, consume until the closing bracket
775+ if tokens [i ].startswith ("[" ):
776+ start = i
777+ j = i
778+ while j < n and not tokens [j ].endswith ("]" ):
779+ j += 1
780+
781+ # join all pieces of the list, strip brackets, split into parts
782+ raw_list = " " .join (tokens [start : j + 1 ])
783+ inner = raw_list .strip ("[]" ).strip ()
784+ parts = re .split (r"[,\s]+" , inner )
785+
786+ # convert each part to int/float if possible
787+ lst = []
788+ for part in parts :
789+ if part .lstrip ("-" ).replace ("." , "" , 1 ).isdigit () and part .count ("." ) < 2 :
790+ lst .append (int (part ) if "." not in part else float (part ))
791+ else :
792+ lst .append (part )
793+
794+ settings [key ] = lst
795+ i = j + 1 # advance past the entire bracketed list
796+
797+ else :
798+ # 3) single-token value case
799+ val_token = tokens [i ]
800+ i += 1
801+
802+ # convert to int/float if it looks like a number
803+ if val_token .lstrip ("-" ).replace ("." , "" , 1 ).isdigit () and val_token .count ("." ) < 2 :
804+ converted = int (val_token ) if "." not in val_token else float (val_token )
805+ else :
806+ converted = val_token
807+
808+ settings [key ] = converted
809+
810+ return settings
772811
773812def generate_device_settings_table ():
774- shproto .dispatcher .spec_stopflag = 0
775- dispatcher = threading .Thread (target = shproto .dispatcher .start )
776- dispatcher .start ()
777- dev_info = get_serial_device_information ()
778- time .sleep (0.3 )
779- info_dict = parse_device_info (dev_info )
780- time .sleep (0.3 )
813+ # 1) Fetch serial number
814+ process_03 ('-cal' )
815+ time .sleep (0.1 )
781816 serial_number = shproto .dispatcher .serial_number
817+ time .sleep (0.1 )
818+
819+ # 2) Fetch settings info
820+ dev_info = get_serial_device_information ()
821+ time .sleep (0.1 )
822+ info = parse_device_info (dev_info )
823+ time .sleep (0.1 )
824+
825+ # 3) Build only the main settings DataTable
782826 table = dash_table .DataTable (
783827 columns = [
784- {"id" : "Setting" , "name" : "Firmware settings " },
785- {"id" : "cmd" , "name" : "Command " },
786- {"id" : "Value" , "name" : "Value" }
828+ {"id" : "Setting" , "name" : "Firmware setting " },
829+ {"id" : "cmd" , "name" : "Cmd " },
830+ {"id" : "Value" , "name" : "Value" },
787831 ],
788832 data = [
789- {"Setting" : "Version" , "cmd" : "-" , "Value" : info_dict .get ('VERSION' )},
790- {"Setting" : "Serial number" , "cmd" : "status" , "Value" : serial_number },
791- {"Setting" : "Samples for X (pulse rise)" , "cmd" : "-ris" , "Value" : info_dict .get ('RISE' )},
792- {"Setting" : "Samples for Y (pulse fall)" , "cmd" : "-fall" , "Value" : info_dict .get ('FALL' )},
793- {"Setting" : "Lower Limit Discriminator LLD" , "cmd" : "-nos" , "Value" : info_dict .get ('NOISE' )},
794- {"Setting" : "ADC Sample Frequency" , "cmd" : "-frq" , "Value" : info_dict .get ('F' )},
795- {"Setting" : "Max integral value" , "cmd" : "-max" , "Value" : info_dict .get ('MAX' )},
796- {"Setting" : "Hysteresis value" , "cmd" : "-hyst" , "Value" : info_dict .get ('HYST' )},
797- {"Setting" : "Working Mode [0, 1, 2]" , "cmd" : "-mode" , "Value" : info_dict .get ('MODE' )},
798- {"Setting" : "Discriminator step (>1)" , "cmd" : "-step" , "Value" : info_dict .get ('STEP' )},
799- {"Setting" : "High Voltage (0-255)" , "cmd" : "-U" , "Value" : info_dict .get ('POT' )},
800- {"Setting" : "Baseline trim (0-255)" , "cmd" : "-V" , "Value" : info_dict .get ('POT2' )},
801- {"Setting" : "Temperature sensor 1" , "cmd" : "status" , "Value" : f"{ info_dict .get ('T1' )} C˚" },
802- {"Setting" : "Energy Window (-win X1 X2)" , "cmd" : "-win" , "Value" : info_dict .get ('OUT' )},
803- {"Setting" : "Temp. compensation status" , "cmd" : "status" , "Value" : info_dict .get ('TCpot' )},
804- {"Setting" : "Temp. compensation table" , "cmd" : "status" , "Value" : info_dict .get ('Tco' )}
833+ {"Setting" : "Version" , "cmd" : "-ver" , "Value" : info .get ("VERSION" )},
834+ {"Setting" : "Serial number" , "cmd" : "-cal" , "Value" : serial_number },
835+ {"Setting" : "Rise samples" , "cmd" : "-ris" , "Value" : info .get ("RISE" )},
836+ {"Setting" : "Fall samples" , "cmd" : "-fall" , "Value" : info .get ("FALL" )},
837+ {"Setting" : "Noise LLD" , "cmd" : "-nos" , "Value" : info .get ("NOISE" )},
838+ {"Setting" : "ADC freq (Hz)" , "cmd" : "-frq" , "Value" : info .get ("F" )},
839+ {"Setting" : "Max integral" , "cmd" : "-max" , "Value" : info .get ("MAX" )},
840+ {"Setting" : "Hysteresis" , "cmd" : "-hyst" , "Value" : info .get ("HYST" )},
841+ {"Setting" : "Mode [0–2]" , "cmd" : "-mode" , "Value" : info .get ("MODE" )},
842+ {"Setting" : "Discriminator step" ,"cmd" : "-step" , "Value" : info .get ("STEP" )},
843+ {"Setting" : "High voltage" , "cmd" : "-U" , "Value" : info .get ("POT" )},
844+ {"Setting" : "Baseline trim" , "cmd" : "-V" , "Value" : info .get ("POT2" )},
845+ {"Setting" : "Temp sensor 1 (°C)" ,"cmd" : "status" ,"Value" : info .get ("T1" )},
846+ {"Setting" : "Energy window" , "cmd" : "-win" , "Value" : info .get ("OUT" )},
847+ {"Setting" : "Temp-comp enabled" , "cmd" : "-tc" , "Value" : info .get ("TC" )},
805848 ],
806849 style_cell = {
807- 'textAlign' : 'left' ,
808- 'padding' : '4px' ,
809- 'fontSize' : '12px' ,
810- 'fontFamily' : 'Arial'
850+ 'textAlign' : 'left' ,
851+ 'padding' : '4px' ,
852+ 'fontSize' : '12px' ,
853+ 'fontFamily' : 'Arial' ,
854+ 'whiteSpace' : 'normal' ,
855+ 'height' : 'auto'
811856 },
812857 style_cell_conditional = [
813- {'if' : {'column_id' : 'Setting' }, 'width' : '60%' },
814- {'if' : {'column_id' : 'cmd' }, 'width' : '10%' },
815- {'if' : {'column_id' : 'Value' }, 'width' : '30%' }
816- ]
858+ {'if' : {'column_id' :'Setting' }, 'width' :'60%' },
859+ {'if' : {'column_id' :'cmd' }, 'width' :'10%' },
860+ {'if' : {'column_id' :'Value' }, 'width' :'30%' },
861+ ],
862+ style_table = {
863+ 'overflowX' : 'auto'
864+ }
865+ )
866+
867+ with global_vars .write_lock :
868+ global_vars .serial_number = serial_number
869+
870+ # 4) Return only that table
871+ return html .Div (table , style = {"width" : "100%" , "overflowX" : "auto" })
872+
873+ def generate_temperature_comp_table ():
874+
875+ # fetch & parse (you can swap to cached or direct fetch if you like)
876+ raw = get_serial_device_information ()
877+ info = parse_device_info (raw )
878+
879+ tco_raw = info .get ("Tco" , []) # should now be a Python list of numbers
880+
881+ # group into (Temp, MaxIntegral) pairs
882+ pairs = [(tco_raw [i ], tco_raw [i + 1 ]) for i in range (0 , len (tco_raw )- 1 , 2 )]
883+
884+ # build header
885+ header = html .Tr ([
886+ html .Th ("Temperature (°C)" , style = {"padding" : "4px" , "fontWeight" : "bold" , "textAlign" :"center" }),
887+ html .Th ("Max Integral" , style = {"padding" : "4px" , "fontWeight" : "bold" , "textAlign" :"center" })
888+ ])
889+
890+ # build data rows
891+ rows = []
892+ for temp , integral in pairs :
893+ rows .append (html .Tr ([
894+ html .Td (str (temp ), style = {"padding" : "4px" , "textAlign" :"center" }),
895+ html .Td (str (integral ),style = {"padding" : "4px" , "textAlign" :"right" })
896+ ]))
897+
898+ return html .Table (
899+ [header ] + rows ,
900+ style = {
901+ "borderCollapse" : "collapse" ,
902+ "width" : "100%" ,
903+ "maxHeight" : "400px" ,
904+ "overflowY" : "auto" ,
905+ "display" : "block" ,
906+ }
817907 )
818- return table
819908
820909# Check if commands sent to processor is safe
821910def allowed_command (cmd ):
@@ -931,7 +1020,6 @@ def reset_stores():
9311020 'store_load_flag_tab4' : False ,
9321021 }
9331022
934-
9351023def save_settings_to_json ():
9361024 settings = {key : getattr (global_vars , key ) for key in [
9371025 "bin_size" ,
@@ -988,7 +1076,18 @@ def save_settings_to_json():
9881076 "val_flag" ,
9891077 "max_pulse_length" ,
9901078 "max_pulse_height" ,
991- "flags_selected"
1079+ "flags_selected" ,
1080+ "tempcal_table" ,
1081+ "tempcal_stability_tolerance" ,
1082+ "tempcal_stability_window_sec" ,
1083+ "tempcal_poll_interval_sec" ,
1084+ "tempcal_spectrum_duration_sec" ,
1085+ "tempcal_smoothing_sigma" ,
1086+ "tempcal_peak_search_range" ,
1087+ "tempcal_cancelled" ,
1088+ "tempcal_base_value" ,
1089+ "tempcal_num_runs" ,
1090+ "tempcal_delta"
9921091 ]}
9931092
9941093 try :
@@ -1063,7 +1162,18 @@ def load_settings_from_json(path):
10631162 "val_flag" : bool ,
10641163 "max_pulse_length" : int ,
10651164 "max_pulse_height" : int ,
1066- "flags_selected" : str
1165+ "flags_selected" : str ,
1166+ "tempcal_table" : list ,
1167+ "tempcal_stability_tolerance" : float ,
1168+ "tempcal_stability_window_sec" : int ,
1169+ "tempcal_poll_interval_sec" : int ,
1170+ "tempcal_spectrum_duration_sec" :int ,
1171+ "tempcal_smoothing_sigma" : float ,
1172+ "tempcal_peak_search_range" : list ,
1173+ "tempcal_cancelled" : bool ,
1174+ "tempcal_base_value" : int ,
1175+ "tempcal_num_runs" : int ,
1176+ "tempcal_delta" : int
10671177 }
10681178
10691179 with global_vars .write_lock :
@@ -1296,4 +1406,4 @@ def read_isotopes_data(data_path):
12961406 return data
12971407 except Exception as e :
12981408 print (f"Error reading isotopes data: { e } " )
1299- return []
1409+ return []
0 commit comments