def _GoniostatRotation_to_python(self, py4jGoniostatRotation, isSweepSetting=False): if py4jGoniostatRotation is None: return None uuidString = py4jGoniostatRotation.getId().toString() axisSettings = py4jGoniostatRotation.getAxisSettings() if isSweepSetting: scanAxis = py4jGoniostatRotation.getScanAxis() result = GphlMessages.GoniostatSweepSetting( id_=uuid.UUID(uuidString), scanAxis=scanAxis, **axisSettings) else: result = GphlMessages.GoniostatRotation(id_=uuid.UUID(uuidString), **axisSettings) py4jGoniostatTranslation = py4jGoniostatRotation.getTranslation() if py4jGoniostatTranslation: translationAxisSettings = py4jGoniostatTranslation.getAxisSettings( ) translationUuidString = py4jGoniostatTranslation.getId().toString() # Next line creates Translation and links it to Rotation GphlMessages.GoniostatTranslation( id_=uuid.UUID(translationUuidString), rotation=result, **translationAxisSettings) return result
def _DetectorSetting_to_python(self, py4jDetectorSetting): if py4jDetectorSetting is None: return None uuidString = py4jDetectorSetting.getId().toString() axisSettings = py4jDetectorSetting.getAxisSettings() # return GphlMessages.DetectorSetting(id_=uuid.UUID(uuidString), **axisSettings)
def _GeometricStrategy_to_python(self, py4jGeometricStrategy): uuidString = py4jGeometricStrategy.getId().toString() sweeps = frozenset( self._Sweep_to_python(x) for x in py4jGeometricStrategy.getSweeps()) beamSetting = py4jGeometricStrategy.getDefaultBeamSetting() if beamSetting: beamSetting = self._BeamSetting_to_python(beamSetting) else: beamSetting = None detectorSetting = py4jGeometricStrategy.getDefaultDetectorSetting() if detectorSetting: detectorSetting = self._DetectorSetting_to_python(detectorSetting) else: detectorSetting = None return GphlMessages.GeometricStrategy( isInterleaved=py4jGeometricStrategy.isInterleaved(), isUserModifiable=py4jGeometricStrategy.isUserModifiable(), allowedWidths=py4jGeometricStrategy.getAllowedWidths(), defaultWidthIdx=py4jGeometricStrategy.getDefaultWidthIdx(), defaultBeamSetting=beamSetting, defaultDetectorSetting=detectorSetting, sweeps=sweeps, id_=uuid.UUID(uuidString), )
def _ScanWidth_to_python(self, py4jScanWidth): uuidString = py4jScanWidth.getId().toString() return GphlMessages.ScanWidth( imageWidth=py4jScanWidth.getImageWidth(), numImages=py4jScanWidth.getNumImages(), id_=uuid.UUID(uuidString), )
def _ChooseLattice_to_python(self, py4jChooseLattice): lattice_format = py4jChooseLattice.getFormat().toString() solutions = py4jChooseLattice.getSolutions() lattices = py4jChooseLattice.getLattices() return GphlMessages.ChooseLattice(lattice_format=lattice_format, solutions=solutions, lattices=lattices)
def _ScanExposure_to_python(self, py4jScanExposure): uuidString = py4jScanExposure.getId().toString() return GphlMessages.ScanExposure( time=py4jScanExposure.getTime(), transmission=py4jScanExposure.getTransmission(), id_=uuid.UUID(uuidString), )
def _BeamSetting_to_python(self, py4jBeamSetting): if py4jBeamSetting is None: return None uuidString = py4jBeamSetting.getId().toString() # return GphlMessages.BeamSetting( id_=uuid.UUID(uuidString), wavelength=py4jBeamSetting.getWavelength())
def _RequestCentring_to_python(self, py4jRequestCentring): goniostatRotation = self._GoniostatRotation_to_python( py4jRequestCentring.getGoniostatRotation()) return GphlMessages.RequestCentring( currentSettingNo=py4jRequestCentring.getCurrentSettingNo(), totalRotations=py4jRequestCentring.getTotalRotations(), goniostatRotation=goniostatRotation, )
def _BeamstopSetting_to_python(self, py4jBeamstopSetting): if py4jBeamstopSetting is None: return None uuidString = py4jBeamstopSetting.getId().toString() axisSettings = py4jBeamstopSetting.getAxisSettings() # return GphlMessages.BeamstopSetting(id=uuid.UUID(uuidString), **axisSettings)
def _Scan_to_python(self, py4jScan, sweep): uuidString = py4jScan.getId().toString() return GphlMessages.Scan( width=self._ScanWidth_to_python(py4jScan.getWidth()), exposure=self._ScanExposure_to_python(py4jScan.getExposure()), imageStartNum=py4jScan.getImageStartNum(), start=py4jScan.getStart(), sweep=sweep, filenameParams=py4jScan.getFilenameParams(), id_=uuid.UUID(uuidString), )
def _CollectionProposal_to_python(self, py4jCollectionProposal): uuidString = py4jCollectionProposal.getId().toString() strategy = self._GeometricStrategy_to_python( py4jCollectionProposal.getStrategy()) id2Sweep = dict((str(x.id), x) for x in strategy.sweeps) scans = [] for py4jScan in py4jCollectionProposal.getScans(): sweep = id2Sweep[py4jScan.getSweep().getId().toString()] scans.append(self._Scan_to_python(py4jScan, sweep)) return GphlMessages.CollectionProposal( relativeImageDir=py4jCollectionProposal.getRelativeImageDir(), strategy=strategy, scans=scans, id=uuid.UUID(uuidString), )
def _decode_py4j_message(self, py4j_message): """Extract messageType and convert py4J object to python object""" # Determine message type message_type = py4j_message.getPayloadClass().getSimpleName() xx0 = py4j_message.getEnactmentId() enactment_id = xx0 and xx0.toString() xx0 = py4j_message.getCorrelationId() correlation_id = xx0 and xx0.toString() logging.getLogger("HWR").debug( "GPhL incoming: message=%s, jobId=%s, messageId=%s" % (message_type, enactment_id, correlation_id) ) if message_type == "String": payload = py4j_message.getPayload() else: if message_type.endswith("Impl"): message_type = message_type[:-4] converterName = "_%s_to_python" % message_type try: # determine converter function converter = getattr(self, converterName) except AttributeError: logging.getLogger("HWR").error( "GPhL Message type %s not recognised (no %s function)" % (message_type, converterName) ) payload = None else: try: # Convert to Python objects payload = converter(py4j_message.getPayload()) except NotImplementedError: logging.getLogger("HWR").error( "Processing of GPhL message %s not implemented", message_type ) payload = None # return GphlMessages.ParsedMessage( message_type, payload, enactment_id, correlation_id )
def abort_workflow(self, message=None): """Abort workflow - may be called from controller in any state""" logging.getLogger("HWR").info("Aborting workflow: %s", message) logging.getLogger("user_level_log").info("Aborting workflow ...") if self._await_result is not None: # Workflow waiting for answer - send abort self._await_result = [(GphlMessages.BeamlineAbort(), None)] # Shut down hardware object que = self.workflow_queue if que is None: self.workflow_ended() else: # If the queue is running, # workflow_ended will be called from post_execute que.put_nowait(StopIteration)
def _Sweep_to_python(self, py4jSweep): # NB scans are not set - where scans are present in a message, # the link is set from the Scan side. uuidString = py4jSweep.getId().toString() return GphlMessages.Sweep( goniostatSweepSetting=self._GoniostatSweepSetting_to_python( py4jSweep.getGoniostatSweepSetting()), detectorSetting=self._DetectorSetting_to_python( py4jSweep.getDetectorSetting()), beamSetting=self._BeamSetting_to_python( py4jSweep.getBeamSetting()), start=py4jSweep.getStart(), width=py4jSweep.getWidth(), beamstopSetting=self._BeamstopSetting_to_python( py4jSweep.getBeamstopSetting()), sweepGroup=py4jSweep.getSweepGroup(), id_=uuid.UUID(uuidString), )
def workflow_ended(self): if self.get_state() == States.OFF: # No workflow to abort return logging.getLogger("HWR").debug("GPhL workflow ended") self.set_state(States.OFF) if self._await_result is not None: # We are awaiting an answer - give an abort self._await_result.append((GphlMessages.BeamlineAbort(), None)) time.sleep(0.2) elif self._running_process is not None: self._running_process = None # NBNB TODO how do we close down the workflow if there is no answer pending? self._enactment_id = None self._workflow_name = None self.workflow_queue = None self._await_result = None # xx0 = self._running_process # self._running_process = None xx0 = self.collect_emulator_process if xx0 is not None: self.collect_emulator_process = "ABORTED" try: if xx0.poll() is None: xx0.send_signal(signal.SIGINT) time.sleep(3) if xx0.poll() is None: xx0.terminate() time.sleep(9) if xx0.poll() is None: xx0.kill() except Exception: logging.getLogger("HWR").info( "Exception while terminating external workflow process %s", xx0) logging.getLogger("HWR").info("Error was:", exc_info=True)
def _SubprocessStopped_to_python(self, py4jSubprocessStopped): return GphlMessages.SubprocessStopped()
def _PrepareForCentring_to_python(self, py4jPrepareForCentring): return GphlMessages.PrepareForCentring()
def _ObtainPriorInformation_to_python(self, py4jObtainPriorInformation): return GphlMessages.ObtainPriorInformation()
def _RequestConfiguration_to_python(self, py4jRequestConfiguration): return GphlMessages.RequestConfiguration()
def processMessage(self, py4j_message): """Receive and process message from workflow server Return goes to server NB Callled freom external java) workflow""" xx0 = self._decode_py4j_message(py4j_message) message_type = xx0.message_type payload = xx0.payload correlation_id = xx0.correlation_id enactment_id = xx0.enactment_id if not enactment_id: logging.getLogger("HWR").error( "GPhL message lacks enactment ID - sending 'Abort' to external workflow" ) return self._response_to_server(GphlMessages.BeamlineAbort(), correlation_id) elif self._enactment_id is None: # NB this should be made less primitive # once we are past direct function calls self._enactment_id = enactment_id elif self._enactment_id != enactment_id: logging.getLogger("HWR").error( "Workflow enactment ID %s != message enactment ID %s" " - sending 'Abort' to external workflow" % (self._enactment_id, enactment_id)) return self._response_to_server(GphlMessages.BeamlineAbort(), correlation_id) elif not payload: logging.getLogger("HWR").error( "GPhL message lacks payload - sending 'Abort' to external workflow" ) return self._response_to_server(GphlMessages.BeamlineAbort(), correlation_id) if message_type in ("SubprocessStarted", "SubprocessStopped"): if self.workflow_queue is not None: # Could happen if we have ended the workflow self.workflow_queue.put_nowait( (message_type, payload, correlation_id, None)) logging.getLogger("HWR").debug( "Subprocess start/stop - return None") return None elif message_type in ( "RequestConfiguration", "GeometricStrategy", "CollectionProposal", "ChooseLattice", "RequestCentring", "ObtainPriorInformation", "PrepareForCentring", ): # Requests: self._await_result = [] self.set_state(States.OPEN) if self.workflow_queue is None: # Could be None if we have ended the workflow return self._response_to_server(GphlMessages.BeamlineAbort(), correlation_id) else: self.workflow_queue.put_nowait( (message_type, payload, correlation_id, self._await_result)) while not self._await_result: time.sleep(0.1) result, correlation_id = self._await_result.pop(0) self._await_result = None if self.get_state() == States.OPEN: self.set_state(States.RUNNING) logging.getLogger("HWR").debug( "GPhL - response=%s jobId=%s messageId=%s" % (result.__class__.__name__, enactment_id, correlation_id)) return self._response_to_server(result, correlation_id) elif message_type in ("WorkflowAborted", "WorkflowCompleted", "WorkflowFailed"): if self.workflow_queue is not None: # Could happen if we have ended the workflow self.workflow_queue.put_nowait( (message_type, payload, correlation_id, None)) self.workflow_queue.put_nowait(StopIteration) logging.getLogger("HWR").debug("Aborting - return None") return None else: logging.getLogger("HWR").error( "GPhL Unknown message type: %s - aborting", message_type) return self._response_to_server(GphlMessages.BeamlineAbort(), correlation_id)
def test_lattice_selection(self): """Dummy test of lattice selection UI""" # |NB @~@~for test only test_payload = GphlMessages.ChooseLattice( lattice_format="IDXREF", crystalSystem="m", lattices=["tP", "aP"], solutions=""" *********** DETERMINATION OF LATTICE CHARACTER AND BRAVAIS LATTICE *********** The CHARACTER OF A LATTICE is defined by the metrical parameters of its reduced cell as described in the INTERNATIONAL TABLES FOR CRYSTALLOGRAPHY Volume A, p. 746 (KLUWER ACADEMIC PUBLISHERS, DORDRECHT/BOSTON/LONDON, 1989). Note that more than one lattice character may have the same BRAVAIS LATTICE. A lattice character is marked "*" to indicate a lattice consistent with the observed locations of the diffraction spots. These marked lattices must have low values for the QUALITY OF FIT and their implicated UNIT CELL CONSTANTS should not violate the ideal values by more than MAXIMUM_ALLOWED_CELL_AXIS_RELATIVE_ERROR= 0.03 MAXIMUM_ALLOWED_CELL_ANGLE_ERROR= 1.5 (Degrees) LATTICE- BRAVAIS- QUALITY UNIT CELL CONSTANTS (ANGSTROEM & DEGREES) CHARACTER LATTICE OF FIT a b c alpha beta gamma * 44 aP 0.0 56.3 56.3 102.3 90.0 90.0 90.0 * 31 aP 0.0 56.3 56.3 102.3 90.0 90.0 90.0 * 33 mP 0.0 56.3 56.3 102.3 90.0 90.0 90.0 * 35 mP 0.0 56.3 56.3 102.3 90.0 90.0 90.0 * 34 mP 0.0 56.3 102.3 56.3 90.0 90.0 90.0 * 32 oP 0.0 56.3 56.3 102.3 90.0 90.0 90.0 * 14 mC 0.1 79.6 79.6 102.3 90.0 90.0 90.0 * 10 mC 0.1 79.6 79.6 102.3 90.0 90.0 90.0 * 13 oC 0.1 79.6 79.6 102.3 90.0 90.0 90.0 * 11 tP 0.1 56.3 56.3 102.3 90.0 90.0 90.0 37 mC 250.0 212.2 56.3 56.3 90.0 90.0 74.6 36 oC 250.0 56.3 212.2 56.3 90.0 90.0 105.4 28 mC 250.0 56.3 212.2 56.3 90.0 90.0 74.6 29 mC 250.0 56.3 125.8 102.3 90.0 90.0 63.4 41 mC 250.0 212.3 56.3 56.3 90.0 90.0 74.6 40 oC 250.0 56.3 212.2 56.3 90.0 90.0 105.4 39 mC 250.0 125.8 56.3 102.3 90.0 90.0 63.4 30 mC 250.0 56.3 212.2 56.3 90.0 90.0 74.6 38 oC 250.0 56.3 125.8 102.3 90.0 90.0 116.6 12 hP 250.1 56.3 56.3 102.3 90.0 90.0 90.0 27 mC 500.0 125.8 56.3 116.8 90.0 115.5 63.4 42 oI 500.0 56.3 56.3 219.6 104.8 104.8 90.0 15 tI 500.0 56.3 56.3 219.6 75.2 75.2 90.0 26 oF 625.0 56.3 125.8 212.2 83.2 105.4 116.6 9 hR 750.0 56.3 79.6 317.1 90.0 100.2 135.0 1 cF 999.0 129.6 129.6 129.6 128.6 75.7 128.6 2 hR 999.0 79.6 116.8 129.6 118.9 90.0 109.9 3 cP 999.0 56.3 56.3 102.3 90.0 90.0 90.0 5 cI 999.0 116.8 79.6 116.8 70.1 39.8 70.1 4 hR 999.0 79.6 116.8 129.6 118.9 90.0 109.9 6 tI 999.0 116.8 116.8 79.6 70.1 70.1 39.8 7 tI 999.0 116.8 79.6 116.8 70.1 39.8 70.1 8 oI 999.0 79.6 116.8 116.8 39.8 70.1 70.1 16 oF 999.0 79.6 79.6 219.6 90.0 111.2 90.0 17 mC 999.0 79.6 79.6 116.8 70.1 109.9 90.0 18 tI 999.0 116.8 129.6 56.3 64.3 90.0 118.9 19 oI 999.0 56.3 116.8 129.6 61.1 64.3 90.0 20 mC 999.0 116.8 116.8 56.3 90.0 90.0 122.4 21 tP 999.0 56.3 102.3 56.3 90.0 90.0 90.0 22 hP 999.0 56.3 102.3 56.3 90.0 90.0 90.0 23 oC 999.0 116.8 116.8 56.3 90.0 90.0 57.6 24 hR 999.0 162.2 116.8 56.3 90.0 69.7 77.4 25 mC 999.0 116.8 116.8 56.3 90.0 90.0 57.6 43 mI 999.0 79.6 219.6 56.3 104.8 135.0 68.8 For protein crystals the possible space group numbers corresponding to""", ) if self.workflow_queue is not None: # Could happen if we have ended the workflow self.workflow_queue.put_nowait( ("ChooseLattice", test_payload, "9999999", None))
def _SubprocessStarted_to_python(self, py4jSubprocessStarted): return GphlMessages.SubprocessStarted( name=py4jSubprocessStarted.getName())