def t1(self): try: raise Exception except Exception as e: pass cothread.Yield()
def _handle(self): E = None try: while True: E = self._Q.Wait() _log.debug("Subscription %s handle %s", self.name, LazyRepr(E)) S = self._S if isinstance(E, Cancelled): return elif isinstance(E, Disconnected): _log.debug('Subscription notify for %s with %s', self.name, E) if self._notify_disconnect: self._cb(E) else: _log.info("Subscription disconnect %s", self.name) continue elif isinstance(E, RemoteError): _log.debug('Subscription notify for %s with %s', self.name, E) if self._notify_disconnect: self._cb(E) elif isinstance(E, RemoteError): _log.error("Subscription Error %s", E) return elif S is None: # already close()'d return i = 0 while True: E = S.pop() if E is None or self._S is None: break self._cb(E) i = (i + 1) % 4 if i == 0: cothread.Yield() if S.done: _log.debug('Subscription complete %s', self.name) S.close() self._S = None if self._notify_disconnect: E = Finished() self._cb(E) break except: _log.exception("Error processing Subscription event: %s", LazyRepr(E)) self._S.close() self._S = None
def __init__(self): """Monitor values of PVs: offsets, scales etc.""" if self.__guard: raise RuntimeError('Do not instantiate. ' + 'If you require an instance use get_instance.') self.arrays = { Arrays.OFFSETS: caget( [ctrl + ':OFFSET' for ctrl in PvReferences.CTRLS]), Arrays.SCALES: caget( [ctrl + ':WFSCA' for ctrl in PvReferences.CTRLS]), Arrays.SET_SCALES: caget( [name + ':SETWFSCA' for name in PvReferences.NAMES]), Arrays.WAVEFORMS: caget(PvReferences.TRACES), Arrays.SETI: caget([name + ':SETI' for name in PvReferences.NAMES]), Arrays.IMIN: caget([name + ':IMIN' for name in PvReferences.NAMES]), Arrays.IMAX: caget([name + ':IMAX' for name in PvReferences.NAMES]), Arrays.ERRORS: caget( [name + ':ERRGSTR' for name in PvReferences.NAMES]) } self.listeners = {'straight': [], 'trace': []} for i in range(len(PvReferences.CTRLS)): camonitor(PvReferences.CTRLS[i] + ':OFFSET', lambda x, i=i: self.update_values( x, Arrays.OFFSETS, i, 'straight')) camonitor(PvReferences.CTRLS[i] + ':WFSCA', lambda x, i=i: self.update_values( x, Arrays.SCALES, i, 'straight')) for idx, ioc in enumerate(PvReferences.NAMES): camonitor(ioc + ':SETWFSCA', lambda x, i=idx: self.update_values( x, Arrays.SET_SCALES, i, 'straight')) camonitor(ioc + ':SETI', lambda x, i=idx: self.update_values( x, Arrays.SETI, i, 'straight')) camonitor(ioc + ':IMIN', lambda x, i=idx: self.update_values( x, Arrays.IMIN, i, 'straight')) camonitor(ioc + ':IMAX', lambda x, i=idx: self.update_values( x, Arrays.IMAX, i, 'straight')) camonitor(ioc + ':ERRGSTR', lambda x, i=idx: self.update_values( x, Arrays.ERRORS, i, 'straight'), format=FORMAT_TIME) camonitor(PvReferences.TRACES[0], lambda x: self.update_values(x, Arrays.WAVEFORMS, 0, 'trace')) camonitor(PvReferences.TRACES[1], lambda x: self.update_values(x, Arrays.WAVEFORMS, 1, 'trace')) cothread.Yield() # Ensure monitored values are connected
def await_ioc_start(stats, prefix): cothread.Yield() pid_rbv = catools.caget(f"{prefix}:PID", timeout=5) if int(pid_rbv) != os.getpid(): raise BadValueError("Got back different PID: " + "is there another system instance on the machine?") catools.caput(f"{prefix}:YAML:PATH", stats["yaml_path"], datatype=catools.DBR_CHAR_STR) catools.caput( f"{prefix}:PYMALCOLM:PATH", stats["pymalcolm_path"], datatype=catools.DBR_CHAR_STR, )
def run_single_bumps(pvmaps): for pvm in pvmaps: if ap.caget(pvm["cmd"]) == 0: continue ap.caput(pvm["cmddone"], 0) ename = pvm["idname"] if not ename: continue if ap.caget(pvm["op"]) == 0: continue xc, xangle = ap.caget([pvm["offset"], pvm["angle"]]) plane = pvm["XY"].lower() print ename, xc, xangle, plane norm0, norm1, norm2, corvals = ap.setIdBump( ename, xc, xangle, plane=plane, check=False, ncor=6, dImax=0.5) print "Norm:", norm0, norm1, norm2 print corvals ap.caput(pvm["cmddone"], 1) ap.caput(pvm["cmd"], 0) cothread.Yield(0.1)
def get_windowed_data(self, trigger, trace): """Overlay the two peaks.""" try: diff = np.diff(trigger) length = len(trace) # TODO: this parameter probably shouldn't be hard coded stepvalue = 0.5 if min(diff) > -1 * stepvalue or max(diff) < stepvalue: raise RangeError maxtrig = next(x for x in diff if x > stepvalue) mintrig = next(x for x in diff if x < -1 * stepvalue) edges = [ np.where(diff == maxtrig)[0][0], np.where(diff == mintrig)[0][0] ] cothread.Yield() trigger_length = (edges[1] - edges[0]) * 2 if length < trigger_length: raise RangeError if edges[1] > edges[0]: # So that colours don't swap around first_peak = np.roll(trace[:trigger_length], -edges[0] - trigger_length / 4)[:trigger_length / 2] second_peak = np.roll(trace[:trigger_length], -edges[1] - trigger_length / 4)[:trigger_length / 2] else: first_peak = np.roll(trace[:trigger_length], -edges[1] - trigger_length / 4)[:trigger_length / 2] second_peak = np.roll(trace[:trigger_length], -edges[0] - trigger_length / 4)[:trigger_length / 2] return first_peak, second_peak except RangeError: print 'Trace is partially cut off' # status bar? callback? first_peak = [float('nan'), float('nan')] second_peak = [float('nan'), float('nan')] return first_peak, second_peak
def _co_execute(self): """Execute the virtual accelerator. This includes the following: 1. Creating a temporary working directory for execution of FLAME. 2. Set up the working directory by symlinking from the data directory. 3. Writing the EPICS DB to the working directory (va.db). 4. Starting the softIoc and channel initializing monitors. 5. Add noise to the settings for all input (CSET) channels. 6. Create or update the FLAME machine configuration. 7. Propagate the FLAME simulation and read the results. 8. Update the READ channels of all devives. 9. Update the REST channels of input devies. 10. Repeat from step #5. """ _LOGGER.debug("VA: Execute virtual accelerator") _LOGGER.info("VA: Running at " + self._ts_now) chanprefix = self._chanprefix if self._pv_suffix != '': suffix_str = "_" + self._pv_suffix else: suffix_str = "" # Add channel for VA rep-rate chanrate = f"{chanprefix}:SVR:RATE{suffix_str}" self._epicsdb.append(("ao", chanrate, OrderedDict([ ("DESC", "Rep-rate of Simulation Engine"), ("VAL", self._rate), ("PREC", 1), ]))) _LOGGER.info("VA: Reprate PV is " + chanrate) # Add channel for sample counting chansample_cnt = f"{chanprefix}:SVR:CNT{suffix_str}" self._epicsdb.append(("ao", chansample_cnt, OrderedDict([ ("DESC", "Sample counter for scan client"), ("VAL", 0) ]))) # Add channel for VA configuration and control channoise = f"{chanprefix}:SVR:NOISE{suffix_str}" self._epicsdb.append(("ao", channoise, OrderedDict([ ("DESC", "Noise level of Virtual Accelerator"), ("VAL", self._noise), ("PREC", 5) ]))) _LOGGER.info("VA: Noise PV is " + channoise) chanstat = f"{chanprefix}:SVR:STATUS{suffix_str}" self._epicsdb.append(("bi", chanstat, OrderedDict([ ("DESC", "Status of Virtual Accelerator"), ("VAL", 1), ("ZNAM", "ERR"), ("ONAM", "OK"), ("PINI", "1") ]))) _LOGGER.info("VA: Status PV is " + chanstat) # MPS status chan_mps_stat = f"{chanprefix}:SVR:MpsStatus" self._epicsdb.append(("mbbi", chan_mps_stat, OrderedDict([ ("DESC", "MPS Status of Virtual Accelerator"), ("VAL", 3), ("ZRST", "Fault"), ("ONST", "Disable"), ("TWST", "Monitor"), ("THST", "Enable"), ("PINI", "1") ]))) _LOGGER.info("VA: MPS PV is " + chan_mps_stat) # chancharge = f"{chanprefix}:SVR:CHARGE{suffix_str}" self._epicsdb.append(("ai", chancharge, OrderedDict([ ("DESC", "Q/M of Virtual Accelerator"), ("VAL", 0.0), ("PREC", 5) ]))) # initial beam condition chanbsrc = f"{chanprefix}:SVR:BEAM{suffix_str}" self._epicsdb.append(("waveform", chanbsrc, OrderedDict([ ("DESC", "Init beam of Virtual Accelerator"), ("NELM", 4096), ("FTVL", "UCHAR") ]))) _LOGGER.info("VA: Init beam condition PV is " + chanbsrc) # if self.work_dir is not None: os.makedirs(self.work_dir) self._rm_work_dir = False latticepath = os.path.join(self.work_dir, "test.lat") else: self.work_dir = tempfile.mkdtemp(_TEMP_DIRECTORY_SUFFIX) self._rm_work_dir = True latticepath = None _LOGGER.info("VA: Working directory: %s", self._work_dir) # input file paths epicsdbpath = os.path.join(self.work_dir, "va.db") #output file paths epicslogpath = os.path.join(self.work_dir, "softioc.log") if os.path.isabs(self.data_dir): abs_data_dir = self.data_dir self._latfactory.dataDir = self.data_dir else: abs_data_dir = os.path.abspath(self.data_dir) self._latfactory.dataDir = os.path.abspath(self.data_dir) with open(epicsdbpath, "w") as outfile: self._write_epicsdb(outfile) _LOGGER.info("VA: Write EPICS database to %s", epicsdbpath) #_LOGGER.debug("VA: Write EPICS database to %s", epicsdbpath) self._ioc_logfile = open(epicslogpath, "w") self._ioc_process = Popen(["softIoc", "-d", "va.db"], cwd=self.work_dir, stdout=self._ioc_logfile, stderr=subprocess.STDOUT) _LOGGER.debug("VA: Start EPICS soft IOC with log %s", epicslogpath) _LOGGER.debug("VA: Connecting to channels: {}".format(len(self._csetmap.keys()))) self._subscriptions = [] self._subscriptions.append(catools.camonitor(chanrate, self._handle_rate_monitor)) self._subscriptions.append(catools.camonitor(channoise, self._handle_noise_monitor)) self._subscriptions.append(catools.camonitor(chanbsrc, self._handle_bsrc_monitor)) self._subscriptions.extend(catools.camonitor(self._csetmap.keys(), self._handle_cset_monitor)) _LOGGER.debug("VA: Connecting to channels: Done") machine = None while self._continue: # update the RSET channels with new settings batch = catools.CABatch() for cset in self._csetmap.items(): name, field = self._fieldmap[cset[0]] batch[cset[1][0]] = self._settings[name][field] batch.caput() settings = self._copy_settings_with_noise() self._latfactory.settings = settings lattice = self._latfactory.build() start = time.time() if machine is None: _LOGGER.debug("VA: Create FLAME machine from configuration") machine = Machine(lattice.conf()) else: _LOGGER.debug("VA: Reconfigure FLAME machine from configuration") for idx, elem in enumerate(lattice.elements): machine.reconfigure(idx, elem[2]) if self._bsrc is not None: _LOGGER.info("VA: Reconfigure FLAME machine with init beam config") machine.reconfigure(self._bsrc['index'], self._bsrc['properties']) if latticepath is not None: _LOGGER.debug(f"VA: Write FLAME lattice file to {outfile}") generate_latfile(machine, latfile=latticepath) _LOGGER.debug("VA: Allocate FLAME state from configuration") S = machine.allocState({}) output_map = [] for elem in lattice.elements: if 'name' in elem[3]: output_map.append(elem[3]['name']) else: output_map.append(None) batch = catools.CABatch() for i in range(0, len(machine)): machine.propagate(S, i, 1) if output_map[i] in self._elemmap: elem = self._elemmap[output_map[i]] if isinstance(elem, BPMElement): x_centroid = S.moment0_env[0]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x_phy], x_centroid) batch[self._readfieldmap[elem.name][elem.fields.x_phy]] = x_centroid y_centroid = S.moment0_env[2]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y_phy], y_centroid) batch[self._readfieldmap[elem.name][elem.fields.y_phy]] = y_centroid # convert rad to deg and adjust for 161MHz sampling frequency phase = _normalize_phase(2.0 * S.ref_phis * (180.0 / math.pi)) _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.phase_phy], phase) batch[self._readfieldmap[elem.name][elem.fields.phase_phy]] = phase energy = S.ref_IonEk/1.0e6 # convert eV to MeV _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.energy_phy], energy) batch[self._readfieldmap[elem.name][elem.fields.energy_phy]] = energy elif isinstance(elem, PMElement): x_centroid = S.moment0_env[0]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], x_centroid) batch[self._readfieldmap[elem.name][elem.fields.x]] = x_centroid y_centroid = S.moment0_env[2]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], y_centroid) batch[self._readfieldmap[elem.name][elem.fields.y]] = y_centroid x_rms = S.moment0_rms[0]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xrms], x_rms) batch[self._readfieldmap[elem.name][elem.fields.xrms]] = x_rms y_rms = S.moment0_rms[2]/1.0e3 _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.yrms], y_rms) batch[self._readfieldmap[elem.name][elem.fields.yrms]] = y_rms sign = elem.sign xy_centroid = (sign*x_centroid + y_centroid)/math.sqrt(2.0) # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xy], xy_centroid) batch[self._readfieldmap[elem.name][elem.fields.xy]] = xy_centroid xy_rms = 1.0e-3*math.sqrt( (S.moment1_env[0, 0] + S.moment1_env[2, 2])*0.5 + sign*S.moment1_env[0, 2] ) _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xyrms], xy_rms) batch[self._readfieldmap[elem.name][elem.fields.xyrms]] = xy_rms cxy = sign * S.moment1_env[0, 2] * 1e-6 / x_rms / y_rms _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.cxy], cxy) batch[self._readfieldmap[elem.name][elem.fields.cxy]] = cxy elif isinstance(elem, (FCElement, VDElement, TargetElement, DumpElement, WedgeElement)): x_centroid = S.moment0_env[0]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], x_centroid) batch[self._readfieldmap[elem.name][elem.fields.x]] = x_centroid y_centroid = S.moment0_env[2]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], y_centroid) batch[self._readfieldmap[elem.name][elem.fields.y]] = y_centroid x_rms = S.moment0_rms[0]/1.0e3 # convert mm to m _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xrms], x_rms) batch[self._readfieldmap[elem.name][elem.fields.xrms]] = x_rms y_rms = S.moment0_rms[2]/1.0e3 _LOGGER.debug("VA: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.yrms], y_rms) batch[self._readfieldmap[elem.name][elem.fields.yrms]] = y_rms batch.caput() _LOGGER.info("VA: FLAME execution time: %f s", time.time()-start) # Allow the BPM, PM, etc. readbacks to update # before the device setting readbacks PVs. cothread.Yield() batch = catools.CABatch() for name, value in self._csetmap.items(): name, field = self._fieldmap[name] _LOGGER.debug("VA: Update read: %s to %s", value[1], settings[name][field]) batch[value[1]] = settings[name][field] batch.caput() # Sleep for a fraction (10%) of the total execution time # when one simulation costs more than 0.50 seconds. # Otherwise, sleep for the rest of 1 second. # If a scan is being done on this virtual accelerator, # then the scan server has a period of time to update # setpoints before the next run of IMPACT. delt = time.time() - start if delt > 0.50: cothread.Sleep(delt * 0.1) else: cothread.Sleep(1.0 / self._rate - delt)
def put(self, pv, op): _log.debug("putting %s <- %s", op.name(), op.value()) cothread.Yield() # because we can pv.post(op.value() * 2) op.done()
def create_generator_profile_sparse(self, start_index: int) -> bool: # Ensure we have the correct trigger type assert (self.output_triggers != scanning.infos.MotionTrigger.EVERY_POINT ), f"{self.name}: trigger should not be every point" # Keep going until we finish or exceed the maximum length of a profile start_batch_index = start_index point_index = start_index while True: # Grab some points points, joined, velocities = self.get_some_points( start_batch_index) if not points: return True # Number of points and check if the last point is in this batch num_points = len(points) last_point_in_batch = self.is_last_point_in_current_batch( start_batch_index, num_points) # Don't do all points in the batch otherwise we can get an index error # when adding the turnaround. points_to_do = num_points - 1 for i in range(points_to_do): point_added = self.add_sparse_point(points, i, joined[i], velocities[i]) # add in the turnaround between non-contiguous points if not (joined[i]): self.insert_gap(points[i], points[i + 1], point_index + 1) # Check if we have exceeded the profile points limit. Only check if we # have actually added a point, otherwise we waste time. if point_added and self.check_profile_length_exceeds_profile_points( ): self.end_index = point_index + 1 return False # Increment point index point_index += 1 # Check for the last point if last_point_in_batch: point_added = self.add_sparse_point(points, points_to_do, False, False) # Check if we have exceeded the profile points limit. Only check if we # have actually added a point, otherwise we waste time. if point_added and self.check_profile_length_exceeds_profile_points( ): self.end_index = point_index + 1 return False # Increment point index point_index += 1 # Increment the index for the next batch start_batch_index = point_index # Check if we are done if start_batch_index == self.steps_up_to: # Return True for a tail-off return True # Yield so that we don't continuously block other threads cothread.Yield()
import cothread import random def worker(n): for i in range(25): print(f"{n}", end="") cothread.Sleep(random.random() * 0.5) # suspend cothread print() threads = [] for n in range(1, 5): t = cothread.Spawn(worker, n) threads.append(t) cothread.Yield() # wait for other cothreads # the cothread will terminate when we reach the end of the program # therefore we must wait for the cothreads to complete for t in threads: t.Wait()
#!/usr/bin/env python3 # Simple recursion overflow test, checks that guard pages do indeed guard # against stack overflow (by generating a segmentation fault). import os os.environ['COTHREAD_CHECK_STACK'] = 'yes' import require import cothread from cothread import _coroutine def recurse(n): print('recursing', n) stack = _coroutine.stack_use(_coroutine.get_current()) print('stack', stack) assert stack[0] <= stack[2] recurse(n + 1) cothread.Spawn(recurse, 0, stack_size=8192) cothread.Yield() # We're dead
def handle_request(self, controller, request): controller.handle_request(request) # Yield control to allow the request to be handled cothread.Yield()
def test_received_message_subscribe(self): self.ps.received_message('{"message":"subscribe","id":0,"channel":"loc/test(43)"}') cothread.Yield() self.sock.sendall.assert_called_once_with('\x81u{"message": "event", "type": "value", "id": 0, "value": {"type": {"version": "1", "name": "VDouble"}, "value": 43.0}}')
pvmaps.append(pvdict) #for b in ap.getElements("UBPM"): # print b.name, b.pv(field="x") print "Start checking IDLocalBump command ..." while True: run_single_bumps(pvmaps) if ap.caget(_pvcmd) == 0: time.sleep(1) continue #obt = ap.getOrbit(spos=True) #print "BPMS:", len(ap.getElements("BPM")), #print np.average(obt[:,0]), np.std(obt[:,0]) for pvm in pvmaps: ap.caput(pvm["cmddone"], 0) for pvm in pvmaps: ename = pvm["idname"] if not ename: continue if ap.caget(pvm["op"]) == 0: continue xc, xangle = ap.caget([pvm["offset"], pvm["angle"]]) plane = pvm["XY"].lower() print ename, xc, xangle, plane norm0, norm1, norm2, corvals = ap.setIdBump( ename, xc, xangle, plane=plane, check=False, ncor=6, dImax=0.5) print "Norm:", norm0, norm1, norm2 print corvals ap.caput(pvm["cmddone"], 1) cothread.Yield(0.1) time.sleep(1) ap.caput(_pvcmd, 0)
def _execute(self): """Execute the virtual accelerator. This includes the following: 1. Creating a temporary working directory for execution of IMPACT. 2. Setup the working directory by symlinking from the data directory. 3. Writing the EPICS DB to the working directory (va.db). 4. Starting the softIoc and channel initializing monitors. 5. Add noise to the settings for all input (CSET) channels. 6. Generate the IMPACT lattice file in working directory (test.in). 7. Execute IMPACT simulation and read the output files (fort.??). 8. Update the READ channels of all devives. 9. Update the REST channels of input devies. 10. Repeat from step #5. """ _LOGGER.debug("VirtualAccelerator: Execute virtual accelerator") if self._chanprefix is None: chanprefix = "" else: chanprefix = self._chanprefix # Add channel for sample counting sample_cnt = chanprefix + "SVR:CNT" self._epicsdb.append(("ai", sample_cnt, OrderedDict([ ("DESC", "Sample counter for scan client"), ("VAL", 0) ]))) # Add channel for VA configuration and control channoise = chanprefix + "SVR:NOISE" self._epicsdb.append(("ao", channoise, OrderedDict([ ("DESC", "Noise level of Virtual Accelerator"), ("VAL", 0.001), ("PREC", 5) ]))) chanstat = chanprefix + "SVR:STATUS" self._epicsdb.append(("bi", chanstat, OrderedDict([ ("DESC", "Status of Virtual Accelerator"), ("VAL", 1), ("ZNAM", "ERR"), ("ONAM", "OK"), ("PINI", "1") ]))) chancharge = chanprefix + "SVR:CHARGE" self._epicsdb.append(("ai", chancharge, OrderedDict([ ("DESC", "Q/M of Virtual Accelerator"), ("VAL", 0.0), ("PREC", 5) ]))) if self.work_dir is not None: os.makedirs(self.work_dir) self._rm_work_dir = False else: self.work_dir = tempfile.mkdtemp(_TEMP_DIRECTORY_SUFFIX) self._rm_work_dir = True _LOGGER.info("VirtualAccelerator: Working directory: %s", self._work_dir) # input file paths epicsdbpath = os.path.join(self.work_dir, "va.db") latticepath = os.path.join(self.work_dir, "test.in") modelmappath = os.path.join(self.work_dir, "model.map") # output file paths fort18path = os.path.join(self.work_dir, "fort.18") fort24path = os.path.join(self.work_dir, "fort.24") fort25path = os.path.join(self.work_dir, "fort.25") epicslogpath = os.path.join(self.work_dir, "softioc.log") if os.path.isabs(self.data_dir): abs_data_dir = self.data_dir else: abs_data_dir = os.path.abspath(self.data_dir) for datafile in os.listdir(abs_data_dir): srcpath = os.path.join(abs_data_dir, datafile) destpath = os.path.join(self.work_dir, datafile) if os.path.isfile(os.path.join(abs_data_dir, datafile)): os.symlink(srcpath, destpath) _LOGGER.debug("VirtualAccelerator: Link data file %s to %s", srcpath, destpath) with open(epicsdbpath, "w") as outfile: self._write_epicsdb(outfile) self._ioc_logfile = open(epicslogpath, "w") self._ioc_process = _Cothread_Popen(["softIoc", "-d", "va.db"], cwd=self.work_dir, stdout=self._ioc_logfile, stderr=subprocess.STDOUT) self._subscriptions = [] self._subscriptions.append(catools.camonitor(channoise, self._handle_noise_monitor)) self._subscriptions.extend(catools.camonitor(self._csetmap.keys(), self._handle_cset_monitor)) while self._continue: # update the RSET channels with new settings for cset in self._csetmap.items(): name, field = self._fieldmap[cset[0]] catools.caput(cset[1][0], self._settings[name][field]) settings = self._copy_settings_with_noise() self._latfactory.settings = settings lattice = self._latfactory.build() catools.caput(chancharge, lattice.initialCharge) with open(latticepath, "w") as outfile: with open(modelmappath, "w") as mapfile: lattice.write(outfile, mapstream=mapfile) start = time.time() if os.path.isfile(fort18path): os.remove(fort18path) if os.path.isfile(fort24path): os.remove(fort24path) if os.path.isfile(fort25path): os.remove(fort25path) impact_process = _Cothread_Popen(["mpirun", "-np", str(lattice.nprocessors), str(self.impact_exe)], cwd=self.work_dir, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) (stdout, _, status) = impact_process.communicate() # The virtual accelerator shutdown is likely to occur while IMPACT is executing, # so check if virtual accelerator has been stopped before proceeding. if not self._continue: break _LOGGER.info("VirtualAccelerator: IMPACT execution time: %f s", time.time() - start) if status == 0: catools.caput(chanstat, _VA_STATUS_GOOD) else: _LOGGER.warning("VirtualAccelerator: IMPACT exited with non-zero status code: %s\r\n%s", status, stdout) catools.caput(chanstat, _VA_STATUS_BAD) if os.path.isfile(fort18path): fort18 = numpy.loadtxt(fort18path, usecols=(0, 1, 3)) fort18length = fort18.shape[0] else: _LOGGER.warning("VirtualAccelerator: IMPACT output not found: %s", fort18path) catools.caput(chanstat, _VA_STATUS_BAD) fort18length = 0 if os.path.isfile(fort24path): fort24 = numpy.loadtxt(fort24path, usecols=(1, 2)) fort24length = fort24.shape[0] else: _LOGGER.warning("VirtualAccelerator: IMPACT output not found: %s", fort24path) catools.caput(chanstat, _VA_STATUS_BAD) fort24length = 0 if os.path.isfile(fort25path): fort25 = numpy.loadtxt(fort25path, usecols=(1, 2)) fort25length = fort25.shape[0] else: _LOGGER.warning("VirtualAccelerator: IMPACT output not found: %s", fort25path) catools.caput(chanstat, _VA_STATUS_BAD) fort25length = 0 output_map = [] for elem in lattice.elements: if elem.itype in [-28]: output_map.append(elem.name) output_length = len(output_map) if fort18length < output_length: _LOGGER.warning("VirtualAccelerator: IMPACT fort.18 length %s, expecting %s", fort18length, output_length) catools.caput(chanstat, _VA_STATUS_BAD) if fort24length < output_length: _LOGGER.warning("VirtualAccelerator: IMPACT fort.24 length %s, expecting %s", fort24length, output_length) catools.caput(chanstat, _VA_STATUS_BAD) if fort25length < output_length: _LOGGER.warning("VirtualAccelerator: IMPACT fort.25 length %s, expecting %s", fort25length, output_length) catools.caput(chanstat, _VA_STATUS_BAD) def get_phase(idx): # IMPACT computes the phase in radians, # need to convert to degrees for PV. return _normalize_phase(2.0 * fort18[idx, 1] * (180.0 / math.pi)) for idx in range(min(fort18length, fort24length, fort25length)): elem = self._elemmap[output_map[idx]] if isinstance(elem, BPMElement): _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], fort24[idx, 0]) catools.caput(self._readfieldmap[elem.name][elem.fields.x], fort24[idx, 0]) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], fort25[idx, 0]) catools.caput(self._readfieldmap[elem.name][elem.fields.y], fort25[idx, 0]) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.phase], get_phase(idx)) catools.caput(self._readfieldmap[elem.name][elem.fields.phase], get_phase(idx)) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.energy], fort18[idx, 2]) catools.caput(self._readfieldmap[elem.name][elem.fields.energy], fort18[idx, 2]) elif isinstance(elem, PMElement): _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], fort24[idx, 0]) catools.caput(self._readfieldmap[elem.name][elem.fields.x], fort24[idx, 0]) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], fort25[idx, 0]) catools.caput(self._readfieldmap[elem.name][elem.fields.y], fort25[idx, 0]) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xrms], fort24[idx, 1]) catools.caput(self._readfieldmap[elem.name][elem.fields.xrms], fort24[idx, 1]) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.yrms], fort25[idx, 1]) catools.caput(self._readfieldmap[elem.name][elem.fields.yrms], fort25[idx, 1]) else: _LOGGER.warning("VirtualAccelerator: Output from element type not supported: %s", type(elem).__name__) # Write the default error value to the remaing output PVs. for idx in range(min(fort18length, fort24length, fort25length), output_length): elem = self._elemmap[output_map[idx]] if isinstance(elem, BPMElement): _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.x], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.y], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.phase], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.phase], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.energy], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.energy], _DEFAULT_ERROR_VALUE) elif isinstance(elem, PMElement): _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.x], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.x], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.y], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.y], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.xrms], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.xrms], _DEFAULT_ERROR_VALUE) _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", self._readfieldmap[elem.name][elem.fields.yrms], _DEFAULT_ERROR_VALUE) catools.caput(self._readfieldmap[elem.name][elem.fields.yrms], _DEFAULT_ERROR_VALUE) else: _LOGGER.warning("VirtualAccelerator: Output from element type not supported: %s", type(elem).__name__) # Allow the BPM, PM, etc. readbacks to update # before the device setting readbacks PVs. cothread.Yield() for name, value in self._csetmap.items(): name, field = self._fieldmap[name] _LOGGER.debug("VirtualAccelerator: Update read: %s to %s", value[1], settings[name][field]) catools.caput(value[1], settings[name][field]) # Sleep for a fraction (10%) of the total execution time # when one simulation costs more than 0.50 seconds. # Otherwise, sleep for the rest of 1 second. # If a scan is being done on this virtual accelerator, # then the scan server has a period of time to update # setpoints before the next run of IMPACT. if (time.time() - start) > 0.50: cothread.Sleep((time.time() - start) * 0.1) else: cothread.Sleep(1.0 - (time.time() - start))