# This file is part of Pymepix## In all scientific work using Pymepix, please reference it as## A. F. Al-Refaie, M. Johny, J. Correa, D. Pennicard, P. Svihra, A. Nomerotski, S. Trippel, and J. Küpper:# "PymePix: a python library for SPIDR readout of Timepix3", J. Inst. 14, P10003 (2019)# https://doi.org/10.1088/1748-0221/14/10/P10003# https://arxiv.org/abs/1905.07999## Pymepix is free software: you can redistribute it and/or modify it under the terms of the GNU# General Public License as published by the Free Software Foundation, either version 3 of the# License, or (at your option) any later version.## This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without# even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU# General Public License for more details.## You should have received a copy of the GNU General Public License along with this program. If not,# see <https://www.gnu.org/licenses/>.importmultiprocessingimportos# Checking if device is thereimportstatimporttimeimportnumpyasnpimportserialimportzmqimportpymepix.config.load_configascfgfrompymepix.core.logimportProcessLogger# from zmq.sugar.constants import NOBLOCK# Class to write raw data to files using ZMQ and a new thread to prevent IO blocking
[docs]classUSBTrainID(multiprocessing.Process):""" Class for asynchronously writing raw files Intended to allow writing of raw data while minimizing impact on UDP reception reliability """def__init__(self,name="USBTrainId"):multiprocessing.Process.__init__(self)ProcessLogger.__init__(self,name)device=cfg.default_cfg["trainID"]["device"]try:self._ser=Noneself.connect_device(device)exceptException:self.log.error(f"Failure connecting {device}{Exception}")
[docs]defconnect_device(self,device):"""Establish connection to USB device"""# doesn't worktry:stat.S_ISBLK(os.stat(device).st_mode)self.log.info(f"{device} connected")exceptException:self.log.error(f"Problem in init connecting to {device}{Exception}")# Configure serial interfaceself._ser=serial.Serial(device,115200)
[docs]defrun(self):ctx=zmq.Context.instance()z_sock=ctx.socket(zmq.PAIR)z_sock.connect("ipc:///tmp/train_sock")# variables needed in loop# times, ids = [], []# Information fields read from the USB interfacetimingInfoNames=["Train ID","Beam Mode","CRC"]# Number of bytes in each information fieldtimingInfoLength=[16,8,2]# State machine etc. local variableswaiting=Truerecord=Falseshutdown=Falsefilehandle=Nonewhilenotshutdown:# wait for instructions, valid commands are# "SHUTDOWN": exits this loop and ends thread# "filename" in the form "/filenamewhilewaiting:cmd=z_sock.recv_string()ifcmd=="SHUTDOWN":self.log.info("SHUTDOWN received")waiting=Falseshutdown=Trueelse:# Interpret as file name / pathfilename=cmddirectory,name=os.path.split(filename)if(notos.path.exists(filename))andos.path.isdir(directory):self.log.info(f"File {filename} opening")# Open filehandlefilehandle=open(filename,"wb")z_sock.send_string("OPENED")waiting=Falserecord=Truez_sock.send_string(filename)else:self.log.info(f'"{cmd}" not a valid command')z_sock.send_string(f'"{cmd}" in an INVALID command')whilerecord:# Align with beginning of word (STX = ASCII 02)whilebytes([2])!=self._ser.read(1):pass# Reset information on each runtimingInfo={k:""forkintimingInfoNames}# Get info according to Information fields and bytes fields# Information fields are in order, so do not use standard Python dictionaryforinfoinrange(len(timingInfoNames)):forsizeInfoinrange(timingInfoLength[info]):timingInfo[timingInfoNames[info]]+=self._ser.read(1).decode("utf-8")zeit=time.time_ns()# Check if last byte is a ETXifbytes([3])!=self._ser.read(1):self.log.info("Not properly align, skipping this run.")continuecrc=""# Calculate crccrcVal=0# data payloadtimingPayload=timingInfo["Train ID"]+timingInfo["Beam Mode"]foriinrange(0,len(timingPayload),2):crcVal^=int(timingPayload[i:i+2],16)ifcrcVal!=int(timingInfo["CRC"],16):crc=" !!!Problem!!! Calculated CRC: "+str(hex(crcVal)[2:]).upper()continue# Train ID in decimaltimingInfo["Train ID"]=int(timingInfo["Train ID"],16)# ids.append(timingInfo['Train ID'])# times.append(zeit)# directly store data to disknp.save(filehandle,zeit)np.save(filehandle,timingInfo["Train ID"])# print(timingInfo['Train ID'], zeit)ifz_sock.poll(timeout=0):cmd=z_sock.recv_string()ifcmd=="STOP RECORDING":record=Falseelse:z_sock.send_string(f'"{cmd}" in this context invalid')# close fileiffilehandleisnotNone:self.log.debug("closing file")filehandle.flush()filehandle.close()self.log.debug("file closed")z_sock.send_string("CLOSED")filehandle=Nonewaiting=True# We reach this point only after "SHUTDOWN" command receivedself.log.debug("Process is finishing")z_sock.close()self.log.debug("Thread is finished")