diff --git a/src/calng/DetectorAssembler.py b/src/calng/DetectorAssembler.py index e8651345c7540effe94a070e46072d904daeb06d..aa5f00eced4bc4cc7b3620747ca2004360d35753 100644 --- a/src/calng/DetectorAssembler.py +++ b/src/calng/DetectorAssembler.py @@ -1,3 +1,4 @@ +import enum import functools import pickle import re @@ -36,13 +37,13 @@ from karabo import version as karaboVersion from . import scenes from ._version import version as deviceVersion -output_schema = Schema() +assembled_schema = Schema() ( - NODE_ELEMENT(output_schema).key("image").commit(), + NODE_ELEMENT(assembled_schema).key("image").commit(), - NDARRAY_ELEMENT(output_schema).key("image.data").commit(), + NDARRAY_ELEMENT(assembled_schema).key("image.data").commit(), - UINT64_ELEMENT(output_schema).key("trainId").readOnly().commit(), + UINT64_ELEMENT(assembled_schema).key("trainId").readOnly().commit(), ) preview_schema = Schema() @@ -58,6 +59,12 @@ xtdf_source_re = re.compile(r".*\/DET\/(\d+)CH0:xtdf") daq_source_re = re.compile(r".*\/DET\/.*?(\d+):daqOutput") +class BridgeOutputOptions(enum.Enum): + MATCHED = "matched" + ASSEMBLED = "assembled" + PREVIEW = "preview" + + # TODO: merge scene with TrainMatcher's nice overview @KARABO_CLASSINFO("DetectorAssembler", deviceVersion) class DetectorAssembler(TrainMatcher.TrainMatcher): @@ -97,6 +104,11 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): .defaultValue("image.data") .commit(), + OUTPUT_CHANNEL(expected) + .key("assembledOutput") + .dataSchema(assembled_schema) + .commit(), + NODE_ELEMENT(expected) .key("preview") .description( @@ -107,6 +119,12 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): ) .commit(), + OUTPUT_CHANNEL(expected) + .key("preview.output") + .dataSchema(preview_schema) + .description("See description of parent node, 'preview'.") + .commit(), + UINT32_ELEMENT(expected) .key("preview.downsamplingFactor") .description( @@ -129,11 +147,6 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): .reconfigurable() .commit(), - OUTPUT_CHANNEL(expected) - .key("preview.output") - .dataSchema(preview_schema) - .commit(), - UINT32_ELEMENT(expected) .key("preview.trainStride") .displayedName("Train stride") @@ -142,15 +155,25 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): .defaultValue(10) .commit(), + STRING_ELEMENT(expected) + .key("outputForBridgeOutput") + .displayedName("Bridge output") + .description( + "The bridge can send the same data as any one of the three Karabo " + "output channels. If this setting is 'matched', it will follow " + "'output', if 'assembled', it will follow 'assembledOutput' and if " + "'preview', it will follow 'preview.output'." + ) + .options(",".join(option.value for option in BridgeOutputOptions)) + .assignmentOptional() + .defaultValue("matched") + .reconfigurable() + .commit(), + INPUT_CHANNEL(expected) .key("geometryInput") .displayedName("Geometry input") .commit(), - - OUTPUT_CHANNEL(expected) # can OVERWRITE_ELEMENT even do this? - .key("output") - .dataSchema(output_schema) - .commit(), ) def initialization(self): @@ -166,6 +189,7 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): self.KARABO_SLOT(self.requestScene) self.ask_for_geometry() + self.assembled_output = self.signalSlotable.getOutputChannel("assembledOutput") self.preview_output = self.signalSlotable.getOutputChannel("preview.output") self.start() @@ -241,9 +265,18 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): my_timestamp = Timestamp(Epochstamp(), Trainstamp(train_id)) my_source = self.getInstanceId() + bridge_output_choice = BridgeOutputOptions( + self.unsafe_get("outputForBridgeOutput") + ) module_indices_unfilled = set(range(self._stack_input_buffer.shape[0])) for source, (data, source_timestamp) in sources.items(): + # regular TrainMatcher output + self.output.write(data, ChannelMetaData(source, source_timestamp)) + if bridge_output_choice is BridgeOutputOptions.MATCHED: + self.zmq_output.write(source, data, source_timestamp) + + # prepare for assembly # TODO: handle failure to "parse" source, get data out module_index = self._source_to_index(source) self._stack_input_buffer[module_index] = np.squeeze( @@ -251,6 +284,10 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): ) module_indices_unfilled.discard(module_index) + self.output.update() + if bridge_output_choice is BridgeOutputOptions.MATCHED: + self.zmq_output.update() + for module_index in module_indices_unfilled: self._stack_input_buffer[module_index].fill(0) # TODO: configurable treatment of missing modules @@ -266,10 +303,11 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): train_id, ) output_metadata = ChannelMetaData(my_source, my_timestamp) - self.output.write(output_hash, output_metadata) - self.output.update() - self.zmq_output.write(my_source, output_hash, my_timestamp) - self.zmq_output.update() + self.assembled_output.write(output_hash, output_metadata) + self.assembled_output.update() + if bridge_output_choice is BridgeOutputOptions.ASSEMBLED: + self.zmq_output.write(my_source, output_hash, my_timestamp) + self.zmq_output.update() if train_id % self.unsafe_get("preview.trainStride") == 0: downsampling_factor = self.unsafe_get("preview.downsamplingFactor") @@ -293,8 +331,18 @@ class DetectorAssembler(TrainMatcher.TrainMatcher): "trainId", train_id, ) - self.preview_output.write(output_hash, output_metadata) + self.preview_output.write( + output_hash, + output_metadata, + ) self.preview_output.update() + if bridge_output_choice is BridgeOutputOptions.PREVIEW: + self.zmq_output.write( + my_source, + output_hash, + my_timestamp, + ) + self.zmq_output.update() self.rate_out.update()