24
24
import numpy as np
25
25
26
26
from pulsecatcher import pulsecatcher
27
- from dash import dash_table
27
+ from dash import dash_table , html
28
28
from scipy .signal import find_peaks , peak_widths
29
29
from collections import defaultdict
30
30
from datetime import datetime
@@ -62,33 +62,6 @@ def normalise_pulse(average):
62
62
normalised = [int (n - mean ) for n in average ]
63
63
return normalised
64
64
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
-
92
65
# Normalized pulse samples less normalized shape samples squared summed and rooted
93
66
def distortion (normalised , shape ):
94
67
product = [(x - y ) ** 2 for x , y in zip (shape , normalised )]
@@ -765,57 +738,173 @@ def fetch_json(file_id):
765
738
logger .error (f"Error fetching JSON: { e } \n " )
766
739
return None
767
740
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
772
811
773
812
def 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 )
781
816
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
782
826
table = dash_table .DataTable (
783
827
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" },
787
831
],
788
832
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" )},
805
848
],
806
849
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'
811
856
},
812
857
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
+ }
817
907
)
818
- return table
819
908
820
909
# Check if commands sent to processor is safe
821
910
def allowed_command (cmd ):
@@ -931,7 +1020,6 @@ def reset_stores():
931
1020
'store_load_flag_tab4' : False ,
932
1021
}
933
1022
934
-
935
1023
def save_settings_to_json ():
936
1024
settings = {key : getattr (global_vars , key ) for key in [
937
1025
"bin_size" ,
@@ -988,7 +1076,18 @@ def save_settings_to_json():
988
1076
"val_flag" ,
989
1077
"max_pulse_length" ,
990
1078
"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"
992
1091
]}
993
1092
994
1093
try :
@@ -1063,7 +1162,18 @@ def load_settings_from_json(path):
1063
1162
"val_flag" : bool ,
1064
1163
"max_pulse_length" : int ,
1065
1164
"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
1067
1177
}
1068
1178
1069
1179
with global_vars .write_lock :
@@ -1296,4 +1406,4 @@ def read_isotopes_data(data_path):
1296
1406
return data
1297
1407
except Exception as e :
1298
1408
print (f"Error reading isotopes data: { e } " )
1299
- return []
1409
+ return []
0 commit comments