Controller Architecture#

fastcs-odin provides a set of controller classes that map an odin-control server’s REST API onto FastCS attributes and commands. This page explains each class and how they relate to one another.

OdinController#

OdinController is the root FastCS Controller for an odin-control server. It is the starting point for any driver that communicates with odin-control.

On initialise it:

  1. Opens an HTTP connection to the server.

  2. Queries api/0.1/adapters to discover registered adapters.

  3. Fetches the full parameter tree for each adapter with metadata headers to determine the adapter’s module type.

  4. Dispatches to the correct sub-controller class based on the module type:

Module type

Adapter Controller

FrameProcessor

FrameProcessorAdapterController

FrameReceiver

FrameReceiverAdapterController

MetaWriter

MetaWriterAdapterController

(anything else)

OdinAdapterController

Odin Controllers#

OdinSubController#

OdinSubController is a common base class for sub-controllers. It holds:

  • The shared HTTPConnection.

  • The list[OdinParameter] assigned to this node in the parameter tree.

  • The api_prefix string that identifies this node’s URL.

It exposes two protected helpers that subclasses call from their own initialise:

_create_attributes()#

Iterates self.parameters and registers a FastCS Attribute for each one. Attributes are backed by ParameterTreeAttributeIO, which reads and writes via the REST API using the parameter’s URI.

_create_commands(path=())#

GETs <api_prefix>/command[/<path>]/allowed, parses the response, and for each allowed command name dynamically attaches a FastCS Command to the controller. The command PUTs to .../execute when invoked. This means detector-specific commands exposed through odin-control appear automatically without any extra code.

OdinAdapterController#

OdinAdapterController is a thin convenience wrapper around OdinSubController. Its entire initialise is:

async def initialise(self):
    await self._create_attributes()
    await self._create_commands()

It is the default used by OdinController for any adapter whose module type is not recognised. For a simple adapter with no special tree structure it is all that is needed, and it is a good starting point for a custom adapter controller.

odin-data Controllers#

These controllers are provided to connect to common odin-data applications; the frame receiver, frame processor and meta writer.

OdinDataAdapterController#

OdinDataAdapterController is a FastCS ControllerVector that manages a numbered set of identical child controllers — one per running odin-data process (frameReceiver or frameProcessor application).

On initialise it:

  1. Partitions self.parameters by leading numeric index in the URI (0/status/..., 1/status/..., …).

  2. Creates one _subcontroller_cls instance per index, scoped to <api_prefix>/<idx>.

  3. Keeps parameters without a numeric prefix at the adapter level and creates attributes for them directly.

  4. Calls _create_config_fan_attributes() to build fan-out write attributes at the adapter level. Any config parameter that appears in every child controller and is not listed in _unique_config gets a top-level attribute whose write propagates to all child controllers simultaneously.

Subclasses set three class variables to specialise behaviour:

Variable

Purpose

_subcontroller_cls

The child controller type to instantiate per index

_subcontroller_label

Short label used to name child controllers (FP, FR, …)

_unique_config

Config keys that differ per process and must not be fan-out’d

FrameReceiverAdapterController / FrameReceiverController#

Merges the decoder and decoder_config sub-trees under a single decoder group before calling add_attribute.

FrameProcessorAdapterController / FrameProcessorController#

Declares class-level summary attributes backed by StatusSummaryAttributeIORef that aggregate values across all FP instances — frames_written (sum) and writing (any). Top-level start_writing and stop_writing commands fan out to the HDF plugin of every child controller.

Queries status/plugins/names to discover loaded plugins and creates a FrameProcessorPluginController sub-controller for each one. Each plugin controller calls _create_commands([plugin_name]) to auto-discover commands.

MetaWriterAdapterController#

A direct subclass of OdinSubController for the meta writer adapter. It combines static class-level attributes (acquisition_id, directory, file_prefix, writing, written) bound to hard-coded URIs with the dynamic attribute creation inherited from OdinSubController. The stop command is also declared statically. The acquisitions sub-tree (temporal per-acquisition state) is excluded during initialise. This is to workaround the dynamic acquisitions the meta writer creates that are not exposed via the parameter tree.

Building a Detector-Specific Driver#

The classes above are designed to be combined or extended for a particular detector. The diagram below gives an overview: fastcs-odin (right) provides the base classes; a detector package (left, with fastcs-detector as an example) inherits from or composes them. The centre column shows a concrete OdinController at runtime with the sub-controllers it creates, colour-matched to the base classes on the right.


eyJ2ZXJzaW9uIjoiMSIsImVuY29kaW5nIjoiYnN0cmluZyIsImNvbXByZXNzZWQiOnRydWUsImVuY29kZWQiOiJ4nO1dWVdcIluyfj+/olb1a5u956HfcJ5QXHUwMDE0XHUwMDEw9Z67XFwggyhcdTAwMDIyy139329cdTAwMDRVSkLuhFx1MDAwNFLFLqh1hlx1MDAwMkxziPjii/n//vrx42fntVn6+e9cdTAwMWY/S4P7fK1abOX7P/+J7/dKrXa1UYeP2Ojv7Ua3dT/65kOn02z/+1//yjeb3vinvPvG86+fLNVKz6V6p1xy3/1cdTAwMWb4+49cdTAwMWb/N/o3fFIt4s8/Pj095Su3e4Nkvdk3+vGQn16nRj86+tLbXHS1SvedfL1SK40/XHUwMDFh4NkwSzzKqKHSMmO45u9cdTAwMWa/wseCKU9cdTAwMWGrODeCK8KkeP+4Xy12XHUwMDFl8Fxiknhcbl5GXHUwMDEzSjU1Wr5/5aFUrTx04DuUWY/DS3BJpJGWm/fv/Dqnf/8g7++0O63GU2mnUWu08MT/QUv4Z3zahfz9U6XV6NaL79/ptPL1djPfgvs0/l65WqulO6+jo8P9hvv6c+p35N4uYer9sJ+CX1p5qJfa+Czo+7uNZv6+2sG7Rcn4KvBcZptHxdFj+9/xObXyz6UjfG71bq32/na1Xizh0/iZp+Rg4vfVi79/39tTXHUwMDFmP1L++53/jM++VCqO7rY2xGrOxmczXHUwMDE2PyP59LtnjfpIXHUwMDE0KdeWXHUwMDEzeNLjp1Nt74L8dUaHLedr7dL4KeC57U3Lpl8+J8SvU1x1MDAxYYxcdTAwMWaNT3qbWbN9WTh5yWyltkn9pbFTTN3Xf75/7z//dFx1MDAxZvb3XHUwMDBmt296jdTZOeeFwydyXFzOZmuvicnf8vb7861Wo1x1MDAxZvW4T8Xh02Gy0z24qaYzTVwi61d7r/VcdTAwMTiOy65cdTAwMTUpplx1MDAxM1x1MDAxN9up1PPOfr1TfzopXHUwMDFlRjvu7/9cdTAwMWJLUrdZzP96LlRrRq0g8DLjJ16r1p+mxazWuH9cdTAwMWE/yr98JzyFKe6nXHUwMDEywJSJh/pcdTAwMGJOuGBcdTAwMWVRWikmjFx1MDAxMlKMwWBcdTAwMDQnhnpMKk6ktEpyxsay+Fx1MDAwNidUKE9rYVx1MDAwMSOsXHUwMDEygDpjYXyHXHUwMDEzn2Bv4GNcbj6u3PAx8f3fOMEo4LnlVitcdTAwMDdOaFx1MDAxYkCPMU5QgHKhfNK2XHUwMDAwTsQpwmOJREmEXHUwMDFisFx1MDAwYudw32m0dlx1MDAxYXV4XHUwMDE0tVqp5Xug8F66Olx1MDAxY12Bmnh3P/9cXK3hXHUwMDEzkFx1MDAxM1x1MDAwN0zUqlx1MDAxNbxcdTAwMTU/7+Hc/UeC+9GpgqV+/8JztVj0W9Z7OGi+Wi+1jqKY6EarWqnW87VMhIvIdzuNy1L712V0Wt2S/26VXHUwMDBl31xyLqjZXGb97m7LgjzYXHUwMDFmJK+KqdTFfXF48cxIdM7AifGoJiObr9hYlPFcdTAwMTZcdTAwMWFBPE2sMdSiKXFo+IdcdTAwMTNcdTAwMDZWtiUh/lx1MDAxY403qzNcdTAwMDarXHUwMDE5I0xcdTAwMTDjl/HfSEApXHKFXHUwMDAyzVxyk/Bzn0dcdTAwMThI5VEnbknpqluk+evqndTHuVxcVFx1MDAwM1xcfDnqlmnr7jU/XHUwMDFjtlx1MDAxM4VK2tZ7Olx1MDAwNsNOjlx1MDAxM3a30S3ePib3avru6UrXzGtcXIZcdTAwMWSMqDU6XHUwMDFlw+6+e5FcZruVnlx1MDAxMVRcdTAwMDJJXHUwMDA0mlxihnlS67X0uFZSciVcdTAwMTWcdkDp8XNcdTAwMDZWXcGPMlx1MDAwMZdcdTAwMTXUebWIVf/TdDzEK3CZdeDvXFz77ahPlVx1MDAwMbNDrTpQLivQXGLHbNVcdTAwMTeW31Cr/nc9Ucw3wVx1MDAxNP9dXy9cdTAwMDM/x56GXHUwMDE5+DnXXHUwMDEzj62njfvi4U1rb6u8V8n0ijV51zy8XFzc1nNlXHUwMDE5qP9YK37JNqOepVaCsVx1MDAxN1x1MDAwMr41fsyfZu3LxNxcdTAwMTPyXHUwMDA3IcHqXHUwMDAxXHUwMDAyzoyBXHUwMDA34uT9YO3F9NtvXHUwMDEwXHUwMDAxLlx1MDAxYvhsVn1ifCD3enNX2CUnJ1fbmabaJzYn6vn/OrPsvspIZtmAv62NNZRaXHUwMDA2ejilnlx1MDAxMsN3XG60U4OXx1Uwelx1MDAwN1x1MDAxYVxmZt1aa4RcdTAwMTaUUVx1MDAxYdTOhezyXHUwMDFmp41cdTAwMGL424pJqVxiXHUwMDBiscwm3DJLoSX849PXNbbM6W5hvYzzXHUwMDFjXHUwMDAzXHUwMDE4wTiHXFxSPPY5rVx1MDAxZbjhncJDKjm8yVx1MDAxZLRcbslS9yG6fVZgOynnoOCCXHUwMDEzXG5cIjaJXHUwMDAwnFxij2nw0aRcdTAwMTHwLzbm3Vx1MDAxYnf8TVx1MDAwMGKGhMOV7TPV1lx1MDAxMiOd8XtKeHhgzlx1MDAwMlx0o1x1MDAxOHr9PFx1MDAwM10uX+1cdTAwMWS/7GdcdTAwMWF3j/tPLyfm5LX+OIxqoF/4TilXPiV3lXziqnR/85xPlnhcXFx1MDAwNlpwoX1UZSVcdTAwMDPtvsooXHUwMDA2WoNcdTAwMDJqMNDAmzCBNsWfOTdcdTAwMWW4XdRqQGJqfNr7pp5cdTAwMTY0z6WSXHUwMDFib3mGXG5cdTAwMWUtXHUwMDEwXHUwMDA0J0CEXHJcdTAwMDGm5FA2a8KTZYTCU1x1MDAwNW1ky+hanGJcdTAwMWJqlFx1MDAxM83m2nrMc6xemFGef0nxXHUwMDE45UyvfTZ4rZ5ccuxWsp/Nt7Odo6PsXHUwMDAyRplLT0nJmFx1MDAxNIZz39PEO8pcdTAwMTX1lJWSXG6hgHBbXHUwMDFhVPpNTj1mQFxiYemL2GRKMUDOqDNZJuj0u284wVxiXHUwMDA38Fx1MDAwN1x1MDAxOfhEm7xzcZvopp937p9cdTAwMWL8+uZZXHUwMDE1XHUwMDBlb++vvzr5fTbkx7lOs56gO/Vujm2lL5pXu3FcdTAwMWP3oFo7ZZXyXHUwMDExeb1tl29vaOb5zsTEIYA5c0Fj4lx1MDAxMO6nXHUwMDEyhUMoTTxJudREUkmJmkqqU+VxQFx1MDAxMlx1MDAwM/9IXCKMcXj5hnhcdTAwMTa9/N/h97FybnLqXHUwMDEx4CNcdTAwMTedTmAsXHUwMDE0XHUwMDE4urIumFBaT7/7TieUXHUwMDAxoFCaLVx1MDAwNVx1MDAxM6FcdTAwMTJsXGbaIF/Jz1xudOK8WF0zXHUwMDEyMcdKh5GIsFx1MDAwYomHOtw/7eV3KneJMsk2moWnZLN/0LeLUFx1MDAwN+5cdTAwMTE0XHUwMDE33FpJOJ3UdSmFxylcdTAwMDV7L4niwpd731CHt6dcdTAwMWWz7ovVqVx1MDAwMyeYOjE+V8Ffj1x1MDAxN8i5j5PrXHUwMDE2bJD6xOR6vyA6Jyd7p8Pr7lU69bjfPerw1ldcdTAwMWL4y7NjKlx1MDAxYvQ51Vx1MDAxYT7d7lby1+K2dlx1MDAxN8NxZftcInud7G038nvDZIXtlnuySOOoXHUwMDFlTGT36n3eL56/dPKVXrkwyPS2YjjuXG7BkpnHXaEoYi6BXHUwMDEyXFxcdTAwMTPryzmvRKDc0lx1MDAxOYVAacI9xVxiY1pcdTAwMTJcdTAwMDJe+ySmXHUwMDFh41ltmNRMXHUwMDAy4/PRvXGSxHqrhmD+NNhcZilKcnImzonhlGknPpLAu++cSVx1MDAwYlx1MDAwMlx1MDAxMub/uVhCMFxcXHUwMDAzWLOVQjCTXHUwMDE04+/63/V14EtzqMk0X5p1XHUwMDEx8XClzkVuu3Z5c3tJsrJcXD5PpuvsUC7ClShorlWaY1xibyq2XG7W0yPgmFx1MDAwYiuVpNpuXG5cdTAwMTE/XFznd1YvTVBcdTAwMTSellx1MDAwMDBwQFx1MDAwMXi+oaVcdExcYqG5oUulSJcjS5WCrdZPquW9rduHx/N2XufqJ/qrSc2jeM31j1x1MDAwYmf87PT6vJZI9fK9l1ZcZsfNlFx1MDAwN5VMtlN9vN2nrJXNJY9faC4mksCZXHUwMDExUsdUSuF+KlFIglLSo8jVXHTnUlx1MDAwMfeehFx1MDAxM809sDeGUU5cclx1MDAwNVx1MDAxMlx1MDAxMWRcdFx1MDAwNnwza2d2LmyyNrPgY4FaXG5cbsRSai6Zu2KZhMZjgeVZMFx1MDAxY3HXUiwuw1x1MDAwMc4wLjzYx5uVajXu4fpcdTAwMWKtNc3gzLHd4WVcdTAwMTVcdTAwMGJcXF1MLGOLZ1x1MDAwZttcdTAwMTfJfrK/u1s01ze1bLVcdTAwMTCZZXBipVx1MDAwN1xuzaUhWlx1MDAwMWeYrHyWXHUwMDEyK58tXHUwMDExVlx1MDAxOOAgjtLnTUQmZpyIoVx1MDAwMFx1MDAxMii+USFcdTAwMDWQXCJcdTAwMTineVx1MDAwM1x1MDAwZstcdTAwMDS3YFx1MDAwM8ZcIvDhLKOU67RcdTAwMDb0qbKd3tfbXHUwMDE5tX99sdWJzDI+KiTzUaGT+o1tZe96j1fPw2q6cNCtb29lMnGxXGZNqWQxsVxm91OJwDI4VcrDWj6CPiYldjI3rCjxXGJcdTAwMThcdTAwMTJGXHUwMDE5+LZSOViGO1x1MDAxNrFJ38xcdTAwMDCMXHUwMDA1eIVUgll4XG5cdTAwMDFHY4RcZsGKrHdaoaSmktL4Q1x1MDAxMVx1MDAwYlxubYBWrF/aZo49nlx1MDAxZIaIm1x1MDAxY1xcXHUwMDFjdo76eX3ZY+V8VudcdTAwMWb2dy9PS4uQXHUwMDAzXHUwMDA1boGQ3Fx1MDAwMFx1MDAwMzDCXHUwMDE36lx1MDAxY6VmpfY0UFx1MDAwN0KAIUhgXHUwMDBmS5FcdTAwMDMl8CCCcPBQOHjHY1CIxYf4R4GVWaHw/dX8emVeXHUwMDAwmEyMMXCXXeovRWj4XHUwMDAxlJRcdTAwMTlLiFx1MDAxOEeZPpxcdTAwMTnc1u5uXHUwMDFhxct90m7lXHUwMDFm6oNjUU89qqiWNn3bbnaTpdbLRXJw99S52cn2XHUwMDEz8YxcIlx1MDAxMCChcmKQyEqW1n2VkSwtV1x1MDAxZVhR0EzCrPGB6K9JXHUwMDA0xKNwqoQpxpl02FlNPYXpXHUwMDAyTiTVUq86huBPc+ZvXHUwMDE3KMFETm2lm41LXHUwMDFlqnRYMKu1jNvkcqOIXFw5+v/br10vyzvH2Lks74zriMdcdTAwMDD3XHUwMDFh57nrYqF7XHUwMDA2vu/uaeLuuTU8jd6fODLARuCIXCKpXHUwMDA1lZPji7gwWDilrFx1MDAxOLnVZHy3P8v+RuiH+ke5XFw0efP9VT6/sv1lXHUwMDA0nqJmylkpQUEtw6DAUibBM/JVxn24+d3q1O5yPFU6rp6WzO3W08NRsXv0X2d+3VdcdTAwMTnJ/EpcdTAwMGWaZVxyJZRTPWl9uSZcdTAwMWUg/q/BYpJzXHUwMDFlVEzKtUdYjHOA/rDOxFxcYVx1MDAwMVx1MDAwM0xcdTAwMDDfNFx1MDAxMVwi4N6OLLBccq1Qolxme1x1MDAxNdhyWbdZXq9cdTAwMDCpoVx1MDAwYkiw01x1MDAwNK9dJ+JcdTAwMWNT5zK/XHUwMDFm2np4edltXHUwMDFlpExqi5V4oXDafHhOy4V8X+5cdTAwMDHuclxyyCvp1ERcdTAwMTBcdTAwMWNcdTAwMTLlcWvgYUplraP1eJN+j1njiyvbXyBKlHJ/yeFcdTAwMDRcdTAwMGXMqF5cdTAwMTbUSCo/s1axUUremeLtces1e3DBk9Xk8FRFXHUwMDFlXHUwMDA0tEKN3lx1MDAxY/uLXHUwMDBlhDV+eV/J/rqvMpL9XHUwMDE1wlx1MDAwMyagwcsllIBcdTAwMDJNqac0XHUwMDFlUfDELMfQhctcdTAwMDJcdTAwMGKHyVx1MDAxNVx1MDAxYp83XFxcdTAwMDFrXHUwMDBixJlcdTAwMDHQcHqaM84srVxy0zR4Wopx/2jPmJzeRWU2YHGTpU4+16qCgVxc04T1XHUwMDFjazdtfSNeTzyWePYk0yksmtBzrCz3OKOSgilcdTAwMTVTUS6pqIedx1pzYYWiwTBcdTAwMTfxrNRowYVijGmAg6DOc+WhhGD/XHUwMDEwXHUwMDA1QPGJySbVNFx1MDAwMVx1MDAwMWdRXHIwXHUwMDBiM8DGXHUwMDEwgpNcXFxcqEBEKCpcdTAwMDCEg+tMluv7nzeQcylcdTAwMWXebFSnrfv4/36MXHUwMDA1ZvSX9///3386v71cdTAwMTUupfhcbsjn+HhcdTAwMDFj3e7kW51teGTVemXy0fyes1x1MDAxZGViJ3z7uVFcdTAwMWNcdFmjVaj66Vx0XGLuoFRM4dVPXFw8XFylJ8m4LFx0X9Qjk6/giY9cdTAwMDWkVC/OP+3Sy25cIlHq1GUml9NP+8n20UVFuU67Wm9Xi6XI582pVehcIjJD6Vx1MDAxOGJ+P1x1MDAxYXyLSqbAmDGOrvqMq1x1MDAxON3+XHUwMDA0othDKVx1MDAxZtAkuEb/Z4BcZtUpv6NUKzT6XHUwMDBlcXtu9ErJ6q/zb+eqnYffXHUwMDAyXHUwMDFlgWHNvmfz56dcdTAwMWLjXHUwMDAxwlxubZUxik/m/1x1MDAxNGVcdTAwMWVyavxIXHUwMDEwtenWWlx1MDAwMn/HXCJ6vlW86G13klv14cHp5Wmi37lcdTAwMTPvs8BcdTAwMTaA6fPVu8FcdTAwMDVcdTAwMDVcXGTOmVx1MDAxMfBJaJiSaVx1MDAxMJSJ0tKPXHUwMDFmsC57p5RVdsTt8eXNINlcdTAwMTXZRP7pXHUwMDEzXHUwMDA2rFx1MDAwN1x1MDAxZcvqxmVcdTAwMTZ/cl5lXHUwMDA0N4lcdTAwMDGf9lx1MDAwNGVcdTAwMTjEUkZRNZlAUEp7XFxcdTAwMTGDyqusJcH5hlRcdTAwMGKP46XElCfcKGxQYVNufXX5VZZIS0JaiSjhoeNcXIA+KSlcYo+5/3pcdTAwMTVcbjVdXHUwMDE3vHbhzDmWM6xcdTAwMDL4Q0OaW6cvmerWizoy5frl3nNvu3vw2Fx1MDAwYlx1MDAwMlx1MDAwMVx1MDAxZXJcdTAwMWFcYlxi80CB6ajkXG7ntkxZcuEp0HVcblxmXTP/XGKGd1MuwFx1MDAwZlx1MDAxM1JyK3FcdTAwMGWD9lFcdTAwMDGfKWdcYlx1MDAxNkIoSy3Vln1/V2p8vl+CXGaRTXm4yyWQ2IZU/PnHI07HPFxyeiNaLVdcZrz+Tpf1pNJcdTAwMTKHXGZcYlx1MDAwM1x1MDAwZdfY68fXllx1MDAwNY9McE6FJIJcdTAwMTE/53dcdTAwMWZNWI9KdCGUZVxcSjnpRFDQaoNTXHLgY6OtlvNcdTAwMGVHlfCYNUZYcFxu4Y9gk8cjqKxcdTAwMTYnlVsmXHUwMDA1+C7zXHUwMDBlXGI83jNSXGLcWsKoMGM7PjqgxvZhoVx1MDAwMCOUVVbOP5xcdTAwMDC4sFx1MDAwNLDCXGKMr5qJ60VcdTAwMTRcdTAwMDCEIWbUi8JcdTAwMTiff7gwbMGXhrNcdTAwMTNcdTAwMDRhXHUwMDBikIlaXHUwMDE25eTAoUP55aOHOHGtVuNAWMngvFxyQFWEw3HhoV9ugEYx9GK4/3BcdTAwMTSeLVx1MDAwMdRUlMKxrFx1MDAxNWZ8vIC+T3nn085hyCdcdTAwMGI5lVx1MDAxM581XHUwMDFitdfKSNPnXHUwMDExzFdeO8tcdTAwMWVcdIDFYrlX3t1ONZLXmWhcdTAwMDRTU8+OSkBcdGPCXyH4OuJcdTAwMWRcZsd9wK2D222pdEzQXHUwMDA2XHUwMDA3c4RcdFx1MDAxNId4auNcYtD5xlx1MDAwNv5cdGZkXHUwMDAx+5COzlx1MDAxYykjVlx1MDAxYiqcIXlcdTAwMTFcZsmNc+Bcbkd8LVeG9q5cdTAwMGLxboua6dR95MAswSRA0EorfO7unoFj3t05XHUwMDE5rs+VmmC4ZuIw71x1MDAwNLZWKndm8NtOo1x1MDAxOUZuJ854mslcdTAwMDZPMVx1MDAxZfIqznjz5lxcbj2dV1xyqbbSiddi+yRcdTAwMTJ5XHUwMDA1M1x1MDAwNyZcdTAwMDUwXHUwMDE4rFx1MDAwMJq8XHSModR4VlGw5GAsgVuYYFx1MDAxY2p8W8dcXFx1MDAxNYynZ1xmN2BdXHLhkn5/rvpBYf+blTkolfDUsOvE1c3KZlxmIVx1MDAwNYZcdTAwMDb0jCy3gGdGOlx1MDAxMGvp+FI1sLFy0IlvXHUwMDA3XHUwMDA18lsyifx2o3zaS+QvXjMkx6ql43t+XHUwMDFmrUFcdTAwMWRYXHUwMDE50GSt4aFTXHUwMDAzXFxwMtZMsaaO4yZPRY3AmpuAllNp0cWdXHUwMDE5q/r+VOKDtHyBgnb4bUqhV+Sqo7Hh2ixxs1x1MDAxMvNXUsUyXHUwMDAzUGtcctq8klEu59ud+/ZW8XdcYmch21x1MDAxY1x1MDAxMn36INtcdTAwMWN6pvGY6Ntep1xcITf5K3Z9cXhWXHUwMDFm3JOn52JcdTAwMTTt5cDtwSlFK0ykXHUwMDA0njSZqce2UE21ooJQLcBcdTAwMTRcdTAwMDRbxShlnsDNmOGrOjbaXHUwMDFipr0htelO7Vx1MDAwNWREd9qZhWdihvqCc66Jjbs2Z7RbyXcyy6tvXHUwMDAzbN76q+7kWcbErM3xfSGbLp4/J9WwsvPA0vy5XHUwMDEzVNu5IyDggWhcdTAwMTOcMyUsXHUwMDAxkGWMcSm+YFx1MDAwNdb3r7NcdTAwMWJT5ezrbvF5u/x6ok7LXHUwMDBmLHOVSm7fkSVcIsOVlXO8XHUwMDE0nrUmQK+dI2ZU+IhcdTAwMTnAcC3ERFx1MDAxN+nHj/w+SknxlM/Vbux2a5vnr5u7x69RI1x1MDAwMuL6vHGYv7LJ19zT4OCkVSUpnp78LZ89NGrmcT90TFx1MDAwNDPMyJj2aLufSiS2XHUwMDAweEAsyJ+As1x1MDAxMXySLGgjMJpcdTAwMGUyZsH/M8I18Hv+LKo/rJQ3foh5WIBT4FZUXHUwMDFlspRTifAkXHUwMDEzkFxuMFx1MDAwNjTutPTiUlx1MDAxZeBcdTAwMTTfYkjVXHUwMDFjuz/NQD59NNXebq1tzlx1MDAxYnvyeDhcdTAwMWP098uZPdani/BcdTAwMTJcdTAwMDHMglx1MDAwMP2gVtPpiIDFxIxg4DBIq6hxNLlvlnMuXHUwMDAwXHUwMDFig4vj88r1oNfbsnuDp1JPqeuz4Vx1MDAxMrDxvHqbXHUwMDBlQ5+AuNv1VPBt30BxQVx1MDAwMYY+cXxcdTAwMTVV/cThiVx1MDAxObaTuWO1my1cdTAwMTUrt5fdr+YliWQv1zzd22n3r0w90ev3zVXiJYbjNvayxdtmq36quUjsdtuqmD5cdTAwMWXExUtcdTAwMDShhsc0vsr9VFwi8Vx1MDAxMmk8YyjAgVCIXHUwMDA1k01/VltcdTAwMGZDx5pwYcF2OZqKXHUwMDE481x1MDAxOJtcdTAwMTnEWIiXbFx1MDAwMCZcYjD16LxEI3ucXHUwMDE4m+tDXHUwMDEyeIqhvISBXHUwMDAwSFx1MDAxYXvxy+JSPoeXrFx1MDAxYiGZY/BnXHUwMDEzko9kXCK3Z9vHpFI57Vx1MDAxZj+Kaq9w1blrXHUwMDFmkiAkXHUwMDA0c4+cXHUwMDEy4ymiqMBaKDK1gthyXFxOhCspJbdYL1x1MDAxNOQgXHUwMDFmXTe3nlx1MDAxMLFcXN1cXFxcXHUwMDEw0Vg5ZYmZXG5FqbNVXHUwMDE4182GXCJcdTAwMDewUTmZ6vxq5NiUzb1cdTAwMWRvUzY35+Q2ZXM/fiyS7M6kXHUwMDEzg/zL/mBnv9TZSV091Fx1MDAwZm+Iiezf4iAgT+DgRFxyt93qydI5MCdcdTAwMWVu1Fx1MDAwNYGh3HzFZuv1tCzLkU/aTZQuu9dX1/1e+VpcdTAwMTav74/On7LLWJZcdTAwMTj2ZVx1MDAxMctcdTAwMTggQEjgnYfbXHUwMDE2QJnlu+PfT3AhXHUwMDA3Nz1olvZPXHS5k8lGKrf7+HhbUlwiqsOY2z9cdTAwMWaQ65NkK9G+OH7OvFx1MDAxY1x1MDAxZGaeazE4olx1MDAxZrVcdTAwMDXig1x1MDAxZNyJQPdKXHUwMDBlrvupRHBw0VCB0VKAu1x1MDAxNqyknsz1XHUwMDAx+ntMXHUwMDE5XHUwMDBiZkOBlTdL+bdcdTAwMGLtgNhAjFx1MDAwM2JcdTAwMTbYLYWbPFx1MDAxNDBO90C5WZOtlFx1MDAwNkoll2vVjFPKXHUwMDAzXHUwMDBlbtgmhXVzdefY/qhcdTAwMGJcIj7S6b1pmIeLISv2tlx1MDAxZvcrV41Sn5dcdTAwMWajXHUwMDE13CpcdTAwMDF+rVx1MDAwNFx1MDAwMi0pkFUjpiPvXHUwMDEyaDFm73CGu1x1MDAxNl/QLbaewLGc11x1MDAxYlx1MDAxYnDsrOz2Wqzx4NS5n8pVKvCGJ0pbXHUwMDBlXHUwMDFll4m5NmhcdTAwMDU42Xi9b8fbeL1zTm7j9f5YyOt9alx1MDAxM5k4sc+D18fkXWd/P/OydVhdrNpMUMLA8TVqem5cdTAwMWJcdTAwMDXTgrsmiFTwXHUwMDE0XHUwMDE5/4K5iutpWpbjpO3t4t718EB3h1x1MDAwZk2RuEpcdTAwMWSY9HF1XHTT0lxc2etFbVSgRu6hXCI6PKurNM78pZ/p9JZcdTAwMGZcdTAwMGVcZlx1MDAxOVx1MDAxNoup7GmTJFx1MDAxNHstluXpV2dfS4etXiN/wq9Or5nJXHUwMDFl3fdb+vg5Puc0vuyr++5FcE45XHUwMDBlRVx1MDAwNlx1MDAwNFx1MDAwNiOFnaSSTtWQU0M8zrjWgPmEyODGU6wyN5JcdTAwMTCiKDbQSN9Ckk36NS4oeInunVx1MDAwMsRbYa172+nMzcdcdTAwMDa4j2FrlERxp19TtS44eevmmc6xz7OTsLMuKVx1MDAxZa80IU+3ilx1MDAxOcmeaTXde96p7lxcXTb3I6ZiwaxLXHSsgVHOVFx1MDAwMFx1MDAxZlx1MDAwMD4kts7BXHUwMDBiyenGK13FK41cdTAwMGIvWis7pcBcdTAwMDCASLurOMBlXGJcdTAwMGZyaVB4oNlcdTAwMWZcdTAwMTDk2uRi51x1MDAxZG7jlfo+3XilXHUwMDFm75VedexjoqpcdTAwMDVPJ7cyicyVKTdcdTAwMTdYtFx1MDAwM1x1MDAwZtl6QlBcdTAwMGVcboB2ZtqyXHUwMDE4j4DjabHxXHUwMDE4yMlcdTAwMTdswV1P07JcdTAwMWNcdTAwMTUtXHUwMDE5sdPfTZ5cdTAwMTR3k+U66Vx1MDAxNm6Gpzs3y5iW1VfljmahXHUwMDAxpriLjbVcZndLqcB2V7VcdTAwMTRFfT+/xVZcdTAwMDLsXHKrXHUwMDA3+0pnT4u3latLnrDFl4uvzsV+sFtcdTAwMWFfztR991wiuKVgUKQnwVJcYlxuLom0063NVONcdTAwMTRcXKpcdTAwMTXQUqapI2lKjYdbgLArRWjirzvdZE1jw4JcdTAwMDW24IKBXHUwMDA20qGVu1x1MDAwMsPMYJRgcrnC0EKcjFIo8FeENovstFxuOKa+/GK1Vvo12X893dM5hnpG4jTShcXjpJ6/sMzL/e3l49PtXHUwMDBi2+ZDcXf88Fx1MDAxMMVJZVxuuFx1MDAxYlx1MDAwM1x1MDAxZVxyfofE3sSAk1xuxFxcmFF9ObOcOvqpN17qXHUwMDE3wEfIcpNcdTAwMDXcVIbpcoPuh9NPXHIu/fOtXHUwMDE5XHUwMDAzl4XoNSrG2Pipb8fb+KlzTm7jp/5YyE/dOdxNqptcXD9le7tG1/YuK/muY4Rz6EJcdTAwMDauPEOQMFx1MDAxMK1AXHUwMDFjpuZtXHUwMDFhhkuh4Vx1MDAwZcL3Jlx1MDAxNi29WVx1MDAxN02Mx1xyTlx1MDAwMucgzNLVOU9BbD3gqVxmW1pcZlx1MDAxOOpFNsJ+4didYr79UPrcuTvtXHUwMDA1SKdWXHUwMDFho1x1MDAwYs490CY4XHUwMDFh7335XHLVhsVfqSc4UFx1MDAxM1+F50q+1c3JS/b69OVo6274eL57Vd3J9I5uosu0XCLEXHUwMDAz70pLzOtxPVlsXHUwMDA2oOdJqVx1MDAxNJZMU6OUXGLm/TigNE6QJVxuV7hcdTAwMDAwuIIvlDNcdTAwMGb8dY5D5Fx1MDAwMFDJRqhDhXpcdTAwMDFXSqONc09cdTAwMTe34XNcdTAwMWZcdTAwMTSaK1x1MDAxM/dYRyE4Mb46gZVE+nmHdOl5r9/oJuVx2uiLMuGOicihRS5Se2DAXHUwMDAwO4E7wC2aLHPRhHtcZiiFoshcdTAwMTiBXywl0nDR2D6CK7tcdTAwMTVhvvaRjUBPXHT0XHUwMDAyw1xyjZCCgFx1MDAwMXXGXHUwMDAz6YxRJvAscUtZ7KNMqFx1MDAwNFx1MDAwM7DIrNJcdTAwMTlC/VRcdTAwMWM+XHUwMDFkJjvdg5tqOtMksn6191pcdTAwMGZcbrVrXHUwMDBmXHUwMDFmxsdcdTAwMTVQYSWQqZOpznghcXapYNpKKUDgXHUwMDFk+3CBmVx1MDAxOPyEXHUwMDAy/Vx1MDAxNZJxR21cdTAwMDYjQN5BlqWlRFx1MDAwMkL47vZai/Tnz/trR16GXHUwMDFinlVcdTAwMTVcdTAwMDZHrrqX4YpgR8H7ik6r8CF9yFwimaUm9sS7iy9UTPFcdTAwMTVcdTAwMTTQ8fH+mjpu1F18mV77bPBaPVx1MDAxYtitZD+bb2c7R0dZX1x1MDAwMH2pXXzgXHUwMDEyXHUwMDEyycEnXHUwMDAzXHUwMDAwXHR4ecTDecxcdTAwMDKolDREXHRLVOAqxtJcdTAwMTJpMd+H7Fx1MDAxM1RwjznhlqCB9I2rdHxcbs7qjEtYw618l2fHVDboc6o1fLrdreSvxW3tLlx1MDAxYVx1MDAxMGtpQFxiXHQ2/WA15SSxUFx1MDAwNlBcdTAwMWHHaeKWKLgrwc5cZuJcdTAwMTlw/TjDhZJSgqtcdTAwMTeEYcE9XHUwMDAxwo2DXFxxt6pZJE35R6FwZ2VcdTAwMTCmuFhcdTAwMWVJg3MyulxuTz9SK4FcdTAwMWLiXHUwMDAzjJluaCr9zcZfuFx1MDAxMzVETvFcdTAwMTWQ0NVh+P5pL79TuUuUSbbRLDwlm/2Dvl1cdTAwMTXCXHUwMDAyK1FXXHUwMDA12s5Fbrt2eXN7SbKyXFw+T6br7FDGfZZkxlmuIZbO7lx1MDAwNp6DpdrjIPfWUGORak+AqdHCw1mnllx1MDAxM01cdDcuMOVCK3DAmGGSKW7HhPVcdTAwMWRNJfeA3mgtLdJjXCI2eybC0HT1ZWdUXHUwMDEzcIaFu7WeaVx1MDAxNlxivY3hXHUwMDE0OIRcdTAwMDFJiHu4tcSYxFhwvmzTRKig4isgoqvD6adcdTAwMDDVynA6u4f3z4RTcpywu41u8fYxuVfTd09XumZcdTAwMWNDfV1wKjj3wCBcdTAwMTMtwXkyIG1cdTAwMTNwarXyXGLmsKQgVlx1MDAxM0fme1x1MDAxNPJcIlxm43jgWuEmXHUwMDEw5Y+nvEMqt+CjKWaxXHUwMDE0XHUwMDAzXHUwMDEzS2NA2kDqJKSuXHUwMDFlJsCNYVx1MDAxNohoMH39c9TLXHUwMDE3zlCJMYLHzU+FXHUwMDE00r+18lx1MDAwYvnpLFnFV0BKV0fV7rYsyIP9QfKqmEpd3Fx1MDAxN4dcdTAwMTfPjKxcdTAwMWSq0sZ98fCmtbdV3qtkesWavGtcdTAwMWVe/uGoOrv8ci5JlVxmx2szoDB6uqCIKONZLjGVgKl44+iYxVx1MDAwNVx1MDAwZUpxZTlXXHUwMDE4vHLVXHUwMDFmco9cdTAwMTgrXGa4VIRYQ1x1MDAxNoHU9awn+ihIXb1QSChAUy3dXHUwMDBiWEwwlzZGVKCSwsSOqetSJ7RcdTAwMTUuqPhcbojo6nj6OfzPs5OvlVx1MDAwMXZ2XHUwMDA15Z9cdLCJdO+CXHUwMDE3c8n86+BGXHUwMDBmKvWz1s7jTlx1MDAxMGBcdTAwMWTl3UwwXHUwMDBm6zKEXHUwMDA2akGFncRXZjSWrymgtCCahrrquzlcdTAwMWOAzG47/i6rq74gO9t1XHUwMDAzqrOGxljMmGs3dM5ATmWJlHLJ0fAz2ChjVMulXHUwMDE2Sb7Vbd+/lzC3/+Wszl6PnbDu04ypIfhE7TZcdTAwMTLNPYC9YuYpt3d722xWoqguPE/hafA3ubZGaWuni+GAgSuCbVlcdTAwMWPLXHUwMDA1XHUwMDFke2Q2mjt6d1nNXaBQXGJcYqrFZ+FcXD9cdTAwMWReVsFcYuVcdTAwMTSXfsadb2ZMcrpI+dtGc39cdTAwMDQ097Rxe/PIcoe2b4b9fLFx/HCViKi5XG5cdTAwMThcdTAwMDdcdTAwMTegoobCs1x1MDAxON+SX+VR2lOcalx1MDAwZfZcdTAwMTh3PI0/XHUwMDFkt1TBj290dVx1MDAxMV1ccvFb3HM7jFWaXHUwMDA2+6BQV4NcdTAwMDN8xt1RXGaIkop7aodgXFwzuVRK8k1VcefiXHUwMDFkXHUwMDFjNb/Oiuo6yXjU9LK0e3a5t/NwLfdL+eFR5aKnzV4kbqyI9Vx1MDAwNHBfXHTPVYFCTlx1MDAxYVjOlKdccvhihjDFXHJzlFx1MDAxYmxcdTAwMTR1UUXtRddTiiV3yGyci5jt9JvvempcZm4piD00i1OtlytcdTAwMWTY6OlIT1Xr3CZcdTAwMWVcdTAwMGVcdTAwMGJ5lWtf8rOXnd3GVjQ9ZUp6XGZncII5pYxMXHUwMDExYa2pXHUwMDA3XpMwXFxjw5RcclbQS/jxjZoupKZcdTAwMGJwX2OUlFY5R98xXHUwMDE1ak8xT2usjbuiWHDF1WrUXHUwMDE3VWDdVTR27eT1eqNbuCyrw+pjjuxeXlx1MDAxNoZcdTAwMGaFXHUwMDA1Olx1MDAwMlx1MDAwNPGwolx1MDAxNDgtN5KR6TCT9IhcdTAwMDYx0cxqxVx1MDAxZD0ugmHNXHUwMDEzMbjxnvpjwP6+LYw1XHUwMDFh3Fx1MDAxMmCZVt+kbu9cdTAwMGKUt7+AjYVbbqj0L13wtW2pUCNrcbUpJv/iVV7MNnK2SEHJXGaJnj2qclZSijNGPFx1MDAwMq6bIZxo7E6dkGZKlPW0xpZfXHUwMDFj9CpkMPRCPMVcdTAwMDRaI2pcdTAwMDRWWPn6scalU9hUXHUwMDAwWoHzcojWwHRcdTAwMTaQ6D8qK9WPoXaKYn8z08JcdTAwMTWhmTVmzYCoW6ljl/Q1mbJcdTAwMTYuqPhcboro+Hh/TVx1MDAxZDdqVmr2XHUwMDEyvVx1MDAxZmuS5Z89ZTKms/xmSajZXHUwMDFiXedcdTAwMDAq0Fx1MDAwZsIlJoexP39cdTAwMTJQsVx1MDAxNNUwnIKsmcGOy1x1MDAwMJ5Sj2B5XHUwMDFm11ZcdTAwMTKGzduOqSGSe1xunD+tMViugVx0jr+z1lxm4fPxdLAynDKQZDCD3J2qkjLcO8fdPFxuXHUwMDFj+7h3j45GMIzv15dl+cMlXHUwMDE1X0FcdTAwMTldXHUwMDFkUGevSV9cdTAwMTdA/Vx1MDAxNNj/ZoBav7Gt7F3v8ep5WE1cdTAwMTdcdTAwMGW69e2tjKNcdTAwMGI7XGZQcSCbXHUwMDAyvMR9NpOAqizxcL9cdTAwMDBcdTAwMGVcdTAwMTLh6J9cdTAwMDdcdTAwMDCVeaA1hGrg/oZqxmxpy1HdXHUwMDBmXlx1MDAxOYgqN5jDXHUwMDEwymwqUcNcdTAwMDA18qjGUETl6PZcdTAwMDJcdTAwMDdzrmRVJFx1MDAxY1BxPFx1MDAxNsWu7phcdTAwMDFcdTAwMTXn5LCvJ6hbsyRcdTAwMTVf0zK6OqB2tnj2sH2R7Cf7u7tFc31Ty1ZcdTAwMGJrXHUwMDA3qJ9cdTAwMDL731xmUM+G/DjXadZcdTAwMTN0p97Nsa30RfNqN2JcdTAwMWSqXHUwMDAyyMSJQdxYYieHtFxiqz3CjJHaotl2tEoxjzL053DYl1x1MDAwNfvuK8VcdTAwMTnzU1x1MDAxY96FNa74S8Cl+iYjWr5cdTAwMDBNV3f3JeFcXFx1MDAxYX98yt/+L0LRVCsgp8DQ4mano77TpSqp4lx1MDAwNtMwOcVXQEJXx9JcdTAwMGbp/49cdTAwMWRLP6U99pthaaY8qGSynerj7T5lrWwuefxCc9HIKTHWs0pwXHTCr1xy861CXHUwMDE5eftEeKBcdTAwMGVKgT1cdTAwMTdcXFDHJlx1MDAwYlx1MDAwMuRcdTAwMTaIjVXAXom2wlHSTzxuOcWCalx1MDAwNYrO/PVvXHUwMDFiOPXDacFcdTAwMWYmXnZxXCL4XHUwMDE4hHLnICzlXHUwMDBi10yn+CjnOKhSx54mWFx1MDAxM2c/XFxQR1x1MDAxZlx1MDAwN2V0dUD9XGLaN3HRI9xaXHUwMDE1UD+gQZYudJZcdTAwMDFAfVx1MDAwN827RrczOdh41uTLXHUwMDBmXHUwMDAw1tl7uOdcdTAwMDCr8ZRcdTAwMDLhJ9SCvzhFU621XHUwMDFlXHUwMDAxdVOcMsa5ddQrwTekXHUwMDE5LVx1MDAxMVwiQlxi5/BloMGgYCCroPHAotRcdTAwMDZXQ3E1hrSUkUpT4S5cdTAwMWNWOrRwmMOj0ULGXTe8NsBcdTAwMWEuqKOPXHUwMDAzMro6rn5EfDJ+XFz9gJau/1x1MDAxYVxcnb2rYy5hXHJd2spcYvFcdTAwMDTG8ilXiiri8P5cdPc4YVx1MDAxOIVcdTAwMTM4lUw6uvqZx3D+q1ZcdTAwMTS7sHBcdTAwMDHkXHUwMDA2V8NwdfUmVFx1MDAxY0wqiXIuVdO+Qt7grHpcdTAwMDBcdTAwMTUuYt/0vS64XHUwMDFhLqn4XG7K6Oq4+lx1MDAxMYn0+HH1XHUwMDAzek7/a3BVti+y18nediO/N0xW2G65J4s0Oq6OXHUwMDAyXHUwMDAxWETCpob5YY1cdTAwMTUzoGugc9K1WVxigVx1MDAxN/xObTRRuGlVuVaFeOT3RH9cdTAwMDPeJs6D3MBqXGKs0pVRVTFcXHEtnGR1xvgp8JJx70Ls46fWZppfuKCOPlx1MDAwZcro6rD6XHUwMDExOar4YfVcdTAwMDPiqv81sLq/VclcZjL1aknu0DR7SvC7bX+d8Ky2JWGxak/bX+N5pmCVXHUwMDFhz1x1MDAwMqZcdTAwMWFsXHUwMDFjxlx060FYZXxuW7A0nrFMXHUwMDE5XG5yS3Dy9Vx1MDAwNlVDUFVFL7ZmlGK9tXRcdTAwMTZbXHUwMDBiX5A7XHUwMDEwRiVGUc7jrkFcdTAwMTW4tsk3hHWZvWx7mX//eNthlijmm51Sa7y27O/6fmr88eQmbseX9/Z9x2o2XHUwMDFk30jm/v0jWerkf+1Jc3yhXHUwMDBmXHUwMDFmXHUwMDAwJP37R6LTafnWrvl6OMDTY1x1MDAwMMSjvnpcclSfT3zpi1o6vst9jKfX5Cq1c9FuVffq9ZtKt3VTTso7eVx1MDAxYVxy+ji2mcDDXHUwMDFibarlU9NMXGbXXHUwMDFlPFacMmQ5fMtcdTAwMTFcdTAwMDDVdP5IXHUwMDA07kk5qtwzoJRAMjbYXHUwMDE3gn06OvZJibtcdTAwMTOscVx1MDAwZUhcYk7pf3fIqZFWaUJjhj6K0+E180vvotCHf8E+ybv7xnOzXHUwMDA1V1x1MDAwZaf9W19y/1x1MDAwM8/mf/+uv39cdTAwMDX/hVI98fn3QadcdTAwMTgvNVx1MDAxZVx1MDAwMGHXilx1MDAxNNOJi+1U6nlnv96pP51cdTAwMTRcdTAwMGaj1flcYuRHYE9cdTAwMDXFLTZ8ulGNc49w+Fx1MDAxMD1cdTAwMWRcdTAwMGL4XHUwMDEwXHUwMDFj48mx7pJiXlOq0Vx1MDAwNFx1MDAwN4dTilOVJH5NUqGMMlx1MDAxYvpcdTAwMTRcdTAwMDYhxlxyIVx1MDAwYm360Fx1MDAwNLDeiSxUhlx1MDAwZk3C+lx1MDAxN078PW5cdTAwMWa+4lrXLo6IIPXb6+3tm73CcOtlh+Sir4xcdTAwMTZcbvc0+2tBv8zjXHJVXHUwMDAyfFx1MDAwNcU/4KEt7O++8tpZ9kiAvFx1MDAxNsu98u52qpG8zvhcdTAwMWTGtagj+pBNId+8jsgt9FHIXHUwMDFl19ZcdTAwMDM2Jy3TOIx2soqIg1x1MDAxM1xm4qWptqNcdTAwMTFKwSpcIqM8jnuKjdCA59SxXG7E5/tscHlcbpdcdTAwMTdcdTAwMThcdTAwMDDARilcdTAwMDP/xFxyX+l66KYlzjSn4NbGzeyMxr1PqzC7x2678zfcil9cdTAwMWWUk6aFXHJcdTAwMDL4oKXic/jONF9cdTAwMGK7gHjI19lBtXbKKuUj8nrbLt/e0MzznYlIvpj0JJfKWG6pb/Gqi3o5Vodj4SC1uEdcdTAwMTNMjjauJkC0PVx1MDAwNlfKKVxy7E0s1FP9Z2m4XZl5SUOZ1dK58pWS0I5qRongozj55zEv8VSopFx1MDAxMluDh3ayoHvXJXU87N0uyLxcdTAwMDTjX7/qXCJcXFx1MDAwN/BcdTAwMTWQ/j+FeH1Emfk/XHUwMDAzp/y9iJdb5qNcdTAwMTAvSZVHiVx1MDAxMri8S0omJudcdTAwMTdyXG60XGZcdTAwMDc9XHUwMDFixvA/NDj+QoBcdFx1MDAxOVxyg1x1MDAwNlx1MDAwZVx1MDAwN1x1MDAwZYt0XGZf2nCvUGRORKdemnLCOHVPtVxiTShYwcF5i73KhWnGXHUwMDA1XWlecFx1MDAxZiT87zpcdTAwMGU4Wlx1MDAwN9o1h+lM0y7XycdDuVx1MDAwZYfFZu3yLPV4f5KxT3dXXHUwMDAz3XthXHUwMDExXHUwMDAz5spcdTAwMTNSU9z5K6hfXHUwMDEzR6PTXHUwMDA08DErhMDdSEYrx/RvSj1LlcLhXHUwMDFmOJ7f0ddcdTAwMDa/gStcdTAwMDOcTeKuP+3/XHUwMDFk8zW7RDjl9E/R7L3omo1cdTAwMTMpLWHWWcFGQlsuLO5cdTAwMGWwMu6OXHUwMDBiXHUwMDA2j8lcdTAwMWamX0K196qVUSbLlb36uz5OPoWExZU1QFx1MDAxY6VcdTAwMTDEwFx0iYkvrYxcdTAwMDPLXHUwMDA2xlx1MDAxN7+mePDA8lxm0eV29WmXqmHenN50XHUwMDEyuypcdTAwMWFcdTAwMWVcYuFJxlx1MDAwNMhcdPxRU+Fv4IxcdTAwMWUhdtRfIFx1MDAxY2iglHOQXCLFtkKGfVx1MDAwN4ZQOCxdZDbbXHUwMDFmhlx1MDAwMFx1MDAwYsRVKFx1MDAwMVx1MDAxMm+ocGOAXHSNrGDXleHwdMdcdTAwMGY3XHUwMDA2XHUwMDEwXHUwMDAwXHUwMDFmiCrtXyCxXHUwMDA0XGKcg3H8nqpcdTAwMWV25vEodG73sjSsX13299Ns+HrYeOi+0OM4XHUwMDE0WmtcdTAwMGbnkIPrynCDQDAj/lx1MDAxMSqNzUn37I9R6ZA1dM40OPBj4GQ8YL/hM1x1MDAxM1xmoL6HTIRcdTAwMDFXyp9cco5Loa3kK1n1jUK7XHUwMDE1+rZZXHUwMDE4XHUwMDBlXHUwMDFmOiltXHUwMDFip6/VcrW71TtcdTAwMTfRXHUwMDE0WmKwgTHDmeVWyamh5Jp70kiMn1x1MDAxMlwi/Uth3tNcdTAwMWXWs0xcdTAwMTLCiaRaauPIe2zs9ds5zVfuXHUwMDA1XHUwMDE2XHUwMDBiSIKbvbQrXHJieHhcIlx1MDAxYbCZUmy8iJey41x1MDAxY2T/zphlKfv31fCZp1x1MDAxZo+al+D3XHUwMDFk3bX3d0snZO+cpVx02yo5XHUwMDA2zriSm0x5mFx1MDAxZFx1MDAxN0IxSqdCbEZYXHUwMDBmXHUwMDBibylV8DHoeUDLXHUwMDExXHUwMDA2QMlnaTlcdTAwMTNcdTAwMWWO9sRcdTAwMTBcdTAwMDBhoOl8oSqUP8uEN6NrOcM5XGJMXHUwMDFhZ29cdTAwMTmlOnS2jCA49NU/3TImI45cdTAwMTNql+ote9Pz6/aoquubO+NRrlwiXHUwMDFlrVx1MDAxZu7vXt5r20+dy7LYXHUwMDFmtOmuOu1E0not0HwzbkbDISWbbOCH51xiplx1MDAxZkxcYrVEU2OYo4CVWU9ohjv74Fjw8Fx1MDAxZFx1MDAwMTlGPPhF8Fx1MDAxZJRGXHUwMDFjjLZQT9Sfpfgv0Vx1MDAxNV9hQ6RRNKDgeC6chtewYvOTpCZ2d1xcYLZ8qZTklOL/2Fx1MDAxYuSfm/5AuE/bKTiIXHUwMDE0m0GNXHUwMDE1XFxcdTAwMTK9XtpcdTAwMWU89XhU/Dqrzo5N9+W41CpccpMvV9e9XHUwMDA0czQ9Olxyu/GMXHUwMDE5XHUwMDE1JVxuTenUslx1MDAxMlxu7NmzRozmwcBfXHUwMDFjs+Qk97DeXHSLatBcdTAwMDNcdTAwMTBcdTAwMGXvfGPa385pvoa3XHUwMDE20HBLcN+Qc5NcdI5UXHTVcLDBVlpcdTAwMWT7XHUwMDFhv+XXwU9p+G7n/pub9dAriEfft9Nblzc5mbX1QaX5XFy+zvfOtWNcdTAwMTSvS99cdDadgOvHjJL+3a2jyZHceGjowdRjwtVXhPhcdTAwMWV/s5uQ+q93l9bwTnRccrc4o4u7d/9ZXHUwMDFhWrJEXHUwMDE1MfgwfE83XHUwMDFlXHUwMDA1R5g3K3H3kZP7PTU79NRjalx1MDAxMum+7na3843t3Mv1QeqM9O6e9lx1MDAxY/NgXHUwMDE2Z+lMe1x1MDAxNCNwODGGg6lcdTAwMGVWKoLr7lx1MDAxOWpGjWi4O4Q5ZmuvxNL/MFxyX2RroFZCWKaNk6b7x+5cdTAwMDTicNjNYKyO2z9cdTAwMDeWplfzz0eK8lx1MDAxZEl6yInHtFi7UCo1b5uHvcLh7XOxf5nsKy4jmmzuWaBrXHUwMDE2J1MoYaaG52NcdTAwMDMoXHUwMDBlXHUwMDEwXHUwMDAy+6tcdTAwMTkxweCbxC49MbO8bSWG/odpd8g6XHUwMDEy565BiaVG1DnX2Vx1MDAwNlx1MDAwYpHHXHUwMDA0XHUwMDFkUFx1MDAxYTiUinmuc1xc9jvZqFc7jW9cdTAwMWJ5m39ccjE55em8uVx1MDAxZIjb5E4vl1x1MDAxNMX75NM1v4mYJSdcdTAwMWXHPIsgSlx1MDAwYl+55EiIOVx1MDAxNVx1MDAxZVx1MDAwZbxcdTAwMDI9Nca49lx1MDAxM2KdnJYzffJNXHUwMDE53Ns5zdX4/ehcbi9Gc/els8CVklx1MDAxOYydXHUwMDAx6+JY21x1MDAxOLPG41RrubrG7+frf9e/ecA94oXEo/vVYWfv/iRbuG7VmoP6wWWleXniKIF1dVx1MDAxZFksRydcdTAwMDJcdTAwMTSfov1cdTAwMTg/vFHbXHUwMDExYVx1MDAxZU6CINjIQrGcOsjmrfEwUodcdTAwMDXxXHUwMDFhOFx1MDAwMXGMI9/MIXs7p7naf7B6zzdFXGbGREmAzf9cdTAwMWPtJlxyRVx1MDAwNWMx1a4/sfPoXGJI4vnJnk1d3uxcXG3fXlxcZlNcdTAwMTeL9XwvXHUwMDFm5o97Jm+YXHUwMDFh/Pp4hSln080yIZ+s8TQv92OOQk1cZlOe1YJcdTAwMTlcdTAwMDJcdTAwMGUoXHUwMDE1k/FDynHUXHUwMDE33FNccp6dNsLRa8NcZoCbP13gQCdf38Q6oJGYev9L0WhngdiCXHUwMDAyt9BKa12wMyNcdTAwMDFohKDK+qNA65JcdTAwMDCstn/kQ1x1MDAwMlxu8XKMOZ02c6z7NPOYPO2Ywlx00qhbJYp75OV6a9C+yNzTXFw1XHUwMDFhwVx1MDAwMI/C01x1MDAwMHmWclx1MDAwZeRQT+owNjYzXG7kXHUwMDAymKwhrlx1MDAxZVx1MDAxYqs8LPXGbX5cdTAwMDL31zuihdRcdTAwMTNcYq+aMEwnc+OLRK+DRq9cdTAwMTW/WH1cdTAwMTlcdTAwMWZcdTAwMTZfKMZUYP4+nqLU4bV8Qlx1MDAwYkvkUlx1MDAxMcTl6MVBdys7uM5esFx1MDAwYnm1c9q9rlx1MDAxZb3m+t+RXoQrwejTgPh/PLn4+lx1MDAwZV73w41GKjDeobHrj2styFRcdTAwMTGCklx1MDAxZWGWXHUwMDE4JonFQGdcdTAwMDCSXHUwMDA086ThnDOiXHUwMDAwubRzoP2GU4RcItD5XCL5XG7M8Sv/XHUwMDFjqkicXHUwMDAyY/1gVVjcLVx1MDAwMatziof8mpCKOVx1MDAxNn2aVEyddzysXCJ92252k6XWy0VycPfUudnJ9lx1MDAxM9Em1XHGmEexrMBKLo2ZXGZbXGJqPaZccrE4opfhXHUwMDFjnYBcdTAwMGVcdTAwMTNvNHRdw3dcdTAwMTQmK5VyRS2ZJ8DUge+BXHUwMDE144Ru5qWEKnVcZtt+pFx1MDAwNeul3Et+XHJcdTAwMGbd9sO4VFx1MDAwMMW+XHUwMDFmi2l+ujBSfr3p35ohqfhcbshodNtcdTAwMWY2L6TXOM9dXHUwMDE3XHUwMDBi3TPB5e5p4u65NVxcdd+DY1CbZydfJnDiXHUwMDBijjm5OOxcdTAwMWP18/qyx8r5rM4/7O9enpZiP+1cdTAwMTlnucZcdTAwMTGYp0R2r97n/eL5Sydf6ZVcdTAwMGKDTG8rmv9cdTAwMDZ+mUdcdTAwMDXnXHUwMDEyiKXmYlx1MDAxMmmVXHUwMDE2ntGUXHUwMDAwVVx1MDAwMlZFXXsqKFxiMCFcdTAwMWN7MYSx1rVVXHKMKu5mwy56yYX1me1ccs5O4uzhyjCLXcrwxJxzqWbVacOjXHUwMDAzL97EvvxnTdZUhIspvlx1MDAwMlx1MDAwMro6yEbe/lCtt6vFUiS4gpOEu0rhWVx1MDAxOW7pOFxuMrpcdTAwMDNcdTAwMWWwJaqIXHUwMDE2OHpUa1x1MDAxZdxfvCDeXl52m1x1MDAwNymT2mIlXiicNlx1MDAxZp7TcmW8XHUwMDA1aTBMoXuluTbaTl6CXHUwMDExlFwiXHUwMDA2KcVcdTAwMTTG0Vx1MDAxN1x1MDAwMeOv91j3dtv57YunzEum0S1393Lph8p9NI9cdTAwMTXuilx1MDAwN8KncYaQ5VNltNZcblx1MDAwZkRcdTAwMTK3UeBOIVx1MDAxNiS7TFxuT2CGftZg96DHKjxNXHUwMDE1alx1MDAwNKGSXHUwMDEypX3L3f5sXHUwMDE0PlrAg/1V52i4q/FVyvCqecExhadt7GMqmLS+wY9LjqH68TY/88f9r6T2j2r9R/5XovtTvNtl0/LRTz5cdTAwMWVcdTAwMTe3bvPPdGfnVPdcdTAwMDbVXHUwMDA3LSupe815NJ1cdTAwMTfCIyNcdTAwMTiXQOuNL5v7a3ZcdTAwMDX1cLCvwVx1MDAxNcpYXHUwMDA1XHUwMDEyVHohPcY2Slx1MDAxZpfSXHUwMDFmR1d6sLFaKH+7g49jhVx1MDAxN9kyglx1MDAxYrJcYidLZeBn6rylPuJcdTAwMTanzpfz7c59+3uq/PS5x1SHl8o/ktOM0VvWVFx1MDAwZcvJw/Iw70iVteB0pijHr/GS2nhcbjhcdTAwMWF48lIrMVl9i7W5TFx1MDAxMVx0+lx0msm4XHUwMDBlZry5XHUwMDAwqsAx9klcdTAwMDFcdTAwMDFcdTAwMDR3aD1wKIZTNFx1MDAwMFSsXHUwMDE0jH9cdTAwMTd/q5hvP5Ri0PoxXHUwMDExtee94WMuc3+cVCmaeT5tZ9XV41uCaCFwWGByldBcdTAwMDJgnVx1MDAwNWdcdTAwMDCPXHUwMDFljVxugMZ43Vx1MDAwYrNgXHUwMDEwlFx1MDAwZkBWh1x1MDAwNy7A6Vx1MDAxOW1DW1x1MDAwMFx1MDAxZWZI//5j7eFoUCidX+pcdTAwMWHppvW9TvRcdTAwMWOVaGHSXHUwMDBm5MRjcG9w/1xmmDw7afS0wlwiXHUwMDFioEYg/FxuLKBD+jG1Y6i0jOHQdeNIXHUwMDE2a6I9LcFygjMnwPv8JrniNVx1MDAxNv6TXHUwMDA16DDwXG7gtMQ9XHUwMDFmQoWveVx1MDAwM1x1MDAwZYMrp8xy6eNPXHUwMDEy/odBu5Ptd1x1MDAwZSrpZvb0MWdv2sNsbVx1MDAxMejXXHUwMDE4ZZBCUFx1MDAxYzNcdTAwMThcdTAwMTB+61x1MDAwMVpLIIbgwTNcdTAwMTVkfMrTilxiTLOrkfg7XCJtQOfAkZRMXGIwXHUwMDBmzMpcdTAwMDUyXHUwMDFhTsF2KMA/yqPXOtK9j5D9RUZcdTAwMTZqXHUwMDAzt1xcumNvUoTORqEop0SytVx1MDAwNv7iy1G3TFt3r/nhsJ0oVNK23tNB2XdFmFx1MDAxNadAWVxiXHUwMDA1d4PBadnJXHUwMDA2XHUwMDA0XHUwMDA1ZFx1MDAwNYRcdTAwMTVrXHUwMDFmLPBcdTAwMWZcdTAwMTHcZaJcdTAwMTWwXHUwMDE5y4RcdTAwMDb7wLhlLspcdTAwMDPukrFgUcBfXHUwMDAy4yHUN6E8X+DnnLpcdTAwMDV6kUyeoaMt6cHNdaMnXHUwMDExvnZKMYJcdTAwMDUuLO62XHUwMDA0XHRcXHm5rSfxpvJCXHUwMDA1XHUwMDE1X0FcdTAwMTFcdTAwMWRcdTAwMWYvXHUwMDAwTHFcdTAwMDeZXHUwMDE3XG7RKiWx91x1MDAxN1x1MDAxN8SBXHUwMDA3oafCzFNcdTAwMWZcdTAwMDcuYsEoc3dbXHUwMDE25MH+IHlVTKUu7ovDi2dGVr1cdTAwMDSDt1x1MDAxN05cdTAwMTLzzVxcXHUwMDFi9c9cdTAwMTmf2m9cdTAwMTZlfuE7pVxc+ZTcVfKJq9L9zXM+WXJEnNxALD2DK2gsleBCTi1cdTAwMTBcdTAwMDXN9IhcdTAwMTHCXG7cXCLEtGP7n/BcZleSYO6cXHUwMDBizVx1MDAxY+OXXHUwMDAwXHUwMDE3PKtxgDI2XHUwMDFj0s3uvzBcdTAwMThOrlx1MDAwZcNwj1FcdTAwMDPdq6dcYlx0jUMhPFx1MDAxMcrirqhcdTAwMDBcdTAwMTg2/mK7r9vPx0BMNZZKUpxcdTAwMDZNxURBhSBY3lx1MDAwN6qvteTKKjvveNSzXHUwMDE2XHUwMDFiI1x1MDAwMSuQw9hxPSC+plx1MDAwNX5NQZ2MpmxqTFx1MDAwZVx1MDAwYvijzETVXHUwMDAztq9wXHUwMDBlxongXHUwMDAwIHDC1KqwnlZcdTAwMGbc8E7hIZVcdTAwMWPe5Fx1MDAwZVqFZKn7sLJlXCJYmKwx56I1PLmJ51xuuG4pXHUwMDA3SVx1MDAxY1x1MDAxNYUx8Ce/XHUwMDE3rp+1WC59cFndarFyq8FcdTAwMTKHzcypI67oyCRYjFx1MDAxYUqBXHUwMDAzeKTB2zNcdTAwMDHrOLGDUWO4XHUwMDExXHUwMDAw/Nw12Vx1MDAxZXB9slQniOtMecr/irnD71x1MDAxZlx1MDAwNVZmhcJ3r309W8BZXHUwMDE0klx1MDAxYox6uNppZlx1MDAwNFx0XHUwMDAx7ykwy7jThqvP6TgpvTrTXHUwMDA0XGadP9/Ld8FR0lx1MDAwNlx1MDAxZrTVeeJs40lcZjxcdTAwMWOlq/luNztMpU1V5lx1MDAxMtn6YfoqqMBh0SHNrKdccsNcdTAwMTZrroiY3Fx1MDAwZkhHS6ngUYFfj8N5fDXp7/lAMHvM91KuRl2uPe1/rVfFK516f1x1MDAxY1x1MDAxY210OnFcdTAwMDRHXHUwMDE30eWQMvYgQXvz44KlWIpoboWrjyY4w2NcdTAwMWNcYiW405WQuGvbXHUwMDE31vBfov7Xb1x1MDAwNvMz32ymO3DI93vws1ct9bfDXHUwMDAzhH/9viUoXHUwMDAwpdGt+89f//l/XHUwMDE5a6ZrIn0=DetectorControllerDetectorAdapterControllerDetectorAdapterSubControllerDetectorAppAdapterControllerDetectorOdinControllerOdinControllerDetectorFrameProcessorAdapterControllerOdinControllerOdinAdapterControllerOdinSubControllerMetaWriterAdapterControllerDetectorSubController__main__fastcs-detectorfastcs-odinFrameProcessorAdapterControllerFrameProcessorControllerDetectorFrameProcessorControllerFrameProcessorPluginControllerDetectorFileWriterPluginControllercontrollers/controllers/odin_data/odin_data/odin/DET: DetectorAdapterControllerFP: DetectorFrameProcessorAdapterControllerEF: DetectorAppAdapterControllerMW: MetaWriterAdapterControllerwriting: AttrR data_compression: AttrRW[str] data_datatype: AttrRW[str]justdetectorwithodinEigerFrameProcessorAdapterControllerOdinControllerOdinControllerEigerOdinControllerXspressAdapterControllerXspress ExampleXspressDtcControllerEigerControllerEiger ExampleEigerMonitorControllerEigerFanAdapterControlleris ahas awith detector control in adapterwith detector control in fastcsKey

This allows a lot of flexibility when implementing a driver for a specific detector to rely primarily on the base implementations while adding detector specific logic through composition and inheritance. The two key patterns are to acheive this are:

  • Creating base classes of the built-in controllers to add type hints that validate when running against a specific odin server that the given attributes are introspected for use in driver logic

  • Sub classing OdinController and overriding _create_adapter_controller to create detector-specific adapter controllers that implement detector specific logic.

For a worked example of putting these pieces together, see the tutorial on Creating an Odin Detector Driver.