Query ASAM ODS Server#
In this example Notebook, we show you how to query (instance) data from your ASAM ODS Server.
The first sections are on initializing and connecting. The fun starts with “Query Instances”.
Dependencies for this notebook#
The ASAM ODSBox contains some functionality that wraps the ODS HTTP API making using Python easier ;-)
It contains:
http wrapper implemented using protobuf and requests
JAQueL implementation that converts MongoDB style JSON queries into a native ODS SelectStatement
utility that converts ODS DataMatrices into pandas.DataFrame
All examples are using the JAQueL query style to help you getting started. Use
jaquel_to_select_statement
to convert JAQueL to a native ODSSelectStatement
.
try:
import odsbox
except:
!pip install odsbox
from odsbox.con_i import ConI
ASAM ODS Queries#
Establish session#
The ODS HTTP API is a session based API. The session ID is called conI in the ODS documentation. The ASAM ODSBox uses con_i as API object representing the session. Close this session to release the connection license. Otherwise the session will be auto closed after 30 minutes of inactivity.
con_i = ConI(url='http://79.140.180.128:10032/api', auth=('Demo','mdm'))
Query Instances#
Query Units#
Engineering units are contained in every ASAM ODS server, so we can use them to get started: Lets search for all units in this server
# There is always one Entity derived from AoUnit
con_i.query_data({
"AoUnit":{}
})
Unit.Id | Unit.Name | Unit.MimeType | Unit.Description | Unit.DateCreated | Unit.Factor | Unit.Offset | Unit.dB | Unit.dB_reference_factor | Unit.PhysDimension | |
---|---|---|---|---|---|---|---|---|---|---|
0 | 1 | m | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 3 | |
1 | 2 | kg | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 2 | |
2 | 3 | s | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 1 | |
3 | 4 | A | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 4 | |
4 | 5 | K | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 5 | |
... | ... | ... | ... | ... | ... | ... | ... | ... | ... | ... |
96 | 97 | Sv/s | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 63 | |
97 | 98 | A*s/kg | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 64 | |
98 | 99 | °C | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 | |
99 | 100 | rpm | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 | |
100 | 101 | Nm | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 |
101 rows × 10 columns
In our first query we’ve used the base name ‘AoUnit’ - so this query works on every ODS server. In our query result above we see that the entity derived from ‘AoUnit’ is called ‘Unit’. So we can use this directly.
con_i.query_data({
"Unit":{}
}).tail()
Unit.Id | Unit.Name | Unit.MimeType | Unit.Description | Unit.DateCreated | Unit.Factor | Unit.Offset | Unit.dB | Unit.dB_reference_factor | Unit.PhysDimension | |
---|---|---|---|---|---|---|---|---|---|---|
96 | 97 | Sv/s | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 63 | |
97 | 98 | A*s/kg | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 64 | |
98 | 99 | °C | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 | |
99 | 100 | rpm | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 | |
100 | 101 | Nm | application/x-asam.aounit | 19700101000000 | 1.0 | 0.0 | False | 0.0 | 73 |
Now lets narrow down the list to the interesting attributes like ‘name’, ‘factor’ and ‘offset’. And instead of its id we want to know the ‘name’ of the referenced physical dimension.
con_i.query_data({
"Unit":{},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension.name":1
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | |
---|---|---|---|---|
96 | Sv/s | 1.0 | 0.0 | equivalent_dose_rate |
97 | A*s/kg | 1.0 | 0.0 | exposure_as_a_quantity |
98 | °C | 1.0 | 0.0 | unknown |
99 | rpm | 1.0 | 0.0 | unknown |
100 | Nm | 1.0 | 0.0 | unknown |
In addition to the ‘name’ of the physical dimension, we are also interested in the lenght exponent.
con_i.query_data({
"Unit":{},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension.name":1,
"PhysDimension.length_exp":1
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | PhysDimension.Length | |
---|---|---|---|---|---|
96 | Sv/s | 1.0 | 0.0 | equivalent_dose_rate | 2 |
97 | A*s/kg | 1.0 | 0.0 | exposure_as_a_quantity | 0 |
98 | °C | 1.0 | 0.0 | unknown | 0 |
99 | rpm | 1.0 | 0.0 | unknown | 0 |
100 | Nm | 1.0 | 0.0 | unknown | 0 |
Ok, and if I’m only interested in Units with a physical dimension with a length exponent of ‘1’?
con_i.query_data({
"Unit":{"PhysDimension.length_exp":1},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension.name":1,
"PhysDimension.length_exp":1
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | PhysDimension.Length | |
---|---|---|---|---|---|
4 | N | 1.0 | 0.0 | force | 1 |
5 | N/s | 1.0 | 0.0 | momentum | 1 |
6 | W/m*K | 1.0 | 0.0 | thermal_conductivity | 1 |
7 | V/m | 1.0 | 0.0 | electric_field_strength | 1 |
8 | H/m | 1.0 | 0.0 | permeability | 1 |
…. or in those, where the ‘name’ of the physical dimension is ‘length’ …
con_i.query_data({
"Unit":{"PhysDimension.name":"length"},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension.name":1,
"PhysDimension.length_exp":1
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | PhysDimension.Length | |
---|---|---|---|---|---|
0 | m | 1.0 | 0.0 | length | 1 |
… we can also make it easier getting a group of attributes of the same type in return…
con_i.query_data({
"Unit":{"PhysDimension.name":"length"},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension": {
"name":1,
"length_exp":1
}
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | PhysDimension.Length | |
---|---|---|---|---|---|
0 | m | 1.0 | 0.0 | length | 1 |
All queries have used ‘equal’ as implicit operator before. Now lets use ‘like’ instead.
con_i.query_data({
"Unit":{
"name":{"$like":"k*"}
},
"$attributes":{
"name":1,
"factor":1,
"offset":1,
"PhysDimension": {
"name":1,
"length_exp":1
}
}
}).tail()
Unit.Name | Unit.Factor | Unit.Offset | PhysDimension.Name | PhysDimension.Length | |
---|---|---|---|---|---|
3 | kg/m^3 | 1.0 | 0.0 | density | -3 |
4 | km/h | 3.6 | 0.0 | velocity | 1 |
5 | kW*h | 3600000.0 | 0.0 | energy | 2 |
6 | kat | 1.0 | 0.0 | catalytic_activity | 0 |
7 | kat/m^3 | 1.0 | 0.0 | catalytic_concentration | -3 |
Query Hierarchy elements#
When you think about using ASAM ODS as a tool to manage and harmonize measurement data files like CSV, XLSX, MDF4, … you can think of the following data model:
file -> ‘TestStep’
group, sheet -> ‘MeaResult’
channel, column -> ‘MeaQuantity’
So lets query for them and reduce the maximal amount returned to a fix number - here: 5.
con_i.query_data({
"TestStep": {},
"$options": { "$rowlimit": 5 }
})
TestStep.Id | TestStep.Name | TestStep.MimeType | TestStep.Description | TestStep.DateCreated | TestStep.Optional | TestStep.Sortindex | TestStep.MDMLinks | TestStep.Test | TestStep.TplTestStep | TestStep.TestEquipment | TestStep.UnitUnderTest | TestStep.TestSequence | TestStep.Classification | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 230 | Profile_76 | application/x-asam.aosubtest.teststep | 20240730000000 | False | 21 | [] | 23 | 0 | 0 | 0 | 0 | 0 | |
1 | 231 | Profile_61 | application/x-asam.aosubtest.teststep | 20240728000000 | False | 22 | [] | 24 | 0 | 0 | 0 | 0 | 0 | |
2 | 232 | Profile_69 | application/x-asam.aosubtest.teststep | 20240725000000 | False | 23 | [] | 23 | 0 | 0 | 0 | 0 | 0 | |
3 | 233 | Profile_45 | application/x-asam.aosubtest.teststep | 20240722000000 | False | 24 | [] | 26 | 0 | 0 | 0 | 0 | 0 | |
4 | 234 | Profile_55 | application/x-asam.aosubtest.teststep | 20240720000000 | False | 25 | [] | 24 | 0 | 0 | 0 | 0 | 0 |
con_i.query_data({
"MeaResult": {},
"$options": { "$rowlimit": 5 }
})
MeaResult.Id | MeaResult.Name | MeaResult.MimeType | MeaResult.Description | MeaResult.DateCreated | MeaResult.MeasurementBegin | MeaResult.MeasurementEnd | MeaResult.MDMLinks | MeaResult.TestStep | MeaResult.TestEquipment | MeaResult.UnitUnderTest | MeaResult.TestSequence | MeaResult.Classification | MeaResult.TplMeaResult | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 297 | Profile_56 | application/x-asam.aomeasurement | Data based on the Kaggle 'Electric Motor Tempe... | 19700101000000 | 20240802000000 | 20240802000000 | [] | 229 | 0 | 0 | 0 | 0 | 0 |
1 | 298 | Profile_76 | application/x-asam.aomeasurement | Data based on the Kaggle 'Electric Motor Tempe... | 19700101000000 | 20240730000000 | 20240730000000 | [] | 230 | 0 | 0 | 0 | 0 | 0 |
2 | 299 | Profile_61 | application/x-asam.aomeasurement | Data based on the Kaggle 'Electric Motor Tempe... | 19700101000000 | 20240728000000 | 20240728000000 | [] | 231 | 0 | 0 | 0 | 0 | 0 |
3 | 300 | Profile_69 | application/x-asam.aomeasurement | Data based on the Kaggle 'Electric Motor Tempe... | 19700101000000 | 20240725000000 | 20240725000000 | [] | 232 | 0 | 0 | 0 | 0 | 0 |
4 | 301 | Profile_45 | application/x-asam.aomeasurement | Data based on the Kaggle 'Electric Motor Tempe... | 19700101000000 | 20240722000000 | 20240722000000 | [] | 233 | 0 | 0 | 0 | 0 | 0 |
con_i.query_data({
"MeaQuantity": {},
"$options": { "$rowlimit": 5 }
})
MeaQuantity.Id | MeaQuantity.Name | MeaQuantity.MimeType | MeaQuantity.Description | MeaQuantity.Interpolation | MeaQuantity.DataType | MeaQuantity.Rank | MeaQuantity.Dimension | MeaQuantity.TypeSize | MeaQuantity.Minimum | MeaQuantity.Maximum | MeaQuantity.Average | MeaQuantity.Deviation | MeaQuantity.non_reference_channel_name | MeaQuantity.reference_channel_name | MeaQuantity.MeaResult | MeaQuantity.Quantity | MeaQuantity.Unit | MeaQuantity.Classification | |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
0 | 3849 | Time | application/x-asam.aomeasurementquantity | Time measured in 2 Hz | 0 | 7 | 0 | [] | 0 | 0.000000 | 7982.000000 | 3991.000000 | 4781.037581 | 297 | 0 | 3 | 0 | ||
1 | 3850 | U_q | application/x-asam.aomeasurementquantity | Voltage q-component measurement in dq-coordinates | 0 | 7 | 0 | [] | 0 | -9.305150 | 133.009676 | 67.712335 | 37.097723 | 297 | 0 | 76 | 0 | ||
2 | 3851 | Coolant | application/x-asam.aomeasurementquantity | Coolant temperature | 0 | 7 | 0 | [] | 0 | 17.354847 | 91.180365 | 58.899453 | 20.461698 | 297 | 0 | 99 | 0 | ||
3 | 3852 | Stator_winding | application/x-asam.aomeasurementquantity | Stator winding temperature measured with therm... | 0 | 7 | 0 | [] | 0 | 45.986175 | 131.675086 | 92.553726 | 22.161028 | 297 | 0 | 99 | 0 | ||
4 | 3853 | U_d | application/x-asam.aomeasurementquantity | Voltage d-component measurement in dq-coordinates | 0 | 7 | 0 | [] | 0 | -131.234010 | 123.049144 | -20.714359 | 72.732526 | 297 | 0 | 76 | 0 |
Get parent properties#
Consider you’re looking for columns (MeaQuantity), but also being interested to which sheet (MeaResult) and file (TestStep) they belong, the following query will give you an idea how this works…
con_i.query_data({
"MeaQuantity": {},
"$attributes": {
"Id": 1,
"Name": 1,
"MeaResult": {
"Id": 1,
"Name": 1,
"TestStep": {
"Id": 1,
"Name": 1
}
},
"Unit.name": 1
},
"$options": {"$rowlimit": 10}
})
MeaQuantity.Id | MeaQuantity.Name | MeaResult.Id | MeaResult.Name | TestStep.Id | TestStep.Name | Unit.Name | |
---|---|---|---|---|---|---|---|
0 | 3849 | Time | 297 | Profile_56 | 229 | Profile_56 | s |
1 | 3850 | U_q | 297 | Profile_56 | 229 | Profile_56 | V |
2 | 3851 | Coolant | 297 | Profile_56 | 229 | Profile_56 | °C |
3 | 3852 | Stator_winding | 297 | Profile_56 | 229 | Profile_56 | °C |
4 | 3853 | U_d | 297 | Profile_56 | 229 | Profile_56 | V |
5 | 3854 | Stator_tooth | 297 | Profile_56 | 229 | Profile_56 | °C |
6 | 3855 | Motor_speed | 297 | Profile_56 | 229 | Profile_56 | rpm |
7 | 3856 | I_d | 297 | Profile_56 | 229 | Profile_56 | A |
8 | 3857 | I_q | 297 | Profile_56 | 229 | Profile_56 | A |
9 | 3858 | Pm | 297 | Profile_56 | 229 | Profile_56 | °C |
Parameter and ParameterSets#
Parameters are a flexible way to carry additional information, not being strictly pre-defined by the data model. Those parameters are name value pairs with an additional data type information, being grouped in parameter sets. The following query returns the parameters belonging to certain MeaResult - including their parameter set information…
con_i.query_data({
"ResultParameter": {"parameter_set.MeaResult.Name": {"$like": "Pend*"}},
"$attributes": {
"Name": 1,
"Value": 1,
"DataType": 1,
"parameter_set.Name": 1,
"parameter_set.MeaResult.Id": 1
},
"$options": {"$rowlimit": 20}
})
ResultParameter.Name | ResultParameter.Value | ResultParameter.DataType | ResultParameterSet.Name | MeaResult.Id |
---|
Given their flexible character, only minimal query capabilities can be offered for Parameters. Be aware that the conditions are combined by “AND” and lead to an empty result if you try to use multiple properties.
con_i.query_data({
"ResultParameter": {
"parameter_set.MeaResult.Name": {"$like": "Pend*"},
"Name":"start_time",
"DataType":1,
"Value": {"$like": "2023*"}
},
"$attributes": {
"Name": 1,
"Value": 1,
"DataType": 1,
"parameter_set.Name": 1,
"parameter_set.MeaResult.Id": 1
},
"$options": {"$rowlimit": 20}
})
ResultParameter.Name | ResultParameter.Value | ResultParameter.DataType | ResultParameterSet.Name | MeaResult.Id |
---|
con_i.query_data({
"ResultParameter": {
"parameter_set.MeaResult.Name": {"$like": "Pend*"},
"$and": [
{
"Name": "start_time",
"DataType": 1,
"Value": {"$like": "2023*"}
}
]
},
"$attributes": {
"Name": 1,
"Value": 1,
"DataType": 1,
"parameter_set.Name": 1,
"parameter_set.MeaResult.Id": 1
},
"$options": {"$rowlimit": 20}
})
ResultParameter.Name | ResultParameter.Value | ResultParameter.DataType | ResultParameterSet.Name | MeaResult.Id |
---|
Aggregates#
Distinct#
con_i.query_data({
"Test": {},
"$attributes": {
"name": {"$distinct": 1}
}
})
Test.Name.AG_DISTINCT | |
---|---|
0 | Campaign_05 |
1 | Campaign_06 |
2 | Campaign_03 |
3 | Campaign_01 |
4 | Campaign_02 |
5 | Campaign_00 |
6 | Campaign_04 |
Lets do it adaptive with query condition
con_i.query_data({
"Test": {"name": {"$like": "*"}},
"$attributes": {
"name": {"$distinct": 1}
}
})
Test.Name.AG_DISTINCT | |
---|---|
0 | Campaign_05 |
1 | Campaign_06 |
2 | Campaign_03 |
3 | Campaign_01 |
4 | Campaign_02 |
5 | Campaign_00 |
6 | Campaign_04 |
Min & Max#
con_i.query_data({
"TestStep": {},
"$attributes": {
"DateCreated": {"$max": 1}
}
})
TestStep.DateCreated.AG_MAX | |
---|---|
0 | 20240919000000 |
con_i.query_data({
"TestStep": {},
"$attributes": {
"DateCreated": {"$min": 1}
}
})
TestStep.DateCreated.AG_MIN | |
---|---|
0 | 20240405000000 |
con_i.query_data({
"TestStep": {},
"$attributes": {
"DateCreated": {"$max": 1, "$min": 1}
}
})
TestStep.DateCreated.AG_MAX | TestStep.DateCreated.AG_MIN | |
---|---|---|
0 | 20240919000000 | 20240405000000 |
Lets do it adaptive with query condition
con_i.query_data({
"TestStep": {"Name": {"$like": "EngineNoise*"}},
"$attributes": {
"DateCreated": {"$max": 1, "$min":1}
}
})
TestStep.DateCreated.AG_MAX | TestStep.DateCreated.AG_MIN | |
---|---|---|
0 | 19700101000000 | 19700101000000 |
Close session#
Don’t forget to close the session to release the connection license. Otherwise the session will be auto closed after 30 minutes of inactivity.
con_i.logout()
License#
Copyright © 2024 Peak Solution GmbH
The training material in this repository is licensed under a Creative Commons BY-NC-SA 4.0 license. See LICENSE file for more information.