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
1311from waveform_editor .settings import settings
1412
@@ -17,7 +15,9 @@ class CoilCurrents(Viewer):
1715 coil_ui = param .List (
1816 doc = "List of tuples containing the checkboxes and sliders for the coil currents"
1917 )
20- export_time = param .Number (doc = "Time to export coil currents to" )
18+ export_time = param .Number (
19+ doc = "Select a time at which coil currents will be saved to waveforms"
20+ )
2121
2222 def __init__ (self , main_gui , ** params ):
2323 super ().__init__ (** params )
@@ -39,19 +39,17 @@ def __init__(self, main_gui, **params):
3939 confirm_button = pn .widgets .Button (
4040 on_click = lambda event : self ._store_coil_currents (),
4141 name = "Save Currents as Waveforms" ,
42- margin = 30 ,
42+ margin = ( 30 , 0 , 0 , 0 ) ,
4343 )
4444 self .panel = pn .Column (
45- pn .Row (export_time_input , confirm_button , no_ids_message ),
45+ pn .Row (
46+ export_time_input , confirm_button , visible = self .param .coil_ui .rx .bool ()
47+ ),
48+ no_ids_message ,
4649 guide_message ,
4750 self .sliders_ui ,
48- self .modal ,
4951 )
5052
51- def _open_modal (self ):
52- self ._coil_currents_valid ()
53- self .modal .show ()
54-
5553 @param .depends ("export_time" , watch = True )
5654 def _coil_currents_valid (self ):
5755 self .coil_export_valid = True
@@ -100,65 +98,119 @@ def create_ui(self, pf_active):
10098 self .coil_ui = new_coil_ui
10199
102100 def _store_coil_currents (self ):
101+ """Store the current values from the coil UI sliders into the waveform
102+ configuration.
103+ """
103104 coil_currents = self ._get_currents ()
104105 config = self .main_gui .config
105106 new_waveforms_created = False
107+ group_name = "Coil Currents"
106108
107- for i in range (len (self .coil_ui )):
108- name = f"pf_active/coil({ i + 1 } )/current/data"
109- if name in self .main_gui .config .waveform_map :
110- tendencies = self .main_gui .config [name ].tendencies
111- if tendencies :
112- end_time = tendencies [- 1 ].end
113- if end_time >= self .export_time :
114- pn .state .notifications .error (
115- "Export time must be later than the end of any existing waveforms"
116- )
117- return
109+ if not self ._has_valid_export_time ():
110+ return
118111
119112 for i , current in enumerate (coil_currents ):
120113 name = f"pf_active/coil({ i + 1 } )/current/data"
121- eps = 1e-100
122- # Piecewise tendencies must contain at least two points
123- new_piecewise = f"- {{type: piecewise, time: [{ self .export_time } , { self .export_time + eps } ], value: [{ current } , { current } ]}}"
124- if not name in config .waveform_map :
125- group_name = "Coil Currents"
114+ if name not in config .waveform_map :
126115 if group_name not in config .groups :
127116 config .add_group (group_name , [])
128- waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
129- config .add_waveform (waveform , [group_name ])
117+ self ._create_new_waveform (config , name , current , group_name )
130118 new_waveforms_created = True
131119 else :
132120 waveform = config [name ]
133121 if isinstance (waveform , DerivedWaveform ):
134- pn .state .error (
135- f"Could not store coil current in { name } , because it is a derived waveform"
122+ pn .state .notifications .error (
123+ f"Could not store coil current in waveform { name !r} , "
124+ "because it is a derived waveform"
136125 )
137126 continue
138-
139- last_tendency = waveform .tendencies [- 1 ]
140- if isinstance (last_tendency , PiecewiseLinearTendency ):
141- waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
142- waveform .yaml [- 1 ]["value" ].append (float (current ))
143- yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
144- else :
145- end = waveform .tendencies [- 1 ].end
146- append_new_piecewise = f"- {{type: piecewise, time: [{ end } , { self .export_time } ], value: [{ current } , { current } ]}}"
147- yaml_str = (
148- f"{ name } :\n { waveform .get_yaml_string ()} { append_new_piecewise } "
149- )
150- new_waveform = config .parse_waveform (yaml_str )
151- config .replace_waveform (new_waveform )
127+ self ._append_to_existing_waveform (config , name , current )
152128
153129 if new_waveforms_created :
154130 self .main_gui .selector .refresh ()
155131 pn .state .notifications .warning (
156- f"Could not find an existing waveform to store the coil current. A new waveform is created in the { group_name !r} group"
132+ "Could not find an existing waveform to store the coil current. "
133+ f"New waveform(s) are created in the { group_name !r} group"
157134 )
158135 else :
159136 pn .state .notifications .success (
160- "The coil currents were appended to their respective waveforms."
137+ "The values of the coil currents were appended to their respective "
138+ "waveforms."
139+ )
140+
141+ def _append_to_existing_waveform (self , config , name , current ):
142+ """Append coil current value to an existing waveform. If the last tendency is a
143+ piecewise tendency, it is extended, otherwise a new piecewise tendency
144+ is added.
145+
146+ Args:
147+ config: The waveform configuration.
148+ name: Name of the waveform.
149+ current: Coil current value to append.
150+ """
151+ waveform = config [name ]
152+ last_tendency = waveform .tendencies [- 1 ]
153+
154+ # Either append to existing piecewise linear tendency, or create new
155+ # piecewise linear tendency
156+ if isinstance (last_tendency , PiecewiseLinearTendency ):
157+ waveform .yaml [- 1 ]["time" ].append (float (self .export_time ))
158+ waveform .yaml [- 1 ]["value" ].append (float (current ))
159+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} "
160+ else :
161+ end = waveform .tendencies [- 1 ].end
162+ new_piecewise = (
163+ f"- {{type: piecewise, time: [{ end } , { self .export_time } ], "
164+ f"value: [{ current } , { current } ]}}"
161165 )
166+ yaml_str = f"{ name } :\n { waveform .get_yaml_string ()} { new_piecewise } "
167+
168+ new_waveform = config .parse_waveform (yaml_str )
169+ config .replace_waveform (new_waveform )
170+
171+ def _create_new_waveform (self , config , name , current , group_name ):
172+ """Create a new waveform for a coil current when none exists.
173+
174+ Args:
175+ config: The waveform configuration.
176+ name: Name of the waveform.
177+ current: Coil current value to append.
178+ group_name: Name of the group to place the new waveform in.
179+ """
180+ # Piecewise tendencies must contain at least two points
181+ eps = 1e-9
182+ new_piecewise = (
183+ f"- {{type: piecewise, time: [{ self .export_time - eps } , "
184+ f"{ self .export_time } ], value: [{ current } , { current } ]}}"
185+ )
186+ waveform = config .parser .parse_waveform (f"{ name } :\n { new_piecewise } " )
187+ config .add_waveform (waveform , [group_name ])
188+
189+ def _has_valid_export_time (self ):
190+ """Check whether the export time is later than the last tendency endpoint
191+ in all existing coil current waveforms.
192+
193+ Returns:
194+ True if export time is valid, False otherwise.
195+ """
196+ latest_time = None
197+ for i in range (len (self .coil_ui )):
198+ name = f"pf_active/coil({ i + 1 } )/current/data"
199+ if name in self .main_gui .config .waveform_map :
200+ tendencies = self .main_gui .config [name ].tendencies
201+ if tendencies :
202+ end_time = tendencies [- 1 ].end
203+ if latest_time is None or end_time > latest_time :
204+ latest_time = end_time
205+
206+ if latest_time is not None and latest_time >= self .export_time :
207+ pn .state .notifications .error (
208+ f"Invalid export time: { self .export_time } . It must be greater than the "
209+ f"last endpoint of existing coil current waveforms ({ latest_time } )."
210+ )
211+ return False
212+
213+ return True
162214
163215 def fill_pf_active (self , pf_active ):
164216 """Update the coil currents of the provided pf_active IDS. Only coils with
@@ -172,11 +224,11 @@ def fill_pf_active(self, pf_active):
172224 pf_active .coil [i ].current .data = np .array ([slider .value ])
173225
174226 def _get_currents (self ):
227+ """Returns the coil current values in a list."""
175228 coil_currents = []
176229 for coil_ui in self .coil_ui :
177230 _ , slider = coil_ui .objects
178231 coil_currents .append (slider .value )
179-
180232 return coil_currents
181233
182234 def sync_ui_with_pf_active (self , pf_active ):
0 commit comments