.. include:: .. _component: ******************** Create an Instrument ******************** Purpose of this Chapter ======================= The aim of this chapter is to explain how you can create an instrument. This will be presented by taking the example of the ICH unit. .. figure:: ../../graphics/instrument_mapping.svg :width: 100% Overview ======== Instrument Mapping ------------------ In principle every instrument can be mapped to an abstract structure. This kind of structure is easy to understand and to import. But from our experience, it's the most challenging step during the complete import process. So stay motivated! **;)** The first step is to identify all technological components inside your instrument *(e.g., sensors, pumps, units, transmitterboxes)*. For the ICH unit the following components were identified: * ICH Unit *(Unit)* * HumiCap *(Unit)* * Transmitterbox *(CPU)* * RH Sensor *(Sensor)* * PT 100 Sensor *(Sensor)* As you may notice, in the brackets stay the classes/categories of the components. The reason for it is that each component has to be assigned to a class. This makes it possible to classify components inside your instrument. Especially if you have many instruments, it will help you to get a better overview of them. After you identified all components, you have to create a hierarchy *(in computer science, also known as a tree)*. The root of the tree is always the instrument itself. The children of the root are the components that are directly installed inside the instrument. In our example that are the components HumiCap and Transmitterbox. Since the PT100 and RH sensors are installed into the HumiCap, they will be assigned to it. You have to go on like this until all components are a part of the tree. If you aren't familiar with the tree data structure, it's highly recommended to check out the reference section's links. Components and Component Types ------------------------------ The difference between a **component** and a **component-type** might be confusing in the beginning. A component-type defines the metadata of the component. That means it saves the *name*, the *description* and the *class/category* of the component. A component is an individual instance that refers to the *component type*. If you have a *PT100* sensor with the serial number *X* and a *PT100* sensor with the serial number *Y*, the share the same metadata *(name: PT100, class: Sensor)*. This approach ensures that components of the same type always share the same metadata. So it's not possible that two components of the same type have a different name. Define the Metadata =================== Now it's time to import our first instrument. First, we have to define the metadata. Therefore, we will create entries for the component classes and component types. After it, we will create an entry for a parameter and assign it to the component type. Create Classes and Types ------------------------ Via Python ^^^^^^^^^^ The first step is to define the metadata of the components. For simplicity's sake, we assume that our types don't need to have a description. For this reason, we can define the metadata in the dictionary **METADATA**. Then we iterate over the dictionary and create the classes and the types. The created types will be stored in the dictionary **component_types** for later purposes. For creating entries we use the method *get_or_create* that checks if the entry already exists. If so, the entry will be returned. Otherwise, the entry will be created and then returned. .. code-block:: python from IAGOS.apps.database.components import models as components METADATA = {"ICH": "Unit", "TB": "CPU", "HC": "Unit", "PT100": "Sensor", "RH": "Sensor"} component_types = {} for type_name in METADATA: class_name = METADATA[type_name] comp_class, _ = components.ComponentClass.objects.get_or_create(name=class_name) comp_type, _ = components.ComponentType.objects.get_or_create( name=type_name, component_class=comp_class ) component_types[type_name] = comp_type Via Web Interface ^^^^^^^^^^^^^^^^^ 1. Make sure that you have the permissions to create new entries *(admin)* 2. Go to the menu *Components* |rarr| *Component Classes* 3. Create the classes **Unit, CPU** and **Sensor** by clicking the button on the top right corner. 4. Go to the menu *Components* |rarr| *Component Types* 5. Create the types ICH (**Unit**), TB (**CPU**), HC (**Unit**), PT100 (**Sensor**) and RH (**Sensor**) by clicking the button on the top right corner. Assign Parameters ----------------- After we created the entries for the component types, we want to assign the parameters that are measured by the types. Therefore, we create an entry for the parameter and then assign it to the type. The ICH unit measures *H2O* and the sensors measure temperature and relative humidity. For simplicity sake, we consider only the ICH unit but the sensors can be handled in the same way. Via Python ^^^^^^^^^^ .. code-block:: python from IAGOS.apps.database.components import models as components PARAMETER = { "name": "H2O_gas", "long_name": "Water vapor volume mixing ratio", "cf_standard_name": "mole_fraction_of_water_vapor_in_air", "unit": "ppm" } parameter, _ = components.Parameter.objects.get_or_create(**PARAMETER) components.ComponentTypeParameter.objects.get_or_create( component_type=component_types["ICH"], parameter=parameter ) Via Web Interface ^^^^^^^^^^^^^^^^^ 1. Make sure that you have the permissions to create new entries *(admin)* 2. Go to the menu *Components* |rarr| *Parameters* 3. Create the parameter by clicking the button on the top right corner. 4. Go to the menu *Components* |rarr| *Component Types* 5. Go to the detail view of the **ICH** type 6. Assign the parameter by clicking the button on the top right corner. Build the Instrument ==================== Create Components ----------------- After we defined the metadata of our components, we can create them. Therefore, we define the dictionary **SERIAL_NUMBERS** that stores the serial number for each component. Subsequently, we iterate over the dictionary and create the components. The created components will be stored in the dictionary **instances** for later purposes. Via Python ^^^^^^^^^^ .. code-block:: python from IAGOS.apps.database.components import models as components SERIAL_NUMBERS = {"ICH": "MKX27", "TB": "AGY34", "HC": "CMP32", "PT100": "UBZ55", "RH": "APQ56"} instances = {} for type_name in SERIAL_NUMBERS: serial_no = SERIAL_NUMBERS[type_name] component_type = component_types[type_name] component, _ = components.Component.objects.get_or_create( component_type=component_type, serial_no=serial_no ) instances[type_name] = component Via Web Interface ^^^^^^^^^^^^^^^^^ 1. Make sure that you have the permissions to create new entries *(admin)* 2. Go to the menu *Components* |rarr| *Components* 3. Create the components by clicking the button on the top right corner. Create Relations ---------------- Since we created all components of our instrument, we can combine them to an assembled instrument by creating relations between the components. A relation describes an installation. It describes that the *component* is installed into the *parent* component for the given installation period. Via Python ^^^^^^^^^^ .. code-block:: python from datetime import datetime from IAGOS.apps.database.components import models as components start = datetime(2020, 1, 1) end = datetime(2020, 12, 31) components.ComponentRelation.objects.create( component=instances["HC"], parent=instances["ICH"], installation_timestamp=start, removal_timestamp=end ) components.ComponentRelation.objects.create( component=instances["TB"], parent=instances["ICH"], installation_timestamp=start, removal_timestamp=end ) components.ComponentRelation.objects.create( component=instances["PT100"], parent=instances["HC"], installation_timestamp=start, removal_timestamp=end ) components.ComponentRelation.objects.create( component=instances["RH"], parent=instances["HC"], installation_timestamp=start, removal_timestamp=end ) Via Web Interface ^^^^^^^^^^^^^^^^^ 1. Make sure that you have the permissions to create new entries *(admin)* 2. Go to the menu *Components* |rarr| *Installations* 3. Create the following installations **start:** 2020-01-01 & **end:** 2020-12-31 * HC *(component)* |rarr| ICH *(parent)* * TB *(component)* |rarr| ICH *(parent)* * PT100 *(component)* |rarr| HC *(parent)* * RH *(component)* |rarr| HC *(parent)* Set a Status ------------ The system gives you the possibility to set a status of a component. For example, a component might break down during deployment, which affects the data. In such a case, it would be helpful to know that the component was defective. Therefore you can use the model **ComponentStatus** that allows you to define a status for a specific period. Via Python ^^^^^^^^^^ .. code-block:: python from datetime import datetime from IAGOS.apps.database.components import models as components status, _ = components.Status.objects.get_or_create( name="Defective", description="Component is defective" ) start = datetime(2020, 1, 1) end = datetime(2020, 12, 31) components.ComponentStatus.objects.create( component=instances["ICH"], status=status, start=start, end=end ) Via Web Interface ^^^^^^^^^^^^^^^^^ 1. Make sure that you have the permissions to create new entries *(admin)* 2. Go to the menu *Components* |rarr| *Components* 3. Go to the detail view of the **ICH** 4. Add a status by clicking the button on the top right corner 5. Set the following attributes: Start: **2020-01-01** & End: **2020-12-31** & Status: **Defective** References ========== * ICH Unit: https://www.iagos.org/iagos-core-instruments/h2o/ * Tree data structure: https://www.freecodecamp.org/news/all-you-need-to-know-about-tree-data-structures-bceacb85490c/