Coverage for  / opt / hostedtoolcache / Python / 3.11.14 / x64 / lib / python3.11 / site-packages / rattlesnake / components / abstract_interactive_control_law.py: 63%

54 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-27 18:22 +0000

1# -*- coding: utf-8 -*- 

2""" 

3This is an abstract interface to that defines an interactive control law in 

4Rattlesnake that can have it's own graphical user interface and interactive 

5elements in the control law. This control law will need to interface with the 

6environment in order to send parameters and receive data. It will also need to 

7handle building up a graphical user interface. 

8 

9The general workflow for this function should be: 

10 1. The control law is selected in the environment user interface with a new 

11 type associated with it: "Interactive". 

12 2. The control law must define four classes. 

13 a. The UI class will handle specification of parameters 

14 and/or visualization of results. Minor calculations may also be 

15 performed in the UI class, accepting that the UI will lock up while the 

16 calculations are performed unless sent to a separate process. 

17 b. The control calculation class will handle the major calculations 

18 involved with computing the next output dataset. 

19 c. The control law must define a "parameters" class that contains 

20 parameters that the control class will need. These can be things 

21 like regularization parameters, weighting parameters, transformation 

22 matrices; whatever the control law needs to perform its calculation. 

23 d. The control law must define a "results" class that contains 

24 information that needs to be passed from the control back to the 

25 user interface. 

26 2. The control law will need functions to handle the passing of the data 

27 objects between UI and calculation class. These should be 

28 `send_parameters` and `update_ui` functions for the UI and 

29 `update_parameters` and `send_results` functions for the calculation. 

30 The environment will handle calling these functions. 

31 3. Communication between the UI and the calculation will occur via channels 

32 set up in the environment. The data analysis queue will be used to send 

33 information to the control law, and the GUI update queue will be used to 

34 send information back to the UI. 

35 4. When the Initialize Environment button is clicked, the control law must 

36 initialize itself and build a graphical user interface for the control 

37 law. Extra parameters from the Rattlesnake box will be sent to this 

38 initialization function, and the GUI builder can use this for initial 

39 values for the control law, or for whatever other reason there might be. 

40 5. When the "Start" button is clicked on the System Identification, the 

41 control law UI will send it's current parameter state to the calculation 

42 class. The calculation class will update itself. 

43 6. When the system identification completes, the control calculation will 

44 receive the system identification information and then perform a control 

45 calculation for the prediction step. After this control calculation, 

46 results will be obtained and sent back to the UI. 

47 7. During control, the control law will repeatedly send updates back to the 

48 UI. The UI will not automatically send parameters unless told to do so 

49 through a callback in its UI. 

50 

51 

52Rattlesnake Vibration Control Software 

53Copyright (C) 2021 National Technology & Engineering Solutions of Sandia, LLC 

54(NTESS). Under the terms of Contract DE-NA0003525 with NTESS, the U.S. 

55Government retains certain rights in this software. 

56 

57This program is free software: you can redistribute it and/or modify 

58it under the terms of the GNU General Public License as published by 

59the Free Software Foundation, either version 3 of the License, or 

60(at your option) any later version. 

61 

62This program is distributed in the hope that it will be useful, 

63but WITHOUT ANY WARRANTY; without even the implied warranty of 

64MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

65GNU General Public License for more details. 

66 

67You should have received a copy of the GNU General Public License 

68along with this program. If not, see <https://www.gnu.org/licenses/>. 

69""" 

70 

71from abc import ABC, abstractmethod 

72 

73from .utilities import GlobalCommands 

74 

75 

76class AbstractControlLawUI(ABC): 

77 """A user interface to allow users to create interactive control laws""" 

78 

79 @abstractmethod 

80 def __init__(self, process_name, send_parameters_queue, window, parent_ui_class): 

81 """Initializes an interactive control law 

82 

83 Parameters 

84 ---------- 

85 process_name : str 

86 The process name associated with this user interface 

87 send_parameters_queue : np.Queue 

88 A Multiprocessing queue into which parameters defined by the UI will be put to be 

89 used by the environment process 

90 window : QDialog 

91 The dialog window that the user interface will be placed in 

92 parent_ui_class : RandomVibrationUI 

93 The user interface object for the environment that spawned this control law. 

94 """ 

95 self.send_parameters_queue = send_parameters_queue 

96 self.process_name = process_name 

97 self.window = window 

98 self.parent_ui_class = parent_ui_class 

99 self.data_acquisition_parameters = None 

100 self.environment_parameters = None 

101 

102 def initialize_parameters(self, data_acquisition_parameters, environment_parameters): 

103 """Stores the data acquisition and environment parameters to the UI 

104 

105 Parameters 

106 ---------- 

107 data_acquisition_parameters : DataAcquisitionParameters 

108 The global data acquisition parameters like sample rate and channel table. 

109 environment_parameters : RandomVibrationMetadata 

110 Parameters defining the environment 

111 """ 

112 self.data_acquisition_parameters = data_acquisition_parameters 

113 self.environment_parameters = environment_parameters 

114 

115 def send_parameters(self): 

116 """Sends parameters from the UI to the environment process""" 

117 self.send_parameters_queue.put( 

118 self.process_name, 

119 ( 

120 GlobalCommands.UPDATE_INTERACTIVE_CONTROL_PARAMETERS, 

121 self.collect_parameters(), 

122 ), 

123 ) 

124 

125 def run_callback(self, command, *args): 

126 """Tells the environment process to run a specific command""" 

127 self.send_parameters_queue.put( 

128 self.process_name, 

129 (GlobalCommands.SEND_INTERACTIVE_COMMAND, (command, args)), 

130 ) 

131 

132 @abstractmethod 

133 def collect_parameters(self) -> dict: 

134 """Collects parameters from the UI to send to the environment process""" 

135 

136 @abstractmethod 

137 def update_ui_control(self, results: dict): 

138 """Updates the UI with results from the control law 

139 

140 Parameters 

141 ---------- 

142 results : dict 

143 A dictionary containing information the UI might need to update itself 

144 """ 

145 

146 @abstractmethod 

147 def update_ui_sysid( 

148 self, 

149 sysid_frf, # Transfer Functions 

150 sysid_response_noise, # Noise levels and correlation 

151 sysid_reference_noise, # from the system identification 

152 sysid_response_cpsd, # Response levels and correlation 

153 sysid_reference_cpsd, # from the system identification 

154 sysid_coherence, # Coherence from the system identification 

155 ): 

156 """Updates the UI with information from the system identification 

157 

158 Parameters 

159 ---------- 

160 sysid_frf : np.ndarray 

161 The system transfer functions 

162 sysid_response_noise : np.ndarray 

163 The noise CPSD matrix at the control channels from the system identification 

164 sysid_reference_noise : np.ndarray 

165 The noise CPSD matrix at the drive channels from the system identification 

166 sysid_response_cpsd : np.ndarray 

167 The Buzz CPSD at the control channels from the system identification 

168 sysid_reference_cpsd : np.ndarray 

169 The Buzz CPSD at the drive channels from the system identification 

170 sysid_coherence : np.ndarray 

171 The multiple coherence for each of the control channels from the system identification 

172 """ 

173 

174 def close(self): 

175 """Closes the UI window""" 

176 self.window.close() 

177 

178 

179class AbstractControlLawComputation(ABC): 

180 """Computation process for the interactive control law that runs on the environment 

181 data analysis process""" 

182 

183 @abstractmethod 

184 def __init__(self, environment_name, gui_update_queue): 

185 """Initializes the control process 

186 

187 Parameters 

188 ---------- 

189 environment_name : str 

190 The name of the environment that the control law is running in 

191 gui_update_queue : mp.Queue 

192 The queue into which GUI updates will be put 

193 """ 

194 self.environment_name = environment_name 

195 self.gui_update_queue = gui_update_queue 

196 self._command_map = {} 

197 

198 def send_results(self): 

199 """Sends results of the control calculation to the UI to update itself""" 

200 self.gui_update_queue.put( 

201 ( 

202 self.environment_name, 

203 ("interactive_control_update", self.collect_results()), 

204 ) 

205 ) 

206 

207 @abstractmethod 

208 def update_parameters(self, parameters: dict): 

209 """Updates the control computation based on parameters sent from the UI 

210 

211 Parameters 

212 ---------- 

213 parameters : dict 

214 A dictionary containing relevant parameters from the user interface 

215 """ 

216 

217 @abstractmethod 

218 def collect_results(self) -> dict: 

219 """Collects results from the computation to send to the user interface for updates 

220 

221 Returns 

222 ------- 

223 dict 

224 A dictionary containing relevant results to display on the user interface 

225 """ 

226 

227 @abstractmethod 

228 def control(self): 

229 """Executes the control calculation""" 

230 

231 @abstractmethod 

232 def system_id_update(self): 

233 """Updates the control law based on parameters received from the system identification""" 

234 

235 @staticmethod 

236 @abstractmethod 

237 def get_ui_class(): 

238 """Returns the User Interface class corresponding to this calculation class""" 

239 

240 @property 

241 def command_map(self) -> dict: 

242 """A dictionary that maps commands received by the ``command_queue`` 

243 to functions in the class""" 

244 return self._command_map 

245 

246 def map_command(self, key, function): 

247 """A function that maps an instruction to a function in the ``command_map`` 

248 

249 Parameters 

250 ---------- 

251 key : 

252 The instruction that will be pulled from the ``command_queue`` 

253 

254 function : 

255 A reference to the function that will be called when the ``key`` 

256 message is received. 

257 

258 """ 

259 self._command_map[key] = function 

260 

261 def send_command(self, command: tuple): 

262 """A function used by the environment process to execute commands sent from the UI 

263 The UI object uses the `send_parameters_queue` (which is also the environment_command_queue) 

264 to send custom instructions (set up using the map_command function) to this computation 

265 object. 

266 

267 Parameters 

268 ---------- 

269 command : tuple 

270 (command enumeration, command arguments) 

271 

272 Returns 

273 ------- 

274 Any 

275 """ 

276 func, args = command 

277 function = self._command_map[func] 

278 return function(*args)