Coverage for src/pytribeam/log.py: 0%
86 statements
« prev ^ index » next coverage.py v7.6.1, created at 2026-06-16 18:30 +0000
« prev ^ index » next coverage.py v7.6.1, created at 2026-06-16 18:30 +0000
1#!/usr/bin/python3
2"""
3Log Module
4==========
6This module contains functions for logging various experiment data, including creating log files, extracting YAML configurations, and logging experiment settings, positions, laser power, and specimen current.
8Functions
9---------
10create_file(path: Path) -> bool
11 Create a log file at the specified path.
13yml_from_log(log_path_h5: Path, output_path_yml: Path, row: int, config_field: str = "Config File") -> bool
14 Extract YAML configuration from a log file and save it to an output path.
16experiment_settings(slice_number: int, step_number: int, log_filepath: Path, yml_path: Path) -> bool
17 Log experiment settings to the log file.
19position(step_number: int, step_name: str, slice_number: int, log_filepath: Path, dataset_name: str, current_position: tbt.StagePositionUser) -> bool
20 Log the current position to the log file.
22laser_power(step_number: int, step_name: str, slice_number: int, log_filepath: Path, dataset_name: str, power_w: float) -> bool
23 Log the laser power to the log file.
25specimen_current(step_number: int, step_name: str, slice_number: int, log_filepath: Path, dataset_name: str, specimen_current_na: float) -> bool
26 Log the specimen current to the log file.
28current_time() -> tbt.TimeStamp
29 Get the current time as a timestamp.
30"""
32# Default python modules
33# from functools import singledispatch
34from pathlib import Path
35import datetime
37# Autoscript included modules
38import numpy as np
39import h5py
41# 3rd party module
43# Local scripts
44from pytribeam.constants import Constants
45import pytribeam.types as tbt
48def create_file(path: Path) -> bool:
49 """
50 Create a log file at the specified path.
52 This function creates a log file at the specified path if it does not already exist.
54 Parameters
55 ----------
56 path : Path
57 The path where the log file should be created.
59 Returns
60 -------
61 bool
62 True if the log file is created successfully.
64 Raises
65 ------
66 ValueError
67 If the log file cannot be created.
68 """
69 if not path.is_file():
70 log = h5py.File(path, "w")
71 log.close()
72 if path.is_file():
73 print(f'Logfile created at "{path}".')
74 if path.is_file():
75 return True
76 raise ValueError(f'Unable to create log file at location "{path}".')
79def current_time() -> tbt.TimeStamp:
80 """
81 Get the current time as a timestamp.
83 This function returns the current time as a `TimeStamp` object, including both human-readable and UNIX time formats.
85 Returns
86 -------
87 tbt.TimeStamp
88 The current time as a `TimeStamp` object.
89 """
90 now = datetime.datetime.now()
91 human_readable = now.strftime("%m/%d/%Y %H:%M:%S")
92 unix_time = int(now.timestamp())
93 time = tbt.TimeStamp(human_readable=human_readable, unix=unix_time)
94 return time
97def yml_from_log(
98 log_path_h5: Path,
99 output_path_yml: Path,
100 row: int,
101 config_field: str = "Config File",
102) -> bool:
103 """
104 Extract YAML configuration from a log file and save it to an output path.
106 This function extracts the YAML configuration from a specified row in the log file and saves it to the output path.
108 Parameters
109 ----------
110 log_path_h5 : Path
111 The path to the log file.
112 output_path_yml : Path
113 The path to save the extracted YAML configuration.
114 row : int
115 The row number to extract the configuration from.
116 config_field : str, optional
117 The field name for the configuration in the log file (default is "Config File").
119 Returns
120 -------
121 bool
122 True if the YAML configuration is extracted and saved successfully.
123 """
124 # TODO enforce file formats on inputs
125 with h5py.File(log_path_h5, "r") as file:
126 data = np.array(file[Constants.settings_dataset_name][:])
127 settings = data[row][Constants.settings_dtype.names.index(config_field)].decode(
128 "utf-8"
129 )
131 with open(output_path_yml, "w") as file:
132 file.write(settings)
134 return True
137def experiment_settings(
138 slice_number: int,
139 step_number: int,
140 log_filepath: Path,
141 yml_path: Path,
142) -> bool:
143 """
144 Log experiment settings to the log file.
146 This function logs the experiment settings from a YAML file to the log file.
148 Parameters
149 ----------
150 slice_number : int
151 The slice number for the experiment.
152 step_number : int
153 The step number for the experiment.
154 log_filepath : Path
155 The path to the log file.
156 yml_path : Path
157 The path to the YAML file containing the experiment settings.
159 Returns
160 -------
161 bool
162 True if the experiment settings are logged successfully.
163 """
164 dataset_name = Constants.settings_dataset_name
165 settings_dtype = Constants.settings_dtype
166 time = current_time()
168 with open(yml_path, "r") as yml_file:
169 yml_data = yml_file.read()
171 if not log_filepath.exists():
172 log_filepath.touch()
174 with h5py.File(log_filepath, "r+") as log:
175 if not dataset_name in log:
176 settings = log.create_dataset(
177 dataset_name,
178 (0,),
179 settings_dtype,
180 maxshape=(None,),
181 )
182 dataset = log[dataset_name]
183 settings_data = np.array(
184 [
185 (
186 slice_number,
187 step_number,
188 yml_data,
189 time.human_readable,
190 time.unix,
191 )
192 ],
193 settings_dtype,
194 )
195 # add one row to table
196 dataset.resize(dataset.shape[0] + 1, axis=0)
197 dataset[-1:] = settings_data
199 return True
202def position(
203 step_number: int,
204 step_name: str,
205 slice_number: int,
206 log_filepath: Path,
207 dataset_name: str,
208 current_position: tbt.StagePositionUser,
209) -> bool:
210 """
211 Log the current position to the log file.
213 This function logs the current position of the stage to the log file.
215 Parameters
216 ----------
217 step_number : int
218 The step number for the experiment.
219 step_name : str
220 The name of the step.
221 slice_number : int
222 The slice number for the experiment.
223 log_filepath : Path
224 The path to the log file.
225 dataset_name : str
226 The name of the dataset to log the position to.
227 current_position : tbt.StagePositionUser
228 The current position of the stage.
230 Returns
231 -------
232 bool
233 True if the current position is logged successfully.
234 """
235 print("\tLogging current position...")
236 dataset_location = f"{step_number:02d}_{step_name}/{dataset_name}"
237 time = current_time()
239 with h5py.File(log_filepath, "r+") as file:
240 if not dataset_location in file:
241 position = file.create_dataset(
242 dataset_location,
243 (0,),
244 Constants.position_dtype,
245 maxshape=(None,),
246 )
247 position.attrs["X Units"] = np.string_("[mm]")
248 position.attrs["Y Units"] = np.string_("[mm]")
249 position.attrs["Z Units"] = np.string_("[mm]")
250 position.attrs["T Units"] = np.string_("[degrees]")
251 position.attrs["R Units"] = np.string_("[degrees]")
253 dataset = file[dataset_location]
254 position_data = np.array(
255 [
256 (
257 slice_number,
258 round(current_position.x_mm, 6),
259 round(current_position.y_mm, 6),
260 round(current_position.z_mm, 6),
261 round(current_position.t_deg, 6),
262 round(current_position.r_deg, 6),
263 time.human_readable,
264 time.unix,
265 )
266 ],
267 Constants.position_dtype,
268 )
269 # add one row to table
270 dataset.resize(dataset.shape[0] + 1, axis=0)
271 dataset[-1:] = position_data
273 return True
276def laser_power(
277 step_number: int,
278 step_name: str,
279 slice_number: int,
280 log_filepath: Path,
281 dataset_name: str,
282 power_w: float,
283) -> bool:
284 """
285 Log the laser power to the log file.
287 This function logs the laser power to the log file.
289 Parameters
290 ----------
291 step_number : int
292 The step number for the experiment.
293 step_name : str
294 The name of the step.
295 slice_number : int
296 The slice number for the experiment.
297 log_filepath : Path
298 The path to the log file.
299 dataset_name : str
300 The name of the dataset to log the laser power to.
301 power_w : float
302 The laser power in watts.
304 Returns
305 -------
306 bool
307 True if the laser power is logged successfully.
308 """
309 print("\tLogging laser power...")
310 dataset_location = f"{step_number:02d}_{step_name}/{dataset_name}"
311 time = current_time()
313 with h5py.File(log_filepath, "r+") as file:
314 if not dataset_location in file:
315 laser_power = file.create_dataset(
316 dataset_location,
317 (0,),
318 Constants.laser_power_dtype,
319 maxshape=(None,),
320 )
321 laser_power.attrs["Units"] = np.string_("[W]")
323 dataset = file[dataset_location]
324 laser_power_data = np.array(
325 [
326 (
327 slice_number,
328 round(power_w, 6),
329 time.human_readable,
330 time.unix,
331 )
332 ],
333 Constants.laser_power_dtype,
334 )
335 # add one row to table
336 dataset.resize(dataset.shape[0] + 1, axis=0)
337 dataset[-1:] = laser_power_data
340def specimen_current(
341 step_number: int,
342 step_name: str,
343 slice_number: int,
344 log_filepath: Path,
345 dataset_name: str,
346 specimen_current_na: float,
347) -> bool:
348 """
349 Log the specimen current to the log file.
351 This function logs the specimen current to the log file.
353 Parameters
354 ----------
355 step_number : int
356 The step number for the experiment.
357 step_name : str
358 The name of the step.
359 slice_number : int
360 The slice number for the experiment.
361 log_filepath : Path
362 The path to the log file.
363 dataset_name : str
364 The name of the dataset to log the specimen current to.
365 specimen_current_na : float
366 The specimen current in nanoamperes.
368 Returns
369 -------
370 bool
371 True if the specimen current is logged successfully.
372 """
373 print("\tLogging sample current...")
374 dataset_location = f"{step_number:02d}_{step_name}/{dataset_name}"
375 time = current_time()
377 with h5py.File(log_filepath, "r+") as file:
378 if not dataset_location in file:
379 specimen_current = file.create_dataset(
380 dataset_location,
381 (0,),
382 Constants.specimen_current_dtype,
383 maxshape=(None,),
384 )
385 specimen_current.attrs["Units"] = np.string_("[nA]")
387 dataset = file[dataset_location]
388 specimen_current_data = np.array(
389 [
390 (
391 slice_number,
392 round(specimen_current_na, 6),
393 time.human_readable,
394 time.unix,
395 )
396 ],
397 Constants.specimen_current_dtype,
398 )
399 # add one row to table
400 dataset.resize(dataset.shape[0] + 1, axis=0)
401 dataset[-1:] = specimen_current_data
402 print("\tLogging sample current complete...")
405if __name__ == "__main__":
406 pass