66from panel .viewable import Viewer
77
88from waveform_editor .derived_waveform import DerivedWaveform
9- from waveform_editor .tendencies .linear import LinearTendency
109from waveform_editor .tendencies .piecewise import PiecewiseLinearTendency
11- from waveform_editor .waveform import Waveform
1210
1311
1412class CoilCurrents (Viewer ):
1513 coil_ui = param .List (
1614 doc = "List of tuples containing the checkboxes and sliders for the coil currents"
1715 )
18- export_time = param .Number (doc = "Time to export coil currents to" )
16+ export_time = param .Number (
17+ doc = "Select a time at which coil currents will be saved to waveforms"
18+ )
1919
2020 def __init__ (self , main_gui ):
2121 super ().__init__ ()
@@ -36,19 +36,17 @@ def __init__(self, main_gui):
3636 confirm_button = pn .widgets .Button (
3737 on_click = lambda event : self ._store_coil_currents (),
3838 name = "Save Currents as Waveforms" ,
39- margin = 30 ,
39+ margin = ( 30 , 0 , 0 , 0 ) ,
4040 )
4141 self .panel = pn .Column (
42- pn .Row (export_time_input , confirm_button , no_ids_message ),
42+ pn .Row (
43+ export_time_input , confirm_button , visible = self .param .coil_ui .rx .bool ()
44+ ),
45+ no_ids_message ,
4346 guide_message ,
4447 self .sliders_ui ,
45- self .modal ,
4648 )
4749
48- def _open_modal (self ):
49- self ._coil_currents_valid ()
50- self .modal .show ()
51-
5250 @param .depends ("export_time" , watch = True )
5351 def _coil_currents_valid (self ):
5452 self .coil_export_valid = True
@@ -95,65 +93,119 @@ def create_ui(self, pf_active):
9593 self .coil_ui = new_coil_ui
9694
9795 def _store_coil_currents (self ):
96+ """Store the current values from the coil UI sliders into the waveform
97+ configuration.
98+ """
9899 coil_currents = self ._get_currents ()
99100 config = self .main_gui .config
100101 new_waveforms_created = False
102+ group_name = "Coil Currents"
101103
102- for i in range (len (self .coil_ui )):
103- name = f"pf_active/coil({ i + 1 } )/current/data"
104- if name in self .main_gui .config .waveform_map :
105- tendencies = self .main_gui .config [name ].tendencies
106- if tendencies :
107- end_time = tendencies [- 1 ].end
108- if end_time >= self .export_time :
109- pn .state .notifications .error (
110- "Export time must be later than the end of any existing waveforms"
111- )
112- return
104+ if not self ._has_valid_export_time ():
105+ return
113106
114107 for i , current in enumerate (coil_currents ):
115108 name = f"pf_active/coil({ i + 1 } )/current/data"
116- eps = 1e-100
117- # Piecewise tendencies must contain at least two points
118- new_piecewise = f"- {{type: piecewise, time: [{ self .export_time } , { self .export_time + eps } ], value: [{ current } , { current } ]}}"
119- if not name in config .waveform_map :
120- group_name = "Coil Currents"
109+ if name not in config .waveform_map :
121110 if group_name not in config .groups :
122111 config .add_group (group_name , [])
123- waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
124- config .add_waveform (waveform , [group_name ])
112+ self ._create_new_waveform (config , name , current , group_name )
125113 new_waveforms_created = True
126114 else :
127115 waveform = config [name ]
128116 if isinstance (waveform , DerivedWaveform ):
129- pn .state .error (
130- f"Could not store coil current in { name } , because it is a derived waveform"
117+ pn .state .notifications .error (
118+ f"Could not store coil current in waveform { name !r} , "
119+ "because it is a derived waveform"
131120 )
132121 continue
133-
134- last_tendency = waveform .tendencies [- 1 ]
135- if isinstance (last_tendency , PiecewiseLinearTendency ):
136- waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
137- waveform .yaml [- 1 ]["value" ].append (float (current ))
138- yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
139- else :
140- end = waveform .tendencies [- 1 ].end
141- append_new_piecewise = f"- {{type: piecewise, time: [{ end } , { self .export_time } ], value: [{ current } , { current } ]}}"
142- yaml_str = (
143- f"{ name } :\n { waveform .get_yaml_string ()} { append_new_piecewise } "
144- )
145- new_waveform = config .parse_waveform (yaml_str )
146- config .replace_waveform (new_waveform )
122+ self ._append_to_existing_waveform (config , name , current )
147123
148124 if new_waveforms_created :
149125 self .main_gui .selector .refresh ()
150126 pn .state .notifications .warning (
151- f"Could not find an existing waveform to store the coil current. A new waveform is created in the { group_name !r} group"
127+ "Could not find an existing waveform to store the coil current. "
128+ f"New waveform(s) are created in the { group_name !r} group"
152129 )
153130 else :
154131 pn .state .notifications .success (
155- "The coil currents were appended to their respective waveforms."
132+ "The values of the coil currents were appended to their respective "
133+ "waveforms."
134+ )
135+
136+ def _append_to_existing_waveform (self , config , name , current ):
137+ """Append coil current value to an existing waveform. If the last tendency is a
138+ piecewise tendency, it is extended, otherwise a new piecewise tendency
139+ is added.
140+
141+ Args:
142+ config: The waveform configuration.
143+ name: Name of the waveform.
144+ current: Coil current value to append.
145+ """
146+ waveform = config [name ]
147+ last_tendency = waveform .tendencies [- 1 ]
148+
149+ # Either append to existing piecewise linear tendency, or create new
150+ # piecewise linear tendency
151+ if isinstance (last_tendency , PiecewiseLinearTendency ):
152+ waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
153+ waveform .yaml [- 1 ]["value" ].append (float (current ))
154+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
155+ else :
156+ end = waveform .tendencies [- 1 ].end
157+ new_piecewise = (
158+ f"- {{type: piecewise, time: [{ end } , { self .export_time } ], "
159+ f"value: [{ current } , { current } ]}}"
156160 )
161+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} { new_piecewise } "
162+
163+ new_waveform = config .parse_waveform (yaml_str )
164+ config .replace_waveform (new_waveform )
165+
166+ def _create_new_waveform (self , config , name , current , group_name ):
167+ """Create a new waveform for a coil current when none exists.
168+
169+ Args:
170+ config: The waveform configuration.
171+ name: Name of the waveform.
172+ current: Coil current value to append.
173+ group_name: Name of the group to place the new waveform in.
174+ """
175+ # Piecewise tendencies must contain at least two points
176+ eps = 1e-9
177+ new_piecewise = (
178+ f"- {{type: piecewise, time: [{ self .export_time - eps } , "
179+ f"{ self .export_time } ], value: [{ current } , { current } ]}}"
180+ )
181+ waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
182+ config .add_waveform (waveform , [group_name ])
183+
184+ def _has_valid_export_time (self ):
185+ """Check whether the export time is later than the last tendency endpoint
186+ in all existing coil current waveforms.
187+
188+ Returns:
189+ True if export time is valid, False otherwise.
190+ """
191+ latest_time = None
192+ for i in range (len (self .coil_ui )):
193+ name = f"pf_active/coil({ i + 1 } )/current/data"
194+ if name in self .main_gui .config .waveform_map :
195+ tendencies = self .main_gui .config [name ].tendencies
196+ if tendencies :
197+ end_time = tendencies [- 1 ].end
198+ if latest_time is None or end_time > latest_time :
199+ latest_time = end_time
200+
201+ if latest_time is not None and latest_time >= self .export_time :
202+ pn .state .notifications .error (
203+ f"Invalid export time: { self .export_time } . It must be greater than the "
204+ f"last endpoint of existing coil current waveforms ({ latest_time } )."
205+ )
206+ return False
207+
208+ return True
157209
158210 def fill_pf_active (self , pf_active ):
159211 """Update the coil currents of the provided pf_active IDS. Only coils with
@@ -168,11 +220,11 @@ def fill_pf_active(self, pf_active):
168220 pf_active .coil [i ].current .data = np .array ([slider .value ])
169221
170222 def _get_currents (self ):
223+ """Returns the coil current values in a list."""
171224 coil_currents = []
172225 for coil_ui in self .coil_ui :
173226 _ , slider = coil_ui .objects
174227 coil_currents .append (slider .value )
175-
176228 return coil_currents
177229
178230 def sync_ui_with_pf_active (self , pf_active ):
0 commit comments