start multiprocessing

This commit is contained in:
CPD 2025-01-31 18:04:58 +01:00
parent dbf1f0b4c2
commit 76e0a4e64a
3 changed files with 121 additions and 69 deletions

View File

@ -19,6 +19,8 @@ from os import path, makedirs
import pickle as pkl import pickle as pkl
import json import json
import atexit import atexit
import multiprocessing as mp
from multiprocessing.managers import BaseManager
import argparse import argparse
@ -47,7 +49,7 @@ from .led_control_device.base import LedControlDevice
from .led_control_device.impl import thorlabs_ledd1b as _led from .led_control_device.impl import thorlabs_ledd1b as _led
from .led_script import LedScript from .led_script import LedScript
from .measurement import Measurement from .measurement import measure as _measure
from .utility.data import DataCollector from .utility.data import DataCollector
@ -72,13 +74,14 @@ settings = {
test = False test = False
BaseManager.register('LedControlDevice', LedControlDevice)
# global variable for the instrument/client returned by pyvisa/bleak # global variable for the instrument/client returned by pyvisa/bleak
dev: VoltageMeasurementDevice|None = None dev: VoltageMeasurementDevice|None = None
led: LedControlDevice|None = None led: LedControlDevice|None = None
data = DataCollector(data_path=settings["datadir"], data_name="interactive", dirname="interactive_test", dir_exists_is_ok=True) data = DataCollector(data_path=settings["datadir"], data_name="interactive", dirname="interactive_test", dir_exists_is_ok=True)
t0 = 0 t0 = 0
def monitor(interval=None, max_measurements=None, max_points_shown=160): def monitor(script: str|int=0, interval=None, flush_after=None, max_measurements=None, max_points_shown=400):
""" """
Monitor the voltage with matplotlib. Monitor the voltage with matplotlib.
@ -91,21 +94,27 @@ def monitor(interval=None, max_measurements=None, max_points_shown=160):
@param max_points_shown : how many points should be shown at once. None means infinite @param max_points_shown : how many points should be shown at once. None means infinite
@param max_measurements : maximum number of measurements. None means infinite @param max_measurements : maximum number of measurements. None means infinite
""" """
global _runtime_vars global _runtime_vars, data, dev, led
_runtime_vars["last_measurement"] = dtime.now().isoformat() _runtime_vars["last_measurement"] = dtime.now().isoformat()
if not interval: interval = settings["interval"] if not interval: interval = settings["interval"]
print(f"Starting measurement with:\n\tinterval = {interval}s\nUse <C-c> to stop. Save the data using 'save_csv()' afterwards.") print(f"Starting measurement with:\n\tinterval = {interval}s\nUse <C-c> to stop. Save the data using 'data.save_csv()' afterwards.")
plt_monitor = _Monitor(use_print=True, max_points_shown=max_points_shown) plt_monitor = _Monitor(use_print=True, max_points_shown=max_points_shown)
led_script = LedScript(script=script)
data.clear() data.clear()
def update_func(i, t, v): queue = mp.Queue()
global t0 pipe_send, pipe_recv = mp.Pipe()
if i == 0: # TODO: pass instruments
t0 = t proc_measure = mp.Process(target=_measure, args=(None, None, led_script, data, interval, flush_after, max_measurements, False, pipe_recv, queue))
t -= t0 proc_measure.start()
data.add_data(i, t, v, 0) try:
plt_monitor.update(i, t, v) while True:
# update_led() data = queue.get(block=True, timeout=30)
dev.measure(interval=interval, max_measurements=max_measurements, update_func=update_func) i, tval, vval, led_val = data
plt_monitor.update(i, tval, vval, led_val)
except KeyboardInterrupt:
pass
pipe_send.send("stop")
proc_measure.join()
def measure(interval=None, max_measurements=None): def measure(interval=None, max_measurements=None):
""" """
@ -276,8 +285,13 @@ Enter 'help()' for a list of commands""")
makedirs(settings["datadir"]) makedirs(settings["datadir"])
try: try:
dev = _volt.init("GPIB0::22::INSTR") pass
led = _led.LEDD1B() # dev = _volt.init("GPIB0::22::INSTR")
# TODO
# manager = BaseManager()
# manager.start()
# led = manager._led.LEDD1B()
# led = _led.LEDD1B()
except Exception as e: except Exception as e:
print(e) print(e)
exit(1) exit(1)

View File

@ -5,62 +5,90 @@ Created on Fri Jan 24 15:18:31 2025
@author: Matthias Quintern @author: Matthias Quintern
""" """
from cpdctrl.voltage_measurement_device.base import VoltageMeasurementDevice from cpdctrl.voltage_measurement_device.base import VoltageMeasurementDevice
from cpdctrl.voltage_measurement_device.impl.keithley2700 import init
from cpdctrl.led_control_device.base import LedControlDevice from cpdctrl.led_control_device.base import LedControlDevice
from cpdctrl.led_control_device.impl.thorlabs_ledd1b import LEDD1B # TODO: remove!
from cpdctrl.led_script import LedScript from cpdctrl.led_script import LedScript
from cpdctrl.utility.data import DataCollector from cpdctrl.utility.data import DataCollector
import time import time
import datetime import datetime
from multiprocessing import Pipe
from multiprocessing.connection import Connection
from queue import Queue
class Measurement:
def __init__(self,
vm_dev: VoltageMeasurementDevice,
led_dev: LedControlDevice,
led_script: LedScript,
data: DataCollector,
):
self.vm_dev = vm_dev
self.led_dev = led_dev
self.led_script = led_script
self.data = data
def measure_finite(self, delta_t: 0.1, flush_after:int|None=None, max_measurements=None, verbose=False): def measure(
# if no "time" in header, set the current local time in ISO 8601 format vm_dev: VoltageMeasurementDevice,
# and without microseconds led_dev: LedControlDevice,
if not "time" in self.data.header: led_script: LedScript,
self.data.header["time"] = datetime.datetime.now().replace(microsecond=0).isoformat() data: DataCollector,
self.vm_dev.reset(True) delta_t: float=0.1,
try: flush_after:int|None=None,
i = 0 max_measurements: int=None,
led_val = self.led_script.start() verbose: bool=False,
pipe: None|Connection=None,
queue: None|Queue=None
):
# TODO: find a way to move inherited objects into a process
if led_dev is None:
led_dev = LEDD1B()
if vm_dev is None:
vm_dev = init("GPIB0::22::INSTR")
# if no "time" in header, set the current local time in ISO 8601 format
# and without microseconds
if not "time" in data.header:
data.header["time"] = datetime.datetime.now().replace(microsecond=0).isoformat()
vm_dev.reset(True)
try:
i = 0
led_val = led_script.start()
t_iter_start = time.time()
while max_measurements is None or i < max_measurements:
# 1) read value
tval, vval = vm_dev.read_value()
if i == 0:
t0 = tval
tval -= t0
current_data = (i, tval, vval, led_val)
data.add_data(*current_data)
# 2) write data
print(f"n = {i:6d}, t = {tval: .2f} s, U = {vval: .5f} V, LED = {led_val:03}%" + " "*10, end='\r')
if flush_after is not None and (i+1) % flush_after == 0:
data.flush(verbose=verbose)
# if a queue was given, put the data
if queue is not None:
queue.put(current_data)
# if a pipe was given, check for messages
if pipe is not None and pipe.poll(0):
recv = pipe.recv()
if recv == "stop":
break
elif type(recv) == tuple and recv[0] == "led_script":
led_script = recv[1]
else:
print(f"Received invalid message: '{recv}'")
# 3) sleep
# substract the execution time from the sleep time for a more
# acurate frequency
dt_sleep = delta_t - (time.time() - t_iter_start)
if dt_sleep > 0:
# print(f"Sleeping for {dt_sleep}")
time.sleep(dt_sleep)
t_iter_start = time.time() t_iter_start = time.time()
while max_measurements is None or i < max_measurements: # 4) update LED
tval, vval = self.vm_dev.read_value() new_led_val = led_script.get_state()
if i == 0: if new_led_val != led_val:
t0 = tval try:
tval -= t0 led_dev.set_level(new_led_val)
self.data.add_data(i, tval, vval, led_val) led_val = new_led_val
print(f"n = {i:6d}, t = {tval: .2f} s, U = {vval: .5f} V, LED = {led_val:03}%" + " "*10, end='\r') except Exception as e:
if flush_after is not None and (i+1) % flush_after == 0: print(f"Error setting led to {new_led_val}%:")
self.data.flush(verbose=verbose) print(e)
# substract the execution time from the sleep time for a more i += 1
# acurate frequency except KeyboardInterrupt:
dt_sleep = delta_t - (time.time() - t_iter_start) pass
if dt_sleep > 0: data.flush(verbose=verbose)
# print(f"Sleeping for {dt_sleep}") led_dev.off()
time.sleep(dt_sleep) print("Measurement stopped" + " "*50)
t_iter_start = time.time()
new_led_val = self.led_script.get_state()
if new_led_val != led_val:
try:
self.led_dev.set_level(new_led_val)
led_val = new_led_val
except Exception as e:
print(f"Error setting led to {new_led_val}%:")
print(e)
i += 1
except KeyboardInterrupt:
pass
self.data.flush(verbose=verbose)
print("Measurement stopped" + " "*50)

View File

@ -8,35 +8,45 @@ class _Monitor:
""" """
Monitor v and i data in a matplotlib window Monitor v and i data in a matplotlib window
""" """
def __init__(self, max_points_shown=None, use_print=False): def __init__(self, led_data=True, max_points_shown=None, use_print=False):
self.max_points_shown = max_points_shown self.max_points_shown = max_points_shown
self.use_print = use_print self.use_print = use_print
self.index = [] self.index = []
self.vdata = [] self.vdata = []
self.tdata = [] self.tdata = []
self.ldata = []
plt.ion() plt.ion()
self.fig1, self.vax = plt.subplots(1, 1, figsize=(8, 5)) self.fig1, self.vax = plt.subplots(1, 1, figsize=(8, 5))
self.led_data = led_data
self.vline, = self.vax.plot(self.tdata, self.vdata, color="g") if self.led_data:
self.lax = self.vax.twinx()
self.lax.set_ylabel("LED [%]")
self.lline, = self.lax.plot(self.tdata, self.ldata, color="orange")
self.lax.set_ylim(-0.5, 100.5)
self.vline, = self.vax.plot(self.tdata, self.vdata, color="blue")
self.vax.set_xlabel("time [s]") self.vax.set_xlabel("time [s]")
self.vax.set_ylabel("Voltage [V]") self.vax.set_ylabel("Voltage [V]")
self.vax.grid(True) self.vax.grid(True)
def update(self, i, tval, vval): def update(self, i, tval, vval, led_val=-1):
if self.use_print: if self.use_print:
_update_print(i, tval, vval) _update_print(i, tval, vval)
self.index.append(i) self.index.append(i)
self.tdata.append(tval) self.tdata.append(tval)
self.vdata.append(vval) self.vdata.append(vval)
if self.led_data:
self.ldata.append(led_val)
self.lline.set_xdata(self.tdata)
self.lline.set_ydata(self.ldata)
# update data # update data
self.vline.set_xdata(self.tdata) self.vline.set_xdata(self.tdata)
self.vline.set_ydata(self.vdata) self.vline.set_ydata(self.vdata)
# recalculate limits and set them for the view # recalculate limits and set them for the view
self.vax.relim() self.vax.relim()
if self.max_points_shown and i > self.max_points_shown: if self.max_points_shown and i > self.max_points_shown:
self.vax.set_xlim(i - self.max_points_shown, i) self.vax.set_xlim(self.tdata[i-self.max_points_shown], self.tdata[i])
self.vax.autoscale_view() self.vax.autoscale_view()
# update plot # update plot
self.fig1.canvas.draw() self.fig1.canvas.draw()