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 json
import atexit
import multiprocessing as mp
from multiprocessing.managers import BaseManager
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_script import LedScript
from .measurement import Measurement
from .measurement import measure as _measure
from .utility.data import DataCollector
@ -72,13 +74,14 @@ settings = {
test = False
BaseManager.register('LedControlDevice', LedControlDevice)
# global variable for the instrument/client returned by pyvisa/bleak
dev: VoltageMeasurementDevice|None = None
led: LedControlDevice|None = None
data = DataCollector(data_path=settings["datadir"], data_name="interactive", dirname="interactive_test", dir_exists_is_ok=True)
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.
@ -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_measurements : maximum number of measurements. None means infinite
"""
global _runtime_vars
global _runtime_vars, data, dev, led
_runtime_vars["last_measurement"] = dtime.now().isoformat()
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)
led_script = LedScript(script=script)
data.clear()
def update_func(i, t, v):
global t0
if i == 0:
t0 = t
t -= t0
data.add_data(i, t, v, 0)
plt_monitor.update(i, t, v)
# update_led()
dev.measure(interval=interval, max_measurements=max_measurements, update_func=update_func)
queue = mp.Queue()
pipe_send, pipe_recv = mp.Pipe()
# TODO: pass instruments
proc_measure = mp.Process(target=_measure, args=(None, None, led_script, data, interval, flush_after, max_measurements, False, pipe_recv, queue))
proc_measure.start()
try:
while True:
data = queue.get(block=True, timeout=30)
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):
"""
@ -276,8 +285,13 @@ Enter 'help()' for a list of commands""")
makedirs(settings["datadir"])
try:
dev = _volt.init("GPIB0::22::INSTR")
led = _led.LEDD1B()
pass
# dev = _volt.init("GPIB0::22::INSTR")
# TODO
# manager = BaseManager()
# manager.start()
# led = manager._led.LEDD1B()
# led = _led.LEDD1B()
except Exception as e:
print(e)
exit(1)

View File

@ -5,62 +5,90 @@ Created on Fri Jan 24 15:18:31 2025
@author: Matthias Quintern
"""
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.impl.thorlabs_ledd1b import LEDD1B # TODO: remove!
from cpdctrl.led_script import LedScript
from cpdctrl.utility.data import DataCollector
import time
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):
# if no "time" in header, set the current local time in ISO 8601 format
# and without microseconds
if not "time" in self.data.header:
self.data.header["time"] = datetime.datetime.now().replace(microsecond=0).isoformat()
self.vm_dev.reset(True)
try:
i = 0
led_val = self.led_script.start()
def measure(
vm_dev: VoltageMeasurementDevice,
led_dev: LedControlDevice,
led_script: LedScript,
data: DataCollector,
delta_t: float=0.1,
flush_after:int|None=None,
max_measurements: int=None,
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()
while max_measurements is None or i < max_measurements:
tval, vval = self.vm_dev.read_value()
if i == 0:
t0 = tval
tval -= t0
self.data.add_data(i, tval, vval, led_val)
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:
self.data.flush(verbose=verbose)
# 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()
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)
# 4) update LED
new_led_val = led_script.get_state()
if new_led_val != led_val:
try:
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
data.flush(verbose=verbose)
led_dev.off()
print("Measurement stopped" + " "*50)

View File

@ -8,35 +8,45 @@ class _Monitor:
"""
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.use_print = use_print
self.index = []
self.vdata = []
self.tdata = []
self.ldata = []
plt.ion()
self.fig1, self.vax = plt.subplots(1, 1, figsize=(8, 5))
self.vline, = self.vax.plot(self.tdata, self.vdata, color="g")
self.led_data = led_data
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_ylabel("Voltage [V]")
self.vax.grid(True)
def update(self, i, tval, vval):
def update(self, i, tval, vval, led_val=-1):
if self.use_print:
_update_print(i, tval, vval)
self.index.append(i)
self.tdata.append(tval)
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
self.vline.set_xdata(self.tdata)
self.vline.set_ydata(self.vdata)
# recalculate limits and set them for the view
self.vax.relim()
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()
# update plot
self.fig1.canvas.draw()