diff --git a/.DS_Store b/.DS_Store deleted file mode 100644 index c19a9a4..0000000 Binary files a/.DS_Store and /dev/null differ diff --git a/.github/actions/common/setup-rust-environment/action.yml b/.github/actions/common/setup-rust-environment/action.yml index 2574059..1b6f5d3 100644 --- a/.github/actions/common/setup-rust-environment/action.yml +++ b/.github/actions/common/setup-rust-environment/action.yml @@ -15,14 +15,6 @@ runs: workspaces: | . -> target - - name: Cache Rustup Toolchains - uses: actions/cache@v3 - with: - path: | - ~/.rustup/toolchains/ - ~/.rustup/update-hashes/ - key: ${{ runner.os }}-rustup-stable-thumbv7em-none-eabihf - - name: Disable man-db (skip Manual Page Installs) shell: bash run: | diff --git a/.github/workflows/argus-build.yml b/.github/workflows/argus-build.yml index f789873..b6a8402 100644 --- a/.github/workflows/argus-build.yml +++ b/.github/workflows/argus-build.yml @@ -36,7 +36,7 @@ jobs: working-directory: boards/argus - name: Check for Clippy Warnings - run: cargo clippy -p argus --all-features --no-deps + run: cargo clippy -p argus --features temperature --no-deps working-directory: boards/argus build_pressure: diff --git a/.vscode/settings.json b/.vscode/settings.json index 8f228d0..6442390 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,20 +1,31 @@ { + // Formatting "editor.detectIndentation": true, - // Default cargo target is for embedded. Use Rust Target extension to dynamically change it based on project. - "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", + "editor.formatOnSave": true, + // Rust Settings + "rust-analyzer.cargo.target": "thumbv7em-none-eabihf", // Default cargo target is for embedded. Use Rust Target extension to dynamically change it based on project. "rust-analyzer.cargo.features": [ "pressure", "strain", "temperature" ], - "editor.formatOnSave": true, "[rust]": { - "editor.defaultFormatter": "rust-lang.rust-analyzer" + "editor.defaultFormatter": "rust-lang.rust-analyzer", }, + // Python Settings "[python]": { "editor.defaultFormatter": "ms-python.black-formatter" }, - "python-envs.pythonProjects": [], + "python.analysis.extraPaths": [ + "common/messages/python" + ], + "protoc": { + "options": [ + "--proto_path=${workspaceRoot}/common/messages/proto", + "--python_out=${workspaceRoot}/common/messages/python", + ] + }, + // Spelling "cSpell.words": [ "uorocketry" ] diff --git a/Cargo.lock b/Cargo.lock index a39bafc..bdae579 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2,24 +2,6 @@ # It is not intended for manual editing. version = 4 -[[package]] -name = "ads126x" -version = "0.1.0" -dependencies = [ - "bitflags 2.9.2", - "cortex-m", - "defmt 0.3.100", - "embassy-embedded-hal 0.3.2", - "embassy-time 0.4.0", - "embedded-hal 1.0.0", - "embedded-hal-async", - "embedded-hal-mock", - "embedded-io-async", - "embedded-nal-async", - "heapless 0.9.1", - "nb 1.1.0", -] - [[package]] name = "ahash" version = "0.8.12" @@ -100,7 +82,6 @@ dependencies = [ "smlang", "static_cell", "stm32-fmc", - "straingauge-converter", "strum", ] @@ -788,20 +769,6 @@ dependencies = [ "embedded-hal-async", ] -[[package]] -name = "embedded-hal-mock" -version = "0.11.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9a0f04f8886106faf281c47b6a0e4054a369baedaf63591fdb8da9761f3f379" -dependencies = [ - "embedded-hal 0.2.7", - "embedded-hal 1.0.0", - "embedded-hal-nb", - "embedded-time", - "nb 1.1.0", - "void", -] - [[package]] name = "embedded-hal-nb" version = "1.0.0" @@ -905,15 +872,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "embedded-time" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a4b4d10ac48d08bfe3db7688c402baadb244721f30a77ce360bd24c3dffe58" -dependencies = [ - "num", -] - [[package]] name = "encode_unicode" version = "0.3.6" @@ -1281,7 +1239,7 @@ dependencies = [ "bitflags 2.9.2", "defmt 0.3.100", "glob", - "heapless 0.8.0", + "heapless 0.9.1", "mavlink", "prost", "prost-build", @@ -1328,28 +1286,6 @@ dependencies = [ "syn 2.0.106", ] -[[package]] -name = "num" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b7a8e9be5e039e2ff869df49155f1c06bd01ade2117ec783e56ab0932b67a8f" -dependencies = [ - "num-complex", - "num-integer", - "num-iter", - "num-rational", - "num-traits", -] - -[[package]] -name = "num-complex" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "747d632c0c558b87dbabbe6a82f3b4ae03720d0646ac5b7b4dae89394be5f2c5" -dependencies = [ - "num-traits", -] - [[package]] name = "num-derive" version = "0.3.3" @@ -1361,37 +1297,6 @@ dependencies = [ "syn 1.0.109", ] -[[package]] -name = "num-integer" -version = "0.1.46" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" -dependencies = [ - "num-traits", -] - -[[package]] -name = "num-iter" -version = "0.1.45" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - -[[package]] -name = "num-rational" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "12ac428b1cb17fce6f731001d307d351ec70a6d202fc2e60f7d4c5e42d8f4f07" -dependencies = [ - "autocfg", - "num-integer", - "num-traits", -] - [[package]] name = "num-traits" version = "0.2.19" @@ -1960,10 +1865,6 @@ dependencies = [ "cortex-m-rt", ] -[[package]] -name = "straingauge-converter" -version = "0.1.1" - [[package]] name = "string_morph" version = "0.1.0" diff --git a/apps/.DS_Store b/apps/.DS_Store deleted file mode 100644 index 8137644..0000000 Binary files a/apps/.DS_Store and /dev/null differ diff --git a/apps/argus/README.md b/apps/argus/README.md index c4e3db2..0e76a6e 100644 --- a/apps/argus/README.md +++ b/apps/argus/README.md @@ -9,7 +9,7 @@ Make sure you have `uv` installed: `pipx install uv` ## Running Ensure `grafana` and `persistence` services are running by calling `docker compose up -d` within their respective directories. -Find out which port your UART-to-USB adapter is mounted on and run the argus service by calling `uv run main.py `. +Find out which port your UART-to-USB adapter is mounted on and run the argus service by calling `uv run argus/main.py `. Once argus is connected and you're seeing logs of data stream coming in, you can open up grafana at `http://localhost:3000` and import one of the dashboards from `apps/grafana/dashboards` that is configured to connect to the `postgres` service defined in `persistence/docker-compose.yml` and you can see the logs coming in. diff --git a/apps/argus/argus/dtos.py b/apps/argus/argus/dtos.py index 768fc6c..6368655 100644 --- a/apps/argus/argus/dtos.py +++ b/apps/argus/argus/dtos.py @@ -1,8 +1,47 @@ +# Temperature from models.thermocouple_reading import ThermocoupleReading -from argus.temperature.thermocouple_reading_pb2 import ( +from messages.argus.temperature.thermocouple_reading_pb2 import ( ThermocoupleReading as ThermocoupleReadingProto, ) -message_type_to_model = { - ThermocoupleReadingProto: ThermocoupleReading, +# Pressure +from models.pressure_reading import PressureReading +from messages.argus.pressure.pressure_reading_pb2 import ( + PressureReading as PressureReadingProto, +) + +# Strain +from models.strain_reading import StrainReading +from messages.argus.strain.strain_reading_pb2 import ( + StrainReading as StrainReadingProto, +) + +proto_to_model = { + ThermocoupleReadingProto: lambda proto: ThermocoupleReading( + local_session=proto.local_session, + adc_device=proto.adc_device, + thermocouple_channel=proto.thermocouple_channel, + recorded_at=int(proto.recorded_at), + voltage=proto.voltage, + compensated_temperature=proto.compensated_temperature, + uncompensated_temperature=proto.uncompensated_temperature, + cold_junction_temperature=proto.cold_junction_temperature, + ), + PressureReadingProto: lambda proto: PressureReading( + local_session=proto.local_session, + adc_device=proto.adc_device, + pressure_channel=proto.pressure_channel, + recorded_at=int(proto.recorded_at), + voltage=proto.voltage, + pressure=proto.pressure, + temperature=proto.temperature, + ), + StrainReadingProto: lambda proto: StrainReading( + local_session=proto.local_session, + adc_device=proto.adc_device, + strain_channel=proto.strain_channel, + recorded_at=int(proto.recorded_at), + voltage=proto.voltage, + strain=proto.strain, + ), } diff --git a/apps/argus/argus/main.py b/apps/argus/argus/main.py index 7b752cb..6c8c0e9 100644 --- a/apps/argus/argus/main.py +++ b/apps/argus/argus/main.py @@ -2,15 +2,14 @@ import argparse import contextlib import utils.logger +import messages.argus from services.persistence_service import PersistenceService from services.protobuf_serial_service import ProtobufSerialService from services.grpc_service import GrpcService from services.argus_service import ArgusService from services.message_ingestion_service import MessageIngestionService from services.session_service import SessionService - -# from argus.envelope_pb2 import Envelope -# from argus.temperature.thermocouple_calibration_pb2 import ThermocoupleCalibration +from utils.database import database program = argparse.ArgumentParser(description="Argus Ground Station Application") @@ -19,6 +18,7 @@ async def main(): + database.evolve() args = program.parse_args() session_service = SessionService() session_service.start_session() diff --git a/apps/argus/argus/models/pressure_reading.py b/apps/argus/argus/models/pressure_reading.py new file mode 100644 index 0000000..92f8ba4 --- /dev/null +++ b/apps/argus/argus/models/pressure_reading.py @@ -0,0 +1,44 @@ +from datetime import datetime +from peewee import ( + Model, + CharField, + TimestampField, + DoubleField, + ForeignKeyField, + IntegerField, +) +from models.recording_session import HostRecordingSession +from utils.database import database + + +class PressureReading(Model): + # The recording session this reading belongs to + host_session = ForeignKeyField(HostRecordingSession, null=True) + + # Local recording session identifier from the device that took the reading + local_session = IntegerField(null=True) + + # ADC device index from which the reading was taken + adc_device = CharField(max_length=255, null=True) + + # Pressure channel from which the reading was taken + pressure_channel = CharField(max_length=255, null=True) + + # Milliseconds since the board's epoch when the reading was recorded + recorded_at = TimestampField(null=True) + + # Full timestamp of when the reading was stored + stored_at = TimestampField(default=datetime.now) + + # Wheatstone bridge voltage difference measured in millivolts + voltage = DoubleField(null=True) + + # Pressure reading in psi + pressure = DoubleField(null=True) + + # Temperature of the manifold from the NTC resistor at the time of the recording in degrees Celsius + temperature = DoubleField(null=True) + + class Meta: + database = database + table_name = "pressure_readings" diff --git a/apps/argus/argus/models/recording_session.py b/apps/argus/argus/models/recording_session.py index 5f28c66..b711637 100644 --- a/apps/argus/argus/models/recording_session.py +++ b/apps/argus/argus/models/recording_session.py @@ -10,6 +10,3 @@ class HostRecordingSession(Model): class Meta: table_name = "host_recording_sessions" database = database - - -database.evolve() diff --git a/apps/argus/argus/models/strain_reading.py b/apps/argus/argus/models/strain_reading.py new file mode 100644 index 0000000..5ced041 --- /dev/null +++ b/apps/argus/argus/models/strain_reading.py @@ -0,0 +1,41 @@ +from datetime import datetime +from peewee import ( + Model, + CharField, + TimestampField, + DoubleField, + ForeignKeyField, + IntegerField, +) +from models.recording_session import HostRecordingSession +from utils.database import database + + +class StrainReading(Model): + # The recording session this reading belongs to + host_session = ForeignKeyField(HostRecordingSession, null=True) + + # Local recording session identifier from the device that took the reading + local_session = IntegerField(null=True) + + # ADC device index from which the reading was taken + adc_device = CharField(max_length=255, null=True) + + # Pressure channel from which the reading was taken + strain_channel = CharField(max_length=255, null=True) + + # Milliseconds since the board's epoch when the reading was recorded + recorded_at = TimestampField(null=True) + + # Full timestamp of when the reading was stored + stored_at = TimestampField(default=datetime.now) + + # Wheatstone bridge voltage difference measured in millivolts + voltage = DoubleField(null=True) + + # Strain reading in microstrain + strain = DoubleField(null=True) + + class Meta: + database = database + table_name = "strain_readings" diff --git a/apps/argus/argus/models/thermocouple_reading.py b/apps/argus/argus/models/thermocouple_reading.py index 8df132c..1d8b1c4 100644 --- a/apps/argus/argus/models/thermocouple_reading.py +++ b/apps/argus/argus/models/thermocouple_reading.py @@ -10,10 +10,6 @@ from models.recording_session import HostRecordingSession from utils.database import database -from argus.temperature.thermocouple_reading_pb2 import ( - ThermocoupleReading as ThermocoupleReadingProto, -) - class ThermocoupleReading(Model): # The recording session this reading belongs to @@ -47,21 +43,5 @@ class ThermocoupleReading(Model): cold_junction_temperature = DoubleField(null=True) class Meta: - database = database # This should be your Peewee database instance + database = database table_name = "thermocouple_readings" - - @staticmethod - def from_protobuf(proto: ThermocoupleReadingProto): - return ThermocoupleReading( - local_session=proto.local_session, - adc_device=proto.adc_device, - thermocouple_channel=proto.thermocouple_channel, - recorded_at=int(proto.recorded_at), - voltage=proto.voltage, - compensated_temperature=proto.compensated_temperature, - uncompensated_temperature=proto.uncompensated_temperature, - cold_junction_temperature=proto.cold_junction_temperature, - ) - - -database.evolve() diff --git a/apps/argus/argus/services/argus_service.py b/apps/argus/argus/services/argus_service.py index 28ff3c8..8605b31 100644 --- a/apps/argus/argus/services/argus_service.py +++ b/apps/argus/argus/services/argus_service.py @@ -1,10 +1,10 @@ -import argus.service_pb2_grpc +import messages.argus.service_pb2_grpc from google.protobuf import empty_pb2 -from argus.envelope_pb2 import Envelope +from messages.argus.envelope_pb2 import Envelope from services.protobuf_serial_service import ProtobufSerialService -class ArgusService(argus.service_pb2_grpc.ArgusServicer): +class ArgusService(messages.argus.service_pb2_grpc.ArgusServicer): def __init__(self, protobuf_serial_service: ProtobufSerialService = None): self.protobuf_serial_service = protobuf_serial_service @@ -13,4 +13,4 @@ async def SendEnvelope(self, envelope: Envelope, context): return empty_pb2.Empty() def register(self, server): - argus.service_pb2_grpc.add_ArgusServicer_to_server(self, server) + messages.argus.service_pb2_grpc.add_ArgusServicer_to_server(self, server) diff --git a/apps/argus/argus/services/persistence_service.py b/apps/argus/argus/services/persistence_service.py index a176670..ab158dc 100644 --- a/apps/argus/argus/services/persistence_service.py +++ b/apps/argus/argus/services/persistence_service.py @@ -1,6 +1,6 @@ import logging from services.session_service import SessionService -from dtos import message_type_to_model +from dtos import proto_to_model class PersistenceService: @@ -10,10 +10,10 @@ def __init__(self, session_service: SessionService): def transform_protobuf_to_model(self, proto): message_type = type(proto) - model_class = message_type_to_model.get(message_type) - if model_class is None: + transformer = proto_to_model.get(message_type) + if transformer is None: raise ValueError(f"No model class found for message type {message_type}") - return model_class.from_protobuf(proto) + return transformer(proto) def store_protobuf(self, proto): model = self.transform_protobuf_to_model(proto) diff --git a/apps/argus/argus/services/protobuf_serial_service.py b/apps/argus/argus/services/protobuf_serial_service.py index e705193..008244f 100644 --- a/apps/argus/argus/services/protobuf_serial_service.py +++ b/apps/argus/argus/services/protobuf_serial_service.py @@ -1,5 +1,5 @@ import serial -from argus.envelope_pb2 import Envelope +from messages.argus.envelope_pb2 import Envelope import logging from services.persistence_service import PersistenceService diff --git a/apps/grafana/.DS_Store b/apps/grafana/.DS_Store deleted file mode 100644 index 5237c3d..0000000 Binary files a/apps/grafana/.DS_Store and /dev/null differ diff --git a/apps/grafana/dashboards/argus/Pressure Readings-1761956768648.json b/apps/grafana/dashboards/argus/Pressure Readings-1761956768648.json new file mode 100644 index 0000000..ffdb82d --- /dev/null +++ b/apps/grafana/dashboards/argus/Pressure Readings-1761956768648.json @@ -0,0 +1,817 @@ +{ + "__inputs": [ + { + "name": "DS_UOROCKETRY-POSTGRES", + "label": "uorocketry-postgres", + "description": "", + "type": "datasource", + "pluginId": "grafana-postgresql-datasource", + "pluginName": "PostgreSQL" + } + ], + "__elements": {}, + "__requires": [ + { + "type": "grafana", + "id": "grafana", + "name": "Grafana", + "version": "12.3.0-18481575143" + }, + { + "type": "datasource", + "id": "grafana-postgresql-datasource", + "name": "PostgreSQL", + "version": "12.3.0-18481575143" + }, + { + "type": "panel", + "id": "stat", + "name": "Stat", + "version": "" + }, + { + "type": "panel", + "id": "timeseries", + "name": "Time series", + "version": "" + } + ], + "annotations": { + "list": [ + { + "builtIn": 1, + "datasource": { + "type": "grafana", + "uid": "-- Grafana --" + }, + "enable": true, + "hide": true, + "iconColor": "rgba(0, 211, 255, 1)", + "name": "Annotations & Alerts", + "type": "dashboard" + } + ] + }, + "editable": true, + "fiscalYearStartMonth": 0, + "graphTooltip": 0, + "id": null, + "links": [], + "liveNow": true, + "panels": [ + { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": true, + "axisColorMode": "text", + "axisLabel": "Pressure (PSI)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pressurepsi" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 0 + }, + "id": 2, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0-18481575143", + "repeat": "pressure_channel", + "repeatDirection": "v", + "targets": [ + { + "dataset": "uorocketry", + "editorMode": "code", + "format": "time_series", + "rawQuery": true, + "rawSql": "SELECT \n \"pressure\" AS \"Pressure\",\n \"stored_at\" AS \"time\" \nFROM pressure_readings \nWHERE \n host_session_id = $host_session_id\n AND adc_device = '0'\n AND pressure_channel = $pressure_channel \nORDER BY stored_at DESC", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"value\"", + "parameters": [ + { + "name": "compensated_temperature", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "alias": "\"time\"", + "parameters": [ + { + "name": "\"timestamp\"", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "thermocouple_readings", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + } + } + ], + "title": "ADC 0 Channel ${pressure_channel}", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": true, + "axisColorMode": "text", + "axisLabel": "Pressure (PSI)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "pressurepsi" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 9, + "y": 0 + }, + "id": 4, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0-18481575143", + "repeat": "pressure_channel", + "repeatDirection": "v", + "targets": [ + { + "dataset": "uorocketry", + "editorMode": "code", + "format": "time_series", + "rawQuery": true, + "rawSql": "SELECT \n \"pressure\" AS \"Pressure\",\n \"stored_at\" AS \"time\" \nFROM pressure_readings \nWHERE \n host_session_id = $host_session_id \n AND adc_device = '1'\n AND pressure_channel = $pressure_channel \nORDER BY stored_at DESC", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"value\"", + "parameters": [ + { + "name": "compensated_temperature", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "alias": "\"time\"", + "parameters": [ + { + "name": "\"timestamp\"", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "thermocouple_readings", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + } + } + ], + "title": "ADC 1 Channel ${pressure_channel}", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "fieldConfig": { + "defaults": { + "color": { + "mode": "thresholds" + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + } + }, + "overrides": [] + }, + "gridPos": { + "h": 3, + "w": 2, + "x": 18, + "y": 0 + }, + "id": 1, + "options": { + "colorMode": "value", + "graphMode": "area", + "justifyMode": "auto", + "orientation": "auto", + "percentChangeColorMode": "standard", + "reduceOptions": { + "calcs": [ + "lastNotNull" + ], + "fields": "", + "values": false + }, + "showPercentChange": false, + "textMode": "auto", + "wideLayout": true + }, + "pluginVersion": "12.3.0-18481575143", + "targets": [ + { + "dataset": "uorocketry", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "editorMode": "code", + "format": "table", + "rawQuery": true, + "rawSql": "SELECT\n MAX(host_session_id)\nFROM\n pressure_readings \nLIMIT\n 50", + "refId": "A", + "sql": { + "columns": [ + { + "name": "MAX", + "parameters": [ + { + "name": "host_session_id", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "thermocouple_readings" + } + ], + "title": "Latest Session Number", + "type": "stat" + }, + { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": true, + "axisColorMode": "text", + "axisLabel": "Voltage (mV)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mvolt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 0, + "y": 32 + }, + "id": 3, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0-18481575143", + "repeat": "pressure_channel", + "repeatDirection": "v", + "targets": [ + { + "dataset": "uorocketry", + "editorMode": "code", + "format": "time_series", + "rawQuery": true, + "rawSql": "SELECT \n \"voltage\" AS \"Voltage\",\n \"stored_at\" AS \"time\" \nFROM pressure_readings \nWHERE \n host_session_id = $host_session_id \n AND adc_device = '0'\n AND pressure_channel = $pressure_channel \nORDER BY stored_at DESC", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"value\"", + "parameters": [ + { + "name": "compensated_temperature", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "alias": "\"time\"", + "parameters": [ + { + "name": "\"timestamp\"", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "thermocouple_readings", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + } + } + ], + "title": "Voltage: ADC 0 Channel ${pressure_channel}", + "type": "timeseries" + }, + { + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + }, + "fieldConfig": { + "defaults": { + "color": { + "fixedColor": "green", + "mode": "palette-classic-by-name" + }, + "custom": { + "axisBorderShow": true, + "axisCenteredZero": true, + "axisColorMode": "text", + "axisLabel": "Voltage (mV)", + "axisPlacement": "auto", + "barAlignment": 0, + "barWidthFactor": 0.6, + "drawStyle": "line", + "fillOpacity": 0, + "gradientMode": "none", + "hideFrom": { + "legend": false, + "tooltip": false, + "viz": false + }, + "insertNulls": false, + "lineInterpolation": "linear", + "lineStyle": { + "fill": "solid" + }, + "lineWidth": 1, + "pointSize": 5, + "scaleDistribution": { + "type": "linear" + }, + "showPoints": "auto", + "showValues": false, + "spanNulls": false, + "stacking": { + "group": "A", + "mode": "none" + }, + "thresholdsStyle": { + "mode": "off" + } + }, + "mappings": [], + "thresholds": { + "mode": "absolute", + "steps": [ + { + "color": "green", + "value": 0 + }, + { + "color": "red", + "value": 80 + } + ] + }, + "unit": "mvolt" + }, + "overrides": [] + }, + "gridPos": { + "h": 8, + "w": 9, + "x": 9, + "y": 32 + }, + "id": 5, + "options": { + "legend": { + "calcs": [ + "lastNotNull", + "min", + "max" + ], + "displayMode": "table", + "placement": "bottom", + "showLegend": true + }, + "tooltip": { + "hideZeros": false, + "mode": "single", + "sort": "none" + } + }, + "pluginVersion": "12.3.0-18481575143", + "repeat": "pressure_channel", + "repeatDirection": "v", + "targets": [ + { + "dataset": "uorocketry", + "editorMode": "code", + "format": "time_series", + "rawQuery": true, + "rawSql": "SELECT \n \"voltage\" AS \"Voltage\",\n \"stored_at\" AS \"time\" \nFROM pressure_readings \nWHERE \n host_session_id = $host_session_id \n AND adc_device = '1'\n AND pressure_channel = $pressure_channel \nORDER BY stored_at DESC", + "refId": "A", + "sql": { + "columns": [ + { + "alias": "\"value\"", + "parameters": [ + { + "name": "compensated_temperature", + "type": "functionParameter" + } + ], + "type": "function" + }, + { + "alias": "\"time\"", + "parameters": [ + { + "name": "\"timestamp\"", + "type": "functionParameter" + } + ], + "type": "function" + } + ], + "groupBy": [ + { + "property": { + "type": "string" + }, + "type": "groupBy" + } + ], + "limit": 50 + }, + "table": "thermocouple_readings", + "datasource": { + "type": "grafana-postgresql-datasource", + "uid": "${DS_UOROCKETRY-POSTGRES}" + } + } + ], + "title": "Voltage: ADC 1 Channel ${pressure_channel}", + "type": "timeseries" + } + ], + "schemaVersion": 42, + "tags": [ + "argus", + "pressure" + ], + "templating": { + "list": [ + { + "allowCustomValue": true, + "current": { + "text": "15", + "value": "15" + }, + "description": "", + "label": "Host Session Id", + "name": "host_session_id", + "options": [], + "query": "", + "type": "custom" + }, + { + "allowCustomValue": false, + "current": { + "text": [ + "0", + "1", + "2", + "3" + ], + "value": [ + "0", + "1", + "2", + "3" + ] + }, + "description": "", + "label": "Pressure Channel", + "multi": true, + "name": "pressure_channel", + "options": [ + { + "selected": true, + "text": "0", + "value": "0" + }, + { + "selected": true, + "text": "1", + "value": "1" + }, + { + "selected": true, + "text": "2", + "value": "2" + }, + { + "selected": true, + "text": "3", + "value": "3" + } + ], + "query": "0,1,2,3", + "type": "custom" + } + ] + }, + "time": { + "from": "now-6h", + "to": "now" + }, + "timepicker": { + "refresh_intervals": [ + "5s", + "10s", + "30s", + "1m", + "5m", + "15m", + "30m", + "1h", + "2h" + ] + }, + "timezone": "utc", + "title": "Pressure Readings", + "uid": "asvas22", + "version": 5, + "weekStart": "" +} \ No newline at end of file diff --git a/boards/argus/Cargo.toml b/boards/argus/Cargo.toml index 03b723f..0e6f6a4 100644 --- a/boards/argus/Cargo.toml +++ b/boards/argus/Cargo.toml @@ -85,7 +85,6 @@ serde = { workspace = true, features = ["derive", "serde_derive"] } smlang = { workspace = true } static_cell = "2" stm32-fmc = "0.3.0" -straingauge-converter = { path = "../../common/straingauge-converter" } strum = { version = "0.27.2", default-features = false, features = ["derive"] } [[test]] diff --git a/boards/argus/src/lib.rs b/boards/argus/src/lib.rs index 0fa1aab..b7b3724 100644 --- a/boards/argus/src/lib.rs +++ b/boards/argus/src/lib.rs @@ -4,10 +4,12 @@ pub mod adc; pub mod linear_transformation; +pub mod node; pub mod sd; pub mod serial; pub mod session; pub mod state_machine; +pub mod strain; pub mod utils; #[cfg(feature = "temperature")] diff --git a/boards/argus/src/linear_transformation/README.md b/boards/argus/src/linear_transformation/README.md new file mode 100644 index 0000000..6db9bd4 --- /dev/null +++ b/boards/argus/src/linear_transformation/README.md @@ -0,0 +1,5 @@ +# Linear Transformation +This service handles loading/storing linear transformations that are applied to any reading. (Could be temperature, pressure, strain, etc.) + +It takes some form of data persistence service (currently only `sd_card_service` supported), and creates a file to which it reads/writes the scale and offset that needs to be applied for a given device and its channels. + diff --git a/boards/argus/src/main.rs b/boards/argus/src/main.rs index 04f4216..8bd1ea2 100644 --- a/boards/argus/src/main.rs +++ b/boards/argus/src/main.rs @@ -2,10 +2,8 @@ #![no_std] #![no_main] -// #[cfg(not(any(feature = "pressure", feature = "temperature", feature = "strain")))] -// compile_error!( -// "You must enable exactly one of the features: 'pressure', 'temperature', or 'strain'." -// ); +#[cfg(not(any(feature = "pressure", feature = "temperature", feature = "strain")))] +compile_error!("You must enable exactly one of the features: 'pressure', 'temperature', or 'strain'."); use argus::adc::service::{AdcConfig, AdcService}; use argus::adc::types::AdcDevice; @@ -45,6 +43,9 @@ static TEMPERATURE_SERVICE: StaticCell>> = StaticCell::new(); +#[cfg(feature = "strain")] +static STRAIN_SERVICE: StaticCell>> = StaticCell::new(); + #[embassy_executor::main] async fn main(spawner: Spawner) { info!("Starting up..."); @@ -141,17 +142,57 @@ async fn main(spawner: Spawner) { use argus::pressure::service::PressureService; use argus::pressure::tasks; - let pressure_service = PRESSURE_SERVICE.init(AsyncMutex::new(PressureService::new(adc_service, sd_card_service, serial_service))); + let pressure_service = PRESSURE_SERVICE.init(AsyncMutex::new(PressureService::new( + adc_service, + sd_card_service, + serial_service, + session_service, + ))); // Setup the pressure service before starting the tasks pressure_service.lock().await.setup().await.unwrap(); - spawner.must_spawn(tasks::measure_pressure( + spawner.must_spawn(tasks::measure_pressure_sensors( + StateMachineWorker::new(state_machine_orchestrator), + pressure_service, + )); + spawner.must_spawn(tasks::measure_manifold_temperature( StateMachineWorker::new(state_machine_orchestrator), pressure_service, )); spawner.must_spawn(tasks::log_measurements( StateMachineWorker::new(state_machine_orchestrator), + serial_service, + sd_card_service, + session_service, + )); + spawner.must_spawn(tasks::calibrate_pressure_sensors( + StateMachineWorker::new(state_machine_orchestrator), + pressure_service, + )); + } + + // Spawn tasks needed for strain board + #[cfg(feature = "strain")] + { + // Imported inside the block to avoid unused leaking the import when the feature is not enabled + use argus::strain::service::StrainService; + use argus::strain::tasks; + + let strain_service = STRAIN_SERVICE.init(AsyncMutex::new(StrainService::new( + adc_service, + sd_card_service, + serial_service, + session_service, + ))); + + // Setup the strain service before starting the tasks + strain_service.lock().await.setup().await.unwrap(); + + spawner.must_spawn(tasks::measure_strain(StateMachineWorker::new(state_machine_orchestrator), strain_service)); + spawner.must_spawn(tasks::log_measurements( + StateMachineWorker::new(state_machine_orchestrator), + serial_service, sd_card_service, session_service, )); diff --git a/boards/argus/src/node/mod.rs b/boards/argus/src/node/mod.rs new file mode 100644 index 0000000..2999b0e --- /dev/null +++ b/boards/argus/src/node/mod.rs @@ -0,0 +1,15 @@ +use messages::argus::envelope::{Node, NodeType}; + +// The identifier for this board (node) with some params set at compile time. +pub const CURRENT_NODE: Node = Node { + // SHOULD DO: inject from environment variable once we have a way to convert option_env to i32 in no_std environments. + id: None, + + // Set node type based on enabled feature. + #[cfg(feature = "temperature")] + r#type: NodeType::ArgusTemperature as i32, + #[cfg(feature = "pressure")] + r#type: NodeType::ArgusPressure as i32, + #[cfg(feature = "strain")] + r#type: NodeType::ArgusStrain as i32, +}; diff --git a/boards/argus/src/pressure/calibration.rs b/boards/argus/src/pressure/calibration.rs new file mode 100644 index 0000000..a7e0961 --- /dev/null +++ b/boards/argus/src/pressure/calibration.rs @@ -0,0 +1,151 @@ +use core::fmt::Debug; +use core::str::FromStr; + +use defmt::Format; +use heapless::{format, String, Vec}; +use strum::EnumCount; + +use crate::adc::types::AdcDevice; +use crate::linear_transformation::types::LinearTransformation; +use crate::pressure::config::MAX_CALIBRATION_DATA_POINTS; +use crate::pressure::service::PressureService; +use crate::pressure::types::{PressureChannel, PressureServiceError}; + +// Calibration logic has been separated into its own file for clarity +impl PressureService { + pub async fn calibrate(&mut self) -> Result<(), PressureServiceError> { + // Prompt for ADC index + let adc_index: usize = self.prompt("Starting pressure calibration. Enter ADC index (Starts from 0):\"\n").await?; + if adc_index >= AdcDevice::COUNT { + self.send_message("Invalid ADC index.\n").await?; + return Ok(()); + } + let adc = AdcDevice::from(adc_index); + + // Prompt for channel + let channel_index: usize = self.prompt("Enter pressure channel index (Starts from 0):\"\n").await?; + if channel_index >= PressureChannel::COUNT { + self.send_message("Invalid channel index.\n").await?; + return Ok(()); + } + let channel = PressureChannel::from(channel_index); + + { + let message: String<64> = + format!("Calibrating ADC {}, Channel {}\n", adc_index, channel_index).map_err(|_| PressureServiceError::FormatError)?; + self.send_message(message.as_str()).await?; + } + + // Prompt for number of data points + let data_points_count: u8 = self.prompt("Enter number of data points to use for the linear fit:\n").await?; + if data_points_count < 2 { + self.send_message("Minimum 2 data points is required.\n").await?; + return Ok(()); + } + if data_points_count > MAX_CALIBRATION_DATA_POINTS as u8 { + let error_message: String<64> = + format!("Too many data points. Maximum is {}.\n", MAX_CALIBRATION_DATA_POINTS).map_err(|_| PressureServiceError::FormatError)?; + self.send_message(error_message.as_str()).await?; + return Ok(()); + } + + // Deregister any existing transformation for this channel + self.linear_transformation_service.deregister_transformation(adc, channel); + + // Start collecting data points + let mut calibration_data_points: Vec = Vec::new(); + for data_point_index in 0..data_points_count { + let message: String<64> = + format!("Data Point #{}. Enter expected value:\n", data_point_index + 1).map_err(|_| PressureServiceError::FormatError)?; + let expected_pressure: f64 = self.prompt(message.as_str()).await?; + let measured_pressure: f64 = self.read_pressure(adc, channel).await?.pressure; + let data_point = CalibrationDataPoint { + expected_pressure, + measured_pressure, + }; + calibration_data_points.push(data_point).unwrap(); // Safe due to prior checks + + let confirmation_message: String<64> = format!("Expected = {:.2}, Measured = {:.2}\n", expected_pressure, measured_pressure) + .map_err(|_| PressureServiceError::FormatError)?; + self.send_message(confirmation_message.as_str()).await?; + } + + // Perform Ordinary Least Squares Fit + let transformation = self.run_ordinary_least_squares_fit(adc, channel, calibration_data_points); + let result_message: String<128> = format!( + "Ordinary Least Squares Fit complete. Scale: {:.6}, Offset: {:.2} °C\n", + transformation.scale, transformation.offset + ) + .map_err(|_| PressureServiceError::FormatError)?; + self.send_message(result_message.as_str()).await?; + + // Update calibration for the channel + self.linear_transformation_service.save_transformation(transformation).await?; + Ok(()) + } + + fn run_ordinary_least_squares_fit( + &self, + adc: AdcDevice, + channel: PressureChannel, + data_points: Vec, + ) -> LinearTransformation { + // Keeping all types as f64 + let data_points_count: f64 = data_points.len() as f64; + + // Accumulate sums + let mut sum_x: f64 = 0.0; // measured + let mut sum_y: f64 = 0.0; // expected + let mut sum_xx: f64 = 0.0; // measured^2 + let mut sum_xy: f64 = 0.0; // measured * expected + + for data_point in data_points.iter() { + let x = data_point.measured_pressure; + let y = data_point.expected_pressure; + sum_x += x; + sum_y += y; + sum_xx += x * x; + sum_xy += x * y; + } + + // Denominator for slope (variance term) + let denom = data_points_count * sum_xx - sum_x * sum_x; + let scale = (data_points_count * sum_xy - sum_x * sum_y) / denom; + let offset = (sum_y - scale * sum_x) / data_points_count; + + LinearTransformation { adc, channel, scale, offset } + } + + async fn prompt( + &mut self, + prompt: &str, + ) -> Result + where + T: FromStr, + ::Err: core::fmt::Debug, { + let mut serial_service = self.serial_service.lock().await; + let mut input = String::<256>::new(); + serial_service.write_str(prompt).await?; + serial_service.read_line(&mut input).await?; + Ok(input.trim().parse().map_err(|_| PressureServiceError::FormatError)?) + } + + async fn send_message( + &mut self, + message: &str, + ) -> Result<(), PressureServiceError> { + let mut serial_service = self.serial_service.lock().await; + serial_service.write_str(message).await?; + Ok(()) + } +} + +// Represents a single calibration data point used to perform a linear regression +#[derive(Debug, Clone, Copy, Format)] +pub struct CalibrationDataPoint { + // Expected pressure value in degrees Celsius measured by the calibration instrument + pub expected_pressure: f64, + + // Value measured by the ADC + pub measured_pressure: f64, +} diff --git a/boards/argus/src/pressure/config.rs b/boards/argus/src/pressure/config.rs index aebb574..f1dd1b4 100644 --- a/boards/argus/src/pressure/config.rs +++ b/boards/argus/src/pressure/config.rs @@ -1,9 +1,15 @@ // Size of the queue used to send pressure readings from the pressure service to the SD card service pub const PRESSURE_READING_QUEUE_SIZE: usize = 16; -// Maximum number of calibration data points allowed to be collected during a calibration session per thermocouple channel +// Maximum number of calibration data points allowed to be collected during a calibration session per pressure channel pub const MAX_CALIBRATION_DATA_POINTS: usize = 10; -// File name used to read/write linear transformations that applied to thermocouple readings to/from the SD card +// File name used to read/write linear transformations that applied to pressure readings to/from the SD card // Linear transformations are stored in CSV format pub const LINEAR_TRANSFORMATIONS_FILE_NAME: &str = "t_pres.csv"; // Cannot be longer than 12 characters + +// Resistance of the NTC at 25 °C. +pub const NTC_RESISTANCE_AT_25C: f32 = 10000.0; // Ohms + +// Measure NTCs at a slower interval than the pressures +pub const NTC_MEASUREMENT_INTERVAL: u64 = 5000; // milliseconds diff --git a/boards/argus/src/pressure/mod.rs b/boards/argus/src/pressure/mod.rs index 80faf82..7c1f41a 100644 --- a/boards/argus/src/pressure/mod.rs +++ b/boards/argus/src/pressure/mod.rs @@ -1,3 +1,4 @@ +pub mod calibration; pub mod config; pub mod service; pub mod tasks; diff --git a/boards/argus/src/pressure/service.rs b/boards/argus/src/pressure/service.rs index beaef5a..bb85c93 100644 --- a/boards/argus/src/pressure/service.rs +++ b/boards/argus/src/pressure/service.rs @@ -1,3 +1,4 @@ +use defmt::error; use embassy_time::Instant; use strum::EnumCount; @@ -9,6 +10,7 @@ use crate::pressure::config::LINEAR_TRANSFORMATIONS_FILE_NAME; use crate::pressure::types::{PressureChannel, PressureReading, PressureReadingQueue, PressureServiceError}; use crate::sd::service::SDCardService; use crate::serial::service::SerialService; +use crate::session::service::SessionService; use crate::utils::types::AsyncMutex; // A channel for buffering the pressure readings and decoupling the logging to sd task from the measurement task @@ -19,8 +21,9 @@ pub struct PressureService { pub adc_service: &'static AsyncMutex>, pub sd_card_service: &'static AsyncMutex, pub serial_service: &'static AsyncMutex, + pub session_service: &'static AsyncMutex, - // Linear transformation service to apply error corrections obtained from calibration to raw readings + // Linear transformations that are applied on top of the raw readings for each ADC and channel pub linear_transformation_service: LinearTransformationService, } @@ -29,11 +32,13 @@ impl PressureService { adc_service: &'static AsyncMutex>, sd_card_service: &'static AsyncMutex, serial_service: &'static AsyncMutex, + session_service: &'static AsyncMutex, ) -> Self { Self { adc_service, sd_card_service, serial_service, + session_service, linear_transformation_service: LinearTransformationService::new(sd_card_service, LINEAR_TRANSFORMATIONS_FILE_NAME), } } @@ -49,18 +54,21 @@ impl PressureService { driver.apply_configurations().await?; } - self.linear_transformation_service.load_transformations().await?; + match self.linear_transformation_service.load_transformations().await { + Err(e) => error!("Failed to load linear transformations: {:?}", e), + _ => {} + } Ok(()) } - pub async fn read_pressure_sensor( + pub async fn read_pressure( &mut self, adc: AdcDevice, channel: PressureChannel, ) -> Result { let mut adc_service = self.adc_service.lock().await; - // Get the respective "adc channel" pair for the "thermocouple channel" + // Get the respective "adc channel" pair for the "pressure channel" let (positive_channel, negative_channel) = channel.to_analog_input_channel_pair(); // Read the voltage from the ADC in millivolts @@ -68,11 +76,17 @@ impl PressureService { .read_differential(positive_channel, negative_channel) .await? * 1000.0; // Convert to millivolts + // Apply any linear transformations to get the pressure in psi + let pressure = self.linear_transformation_service.apply_transformation(adc, channel, voltage as f64); + let pressure_reading = PressureReading { - timestamp: Instant::now().as_millis(), + local_session: self.session_service.lock().await.current_session.clone(), + adc_device: adc, + pressure_channel: channel, + recorded_at: Instant::now().as_millis(), voltage, - pressure: 0.0, // Placeholder, actual pressure calculation can be added later - temperature: 0.0, // Placeholder, actual temperature calculation can be added + pressure, + temperature: 0.0, // SHOULD DO: replace once NTC temperature measurement is implemented }; Ok(pressure_reading) diff --git a/boards/argus/src/pressure/tasks/calibrate_pressure_sensors.rs b/boards/argus/src/pressure/tasks/calibrate_pressure_sensors.rs new file mode 100644 index 0000000..5a5ca5c --- /dev/null +++ b/boards/argus/src/pressure/tasks/calibrate_pressure_sensors.rs @@ -0,0 +1,40 @@ +use defmt::error; +use embassy_executor::task; +use embassy_futures::yield_now; +use strum::EnumCount; + +use crate::adc::types::AdcDevice; +use crate::pressure::service::PressureService; +use crate::state_machine::service::StateMachineWorker; +use crate::state_machine::types::States; +use crate::utils::types::AsyncMutex; + +#[task] +pub async fn calibrate_pressure_sensors( + mut worker: StateMachineWorker, + pressure_service_mutex: &'static AsyncMutex>, +) { + worker + .run_while(&[States::Calibrating], async |_| -> Result<(), ()> { + let mut pressure_service = pressure_service_mutex.lock().await; + // SHOULD DO: Refresh temperature readings for all ADCs once NTC reading is implemented + // for adc_index in 0..AdcDevice::COUNT { + // let adc = AdcDevice::from(adc_index); + // match pressure_service.refresh_temperature_readings(adc).await { + // Err(e) => { + // error!("Failed to read NTC on {:?} during calibration: {:?}", adc, e); + // } + // _ => {} + // } + // } + + match pressure_service.calibrate().await { + Ok(_) => {} + Err(e) => error!("Pressure calibration failed: {:?}", e), + } + yield_now().await; + Ok(()) + }) + .await + .unwrap(); +} diff --git a/boards/argus/src/pressure/tasks/log_measurements.rs b/boards/argus/src/pressure/tasks/log_measurements.rs index 8a15b52..d7de373 100644 --- a/boards/argus/src/pressure/tasks/log_measurements.rs +++ b/boards/argus/src/pressure/tasks/log_measurements.rs @@ -1,14 +1,16 @@ use csv::SerializeCSV; +use defmt::info; use embassy_executor::task; use heapless::format; +use messages::argus::envelope::envelope::Message; use strum::EnumCount; use crate::adc::types::AdcDevice; use crate::pressure::service::PRESSURE_READING_QUEUE; -use crate::pressure::types::pressure_reading::PressureReading; -use crate::pressure::types::PressureChannel; +use crate::pressure::types::{PressureChannel, PressureReading}; use crate::sd::service::SDCardService; use crate::sd::types::{FileName, OperationScope}; +use crate::serial::service::SerialService; use crate::session::service::SessionService; use crate::state_machine::service::StateMachineWorker; use crate::state_machine::types::States; @@ -18,17 +20,29 @@ use crate::utils::types::AsyncMutex; #[task] pub async fn log_measurements( mut worker: StateMachineWorker, + serial_service_mutex: &'static AsyncMutex, sd_card_service_mutex: &'static AsyncMutex, session_service: &'static AsyncMutex, ) { - initialize_csv_files(sd_card_service_mutex, session_service).await; + worker + .run_once(&[States::Recording], async |_| -> Result<(), ()> { + initialize_csv_files(sd_card_service_mutex, session_service).await; + Ok(()) + }) + .await + .unwrap(); worker .run_while(&[States::Recording], async |_| -> Result<(), ()> { - let (adc, channel, pressure_reading) = PRESSURE_READING_QUEUE.receive().await; - let path = get_path_from_adc_and_channel(adc as usize, channel as usize); + let pressure_reading = PRESSURE_READING_QUEUE.receive().await; + let path = get_path_from_adc_and_channel(pressure_reading.adc_device as usize, pressure_reading.pressure_channel as usize); let line = pressure_reading.to_csv_line(); SDCardService::enqueue_write(OperationScope::CurrentSession, path, line).await; + let _ = serial_service_mutex + .lock() + .await + .write_envelope_message(Message::PressureReading(pressure_reading.to_protobuf())) + .await; Ok(()) }) .await @@ -43,6 +57,7 @@ async fn initialize_csv_files( // Ensure session is set. Ignore if it errors like SD card not mounted, etc. let _ = session_service.lock().await.ensure_session().await; + info!("Initializing CSV files for measurement logging."); let mut sd_card_service = sd_card_service_mutex.lock().await; for adc_index in 0..AdcDevice::COUNT { for channel in 0..PressureChannel::COUNT { @@ -58,5 +73,5 @@ fn get_path_from_adc_and_channel( adc_index: usize, channel: usize, ) -> FileName { - format!("P_{}_{}.csv", adc_index, channel).unwrap() as FileName + format!("T_{}_{}.csv", adc_index, channel).unwrap() as FileName } diff --git a/boards/argus/src/pressure/tasks/measure_manifold_temperature.rs b/boards/argus/src/pressure/tasks/measure_manifold_temperature.rs new file mode 100644 index 0000000..c813b99 --- /dev/null +++ b/boards/argus/src/pressure/tasks/measure_manifold_temperature.rs @@ -0,0 +1,39 @@ +use embassy_executor::task; +use embassy_time::Timer; +use strum::EnumCount; + +use crate::adc::types::AdcDevice; +use crate::pressure::config::NTC_MEASUREMENT_INTERVAL; +use crate::pressure::service::PressureService; +use crate::state_machine::service::StateMachineWorker; +use crate::state_machine::types::States; +use crate::utils::types::AsyncMutex; + +// Task that iterates through the ADCs and measures the NTCs at a slower interval than the pressures being read +// We don't need that frequent readings for the NTCs +#[task] +pub async fn measure_manifold_temperature( + mut worker: StateMachineWorker, + _pressure_service_mutex: &'static AsyncMutex>, +) { + worker + .run_while(&[States::Recording, States::Calibrating], async |_| -> Result<(), ()> { + // SHOULD DO: Refresh temperature readings for all ADCs once NTC reading is implemented + // for adc_index in 0..AdcDevice::COUNT { + // let adc = AdcDevice::from(adc_index); + // match pressure_service_mutex.lock().await.refresh_rtd_reading(adc).await { + // Err(e) => { + // error!("Failed to read NTC on {:?}: {:?}", adc, e); + // } + // _ => {} + // } + // } + + // Delay the NTC measurement because it's not as critical as the pressures. We just need to read every once in a while + Timer::after_millis(NTC_MEASUREMENT_INTERVAL).await; + + Ok(()) + }) + .await + .unwrap(); +} diff --git a/boards/argus/src/pressure/tasks/measure_pressure.rs b/boards/argus/src/pressure/tasks/measure_pressure_sensors.rs similarity index 71% rename from boards/argus/src/pressure/tasks/measure_pressure.rs rename to boards/argus/src/pressure/tasks/measure_pressure_sensors.rs index 379a1df..49ceed7 100644 --- a/boards/argus/src/pressure/tasks/measure_pressure.rs +++ b/boards/argus/src/pressure/tasks/measure_pressure_sensors.rs @@ -1,5 +1,6 @@ -use defmt::{debug, error}; +use defmt::{error, info}; use embassy_executor::task; +use embassy_futures::yield_now; use strum::EnumCount; use crate::adc::types::AdcDevice; @@ -11,23 +12,21 @@ use crate::utils::types::AsyncMutex; // Task that iterates through the ADCs and channels, measures the pressure, and enqueues the readings to a channel #[task] -pub async fn measure_pressure( +pub async fn measure_pressure_sensors( mut worker: StateMachineWorker, pressure_service_mutex: &'static AsyncMutex>, ) { worker .run_while(&[States::Recording], async |_| -> Result<(), ()> { - let mut pressure_service = pressure_service_mutex.lock().await; - for adc_index in 0..AdcDevice::COUNT { for channel_index in 0..PressureChannel::COUNT { let adc = AdcDevice::from(adc_index); let channel = PressureChannel::from(channel_index); - let data = pressure_service.read_pressure_sensor(adc, channel).await; + let data = pressure_service_mutex.lock().await.read_pressure(adc, channel).await; match data { - Ok(data) => { - debug!("ADC {} Channel {}: {}", adc, channel, data); - PRESSURE_READING_QUEUE.send((adc, channel, data)).await; + Ok(pressure_reading) => { + info!("{}", pressure_reading); + PRESSURE_READING_QUEUE.send(pressure_reading).await; } Err(err) => { error!("Error reading ADC {} Channel {}: {:?}", adc, channel, err); @@ -36,6 +35,9 @@ pub async fn measure_pressure( } } } + + // Yield to allow other tasks to run, especially the NTC measurement task + yield_now().await; Ok(()) }) .await diff --git a/boards/argus/src/pressure/tasks/mod.rs b/boards/argus/src/pressure/tasks/mod.rs index 9103d4f..c082898 100644 --- a/boards/argus/src/pressure/tasks/mod.rs +++ b/boards/argus/src/pressure/tasks/mod.rs @@ -1,5 +1,9 @@ +mod calibrate_pressure_sensors; mod log_measurements; -mod measure_pressure; +mod measure_manifold_temperature; +mod measure_pressure_sensors; +pub use calibrate_pressure_sensors::*; pub use log_measurements::*; -pub use measure_pressure::*; +pub use measure_manifold_temperature::*; +pub use measure_pressure_sensors::*; diff --git a/boards/argus/src/pressure/types/error.rs b/boards/argus/src/pressure/types/error.rs index 4256147..4878757 100644 --- a/boards/argus/src/pressure/types/error.rs +++ b/boards/argus/src/pressure/types/error.rs @@ -10,4 +10,5 @@ pub enum PressureServiceError { AdcError(AdcError), UsartError(UsartError), SdCardError(SdCardError), + FormatError, } diff --git a/boards/argus/src/pressure/types/pressure_channel.rs b/boards/argus/src/pressure/types/pressure_channel.rs index ca8c2ee..ef145c0 100644 --- a/boards/argus/src/pressure/types/pressure_channel.rs +++ b/boards/argus/src/pressure/types/pressure_channel.rs @@ -1,9 +1,11 @@ use defmt::Format; +use messages::argus::pressure::pressure_channel::PressureChannel as PressureChannelProtobuf; use serde::{Deserialize, Serialize}; use strum::EnumCount; use crate::adc::driver::types::AnalogChannel; +#[repr(usize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Format, Serialize, Deserialize, EnumCount, Default)] pub enum PressureChannel { #[default] @@ -36,4 +38,13 @@ impl PressureChannel { PressureChannel::Channel4 => (AnalogChannel::AIN6, AnalogChannel::AIN7), } } + + pub fn to_protobuf(&self) -> PressureChannelProtobuf { + match self { + PressureChannel::Channel1 => PressureChannelProtobuf::Channel1, + PressureChannel::Channel2 => PressureChannelProtobuf::Channel2, + PressureChannel::Channel3 => PressureChannelProtobuf::Channel3, + PressureChannel::Channel4 => PressureChannelProtobuf::Channel4, + } + } } diff --git a/boards/argus/src/pressure/types/pressure_reading.rs b/boards/argus/src/pressure/types/pressure_reading.rs index e9da90f..c2a0cac 100644 --- a/boards/argus/src/pressure/types/pressure_reading.rs +++ b/boards/argus/src/pressure/types/pressure_reading.rs @@ -2,31 +2,46 @@ use core::str::FromStr; use csv::SerializeCSV; use defmt::Format; +use messages::argus::pressure::pressure_reading::PressureReading as PressureReadingProtobuf; use serde::{Deserialize, Serialize}; +use crate::adc::types::AdcDevice; +use crate::pressure::types::PressureChannel; use crate::sd::config::MAX_LINE_LENGTH; use crate::sd::types::Line; // Represents a single pressure reading from a pressure channel #[derive(Debug, Clone, Copy, Format, Serialize, Deserialize)] pub struct PressureReading { + // Local session from the device that took the reading + pub local_session: Option, + + // ADC device from which the reading was taken + pub adc_device: AdcDevice, + + // Identifier for the pressure within the ADC device + pub pressure_channel: PressureChannel, + // Milliseconds since the board's epoch when the reading was recorded - pub timestamp: u64, + pub recorded_at: u64, - // pressure voltage difference measured in millivolts + // Voltage difference measured at the pressure sensor wheatstone bridge in millivolts pub voltage: f32, - // pressure calculated in psi - pub pressure: f32, + // Pressure reading in psi + pub pressure: f64, - // manifold temperature at which this pressure reading was taken - pub temperature: f32, + // Temperature of the manifold from the NTC resistor at the time of the recording in degrees Celsius + pub temperature: f64, } impl SerializeCSV for PressureReading { fn get_csv_header() -> Line { Line::from_str( - "Timestamp (ms),\ + "Local Session #,\ + ADC Device,\ + Pressure Channel,\ + Timestamp (ms),\ Voltage (mV),\ Pressure (psi),\ Manifold Temperature (C)", @@ -34,3 +49,18 @@ impl SerializeCSV for PressureReading { .unwrap() } } + +impl PressureReading { + // Convert to the protobuf representation + pub fn to_protobuf(&self) -> PressureReadingProtobuf { + PressureReadingProtobuf { + local_session: self.local_session, + adc_device: self.adc_device.to_protobuf() as i32, + pressure_channel: self.pressure_channel.to_protobuf() as i32, + recorded_at: self.recorded_at, + voltage: self.voltage, + pressure: self.pressure, + temperature: self.temperature, + } + } +} diff --git a/boards/argus/src/pressure/types/queue.rs b/boards/argus/src/pressure/types/queue.rs index 93e1e07..1cc2ab5 100644 --- a/boards/argus/src/pressure/types/queue.rs +++ b/boards/argus/src/pressure/types/queue.rs @@ -1,10 +1,8 @@ use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::channel::Channel; -use crate::adc::types::AdcDevice; use crate::pressure::config::PRESSURE_READING_QUEUE_SIZE; -use crate::pressure::types::pressure_channel::PressureChannel; use crate::pressure::types::pressure_reading::PressureReading; // Type alias for the pressure reading queue used to decouple reading from ADC and writing to logging pipes -pub type PressureReadingQueue = Channel; +pub type PressureReadingQueue = Channel; diff --git a/boards/argus/src/serial/service.rs b/boards/argus/src/serial/service.rs index 7767b2f..2d4f519 100644 --- a/boards/argus/src/serial/service.rs +++ b/boards/argus/src/serial/service.rs @@ -10,6 +10,8 @@ use heapless::String; use messages::argus::envelope::{envelope::Message as EnvelopeMessage, Envelope}; use prost::Message; +use crate::node::CURRENT_NODE; + pub struct SerialService { pub uart: Uart<'static, mode::Async>, } @@ -44,7 +46,10 @@ impl SerialService { &mut self, message: EnvelopeMessage, ) -> Result<(), UsartError> { - let envelope = Envelope { message: Some(message) }; + let envelope = Envelope { + created_by: Some(CURRENT_NODE), + message: Some(message), + }; let frame = envelope.encode_length_delimited_to_vec(); self.uart.write_all(&frame).await?; diff --git a/boards/argus/src/strain/config.rs b/boards/argus/src/strain/config.rs new file mode 100644 index 0000000..a8ff649 --- /dev/null +++ b/boards/argus/src/strain/config.rs @@ -0,0 +1,6 @@ +// Size of the queue used to send strain readings from the strain service to the SD card service +pub const STRAIN_READING_QUEUE_SIZE: usize = 16; + +// File name used to read/write linear transformations that applied to strain readings to/from the SD card +// Linear transformations are stored in CSV format +pub const LINEAR_TRANSFORMATIONS_FILE_NAME: &str = "t_strain.csv"; // Cannot be longer than 12 characters diff --git a/boards/argus/src/strain/mod.rs b/boards/argus/src/strain/mod.rs new file mode 100644 index 0000000..80faf82 --- /dev/null +++ b/boards/argus/src/strain/mod.rs @@ -0,0 +1,4 @@ +pub mod config; +pub mod service; +pub mod tasks; +pub mod types; diff --git a/boards/argus/src/strain/service.rs b/boards/argus/src/strain/service.rs new file mode 100644 index 0000000..83d7692 --- /dev/null +++ b/boards/argus/src/strain/service.rs @@ -0,0 +1,93 @@ +use defmt::error; +use embassy_time::Instant; +use strum::EnumCount; + +use crate::adc::driver::types::{DataRate, Filter, Gain, ReferenceRange}; +use crate::adc::service::AdcService; +use crate::adc::types::AdcDevice; +use crate::linear_transformation::service::LinearTransformationService; +use crate::sd::service::SDCardService; +use crate::serial::service::SerialService; +use crate::session::service::SessionService; +use crate::strain::config::LINEAR_TRANSFORMATIONS_FILE_NAME; +use crate::strain::types::{StrainChannel, StrainReading, StrainReadingQueue, StrainServiceError}; +use crate::utils::types::AsyncMutex; + +// A channel for buffering the strain readings and decoupling the logging to sd task from the measurement task +pub static STRAIN_READING_QUEUE: StrainReadingQueue = StrainReadingQueue::new(); + +pub struct StrainService { + // Other services are passed by a mutex to ensure safe concurrent access + pub adc_service: &'static AsyncMutex>, + pub sd_card_service: &'static AsyncMutex, + pub serial_service: &'static AsyncMutex, + pub session_service: &'static AsyncMutex, + + // Linear transformations that are applied on top of the raw readings for each ADC and channel + pub linear_transformation_service: LinearTransformationService, +} + +impl StrainService { + pub fn new( + adc_service: &'static AsyncMutex>, + sd_card_service: &'static AsyncMutex, + serial_service: &'static AsyncMutex, + session_service: &'static AsyncMutex, + ) -> Self { + Self { + adc_service, + sd_card_service, + serial_service, + session_service, + linear_transformation_service: LinearTransformationService::new(sd_card_service, LINEAR_TRANSFORMATIONS_FILE_NAME), + } + } + + pub async fn setup(&mut self) -> Result<(), StrainServiceError> { + for driver in self.adc_service.lock().await.drivers.iter_mut() { + driver.reference_range = ReferenceRange::Avdd; + driver.data_rate = DataRate::Sps100; + driver.filter = Filter::Sinc3; + driver.enable_internal_reference = true; + driver.gain = Gain::G32; + driver.delay_after_setting_channel = 50; // 50 ms delay to allow the ADC to stabilize after switching channels + driver.apply_configurations().await?; + } + + match self.linear_transformation_service.load_transformations().await { + Err(e) => error!("Failed to load linear transformations: {:?}", e), + _ => {} + } + Ok(()) + } + + pub async fn read_strain( + &mut self, + adc: AdcDevice, + channel: StrainChannel, + ) -> Result { + let mut adc_service = self.adc_service.lock().await; + + // Get the respective "adc channel" pair for the "strain channel" + let (positive_channel, negative_channel) = channel.to_analog_input_channel_pair(); + + // Read the voltage from the ADC in millivolts + let voltage = adc_service.drivers[adc as usize] + .read_differential(positive_channel, negative_channel) + .await? * 1000.0; // Convert to millivolts + + // Apply any linear transformations to get the strain in psi + let strain = self.linear_transformation_service.apply_transformation(adc, channel, voltage as f64); + + let strain_reading = StrainReading { + local_session: self.session_service.lock().await.current_session.clone(), + adc_device: adc, + strain_channel: channel, + recorded_at: Instant::now().as_millis(), + voltage, + strain, + }; + + Ok(strain_reading) + } +} diff --git a/boards/argus/src/strain/tasks/log_measurements.rs b/boards/argus/src/strain/tasks/log_measurements.rs new file mode 100644 index 0000000..fd13eaf --- /dev/null +++ b/boards/argus/src/strain/tasks/log_measurements.rs @@ -0,0 +1,77 @@ +use csv::SerializeCSV; +use defmt::info; +use embassy_executor::task; +use heapless::format; +use messages::argus::envelope::envelope::Message; +use strum::EnumCount; + +use crate::adc::types::AdcDevice; +use crate::sd::service::SDCardService; +use crate::sd::types::{FileName, OperationScope}; +use crate::serial::service::SerialService; +use crate::session::service::SessionService; +use crate::state_machine::service::StateMachineWorker; +use crate::state_machine::types::States; +use crate::strain::service::STRAIN_READING_QUEUE; +use crate::strain::types::{StrainChannel, StrainReading}; +use crate::utils::types::AsyncMutex; + +// Task for picking up the readings from the channel and logging them to the SD card +#[task] +pub async fn log_measurements( + mut worker: StateMachineWorker, + serial_service_mutex: &'static AsyncMutex, + sd_card_service_mutex: &'static AsyncMutex, + session_service: &'static AsyncMutex, +) { + worker + .run_once(&[States::Recording], async |_| -> Result<(), ()> { + initialize_csv_files(sd_card_service_mutex, session_service).await; + Ok(()) + }) + .await + .unwrap(); + + worker + .run_while(&[States::Recording], async |_| -> Result<(), ()> { + let strain_reading = STRAIN_READING_QUEUE.receive().await; + let path = get_path_from_adc_and_channel(strain_reading.adc_device as usize, strain_reading.strain_channel as usize); + let line = strain_reading.to_csv_line(); + SDCardService::enqueue_write(OperationScope::CurrentSession, path, line).await; + let _ = serial_service_mutex + .lock() + .await + .write_envelope_message(Message::StrainReading(strain_reading.to_protobuf())) + .await; + Ok(()) + }) + .await + .unwrap(); +} + +// Create the files and write the CSV headers before starting the logging loop +async fn initialize_csv_files( + sd_card_service_mutex: &'static AsyncMutex, + session_service: &'static AsyncMutex, +) { + // Ensure session is set. Ignore if it errors like SD card not mounted, etc. + let _ = session_service.lock().await.ensure_session().await; + + info!("Initializing CSV files for measurement logging."); + let mut sd_card_service = sd_card_service_mutex.lock().await; + for adc_index in 0..AdcDevice::COUNT { + for channel in 0..StrainChannel::COUNT { + let path = get_path_from_adc_and_channel(adc_index, channel); + + // Ignore because if the SD card isn't mounted we don't want to panic + let _ = sd_card_service.write(OperationScope::CurrentSession, path, StrainReading::get_csv_header()); + } + } +} + +fn get_path_from_adc_and_channel( + adc_index: usize, + channel: usize, +) -> FileName { + format!("T_{}_{}.csv", adc_index, channel).unwrap() as FileName +} diff --git a/boards/argus/src/strain/tasks/measure_strain.rs b/boards/argus/src/strain/tasks/measure_strain.rs new file mode 100644 index 0000000..8af1270 --- /dev/null +++ b/boards/argus/src/strain/tasks/measure_strain.rs @@ -0,0 +1,45 @@ +use defmt::{error, info}; +use embassy_executor::task; +use embassy_futures::yield_now; +use strum::EnumCount; + +use crate::adc::types::AdcDevice; +use crate::state_machine::service::StateMachineWorker; +use crate::state_machine::types::States; +use crate::strain::service::{StrainService, STRAIN_READING_QUEUE}; +use crate::strain::types::StrainChannel; +use crate::utils::types::AsyncMutex; + +// Task that iterates through the ADCs and channels, measures the strain, and enqueues the readings to a channel +#[task] +pub async fn measure_strain( + mut worker: StateMachineWorker, + strain_service_mutex: &'static AsyncMutex>, +) { + worker + .run_while(&[States::Recording], async |_| -> Result<(), ()> { + for adc_index in 0..AdcDevice::COUNT { + for channel_index in 0..StrainChannel::COUNT { + let adc = AdcDevice::from(adc_index); + let channel = StrainChannel::from(channel_index); + let data = strain_service_mutex.lock().await.read_strain(adc, channel).await; + match data { + Ok(strain_reading) => { + info!("{}", strain_reading); + STRAIN_READING_QUEUE.send(strain_reading).await; + } + Err(err) => { + error!("Error reading ADC {} Channel {}: {:?}", adc, channel, err); + continue; + } + } + } + } + + // Yield to allow other tasks to run, especially the NTC measurement task + yield_now().await; + Ok(()) + }) + .await + .unwrap(); +} diff --git a/boards/argus/src/strain/tasks/mod.rs b/boards/argus/src/strain/tasks/mod.rs new file mode 100644 index 0000000..c7c0c86 --- /dev/null +++ b/boards/argus/src/strain/tasks/mod.rs @@ -0,0 +1,5 @@ +mod log_measurements; +mod measure_strain; + +pub use log_measurements::*; +pub use measure_strain::*; diff --git a/boards/argus/src/strain/types/error.rs b/boards/argus/src/strain/types/error.rs new file mode 100644 index 0000000..ec004a7 --- /dev/null +++ b/boards/argus/src/strain/types/error.rs @@ -0,0 +1,13 @@ +use defmt::Format; +use derive_more::From; + +use crate::adc::service::AdcError; +use crate::sd::types::SdCardError; +use crate::serial::service::UsartError; + +#[derive(Debug, Format, From)] +pub enum StrainServiceError { + AdcError(AdcError), + UsartError(UsartError), + SdCardError(SdCardError), +} diff --git a/boards/argus/src/strain/types/mod.rs b/boards/argus/src/strain/types/mod.rs new file mode 100644 index 0000000..6624d95 --- /dev/null +++ b/boards/argus/src/strain/types/mod.rs @@ -0,0 +1,9 @@ +pub mod error; +pub mod queue; +pub mod strain_channel; +pub mod strain_reading; + +pub use error::*; +pub use queue::*; +pub use strain_channel::*; +pub use strain_reading::*; diff --git a/boards/argus/src/strain/types/queue.rs b/boards/argus/src/strain/types/queue.rs new file mode 100644 index 0000000..f4fb974 --- /dev/null +++ b/boards/argus/src/strain/types/queue.rs @@ -0,0 +1,8 @@ +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; +use embassy_sync::channel::Channel; + +use crate::strain::config::STRAIN_READING_QUEUE_SIZE; +use crate::strain::types::strain_reading::StrainReading; + +// Type alias for the strain reading queue used to decouple reading from ADC and writing to logging pipes +pub type StrainReadingQueue = Channel; diff --git a/boards/argus/src/strain/types/strain_channel.rs b/boards/argus/src/strain/types/strain_channel.rs new file mode 100644 index 0000000..df9fece --- /dev/null +++ b/boards/argus/src/strain/types/strain_channel.rs @@ -0,0 +1,50 @@ +use defmt::Format; +use messages::argus::strain::strain_channel::StrainChannel as StrainChannelProtobuf; +use serde::{Deserialize, Serialize}; +use strum::EnumCount; + +use crate::adc::driver::types::AnalogChannel; + +#[repr(usize)] +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Format, Serialize, Deserialize, EnumCount, Default)] +pub enum StrainChannel { + #[default] + Channel1 = 0, + Channel2 = 1, + Channel3 = 2, + Channel4 = 3, +} + +// Support for implicit conversion from usize to StrainChannel +impl From for StrainChannel { + fn from(value: usize) -> Self { + match value { + 0 => StrainChannel::Channel1, + 1 => StrainChannel::Channel2, + 2 => StrainChannel::Channel3, + 3 => StrainChannel::Channel4, + _ => panic!("Invalid strain channel index: {}", value), + } + } +} + +// Configure which analog input channel pair each strain channel uses +impl StrainChannel { + pub fn to_analog_input_channel_pair(&self) -> (AnalogChannel, AnalogChannel) { + match self { + StrainChannel::Channel1 => (AnalogChannel::AIN0, AnalogChannel::AIN1), + StrainChannel::Channel2 => (AnalogChannel::AIN2, AnalogChannel::AIN3), + StrainChannel::Channel3 => (AnalogChannel::AIN4, AnalogChannel::AIN5), + StrainChannel::Channel4 => (AnalogChannel::AIN6, AnalogChannel::AIN7), + } + } + + pub fn to_protobuf(&self) -> StrainChannelProtobuf { + match self { + StrainChannel::Channel1 => StrainChannelProtobuf::Channel1, + StrainChannel::Channel2 => StrainChannelProtobuf::Channel2, + StrainChannel::Channel3 => StrainChannelProtobuf::Channel3, + StrainChannel::Channel4 => StrainChannelProtobuf::Channel4, + } + } +} diff --git a/boards/argus/src/strain/types/strain_reading.rs b/boards/argus/src/strain/types/strain_reading.rs new file mode 100644 index 0000000..196d23f --- /dev/null +++ b/boards/argus/src/strain/types/strain_reading.rs @@ -0,0 +1,61 @@ +use core::str::FromStr; + +use csv::SerializeCSV; +use defmt::Format; +use messages::argus::strain::strain_reading::StrainReading as StrainReadingProtobuf; +use serde::{Deserialize, Serialize}; + +use crate::adc::types::AdcDevice; +use crate::sd::config::MAX_LINE_LENGTH; +use crate::sd::types::Line; +use crate::strain::types::StrainChannel; + +// Represents a single strain reading from a strain channel +#[derive(Debug, Clone, Copy, Format, Serialize, Deserialize)] +pub struct StrainReading { + // Local session from the device that took the reading + pub local_session: Option, + + // ADC device from which the reading was taken + pub adc_device: AdcDevice, + + // Identifier for the strain gauge within the ADC device + pub strain_channel: StrainChannel, + + // Milliseconds since the board's epoch when the reading was recorded + pub recorded_at: u64, + + // Voltage difference measured at the strain sensor wheatstone bridge in millivolts + pub voltage: f32, + + // Strain reading + pub strain: f64, +} + +impl SerializeCSV for StrainReading { + fn get_csv_header() -> Line { + Line::from_str( + "Local Session #,\ + ADC Device,\ + Strain Channel,\ + Timestamp (ms),\ + Voltage (mV),\ + Strain", + ) + .unwrap() + } +} + +impl StrainReading { + // Convert to the protobuf representation + pub fn to_protobuf(&self) -> StrainReadingProtobuf { + StrainReadingProtobuf { + local_session: self.local_session, + adc_device: self.adc_device.to_protobuf() as i32, + strain_channel: self.strain_channel.to_protobuf() as i32, + recorded_at: self.recorded_at, + voltage: self.voltage, + strain: self.strain, + } + } +} diff --git a/boards/argus/src/temperature/calibration.rs b/boards/argus/src/temperature/calibration.rs index e005982..3935604 100644 --- a/boards/argus/src/temperature/calibration.rs +++ b/boards/argus/src/temperature/calibration.rs @@ -57,7 +57,7 @@ impl TemperatureService { // Start collecting data points let mut calibration_data_points: Vec = Vec::new(); for data_point_index in 0..data_points_count { - let message: String<64> = format!("Data Point #{}. Enter expected value in degrees celsius:\n", data_point_index + 1) + let message: String<64> = format!("Data Point #{}. Enter expected value in degrees Celsius:\n", data_point_index + 1) .map_err(|_| TemperatureServiceError::FormatError)?; let expected_temperature: f64 = self.prompt(message.as_str()).await?; let measured_temperature: f64 = self.read_thermocouple(adc, channel).await?.compensated_temperature; diff --git a/common/ads126x/Cargo.toml b/common/ads126x/Cargo.toml deleted file mode 100644 index d05f78c..0000000 --- a/common/ads126x/Cargo.toml +++ /dev/null @@ -1,20 +0,0 @@ -[package] -name = "ads126x" -version = "0.1.0" -edition = "2021" - -[dependencies] -defmt = {workspace = true} -embassy-time = { version = "0.4.0", features = ["defmt", "defmt-timestamp-uptime", "tick-hz-32_768"] } -embassy-embedded-hal = { version = "0.3.0" } -embedded-hal-async = { version = "1.0" } -embedded-nal-async = "0.8.0" -embedded-io-async = { version = "0.6.1" } -embedded-hal = "1.0.0" -bitflags = { version = "2.3.1", features = ["serde"] } -nb = "1.1.0" -heapless = {workspace = true} -cortex-m = { workspace = true } - -[dev-dependencies] -embedded-hal-mock = "0.11.1" \ No newline at end of file diff --git a/common/ads126x/README.md b/common/ads126x/README.md deleted file mode 100644 index dac2de3..0000000 --- a/common/ads126x/README.md +++ /dev/null @@ -1 +0,0 @@ -# ADS126x Device Driver diff --git a/common/ads126x/docs/ads1262.pdf b/common/ads126x/docs/ads1262.pdf deleted file mode 100644 index cae5756..0000000 Binary files a/common/ads126x/docs/ads1262.pdf and /dev/null differ diff --git a/common/ads126x/src/error.rs b/common/ads126x/src/error.rs deleted file mode 100644 index e88cc0f..0000000 --- a/common/ads126x/src/error.rs +++ /dev/null @@ -1,7 +0,0 @@ -use core::fmt::Debug; - -#[derive(Debug)] -pub enum ADS126xError { - IO, - InvalidInputData, -} diff --git a/common/ads126x/src/lib.rs b/common/ads126x/src/lib.rs deleted file mode 100644 index 7168358..0000000 --- a/common/ads126x/src/lib.rs +++ /dev/null @@ -1,699 +0,0 @@ -#![no_std] -#![no_main] - -pub mod error; -pub mod register; - -use defmt::info; -use embedded_hal::digital::OutputPin; -use error::ADS126xError; -use register::{ - Adc2CfgRegister, Adc2MuxRegister, GpioConRegister, GpioDatRegister, GpioDirRegister, IdRegister, IdacMagRegister, IdacMuxRegister, - InpMuxRegister, InterfaceRegister, Mode0Register, Mode1Register, Mode2Register, PowerRegister, RefMuxRegister, Register, TdacnRegister, - TdacpRegister, -}; - -/// The [`Result`] type for ADS126x operations. -pub type Result = core::result::Result; - -pub struct Ads126x -where - GpioPin: OutputPin, { - reset_pin: GpioPin, -} - -pub enum ADCCommand { - NOP, - RESET, - START1, - STOP1, - START2, - STOP2, - RDATA1, - RDATA2, - SYOCAL1, - SYGCAL1, - SFOCAL1, - SYOCAL2, - SYGCAL2, - SFOCAL2, - RREG(Register, u8), // (register address, number of registers) - WREG(Register, u8), // (register address, number of registers) -} - -impl Ads126x -where - GpioPin: OutputPin, -{ - pub fn new(reset_pin: GpioPin) -> Self { - Self { reset_pin } - } - - // consolidate this logic to one function. - pub fn set_reset_high(&mut self) -> Result<()> { - self.reset_pin.set_high().map_err(|_| ADS126xError::IO)?; - Ok(()) - } - - pub fn set_reset_low(&mut self) -> Result<()> { - self.reset_pin.set_low().map_err(|_| ADS126xError::IO)?; - Ok(()) - } - - /// to issue read data command call read_data1 or read_data2. - pub fn send_command( - &mut self, - command: ADCCommand, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - let (opcode1, opcode2) = match command { - ADCCommand::NOP => (0x00, None), - ADCCommand::RESET => (0x06, None), - ADCCommand::START1 => (0x08, None), - ADCCommand::STOP1 => (0x0A, None), - ADCCommand::START2 => (0x0C, None), - ADCCommand::STOP2 => (0x0E, None), - ADCCommand::RDATA1 => (0x12, None), - ADCCommand::RDATA2 => (0x14, None), - ADCCommand::SYOCAL1 => (0x16, None), - ADCCommand::SYGCAL1 => (0x17, None), - ADCCommand::SFOCAL1 => (0x19, None), - ADCCommand::SYOCAL2 => (0x1B, None), - ADCCommand::SYGCAL2 => (0x1C, None), - ADCCommand::SFOCAL2 => (0x1E, None), - ADCCommand::RREG(addr, num) => (0x20 | addr as u8, Some(num)), - ADCCommand::WREG(addr, num) => (0x40 | addr as u8, Some(num)), - }; - info!("Sending opcode 1: {:#04x}", opcode1); - - let mut opcodes = [opcode1, if let Some(opcode) = opcode2 { opcode } else { 0x00 }]; - - spi.transfer(&mut [0x00, 0x00], &mut opcodes).map_err(|_| ADS126xError::IO)?; // this ?)?; is weird, why can't I just ? on the block result. - Ok(()) - } - - pub fn read_data1( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - // 0x00 gets interpretted as NOP command - let mut buffer: [u8; 7] = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; - spi.transfer(&mut buffer, &mut [0x12]).map_err(|_| ADS126xError::IO)?; - info!("Read buffer: {:#010b}", buffer); - let data_buffer: [u8; 4] = [buffer[3], buffer[4], buffer[5], buffer[6]]; - let data: i32 = i32::from_be_bytes(data_buffer); - Ok(data) - } - - /// Reads data from multiple registers starting at the provided register. - /// To read a single register, see [`ADS126x::read_register`]. - /// - /// Vector returns byte for each register read in order registers were read (increasing address). - pub fn read_multiple_registers( - &mut self, - reg: Register, - num: u8, - spi: &mut SPI, - ) -> Result<[u8; 25]> - where - SPI: embedded_hal::spi::SpiBus, { - if num > 27 { - return Err(ADS126xError::InvalidInputData); - } - // self.send_command(ADCCommand::RREG(reg, num - 1), spi)?; - let mut buffer: [u8; 25] = [ - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, - ]; - - spi.transfer(&mut buffer, &mut [num, 0x20 | reg as u8]).map_err(|_| ADS126xError::IO)?; - - // let mut buffer: Vec = Vec::new(); - // for _ in 0..num { - // buffer - // .push(spi.read().map_err(|_| ADS126xError::IO)?) - // .map_err(|_| ADS126xError::InvalidInputData)?; - // } - Ok(buffer) - } - - /// Reads data from only the single provided register. - /// To read multiple registers, see [`ADS126x::read_multiple_registers`]. - pub fn read_register( - &mut self, - reg: Register, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - // zero since number of registers read - 1, so 1-1=0. - // self.send_command(ADCCommand::RREG(reg, 0), spi)?; - // let data = spi.read().map_err(|_| ADS126xError::IO)?; - let mut buffer = [reg as u8 | 0x20]; - let mut read_buffer = [0x00; 3]; - spi.transfer(&mut read_buffer, &mut buffer).map_err(|_| ADS126xError::IO)?; - info!("Read buffer: {:#010b}", read_buffer.clone()); - Ok(read_buffer[2]) - } - - /// Writes data to multiple registers starting at the provided register. - /// To write data to a single register, see [`ADS126x::write_register`]. - /// - /// Data has byte for each register in order registers are written to (increasing address). - pub fn write_multiple_registers( - &mut self, - reg: Register, - data: &[u8], - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - if data.len() > 27 { - return Err(ADS126xError::InvalidInputData); - } - self.send_command(ADCCommand::WREG(reg, data.len() as u8 - 1), spi)?; - for &byte in data { - spi.write(&[byte]).map_err(|_| ADS126xError::IO)?; - } - Ok(()) - } - - /// Writes data to only the single provided register. - /// To write data to multiple registers, see [`ADS126x::write_multiple_registers`]. - pub fn write_register( - &mut self, - reg: Register, - data: u8, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.send_command(ADCCommand::WREG(reg, 0), spi)?; - info!("Writing {:#010b} ", data); - // panic!(); - spi.write(&[data]).map_err(|_| ADS126xError::IO) - } - - pub fn get_id( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::ID, spi)?; - let data = IdRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn get_power( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::POWER, spi)?; - let data = PowerRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_power( - &mut self, - reg: &PowerRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::POWER, reg.bits(), spi) - } - - pub fn get_interface( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::INTERFACE, spi)?; - let data = InterfaceRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_interface( - &mut self, - reg: &InterfaceRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::INTERFACE, reg.bits(), spi) - } - - pub fn get_mode0( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::MODE0, spi)?; - let data = Mode0Register::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_mode0( - &mut self, - reg: &Mode0Register, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::MODE0, reg.bits(), spi) - } - - pub fn get_mode1( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::MODE1, spi)?; - let data = Mode1Register::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_mode1( - &mut self, - reg: &Mode1Register, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - info!("Setting register to {:#010b}", reg.bits()); - self.write_register(Register::MODE1, reg.bits(), spi) - } - - pub fn get_mode2( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::MODE2, spi)?; - let data = Mode2Register::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_mode2( - &mut self, - reg: &Mode2Register, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::MODE2, reg.bits(), spi) - } - - pub fn get_inpmux( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::INPMUX, spi)?; - let data = InpMuxRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_inpmux( - &mut self, - reg: &InpMuxRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::INPMUX, reg.bits(), spi) - } - - pub fn get_ofcal( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bytes = self.read_multiple_registers(Register::OFCAL0, 3, spi)?; // [OFCAL0, OFCAL1, OFCAL2] - let res = (bytes[2] as u32) << 16 | (bytes[1] as u32) << 8 | (bytes[0] as u32); - Ok(res) - } - - pub fn set_ofcal( - &mut self, - ofcal: u32, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - // Will not panic as & 0xFF ensures values are u8 - let res: [u8; 3] = [ - u8::try_from(ofcal & 0xFF).unwrap(), - u8::try_from((ofcal >> 8) & 0xFF).unwrap(), - u8::try_from((ofcal >> 16) & 0xFF).unwrap(), - ]; - self.write_multiple_registers(Register::OFCAL0, &res, spi) - } - - pub fn get_fscal( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bytes = self.read_multiple_registers(Register::FSCAL0, 3, spi)?; // [FSCAL0, FSCAL1, FSCAL2] - let res = (bytes[2] as u32) << 16 | (bytes[1] as u32) << 8 | (bytes[0] as u32); - Ok(res) - } - - pub fn set_fscal( - &mut self, - fscal: u32, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - // Will not panic as & 0xFF ensures values are u8 - let res: [u8; 3] = [ - u8::try_from(fscal & 0xFF).unwrap(), - u8::try_from((fscal >> 8) & 0xFF).unwrap(), - u8::try_from((fscal >> 16) & 0xFF).unwrap(), - ]; - self.write_multiple_registers(Register::FSCAL0, &res, spi) - } - - pub fn get_idacmux( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::IDACMUX, spi)?; - let data = IdacMuxRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_idacmux( - &mut self, - reg: &IdacMuxRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::IDACMUX, reg.bits(), spi) - } - - pub fn get_idacmag( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::IDACMAG, spi)?; - let data = IdacMagRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_idacmag( - &mut self, - reg: &IdacMagRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::IDACMAG, reg.bits(), spi) - } - - pub fn get_refmux( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::REFMUX, spi)?; - let data = RefMuxRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_refmux( - &mut self, - reg: &RefMuxRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::REFMUX, reg.bits(), spi) - } - - pub fn get_tdacp( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::TDACP, spi)?; - let data = TdacpRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_tdacp( - &mut self, - reg: &TdacpRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::TDACP, reg.bits(), spi) - } - - pub fn get_tdacn( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::TDACN, spi)?; - let data = TdacnRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_tdacn( - &mut self, - reg: &TdacnRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::TDACN, reg.bits(), spi) - } - - pub fn get_gpiocon( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::GPIOCON, spi)?; - let data = GpioConRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_gpiocon( - &mut self, - reg: &GpioConRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::GPIOCON, reg.bits(), spi) - } - - pub fn get_gpiodir( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::GPIODIR, spi)?; - let data = GpioDirRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_gpiodir( - &mut self, - reg: &GpioDirRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::GPIODIR, reg.bits(), spi) - } - - pub fn get_gpiodat( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::GPIODAT, spi)?; - let data = GpioDatRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_gpiodat( - &mut self, - reg: &GpioDatRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::GPIODAT, reg.bits(), spi) - } - - pub fn get_adc2cfg( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::ADC2CFG, spi)?; - let data = Adc2CfgRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_adc2cfg( - &mut self, - reg: &Adc2CfgRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::ADC2CFG, reg.bits(), spi) - } - - pub fn get_adc2mux( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bits = self.read_register(Register::ADC2MUX, spi)?; - let data = Adc2MuxRegister::from_bits(bits); - match data { - Some(reg) => Ok(reg), - None => Err(ADS126xError::InvalidInputData), - } - } - - pub fn set_adc2mux( - &mut self, - reg: &Adc2MuxRegister, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - self.write_register(Register::ADC2MUX, reg.bits(), spi) - } - - pub fn get_adc2ofc( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bytes = self.read_multiple_registers(Register::ADC2OFC0, 2, spi)?; // [ADC2OFC0, ADC2OFC1] - let res = (bytes[1] as u16) << 8 | (bytes[0] as u16); - Ok(res) - } - - pub fn set_adc2ofc( - &mut self, - ofc2: u16, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - // Will not panic as & 0xFF ensures values are u8 - let res: [u8; 2] = [u8::try_from(ofc2 & 0xFF).unwrap(), u8::try_from((ofc2 >> 8) & 0xFF).unwrap()]; - self.write_multiple_registers(Register::ADC2OFC0, &res, spi) - } - - pub fn get_adc2fsc( - &mut self, - spi: &mut SPI, - ) -> Result - where - SPI: embedded_hal::spi::SpiBus, { - let bytes = self.read_multiple_registers(Register::ADC2FSC0, 2, spi)?; // [ADC2FSC0, ADC2FSC1] - let res = (bytes[1] as u16) << 8 | (bytes[0] as u16); - Ok(res) - } - - pub fn set_adc2fsc( - &mut self, - fsc2: u32, - spi: &mut SPI, - ) -> Result<()> - where - SPI: embedded_hal::spi::SpiBus, { - // Will not panic as & 0xFF ensures values are u8 - let res: [u8; 2] = [u8::try_from(fsc2 & 0xFF).unwrap(), u8::try_from((fsc2 >> 8) & 0xFF).unwrap()]; - self.write_multiple_registers(Register::ADC2FSC0, &res, spi) - } -} diff --git a/common/ads126x/src/register.rs b/common/ads126x/src/register.rs deleted file mode 100644 index 88ee83e..0000000 --- a/common/ads126x/src/register.rs +++ /dev/null @@ -1,972 +0,0 @@ -mod enums; - -use bitflags::bitflags; -pub use enums::*; - -#[repr(u8)] -pub enum Register { - ID = 0x00, - POWER = 0x01, - INTERFACE = 0x02, - MODE0 = 0x03, - MODE1 = 0x04, - MODE2 = 0x05, - INPMUX = 0x06, - OFCAL0 = 0x07, - OFCAL1 = 0x08, - OFCAL2 = 0x09, - FSCAL0 = 0x0A, - FSCAL1 = 0x0B, - FSCAL2 = 0x0C, - IDACMUX = 0x0D, - IDACMAG = 0x0E, - REFMUX = 0x0F, - TDACP = 0x10, - TDACN = 0x11, - GPIOCON = 0x12, - GPIODIR = 0x13, - GPIODAT = 0x14, - ADC2CFG = 0x15, - ADC2MUX = 0x16, - ADC2OFC0 = 0x17, - ADC2OFC1 = 0x18, - ADC2FSC0 = 0x19, - ADC2FSC1 = 0x1A, -} - -bitflags! { - pub struct StatusRegister: u8 { - const ADC2 = 0b1000_0000; - const ADC1 = 0b0100_0000; - const EXTCLK = 0b0010_0000; - const REF_ALM = 0b0001_0000; - const PGAL_ALM = 0b0000_1000; - const PGAH_ALM = 0b0000_0100; - const PGAD_ALM = 0b0000_0010; - const RESET = 0b0000_0001; - } -} - -impl StatusRegister { - pub fn get_adc1_new(&self) -> bool { - self.contains(StatusRegister::ADC1) - } - - pub fn get_adc2_new(&self) -> bool { - self.contains(StatusRegister::ADC2) - } - - pub fn get_extclk(&self) -> bool { - self.contains(StatusRegister::EXTCLK) - } - - pub fn get_ref_alarm(&self) -> bool { - self.contains(StatusRegister::REF_ALM) - } - - pub fn get_pga_low_alarm(&self) -> bool { - self.contains(StatusRegister::PGAL_ALM) - } - - pub fn get_pga_high_alarm(&self) -> bool { - self.contains(StatusRegister::PGAH_ALM) - } - - pub fn get_pga_diff_alarm(&self) -> bool { - self.contains(StatusRegister::PGAD_ALM) - } - - /// The user needs to clear this byte from the Power register. - pub fn get_reset(&self) -> bool { - self.contains(StatusRegister::RESET) - } -} - -bitflags! { - pub struct IdRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl IdRegister { - pub fn get_rev_id(&self) -> u8 { - self.bits() & 0b0001_1111 - } - - pub fn get_dev_id(&self) -> DevId { - match (self.bits() & 0b1110_0000) >> 5 { - 0b000 => DevId::ADS1262, - 0b001 => DevId::ADS1263, - - _ => panic!("Device ID must be 0b000 or 0b001."), - } - } -} - -bitflags! { - pub struct PowerRegister: u8 { - const INTREF = 0b0000_0001; - const VBIAS = 0b0000_0010; - const RESET = 0b0001_0000; - } -} - -impl PowerRegister { - pub fn default() -> Self { - PowerRegister::from_bits_truncate(0b0001_0001) - } - - pub fn set_vbias( - &mut self, - vbias: bool, - ) { - if vbias { - self.insert(PowerRegister::VBIAS); - } else { - self.remove(PowerRegister::VBIAS); - } - } - - pub fn set_intref( - &mut self, - intref: bool, - ) { - if intref { - self.insert(PowerRegister::INTREF); - } else { - self.remove(PowerRegister::INTREF); - } - } - - pub fn get_reset(&self) -> bool { - self.contains(PowerRegister::RESET) - } - - // must be cleared if set to detect further resets. - pub fn clear_reset(&mut self) { - self.remove(PowerRegister::RESET); - } -} - -bitflags! { - /// WARNING: If CRC is 0b11 set by ADC, it will reflect as CRC enabled not reserved. - /// CRC only accounts for 0b00 disabled and 0b01 enabled. - pub struct InterfaceRegister: u8 { - const CRC = 0b0000_0001; - const STATUS = 0b0000_0100; - const TIMEOUT = 0b0000_1000; - } -} - -impl InterfaceRegister { - pub fn default() -> Self { - InterfaceRegister::from_bits_truncate(0b0000_0101) - } - - pub fn get_crc(&self) -> CrcMode { - match self.bits() & 0b0000_0001 { - 0b00 => CrcMode::Disabled, - 0b01 => CrcMode::Checksum, - 0b10 => CrcMode::CRC, - _ => unreachable!(), - } - } - - pub fn set_crc( - &mut self, - crc: CrcMode, - ) { - let bits = crc as u8; - self.insert(InterfaceRegister::from_bits_retain(bits)); - } - - pub fn get_status(&self) -> bool { - self.contains(InterfaceRegister::STATUS) - } - - pub fn set_status( - &mut self, - status: bool, - ) { - if status { - self.insert(InterfaceRegister::STATUS); - } else { - self.remove(InterfaceRegister::STATUS); - } - } - - pub fn get_timeout(&self) -> bool { - self.contains(InterfaceRegister::TIMEOUT) - } - - pub fn set_timeout( - &mut self, - timeout: bool, - ) { - if timeout { - self.insert(InterfaceRegister::TIMEOUT); - } else { - self.remove(InterfaceRegister::TIMEOUT); - } - } -} - -bitflags! { - pub struct Mode0Register: u8 { - const RUNMODE = 0b0100_0000; - const REFREV = 0b1000_0000; - - const _ = !0; // Source may set any bits - } -} - -impl Mode0Register { - pub fn default() -> Self { - Mode0Register::from_bits_truncate(0b0000_0000) - } - - pub fn get_delay(&self) -> ConversionDelay { - match self.bits() & 0b0000_1111 { - 0b0000 => ConversionDelay::DNone, - 0b0001 => ConversionDelay::D8_7us, - 0b0010 => ConversionDelay::D17us, - 0b0011 => ConversionDelay::D35us, - 0b0100 => ConversionDelay::D69us, - 0b0101 => ConversionDelay::D139us, - 0b0110 => ConversionDelay::D278us, - 0b0111 => ConversionDelay::D555us, - 0b1000 => ConversionDelay::D1_1ms, - 0b1001 => ConversionDelay::D2_2ms, - 0b1010 => ConversionDelay::D4_4ms, - 0b1011 => ConversionDelay::D8_8ms, - - 0b1100..=0b1111 => panic!("Unknown conversion delay"), - _ => unreachable!(), - } - } - - pub fn set_delay( - &mut self, - delay: ConversionDelay, - ) { - let bits = delay as u8; - self.insert(Mode0Register::from_bits_retain(bits)); - } - - pub fn get_chop(&self) -> ChopMode { - match (self.bits() & 0b0011_0000) >> 4 { - 0b00 => ChopMode::Disabled, - 0b01 => ChopMode::InChopEnabled, - 0b10 => ChopMode::IdacEnabled, - 0b11 => ChopMode::InChopAndIdacEnabled, - - _ => unreachable!(), - } - } - - pub fn set_chop( - &mut self, - chop: ChopMode, - ) { - let bits = chop as u8; - self.insert(Mode0Register::from_bits_retain(bits << 4)); - } -} - -bitflags! { - pub struct Mode1Register: u8 { - const SBPOL = 0b0000_1000; - const SBADC = 0b0001_0000; - - const _ = !0; // Source may set any bits - } -} - -impl Mode1Register { - pub fn default() -> Self { - Mode1Register::from_bits_truncate(0b1000_0000) - } - - pub fn get_sbmag(&self) -> SensorBiasMagnitude { - match self.bits() & 0b0000_0111 { - 0b000 => SensorBiasMagnitude::BNone, - 0b001 => SensorBiasMagnitude::B0_5uA, - 0b010 => SensorBiasMagnitude::B2uA, - 0b011 => SensorBiasMagnitude::B10uA, - 0b100 => SensorBiasMagnitude::B50uA, - 0b101 => SensorBiasMagnitude::B200uA, - 0b110 => SensorBiasMagnitude::R10MOhm, - - 0b111 => panic!("Reserved SBMAG"), - _ => unreachable!(), - } - } - - pub fn set_sbmag( - &mut self, - sbmag: SensorBiasMagnitude, - ) { - let bits = sbmag as u8; - self.insert(Mode1Register::from_bits_retain(bits)); - } - - pub fn get_filter(&self) -> DigitalFilter { - match (self.bits() & 0b1110_0000) >> 5 { - 0b000 => DigitalFilter::Sinc1, - 0b001 => DigitalFilter::Sinc2, - 0b010 => DigitalFilter::Sinc3, - 0b011 => DigitalFilter::Sinc4, - 0b100 => DigitalFilter::FIR, - - 0b101..=0b111 => panic!("Reserved filter"), - _ => unreachable!(), - } - } - - pub fn set_filter( - &mut self, - filter: DigitalFilter, - ) { - let bits = filter as u8; - self.insert(Mode1Register::from_bits_retain(bits << 5)); - } -} - -bitflags! { - pub struct Mode2Register: u8 { - const BYPASS = 0b1000_0000; - - const _ = !0; // Source may set any bits - } -} - -impl Mode2Register { - pub fn default() -> Self { - Mode2Register::from_bits_truncate(0b0000_0100) - } - - pub fn get_dr(&self) -> DataRate { - match self.bits() & 0b0000_1111 { - 0b0000 => DataRate::SPS2_5, - 0b0001 => DataRate::SPS5, - 0b0010 => DataRate::SPS10, - 0b0011 => DataRate::SPS16_6, - 0b0100 => DataRate::SPS20, - 0b0101 => DataRate::SPS50, - 0b0110 => DataRate::SPS60, - 0b0111 => DataRate::SPS100, - 0b1000 => DataRate::SPS400, - 0b1001 => DataRate::SPS1200, - 0b1010 => DataRate::SPS2400, - 0b1011 => DataRate::SPS4800, - 0b1100 => DataRate::SPS7200, - 0b1101 => DataRate::SPS14400, - 0b1110 => DataRate::SPS19200, - 0b1111 => DataRate::SPS38400, - - _ => unreachable!(), - } - } - - pub fn set_dr( - &mut self, - rate: DataRate, - ) { - let bits = rate as u8; - self.insert(Mode2Register::from_bits_retain(bits)); - } - - pub fn get_gain(&self) -> PGAGain { - match (self.bits() & 0b0111_0000) >> 4 { - 0b000 => PGAGain::VV1, - 0b001 => PGAGain::VV2, - 0b010 => PGAGain::VV4, - 0b011 => PGAGain::VV8, - 0b100 => PGAGain::VV16, - 0b101 => PGAGain::VV32, - - 0b110 | 0b111 => panic!("Reserved gain"), - _ => unreachable!(), - } - } - - pub fn set_gain( - &mut self, - gain: PGAGain, - ) { - let bits = gain as u8; - self.insert(Mode2Register::from_bits_retain(bits << 4)); - } -} - -bitflags! { - pub struct InpMuxRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl InpMuxRegister { - pub fn default() -> Self { - InpMuxRegister::from_bits_truncate(0b0000_0001) - } - - pub fn get_muxn(&self) -> NegativeInpMux { - match self.bits() & 0b0000_1111 { - 0b0000 => NegativeInpMux::AIN0, - 0b0001 => NegativeInpMux::AIN1, - 0b0010 => NegativeInpMux::AIN2, - 0b0011 => NegativeInpMux::AIN3, - 0b0100 => NegativeInpMux::AIN4, - 0b0101 => NegativeInpMux::AIN5, - 0b0110 => NegativeInpMux::AIN6, - 0b0111 => NegativeInpMux::AIN7, - 0b1000 => NegativeInpMux::AIN8, - 0b1001 => NegativeInpMux::AIN9, - 0b1010 => NegativeInpMux::AINCOM, - 0b1011 => NegativeInpMux::TempSensMonNeg, - 0b1100 => NegativeInpMux::AnlgPwrSupMonNeg, - 0b1101 => NegativeInpMux::DgtlPwrSubMonNeg, - 0b1110 => NegativeInpMux::TDACTestSignalNeg, - 0b1111 => NegativeInpMux::Float, - - _ => unreachable!(), - } - } - - pub fn set_muxn( - &mut self, - muxn: NegativeInpMux, - ) { - let bits = muxn as u8; - self.insert(InpMuxRegister::from_bits_retain(bits)); - } - - pub fn get_muxp(&self) -> PositiveInpMux { - match (self.bits() & 0b1111_0000) >> 4 { - 0b0000 => PositiveInpMux::AIN0, - 0b0001 => PositiveInpMux::AIN1, - 0b0010 => PositiveInpMux::AIN2, - 0b0011 => PositiveInpMux::AIN3, - 0b0100 => PositiveInpMux::AIN4, - 0b0101 => PositiveInpMux::AIN5, - 0b0110 => PositiveInpMux::AIN6, - 0b0111 => PositiveInpMux::AIN7, - 0b1000 => PositiveInpMux::AIN8, - 0b1001 => PositiveInpMux::AIN9, - 0b1010 => PositiveInpMux::AINCOM, - 0b1011 => PositiveInpMux::TempSensMonPos, - 0b1100 => PositiveInpMux::AnlgPwrSupMonPos, - 0b1101 => PositiveInpMux::DgtlPwrSubMonPos, - 0b1110 => PositiveInpMux::TDACTestSignalPos, - 0b1111 => PositiveInpMux::Float, - - _ => unreachable!(), - } - } - - pub fn set_muxp( - &mut self, - muxp: PositiveInpMux, - ) { - let bits = muxp as u8; - self.insert(InpMuxRegister::from_bits_retain(bits << 4)); - } -} - -bitflags! { - pub struct IdacMuxRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl IdacMuxRegister { - pub fn default() -> Self { - IdacMuxRegister::from_bits_truncate(0b1011_1011) - } - - pub fn get_mux1(&self) -> IdacOutMux { - match self.bits() & 0b0000_1111 { - 0b0000 => IdacOutMux::AIN0, - 0b0001 => IdacOutMux::AIN1, - 0b0010 => IdacOutMux::AIN2, - 0b0011 => IdacOutMux::AIN3, - 0b0100 => IdacOutMux::AIN4, - 0b0101 => IdacOutMux::AIN5, - 0b0110 => IdacOutMux::AIN6, - 0b0111 => IdacOutMux::AIN7, - 0b1000 => IdacOutMux::AIN8, - 0b1001 => IdacOutMux::AIN9, - 0b1010 => IdacOutMux::AINCOM, - 0b1011 => IdacOutMux::NoConnection, - - 0b1100..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), - _ => unreachable!(), - } - } - - pub fn set_mux1( - &mut self, - mux1: IdacOutMux, - ) { - let bits = mux1 as u8; - self.insert(IdacMuxRegister::from_bits_retain(bits)); - } - - pub fn get_mux2(&self) -> IdacOutMux { - match (self.bits() & 0b1111_0000) >> 4 { - 0b0000 => IdacOutMux::AIN0, - 0b0001 => IdacOutMux::AIN1, - 0b0010 => IdacOutMux::AIN2, - 0b0011 => IdacOutMux::AIN3, - 0b0100 => IdacOutMux::AIN4, - 0b0101 => IdacOutMux::AIN5, - 0b0110 => IdacOutMux::AIN6, - 0b0111 => IdacOutMux::AIN7, - 0b1000 => IdacOutMux::AIN8, - 0b1001 => IdacOutMux::AIN9, - 0b1010 => IdacOutMux::AINCOM, - 0b1011 => IdacOutMux::NoConnection, - - 0b1100..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), - _ => unreachable!(), - } - } - - pub fn set_mux2( - &mut self, - mux2: IdacOutMux, - ) { - let bits = mux2 as u8; - self.insert(IdacMuxRegister::from_bits_retain(bits << 4)); - } -} - -bitflags! { - pub struct IdacMagRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl IdacMagRegister { - pub fn default() -> Self { - IdacMagRegister::from_bits_truncate(0b0000_0000) - } - - pub fn get_mag1(&self) -> IdacCurMag { - match self.bits() & 0b0000_1111 { - 0b0000 => IdacCurMag::I50uA, - 0b0001 => IdacCurMag::I100uA, - 0b0010 => IdacCurMag::I250uA, - 0b0011 => IdacCurMag::I500uA, - 0b0100 => IdacCurMag::I750uA, - 0b0101 => IdacCurMag::I1000uA, - 0b0110 => IdacCurMag::I1500uA, - 0b0111 => IdacCurMag::I2000uA, - 0b1000 => IdacCurMag::I2500uA, - 0b1001 => IdacCurMag::I3000uA, - - 0b1010..=0b1111 => panic!("Reserved IDAC Magnitude Multiplexer"), - _ => unreachable!(), - } - } - - pub fn set_mag1( - &mut self, - mag1: IdacCurMag, - ) { - let bits = mag1 as u8; - self.insert(IdacMagRegister::from_bits_retain(bits)); - } - - pub fn get_mag2(&self) -> IdacCurMag { - match (self.bits() & 0b1111_0000) >> 4 { - 0b0000 => IdacCurMag::I50uA, - 0b0001 => IdacCurMag::I100uA, - 0b0010 => IdacCurMag::I250uA, - 0b0011 => IdacCurMag::I500uA, - 0b0100 => IdacCurMag::I750uA, - 0b0101 => IdacCurMag::I1000uA, - 0b0110 => IdacCurMag::I1500uA, - 0b0111 => IdacCurMag::I2000uA, - 0b1000 => IdacCurMag::I2500uA, - 0b1001 => IdacCurMag::I3000uA, - - 0b1010..=0b1111 => panic!("Reserved IDAC Output Multiplexer"), - _ => unreachable!(), - } - } - - pub fn set_mag2( - &mut self, - mag2: IdacCurMag, - ) { - let bits = mag2 as u8; - self.insert(IdacMagRegister::from_bits_retain(bits << 4)); - } -} - -bitflags! { - pub struct RefMuxRegister: u8 { - const _ = 0b0011_1111; - } -} - -impl RefMuxRegister { - pub fn default() -> Self { - RefMuxRegister::from_bits_truncate(0b0000_0000) - } - - pub fn get_rmuxn(&self) -> RefNegativeInp { - match self.bits() & 0b0000_0111 { - 0b000 => RefNegativeInp::Int2_5VRef, - 0b001 => RefNegativeInp::ExtAIN1, - 0b010 => RefNegativeInp::ExtAIN3, - 0b011 => RefNegativeInp::ExtAIN5, - 0b100 => RefNegativeInp::IntAnlgSup, - - 0b101..=0b111 => panic!("Reserved Reference Negative Input"), - _ => unreachable!(), - } - } - - pub fn set_rmuxn( - &mut self, - rmuxn: RefNegativeInp, - ) { - let bits = rmuxn as u8; - self.insert(RefMuxRegister::from_bits_retain(bits)); - } - - pub fn get_rmuxp(&self) -> RefPositiveInp { - match (self.bits() & 0b0011_1000) >> 3 { - 0b000 => RefPositiveInp::Int2_5VRef, - 0b001 => RefPositiveInp::ExtAIN0, - 0b010 => RefPositiveInp::ExtAIN2, - 0b011 => RefPositiveInp::ExtAIN4, - 0b100 => RefPositiveInp::IntAnlgSup, - - 0b101..=0b111 => panic!("Reserved Reference Positive Input"), - _ => unreachable!(), - } - } - - pub fn set_rmuxp( - &mut self, - rmuxp: RefPositiveInp, - ) { - let bits = rmuxp as u8; - self.insert(RefMuxRegister::from_bits_retain(bits << 3)); - } -} - -bitflags! { - pub struct TdacpRegister: u8 { - const OUTP = 0b1000_0000; - - const _ = 0b1001_1111; - } -} - -impl TdacpRegister { - // there is no defined default values in the datasheet so just zeros. - pub fn default() -> Self { - TdacpRegister::from_bits_truncate(0b0000_0000) - } - - pub fn get_magp(&self) -> TdacOutMag { - match self.bits() & 0b0001_1111 { - 0b01001 => TdacOutMag::V4_5, - 0b01000 => TdacOutMag::V3_5, - 0b00111 => TdacOutMag::V3, - 0b00110 => TdacOutMag::V2_75, - 0b00101 => TdacOutMag::V2_625, - 0b00100 => TdacOutMag::V2_5625, - 0b00011 => TdacOutMag::V2_53125, - 0b00010 => TdacOutMag::V2_515625, - 0b00001 => TdacOutMag::V2_5078125, - 0b00000 => TdacOutMag::V2_5, - 0b10001 => TdacOutMag::V2_4921875, - 0b10010 => TdacOutMag::V2_484375, - 0b10011 => TdacOutMag::V2_46875, - 0b10100 => TdacOutMag::V2_4375, - 0b10101 => TdacOutMag::V2_375, - 0b10110 => TdacOutMag::V2_25, - 0b10111 => TdacOutMag::V2, - 0b11000 => TdacOutMag::V1_5, - 0b11001 => TdacOutMag::V0_5, - - 0b01010..=0b10000 | 0b11010..=0b11111 => panic!("Reserved MAGP"), - _ => unreachable!(), - } - } - - pub fn set_magp( - &mut self, - magp: TdacOutMag, - ) { - let bits = magp as u8; - self.insert(TdacpRegister::from_bits_retain(bits)); - } -} - -bitflags! { - pub struct TdacnRegister: u8 { - const OUTN = 0b1000_0000; - - const _ = 0b1001_1111; - } -} - -impl TdacnRegister { - // there is no defined default values in the datasheet so just zeros. - pub fn default() -> Self { - TdacnRegister::from_bits_truncate(0b0000_0000) - } - - pub fn get_magn(&self) -> TdacOutMag { - match self.bits() & 0b0001_1111 { - 0b01001 => TdacOutMag::V4_5, - 0b01000 => TdacOutMag::V3_5, - 0b00111 => TdacOutMag::V3, - 0b00110 => TdacOutMag::V2_75, - 0b00101 => TdacOutMag::V2_625, - 0b00100 => TdacOutMag::V2_5625, - 0b00011 => TdacOutMag::V2_53125, - 0b00010 => TdacOutMag::V2_515625, - 0b00001 => TdacOutMag::V2_5078125, - 0b00000 => TdacOutMag::V2_5, - 0b10001 => TdacOutMag::V2_4921875, - 0b10010 => TdacOutMag::V2_484375, - 0b10011 => TdacOutMag::V2_46875, - 0b10100 => TdacOutMag::V2_4375, - 0b10101 => TdacOutMag::V2_375, - 0b10110 => TdacOutMag::V2_25, - 0b10111 => TdacOutMag::V2, - 0b11000 => TdacOutMag::V1_5, - 0b11001 => TdacOutMag::V0_5, - - 0b01010..=0b10000 | 0b11010..=0b11111 => panic!("Reserved MAGN"), - _ => unreachable!(), - } - } - - pub fn set_magn( - &mut self, - magn: TdacOutMag, - ) { - let bits = magn as u8; - self.insert(TdacnRegister::from_bits_retain(bits)); - } -} - -bitflags! { - pub struct GpioConRegister: u8 { - const CON0 = 0b0000_0001; // GPIO[0] -> AIN3 - const CON1 = 0b0000_0010; // GPIO[1] -> AIN4 - const CON2 = 0b0000_0100; // GPIO[2] -> AIN5 - const CON3 = 0b0000_1000; // GPIO[3] -> AIN6 - const CON4 = 0b0001_0000; // GPIO[4] -> AIN7 - const CON5 = 0b0010_0000; // GPIO[5] -> AIN8 - const CON6 = 0b0100_0000; // GPIO[6] -> AIN9 - const CON7 = 0b1000_0000; // GPIO[7] -> AINCOM - } -} - -impl GpioConRegister { - pub fn default() -> Self { - GpioConRegister::from_bits_truncate(0b0000_0000) - } -} - -bitflags! { - /// Setting `DIR` to: - /// - 0 = `GPIO` is output - /// - 1 = `GPIO` is input - pub struct GpioDirRegister: u8 { - const DIR0 = 0b0000_0001; - const DIR1 = 0b0000_0010; - const DIR2 = 0b0000_0100; - const DIR3 = 0b0000_1000; - const DIR4 = 0b0001_0000; - const DIR5 = 0b0010_0000; - const DIR6 = 0b0100_0000; - const DIR7 = 0b1000_0000; - } -} - -impl GpioDirRegister { - pub fn default() -> Self { - GpioDirRegister::from_bits_truncate(0b0000_0000) - } -} - -bitflags! { - /// If `GPIO` is output, read returns 0b. - /// If `GPIO` is input, write sets `GPIO` to high (if 1) or low (if 0). - pub struct GpioDatRegister: u8 { - const DAT0 = 0b0000_0001; - const DAT1 = 0b0000_0010; - const DAT2 = 0b0000_0100; - const DAT3 = 0b0000_1000; - const DAT4 = 0b0001_0000; - const DAT5 = 0b0010_0000; - const DAT6 = 0b0100_0000; - const DAT7 = 0b1000_0000; - } -} - -impl GpioDatRegister { - // there is no defined default values in the datasheet so just zeros. - pub fn default() -> Self { - GpioDatRegister::from_bits_truncate(0b0000_0000) - } -} - -bitflags! { - pub struct Adc2CfgRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl Adc2CfgRegister { - pub fn default() -> Self { - Adc2CfgRegister::from_bits_truncate(0b0000_0000) - } - - pub fn get_gain2(&self) -> Adc2Gain { - match self.bits() & 0b0000_0111 { - 0b000 => Adc2Gain::VV1, - 0b001 => Adc2Gain::VV2, - 0b010 => Adc2Gain::VV4, - 0b011 => Adc2Gain::VV8, - 0b100 => Adc2Gain::VV16, - 0b101 => Adc2Gain::VV32, - 0b110 => Adc2Gain::VV64, - 0b111 => Adc2Gain::VV128, - - _ => unreachable!(), - } - } - - pub fn set_gain2( - &mut self, - gain2: Adc2Gain, - ) { - let bits = gain2 as u8; - self.insert(Adc2CfgRegister::from_bits_retain(bits)); - } - - pub fn get_ref2(&self) -> Adc2RefInp { - match (self.bits() & 0b0011_1000) >> 3 { - 0b000 => Adc2RefInp::Int2_5VRef, - 0b001 => Adc2RefInp::ExtAIN0_1, - 0b010 => Adc2RefInp::ExtAIN2_3, - 0b011 => Adc2RefInp::ExtAIN4_5, - 0b100 => Adc2RefInp::IntAnlgSup, - - 0b101..=0b111 => panic!("Reserved ADC2 reference input"), - _ => unreachable!(), - } - } - - pub fn set_ref2( - &mut self, - ref2: Adc2RefInp, - ) { - let bits = ref2 as u8; - self.insert(Adc2CfgRegister::from_bits_retain(bits << 3)); - } - - pub fn get_dr2(&self) -> Adc2DataRate { - match (self.bits() & 0b1100_0000) >> 6 { - 0b00 => Adc2DataRate::SPS10, - 0b01 => Adc2DataRate::SPS100, - 0b10 => Adc2DataRate::SPS400, - 0b11 => Adc2DataRate::SPS800, - - _ => unreachable!(), - } - } - - pub fn set_dr2( - &mut self, - dr2: Adc2DataRate, - ) { - let bits = dr2 as u8; - self.insert(Adc2CfgRegister::from_bits_retain(bits << 6)); - } -} - -bitflags! { - pub struct Adc2MuxRegister: u8 { - const _ = !0; // Source may set any bits - } -} - -impl Adc2MuxRegister { - pub fn default() -> Self { - Adc2MuxRegister::from_bits_truncate(0b0000_0001) - } - - pub fn get_muxn2(&self) -> NegativeInpMux { - match self.bits() & 0b0000_1111 { - 0b0000 => NegativeInpMux::AIN0, - 0b0001 => NegativeInpMux::AIN1, - 0b0010 => NegativeInpMux::AIN2, - 0b0011 => NegativeInpMux::AIN3, - 0b0100 => NegativeInpMux::AIN4, - 0b0101 => NegativeInpMux::AIN5, - 0b0110 => NegativeInpMux::AIN6, - 0b0111 => NegativeInpMux::AIN7, - 0b1000 => NegativeInpMux::AIN8, - 0b1001 => NegativeInpMux::AIN9, - 0b1010 => NegativeInpMux::AINCOM, - 0b1011 => NegativeInpMux::TempSensMonNeg, - 0b1100 => NegativeInpMux::AnlgPwrSupMonNeg, - 0b1101 => NegativeInpMux::DgtlPwrSubMonNeg, - 0b1110 => NegativeInpMux::TDACTestSignalNeg, - 0b1111 => NegativeInpMux::Float, - - _ => unreachable!(), - } - } - - pub fn set_muxn2( - &mut self, - muxn2: NegativeInpMux, - ) { - let bits = muxn2 as u8; - self.insert(Adc2MuxRegister::from_bits_retain(bits)); - } - - pub fn get_muxp2(&self) -> PositiveInpMux { - match (self.bits() & 0b1111_0000) >> 4 { - 0b0000 => PositiveInpMux::AIN0, - 0b0001 => PositiveInpMux::AIN1, - 0b0010 => PositiveInpMux::AIN2, - 0b0011 => PositiveInpMux::AIN3, - 0b0100 => PositiveInpMux::AIN4, - 0b0101 => PositiveInpMux::AIN5, - 0b0110 => PositiveInpMux::AIN6, - 0b0111 => PositiveInpMux::AIN7, - 0b1000 => PositiveInpMux::AIN8, - 0b1001 => PositiveInpMux::AIN9, - 0b1010 => PositiveInpMux::AINCOM, - 0b1011 => PositiveInpMux::TempSensMonPos, - 0b1100 => PositiveInpMux::AnlgPwrSupMonPos, - 0b1101 => PositiveInpMux::DgtlPwrSubMonPos, - 0b1110 => PositiveInpMux::TDACTestSignalPos, - 0b1111 => PositiveInpMux::Float, - - _ => unreachable!(), - } - } - - pub fn set_muxp2( - &mut self, - muxp2: PositiveInpMux, - ) { - let bits = muxp2 as u8; - self.insert(Adc2MuxRegister::from_bits_retain(bits << 4)); - } -} diff --git a/common/ads126x/src/register/enums.rs b/common/ads126x/src/register/enums.rs deleted file mode 100644 index 3527896..0000000 --- a/common/ads126x/src/register/enums.rs +++ /dev/null @@ -1,271 +0,0 @@ -pub enum DevId { - ADS1262 = 0b000, - ADS1263 = 0b001, -} - -/// Conversion delays follow the pattern `D`. -/// - `len` is the length of time where _ is a substitute for a decimal point. -/// - `units` are the units of time where us is microseconds and ms is milliseconds. -/// -/// D8_7us = delay of 8.7 microseconds. D8_8ms = delay of 8.8 milliseconds. -#[repr(u8)] -pub enum ConversionDelay { - DNone = 0b0000, - D8_7us = 0b0001, - D17us = 0b0010, - D35us = 0b0011, - D69us = 0b0100, - D139us = 0b0101, - D278us = 0b0110, - D555us = 0b0111, - D1_1ms = 0b1000, - D2_2ms = 0b1001, - D4_4ms = 0b1010, - D8_8ms = 0b1011, -} - -impl ConversionDelay { - /// Returns the delay in nanoseconds. - pub fn delay(&self) -> u32 { - match self { - Self::DNone => 0, - Self::D8_7us => 8_700, - Self::D17us => 17_000, - Self::D35us => 35_000, - Self::D69us => 69_000, - Self::D139us => 139_000, - Self::D278us => 278_000, - Self::D555us => 555_000, - Self::D1_1ms => 1_100_000, - Self::D2_2ms => 2_200_000, - Self::D4_4ms => 4_400_000, - Self::D8_8ms => 8_800_000, - } - } -} - -#[repr(u8)] -pub enum ChopMode { - Disabled = 0b00, - InChopEnabled = 0b01, - IdacEnabled = 0b10, - InChopAndIdacEnabled = 0b11, -} - -/// SBMAGs follow the pattern `B` or `R`. -/// - `mag` is the magnitude of current or resistance where _ is a substitute for a decimal point. -/// - `units` are the units of current or resistance where uA is microamperes and MOhm is megaohms. -/// -/// B0_5uA = 0.5 microamps of current. R10MOhm = resistance of 10 megaohms. -#[repr(u8)] -pub enum SensorBiasMagnitude { - BNone = 0b000, - B0_5uA = 0b001, - B2uA = 0b010, - B10uA = 0b011, - B50uA = 0b100, - B200uA = 0b101, - R10MOhm = 0b110, -} - -#[repr(u8)] -pub enum CrcMode { - Disabled = 0b00, - Checksum = 0b01, - CRC = 0b10, -} - -#[repr(u8)] -pub enum DigitalFilter { - Sinc1 = 0b000, - Sinc2 = 0b001, - Sinc3 = 0b010, - Sinc4 = 0b011, - FIR = 0b100, -} - -/// Data rates follow the pattern `SPS`. -/// - `num` is the SPS rate where _ is a substitute for a decimal point. -/// -/// SPS2_5 = 2.5 SPS. -#[repr(u8)] -pub enum DataRate { - SPS2_5 = 0b0000, - SPS5 = 0b0001, - SPS10 = 0b0010, - SPS16_6 = 0b0011, // 16.6666... = 50/3 - SPS20 = 0b0100, - SPS50 = 0b0101, - SPS60 = 0b0110, - SPS100 = 0b0111, - SPS400 = 0b1000, - SPS1200 = 0b1001, - SPS2400 = 0b1010, - SPS4800 = 0b1011, - SPS7200 = 0b1100, - SPS14400 = 0b1101, - SPS19200 = 0b1110, - SPS38400 = 0b1111, -} - -#[repr(u8)] -pub enum PGAGain { - VV1 = 0b000, - VV2 = 0b001, - VV4 = 0b010, - VV8 = 0b011, - VV16 = 0b100, - VV32 = 0b101, -} - -#[repr(u8)] -#[derive(PartialEq, Clone, Copy)] -pub enum NegativeInpMux { - AIN0 = 0b0000, - AIN1 = 0b0001, - AIN2 = 0b0010, - AIN3 = 0b0011, - AIN4 = 0b0100, - AIN5 = 0b0101, - AIN6 = 0b0110, - AIN7 = 0b0111, - AIN8 = 0b1000, - AIN9 = 0b1001, - AINCOM = 0b1010, - TempSensMonNeg = 0b1011, - AnlgPwrSupMonNeg = 0b1100, - DgtlPwrSubMonNeg = 0b1101, - TDACTestSignalNeg = 0b1110, - Float = 0b1111, -} - -#[repr(u8)] -#[derive(PartialEq, Clone, Copy)] -pub enum PositiveInpMux { - AIN0 = 0b0000, - AIN1 = 0b0001, - AIN2 = 0b0010, - AIN3 = 0b0011, - AIN4 = 0b0100, - AIN5 = 0b0101, - AIN6 = 0b0110, - AIN7 = 0b0111, - AIN8 = 0b1000, - AIN9 = 0b1001, - AINCOM = 0b1010, - TempSensMonPos = 0b1011, - AnlgPwrSupMonPos = 0b1100, - DgtlPwrSubMonPos = 0b1101, - TDACTestSignalPos = 0b1110, - Float = 0b1111, -} - -#[repr(u8)] -pub enum IdacOutMux { - AIN0 = 0b0000, - AIN1 = 0b0001, - AIN2 = 0b0010, - AIN3 = 0b0011, - AIN4 = 0b0100, - AIN5 = 0b0101, - AIN6 = 0b0110, - AIN7 = 0b0111, - AIN8 = 0b1000, - AIN9 = 0b1001, - AINCOM = 0b1010, - NoConnection = 0b1011, -} - -/// Current magnitudes follow the pattern `I`. -/// - `mag` is the magnitude of current. -/// - `units` are uA meaning microamperes. -/// -/// I50uA = 50 microamps of current. -#[repr(u8)] -pub enum IdacCurMag { - I50uA = 0b0000, - I100uA = 0b0001, - I250uA = 0b0010, - I500uA = 0b0011, - I750uA = 0b0100, - I1000uA = 0b0101, - I1500uA = 0b0110, - I2000uA = 0b0111, - I2500uA = 0b1000, - I3000uA = 0b1001, -} - -#[repr(u8)] -pub enum RefNegativeInp { - Int2_5VRef = 0b000, - ExtAIN1 = 0b001, - ExtAIN3 = 0b010, - ExtAIN5 = 0b011, - IntAnlgSup = 0b100, -} - -#[repr(u8)] -pub enum RefPositiveInp { - Int2_5VRef = 0b000, - ExtAIN0 = 0b001, - ExtAIN2 = 0b010, - ExtAIN4 = 0b011, - IntAnlgSup = 0b100, -} - -/// Voltages are with respect to V_AVSS. -/// Output magnitudes follow the pattern `V`. -/// - `num` is the output magnitude in volts where _ is a substitute for a decimal point. -/// -/// V4_5 = 4.5 V. -#[repr(u8)] -pub enum TdacOutMag { - V4_5 = 0b01001, - V3_5 = 0b01000, - V3 = 0b00111, - V2_75 = 0b00110, - V2_625 = 0b00101, - V2_5625 = 0b00100, - V2_53125 = 0b00011, - V2_515625 = 0b00010, - V2_5078125 = 0b00001, - V2_5 = 0b00000, - V2_4921875 = 0b10001, - V2_484375 = 0b10010, - V2_46875 = 0b10011, - V2_4375 = 0b10100, - V2_375 = 0b10101, - V2_25 = 0b10110, - V2 = 0b10111, - V1_5 = 0b11000, - V0_5 = 0b11001, -} - -#[repr(u8)] -pub enum Adc2Gain { - VV1 = 0b000, - VV2 = 0b001, - VV4 = 0b010, - VV8 = 0b011, - VV16 = 0b100, - VV32 = 0b101, - VV64 = 0b110, - VV128 = 0b111, -} - -#[repr(u8)] -pub enum Adc2RefInp { - Int2_5VRef = 0b000, - ExtAIN0_1 = 0b001, - ExtAIN2_3 = 0b010, - ExtAIN4_5 = 0b011, - IntAnlgSup = 0b100, -} - -#[repr(u8)] -pub enum Adc2DataRate { - SPS10 = 0b00, - SPS100 = 0b01, - SPS400 = 0b10, - SPS800 = 0b11, -} diff --git a/common/messages/.vscode/settings.json b/common/messages/.vscode/settings.json new file mode 100644 index 0000000..35c23bb --- /dev/null +++ b/common/messages/.vscode/settings.json @@ -0,0 +1,8 @@ +{ + "protoc": { + "options": [ + "--proto_path=${workspaceRoot}/common/messages/proto", + "--python_out=${workspaceRoot}/common/messages/python", + ] + }, +} \ No newline at end of file diff --git a/common/messages/Cargo.toml b/common/messages/Cargo.toml index 3053ef3..e3df498 100644 --- a/common/messages/Cargo.toml +++ b/common/messages/Cargo.toml @@ -16,16 +16,13 @@ std = [ [dependencies] bitflags = { version = "2.3.1", default-features = false, features = ["serde"] } -defmt = "0.3.2" -heapless = { version = "0.8", features = ["defmt-03"] } +defmt = { workspace = true } +heapless = { workspace = true } mavlink = { git = "https://github.com/uorocketry/rust-mavlink.git", features = [ "uorocketry", ], default-features = false } prost = { workspace = true } -serde = { version = "1.0.150", features = [ - "derive", - "alloc", -], default-features = false } +serde = { workspace = true } ublox = { git = "https://github.com/uorocketry/ublox", default-features = false, features = [ "serde", ] } diff --git a/common/messages/Makefile.toml b/common/messages/Makefile.toml index cd99622..64dfc4b 100644 --- a/common/messages/Makefile.toml +++ b/common/messages/Makefile.toml @@ -7,11 +7,16 @@ args = ["build"] description = "Build protobuf messages for Python" script_runner = "@shell" script = [ - "rm -rf python", - "mkdir python", - "python3 -m grpc_tools.protoc --proto_path=proto --python_out=python --mypy_out=python --grpc_python_out=python $(find proto -type f -name '*.proto')", - "find python -type d -exec touch {}/__init__.py \\;", - "cat assets/pyproject.toml > python/pyproject.toml" + "rm -rf python/messages", + "mkdir python/messages", + """python3 -m grpc_tools.protoc \ + --proto_path=proto \ + --python_out=python/messages \ + --mypy_out=python/messages \ + --grpc_python_out=python/messages \ + $(find proto -type f -name '*.proto')""", + "find python/messages -type d -exec touch {}/__init__.py \\;", + "./utils/prefix-python-imports.sh python/messages messages", ] [tasks.all] diff --git a/common/messages/assets/pyproject.toml b/common/messages/assets/pyproject.toml deleted file mode 100644 index a5a9895..0000000 --- a/common/messages/assets/pyproject.toml +++ /dev/null @@ -1,7 +0,0 @@ -[project] -name = "messages" -version = "0.1.0" - -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/common/messages/proto/argus/envelope.proto b/common/messages/proto/argus/envelope.proto index d6275f8..527e662 100644 --- a/common/messages/proto/argus/envelope.proto +++ b/common/messages/proto/argus/envelope.proto @@ -2,12 +2,28 @@ syntax = "proto3"; package messages.argus.envelope; -import "argus/temperature/thermocouple_calibration.proto"; import "argus/temperature/thermocouple_reading.proto"; +import "argus/pressure/pressure_reading.proto"; +import "argus/strain/strain_reading.proto"; message Envelope { + Node created_by = 1; oneof message { - temperature.thermocouple_calibration.ThermocoupleCalibration thermocouple_calibration = 1; temperature.thermocouple_reading.ThermocoupleReading thermocouple_reading = 2; + pressure.pressure_reading.PressureReading pressure_reading = 3; + strain.strain_reading.StrainReading strain_reading = 4; } +} + +message Node { + NodeType type = 1; + optional int32 id = 2; +} + +enum NodeType { + UNSPECIFIED = 0; + PHOENIX = 1; + ARGUS_TEMPERATURE = 2; + ARGUS_PRESSURE = 3; + ARGUS_STRAIN = 4; } \ No newline at end of file diff --git a/common/messages/proto/argus/pressure/pressure_channel.proto b/common/messages/proto/argus/pressure/pressure_channel.proto new file mode 100644 index 0000000..6ccb400 --- /dev/null +++ b/common/messages/proto/argus/pressure/pressure_channel.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package messages.argus.pressure.pressure_channel; + +enum PressureChannel { + CHANNEL_1 = 0; + CHANNEL_2 = 1; + CHANNEL_3 = 2; + CHANNEL_4 = 3; +} + diff --git a/common/messages/proto/argus/pressure/pressure_reading.proto b/common/messages/proto/argus/pressure/pressure_reading.proto new file mode 100644 index 0000000..7b0d5d7 --- /dev/null +++ b/common/messages/proto/argus/pressure/pressure_reading.proto @@ -0,0 +1,29 @@ +syntax = "proto3"; + +package messages.argus.pressure.pressure_reading; + +import "argus/adc.proto"; +import "argus/pressure/pressure_channel.proto"; + +message PressureReading { + // Local session from the device that took the reading + optional int32 local_session = 1; + + // ADC device from which the reading was taken + adc.AdcDevice adc_device = 2; + + // Identifier for the pressure within the ADC device + pressure_channel.PressureChannel pressure_channel = 3; + + // Milliseconds since the board's epoch when the reading was recorded + uint64 recorded_at = 4; + + // Voltage difference measured at the pressure sensor wheatstone bridge in millivolts + float voltage = 5; + + // Pressure reading in psi + double pressure = 8; + + // Temperature of the manifold from the NTC resistor at the time of the recording in degrees Celsius + double temperature = 6; +} \ No newline at end of file diff --git a/common/messages/proto/argus/strain/strain_channel.proto b/common/messages/proto/argus/strain/strain_channel.proto new file mode 100644 index 0000000..44287c6 --- /dev/null +++ b/common/messages/proto/argus/strain/strain_channel.proto @@ -0,0 +1,11 @@ +syntax = "proto3"; + +package messages.argus.strain.strain_channel; + +enum StrainChannel { + CHANNEL_1 = 0; + CHANNEL_2 = 1; + CHANNEL_3 = 2; + CHANNEL_4 = 3; +} + diff --git a/common/messages/proto/argus/strain/strain_reading.proto b/common/messages/proto/argus/strain/strain_reading.proto new file mode 100644 index 0000000..826ecd1 --- /dev/null +++ b/common/messages/proto/argus/strain/strain_reading.proto @@ -0,0 +1,26 @@ +syntax = "proto3"; + +package messages.argus.strain.strain_reading; + +import "argus/adc.proto"; +import "argus/strain/strain_channel.proto"; + +message StrainReading { + // Local session from the device that took the reading + optional int32 local_session = 1; + + // ADC device from which the reading was taken + adc.AdcDevice adc_device = 2; + + // Identifier for the strain gauge within the ADC device + strain_channel.StrainChannel strain_channel = 3; + + // Milliseconds since the board's epoch when the reading was recorded + uint64 recorded_at = 4; + + // Voltage difference measured at the strain wheatstone bridge in millivolts + float voltage = 5; + + // Strain reading in microstrain + double strain = 8; +} \ No newline at end of file diff --git a/common/messages/proto/argus/temperature/thermocouple_calibration.proto b/common/messages/proto/argus/temperature/thermocouple_calibration.proto deleted file mode 100644 index 2368e9f..0000000 --- a/common/messages/proto/argus/temperature/thermocouple_calibration.proto +++ /dev/null @@ -1,41 +0,0 @@ -syntax = "proto3"; - -package messages.argus.temperature.thermocouple_calibration; - -import "argus/adc.proto"; -import "argus/temperature/thermocouple_channel.proto"; - -message ThermocoupleCalibration { - oneof message { - SelectThermocoupleRequest select_thermocouple_request = 1; - CollectDataPointRequest collect_data_point_request = 2; - CollectDataPointResponse collect_data_point_response = 3; - PerformRegressionRequest perform_regression_request = 4; - CalibrationResultResponse calibration_result_response = 5; - } - - // Request to select which thermocouple to calibrate - message SelectThermocoupleRequest { - adc.AdcDevice adc_device = 1; - thermocouple_channel.ThermocoupleChannel thermocouple_channel = 2; - } - - // Request/Response for a data collection step during calibration. These datapoints are then later used to perform the linear regression. - message CollectDataPointRequest { - double expected = 1; - } - message CollectDataPointResponse { - double measured = 1; - double expected = 2; - } - - message PerformRegressionRequest {} - - // Response containing the results of the calibration after all data points have been collected - message CalibrationResultResponse { - bool success = 1; - optional double scale = 2; - optional double offset = 3; - } -} - diff --git a/common/messages/python/argus/envelope_pb2.py b/common/messages/python/argus/envelope_pb2.py deleted file mode 100644 index 40e84a4..0000000 --- a/common/messages/python/argus/envelope_pb2.py +++ /dev/null @@ -1,38 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: argus/envelope.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'argus/envelope.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from argus.temperature import thermocouple_calibration_pb2 as argus_dot_temperature_dot_thermocouple__calibration__pb2 -from argus.temperature import thermocouple_reading_pb2 as argus_dot_temperature_dot_thermocouple__reading__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61rgus/envelope.proto\x12\x17messages.argus.envelope\x1a\x30\x61rgus/temperature/thermocouple_calibration.proto\x1a,argus/temperature/thermocouple_reading.proto\"\xed\x01\n\x08\x45nvelope\x12p\n\x18thermocouple_calibration\x18\x01 \x01(\x0b\x32L.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibrationH\x00\x12\x64\n\x14thermocouple_reading\x18\x02 \x01(\x0b\x32\x44.messages.argus.temperature.thermocouple_reading.ThermocoupleReadingH\x00\x42\t\n\x07messageb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.envelope_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_ENVELOPE']._serialized_start=146 - _globals['_ENVELOPE']._serialized_end=383 -# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/argus/envelope_pb2.pyi b/common/messages/python/argus/envelope_pb2.pyi deleted file mode 100644 index 097a1f3..0000000 --- a/common/messages/python/argus/envelope_pb2.pyi +++ /dev/null @@ -1,35 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" - -import argus.temperature.thermocouple_calibration_pb2 -import argus.temperature.thermocouple_reading_pb2 -import builtins -import google.protobuf.descriptor -import google.protobuf.message -import typing - -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -@typing.final -class Envelope(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - THERMOCOUPLE_CALIBRATION_FIELD_NUMBER: builtins.int - THERMOCOUPLE_READING_FIELD_NUMBER: builtins.int - @property - def thermocouple_calibration(self) -> argus.temperature.thermocouple_calibration_pb2.ThermocoupleCalibration: ... - @property - def thermocouple_reading(self) -> argus.temperature.thermocouple_reading_pb2.ThermocoupleReading: ... - def __init__( - self, - *, - thermocouple_calibration: argus.temperature.thermocouple_calibration_pb2.ThermocoupleCalibration | None = ..., - thermocouple_reading: argus.temperature.thermocouple_reading_pb2.ThermocoupleReading | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["message", b"message", "thermocouple_calibration", b"thermocouple_calibration", "thermocouple_reading", b"thermocouple_reading"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["message", b"message", "thermocouple_calibration", b"thermocouple_calibration", "thermocouple_reading", b"thermocouple_reading"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["thermocouple_calibration", "thermocouple_reading"] | None: ... - -global___Envelope = Envelope diff --git a/common/messages/python/argus/temperature/thermocouple_calibration_pb2.py b/common/messages/python/argus/temperature/thermocouple_calibration_pb2.py deleted file mode 100644 index bd666e7..0000000 --- a/common/messages/python/argus/temperature/thermocouple_calibration_pb2.py +++ /dev/null @@ -1,48 +0,0 @@ -# -*- coding: utf-8 -*- -# Generated by the protocol buffer compiler. DO NOT EDIT! -# NO CHECKED-IN PROTOBUF GENCODE -# source: argus/temperature/thermocouple_calibration.proto -# Protobuf Python Version: 6.31.1 -"""Generated protocol buffer code.""" -from google.protobuf import descriptor as _descriptor -from google.protobuf import descriptor_pool as _descriptor_pool -from google.protobuf import runtime_version as _runtime_version -from google.protobuf import symbol_database as _symbol_database -from google.protobuf.internal import builder as _builder -_runtime_version.ValidateProtobufRuntimeVersion( - _runtime_version.Domain.PUBLIC, - 6, - 31, - 1, - '', - 'argus/temperature/thermocouple_calibration.proto' -) -# @@protoc_insertion_point(imports) - -_sym_db = _symbol_database.Default() - - -from argus import adc_pb2 as argus_dot_adc__pb2 -from argus.temperature import thermocouple_channel_pb2 as argus_dot_temperature_dot_thermocouple__channel__pb2 - - -DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n0argus/temperature/thermocouple_calibration.proto\x12\x33messages.argus.temperature.thermocouple_calibration\x1a\x0f\x61rgus/adc.proto\x1a,argus/temperature/thermocouple_channel.proto\"\x98\t\n\x17ThermocoupleCalibration\x12\x8d\x01\n\x1bselect_thermocouple_request\x18\x01 \x01(\x0b\x32\x66.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibration.SelectThermocoupleRequestH\x00\x12\x8a\x01\n\x1a\x63ollect_data_point_request\x18\x02 \x01(\x0b\x32\x64.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibration.CollectDataPointRequestH\x00\x12\x8c\x01\n\x1b\x63ollect_data_point_response\x18\x03 \x01(\x0b\x32\x65.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibration.CollectDataPointResponseH\x00\x12\x8b\x01\n\x1aperform_regression_request\x18\x04 \x01(\x0b\x32\x65.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibration.PerformRegressionRequestH\x00\x12\x8d\x01\n\x1b\x63\x61libration_result_response\x18\x05 \x01(\x0b\x32\x66.messages.argus.temperature.thermocouple_calibration.ThermocoupleCalibration.CalibrationResultResponseH\x00\x1a\xb2\x01\n\x19SelectThermocoupleRequest\x12\x31\n\nadc_device\x18\x01 \x01(\x0e\x32\x1d.messages.argus.adc.AdcDevice\x12\x62\n\x14thermocouple_channel\x18\x02 \x01(\x0e\x32\x44.messages.argus.temperature.thermocouple_channel.ThermocoupleChannel\x1a+\n\x17\x43ollectDataPointRequest\x12\x10\n\x08\x65xpected\x18\x01 \x01(\x01\x1a>\n\x18\x43ollectDataPointResponse\x12\x10\n\x08measured\x18\x01 \x01(\x01\x12\x10\n\x08\x65xpected\x18\x02 \x01(\x01\x1a\x1a\n\x18PerformRegressionRequest\x1aj\n\x19\x43\x61librationResultResponse\x12\x0f\n\x07success\x18\x01 \x01(\x08\x12\x12\n\x05scale\x18\x02 \x01(\x01H\x00\x88\x01\x01\x12\x13\n\x06offset\x18\x03 \x01(\x01H\x01\x88\x01\x01\x42\x08\n\x06_scaleB\t\n\x07_offsetB\t\n\x07messageb\x06proto3') - -_globals = globals() -_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) -_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.temperature.thermocouple_calibration_pb2', _globals) -if not _descriptor._USE_C_DESCRIPTORS: - DESCRIPTOR._loaded_options = None - _globals['_THERMOCOUPLECALIBRATION']._serialized_start=169 - _globals['_THERMOCOUPLECALIBRATION']._serialized_end=1345 - _globals['_THERMOCOUPLECALIBRATION_SELECTTHERMOCOUPLEREQUEST']._serialized_start=911 - _globals['_THERMOCOUPLECALIBRATION_SELECTTHERMOCOUPLEREQUEST']._serialized_end=1089 - _globals['_THERMOCOUPLECALIBRATION_COLLECTDATAPOINTREQUEST']._serialized_start=1091 - _globals['_THERMOCOUPLECALIBRATION_COLLECTDATAPOINTREQUEST']._serialized_end=1134 - _globals['_THERMOCOUPLECALIBRATION_COLLECTDATAPOINTRESPONSE']._serialized_start=1136 - _globals['_THERMOCOUPLECALIBRATION_COLLECTDATAPOINTRESPONSE']._serialized_end=1198 - _globals['_THERMOCOUPLECALIBRATION_PERFORMREGRESSIONREQUEST']._serialized_start=1200 - _globals['_THERMOCOUPLECALIBRATION_PERFORMREGRESSIONREQUEST']._serialized_end=1226 - _globals['_THERMOCOUPLECALIBRATION_CALIBRATIONRESULTRESPONSE']._serialized_start=1228 - _globals['_THERMOCOUPLECALIBRATION_CALIBRATIONRESULTRESPONSE']._serialized_end=1334 -# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/argus/temperature/thermocouple_calibration_pb2.pyi b/common/messages/python/argus/temperature/thermocouple_calibration_pb2.pyi deleted file mode 100644 index d13d2b1..0000000 --- a/common/messages/python/argus/temperature/thermocouple_calibration_pb2.pyi +++ /dev/null @@ -1,130 +0,0 @@ -""" -@generated by mypy-protobuf. Do not edit manually! -isort:skip_file -""" - -import argus.adc_pb2 -import argus.temperature.thermocouple_channel_pb2 -import builtins -import google.protobuf.descriptor -import google.protobuf.message -import typing - -DESCRIPTOR: google.protobuf.descriptor.FileDescriptor - -@typing.final -class ThermocoupleCalibration(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - @typing.final - class SelectThermocoupleRequest(google.protobuf.message.Message): - """Request to select which thermocouple to calibrate""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - ADC_DEVICE_FIELD_NUMBER: builtins.int - THERMOCOUPLE_CHANNEL_FIELD_NUMBER: builtins.int - adc_device: argus.adc_pb2.AdcDevice.ValueType - thermocouple_channel: argus.temperature.thermocouple_channel_pb2.ThermocoupleChannel.ValueType - def __init__( - self, - *, - adc_device: argus.adc_pb2.AdcDevice.ValueType = ..., - thermocouple_channel: argus.temperature.thermocouple_channel_pb2.ThermocoupleChannel.ValueType = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["adc_device", b"adc_device", "thermocouple_channel", b"thermocouple_channel"]) -> None: ... - - @typing.final - class CollectDataPointRequest(google.protobuf.message.Message): - """Request/Response for a data collection step during calibration. These datapoints are then later used to perform the linear regression.""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - EXPECTED_FIELD_NUMBER: builtins.int - expected: builtins.float - def __init__( - self, - *, - expected: builtins.float = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["expected", b"expected"]) -> None: ... - - @typing.final - class CollectDataPointResponse(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - MEASURED_FIELD_NUMBER: builtins.int - EXPECTED_FIELD_NUMBER: builtins.int - measured: builtins.float - expected: builtins.float - def __init__( - self, - *, - measured: builtins.float = ..., - expected: builtins.float = ..., - ) -> None: ... - def ClearField(self, field_name: typing.Literal["expected", b"expected", "measured", b"measured"]) -> None: ... - - @typing.final - class PerformRegressionRequest(google.protobuf.message.Message): - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - def __init__( - self, - ) -> None: ... - - @typing.final - class CalibrationResultResponse(google.protobuf.message.Message): - """Response containing the results of the calibration after all data points have been collected""" - - DESCRIPTOR: google.protobuf.descriptor.Descriptor - - SUCCESS_FIELD_NUMBER: builtins.int - SCALE_FIELD_NUMBER: builtins.int - OFFSET_FIELD_NUMBER: builtins.int - success: builtins.bool - scale: builtins.float - offset: builtins.float - def __init__( - self, - *, - success: builtins.bool = ..., - scale: builtins.float | None = ..., - offset: builtins.float | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["_offset", b"_offset", "_scale", b"_scale", "offset", b"offset", "scale", b"scale"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["_offset", b"_offset", "_scale", b"_scale", "offset", b"offset", "scale", b"scale", "success", b"success"]) -> None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing.Literal["_offset", b"_offset"]) -> typing.Literal["offset"] | None: ... - @typing.overload - def WhichOneof(self, oneof_group: typing.Literal["_scale", b"_scale"]) -> typing.Literal["scale"] | None: ... - - SELECT_THERMOCOUPLE_REQUEST_FIELD_NUMBER: builtins.int - COLLECT_DATA_POINT_REQUEST_FIELD_NUMBER: builtins.int - COLLECT_DATA_POINT_RESPONSE_FIELD_NUMBER: builtins.int - PERFORM_REGRESSION_REQUEST_FIELD_NUMBER: builtins.int - CALIBRATION_RESULT_RESPONSE_FIELD_NUMBER: builtins.int - @property - def select_thermocouple_request(self) -> global___ThermocoupleCalibration.SelectThermocoupleRequest: ... - @property - def collect_data_point_request(self) -> global___ThermocoupleCalibration.CollectDataPointRequest: ... - @property - def collect_data_point_response(self) -> global___ThermocoupleCalibration.CollectDataPointResponse: ... - @property - def perform_regression_request(self) -> global___ThermocoupleCalibration.PerformRegressionRequest: ... - @property - def calibration_result_response(self) -> global___ThermocoupleCalibration.CalibrationResultResponse: ... - def __init__( - self, - *, - select_thermocouple_request: global___ThermocoupleCalibration.SelectThermocoupleRequest | None = ..., - collect_data_point_request: global___ThermocoupleCalibration.CollectDataPointRequest | None = ..., - collect_data_point_response: global___ThermocoupleCalibration.CollectDataPointResponse | None = ..., - perform_regression_request: global___ThermocoupleCalibration.PerformRegressionRequest | None = ..., - calibration_result_response: global___ThermocoupleCalibration.CalibrationResultResponse | None = ..., - ) -> None: ... - def HasField(self, field_name: typing.Literal["calibration_result_response", b"calibration_result_response", "collect_data_point_request", b"collect_data_point_request", "collect_data_point_response", b"collect_data_point_response", "message", b"message", "perform_regression_request", b"perform_regression_request", "select_thermocouple_request", b"select_thermocouple_request"]) -> builtins.bool: ... - def ClearField(self, field_name: typing.Literal["calibration_result_response", b"calibration_result_response", "collect_data_point_request", b"collect_data_point_request", "collect_data_point_response", b"collect_data_point_response", "message", b"message", "perform_regression_request", b"perform_regression_request", "select_thermocouple_request", b"select_thermocouple_request"]) -> None: ... - def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["select_thermocouple_request", "collect_data_point_request", "collect_data_point_response", "perform_regression_request", "calibration_result_response"] | None: ... - -global___ThermocoupleCalibration = ThermocoupleCalibration diff --git a/common/messages/python/__init__.py b/common/messages/python/messages/__init__.py similarity index 100% rename from common/messages/python/__init__.py rename to common/messages/python/messages/__init__.py diff --git a/common/messages/python/argus/__init__.py b/common/messages/python/messages/argus/__init__.py similarity index 100% rename from common/messages/python/argus/__init__.py rename to common/messages/python/messages/argus/__init__.py diff --git a/common/messages/python/argus/adc_pb2.py b/common/messages/python/messages/argus/adc_pb2.py similarity index 100% rename from common/messages/python/argus/adc_pb2.py rename to common/messages/python/messages/argus/adc_pb2.py diff --git a/common/messages/python/argus/adc_pb2.pyi b/common/messages/python/messages/argus/adc_pb2.pyi similarity index 100% rename from common/messages/python/argus/adc_pb2.pyi rename to common/messages/python/messages/argus/adc_pb2.pyi diff --git a/common/messages/python/argus/adc_pb2_grpc.py b/common/messages/python/messages/argus/adc_pb2_grpc.py similarity index 100% rename from common/messages/python/argus/adc_pb2_grpc.py rename to common/messages/python/messages/argus/adc_pb2_grpc.py diff --git a/common/messages/python/messages/argus/envelope_pb2.py b/common/messages/python/messages/argus/envelope_pb2.py new file mode 100644 index 0000000..54bf893 --- /dev/null +++ b/common/messages/python/messages/argus/envelope_pb2.py @@ -0,0 +1,43 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: argus/envelope.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'argus/envelope.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from messages.argus.temperature import thermocouple_reading_pb2 as argus_dot_temperature_dot_thermocouple__reading__pb2 +from messages.argus.pressure import pressure_reading_pb2 as argus_dot_pressure_dot_pressure__reading__pb2 +from messages.argus.strain import strain_reading_pb2 as argus_dot_strain_dot_strain__reading__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x14\x61rgus/envelope.proto\x12\x17messages.argus.envelope\x1a,argus/temperature/thermocouple_reading.proto\x1a%argus/pressure/pressure_reading.proto\x1a!argus/strain/strain_reading.proto\"\xd4\x02\n\x08\x45nvelope\x12\x31\n\ncreated_by\x18\x01 \x01(\x0b\x32\x1d.messages.argus.envelope.Node\x12\x64\n\x14thermocouple_reading\x18\x02 \x01(\x0b\x32\x44.messages.argus.temperature.thermocouple_reading.ThermocoupleReadingH\x00\x12U\n\x10pressure_reading\x18\x03 \x01(\x0b\x32\x39.messages.argus.pressure.pressure_reading.PressureReadingH\x00\x12M\n\x0estrain_reading\x18\x04 \x01(\x0b\x32\x33.messages.argus.strain.strain_reading.StrainReadingH\x00\x42\t\n\x07message\"O\n\x04Node\x12/\n\x04type\x18\x01 \x01(\x0e\x32!.messages.argus.envelope.NodeType\x12\x0f\n\x02id\x18\x02 \x01(\x05H\x00\x88\x01\x01\x42\x05\n\x03_id*e\n\x08NodeType\x12\x0f\n\x0bUNSPECIFIED\x10\x00\x12\x0b\n\x07PHOENIX\x10\x01\x12\x15\n\x11\x41RGUS_TEMPERATURE\x10\x02\x12\x12\n\x0e\x41RGUS_PRESSURE\x10\x03\x12\x10\n\x0c\x41RGUS_STRAIN\x10\x04\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.envelope_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_NODETYPE']._serialized_start=593 + _globals['_NODETYPE']._serialized_end=694 + _globals['_ENVELOPE']._serialized_start=170 + _globals['_ENVELOPE']._serialized_end=510 + _globals['_NODE']._serialized_start=512 + _globals['_NODE']._serialized_end=591 +# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/messages/argus/envelope_pb2.pyi b/common/messages/python/messages/argus/envelope_pb2.pyi new file mode 100644 index 0000000..dd50cb4 --- /dev/null +++ b/common/messages/python/messages/argus/envelope_pb2.pyi @@ -0,0 +1,92 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import messages.argus.pressure.pressure_reading_pb2 +import messages.argus.strain.strain_reading_pb2 +import messages.argus.temperature.thermocouple_reading_pb2 +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import google.protobuf.message +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _NodeType: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _NodeTypeEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_NodeType.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + UNSPECIFIED: _NodeType.ValueType # 0 + PHOENIX: _NodeType.ValueType # 1 + ARGUS_TEMPERATURE: _NodeType.ValueType # 2 + ARGUS_PRESSURE: _NodeType.ValueType # 3 + ARGUS_STRAIN: _NodeType.ValueType # 4 + +class NodeType(_NodeType, metaclass=_NodeTypeEnumTypeWrapper): ... + +UNSPECIFIED: NodeType.ValueType # 0 +PHOENIX: NodeType.ValueType # 1 +ARGUS_TEMPERATURE: NodeType.ValueType # 2 +ARGUS_PRESSURE: NodeType.ValueType # 3 +ARGUS_STRAIN: NodeType.ValueType # 4 +global___NodeType = NodeType + +@typing.final +class Envelope(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + CREATED_BY_FIELD_NUMBER: builtins.int + THERMOCOUPLE_READING_FIELD_NUMBER: builtins.int + PRESSURE_READING_FIELD_NUMBER: builtins.int + STRAIN_READING_FIELD_NUMBER: builtins.int + @property + def created_by(self) -> global___Node: ... + @property + def thermocouple_reading(self) -> argus.temperature.thermocouple_reading_pb2.ThermocoupleReading: ... + @property + def pressure_reading(self) -> argus.pressure.pressure_reading_pb2.PressureReading: ... + @property + def strain_reading(self) -> argus.strain.strain_reading_pb2.StrainReading: ... + def __init__( + self, + *, + created_by: global___Node | None = ..., + thermocouple_reading: argus.temperature.thermocouple_reading_pb2.ThermocoupleReading | None = ..., + pressure_reading: argus.pressure.pressure_reading_pb2.PressureReading | None = ..., + strain_reading: argus.strain.strain_reading_pb2.StrainReading | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["created_by", b"created_by", "message", b"message", "pressure_reading", b"pressure_reading", "strain_reading", b"strain_reading", "thermocouple_reading", b"thermocouple_reading"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["created_by", b"created_by", "message", b"message", "pressure_reading", b"pressure_reading", "strain_reading", b"strain_reading", "thermocouple_reading", b"thermocouple_reading"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["message", b"message"]) -> typing.Literal["thermocouple_reading", "pressure_reading", "strain_reading"] | None: ... + +global___Envelope = Envelope + +@typing.final +class Node(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + TYPE_FIELD_NUMBER: builtins.int + ID_FIELD_NUMBER: builtins.int + type: global___NodeType.ValueType + id: builtins.int + def __init__( + self, + *, + type: global___NodeType.ValueType = ..., + id: builtins.int | None = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_id", b"_id", "id", b"id"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_id", b"_id", "id", b"id", "type", b"type"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_id", b"_id"]) -> typing.Literal["id"] | None: ... + +global___Node = Node diff --git a/common/messages/python/argus/envelope_pb2_grpc.py b/common/messages/python/messages/argus/envelope_pb2_grpc.py similarity index 100% rename from common/messages/python/argus/envelope_pb2_grpc.py rename to common/messages/python/messages/argus/envelope_pb2_grpc.py diff --git a/common/messages/python/argus/temperature/__init__.py b/common/messages/python/messages/argus/pressure/__init__.py similarity index 100% rename from common/messages/python/argus/temperature/__init__.py rename to common/messages/python/messages/argus/pressure/__init__.py diff --git a/common/messages/python/messages/argus/pressure/pressure_channel_pb2.py b/common/messages/python/messages/argus/pressure/pressure_channel_pb2.py new file mode 100644 index 0000000..f94f640 --- /dev/null +++ b/common/messages/python/messages/argus/pressure/pressure_channel_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: argus/pressure/pressure_channel.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'argus/pressure/pressure_channel.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%argus/pressure/pressure_channel.proto\x12(messages.argus.pressure.pressure_channel*M\n\x0fPressureChannel\x12\r\n\tCHANNEL_1\x10\x00\x12\r\n\tCHANNEL_2\x10\x01\x12\r\n\tCHANNEL_3\x10\x02\x12\r\n\tCHANNEL_4\x10\x03\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.pressure.pressure_channel_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_PRESSURECHANNEL']._serialized_start=83 + _globals['_PRESSURECHANNEL']._serialized_end=160 +# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/messages/argus/pressure/pressure_channel_pb2.pyi b/common/messages/python/messages/argus/pressure/pressure_channel_pb2.pyi new file mode 100644 index 0000000..ac341b6 --- /dev/null +++ b/common/messages/python/messages/argus/pressure/pressure_channel_pb2.pyi @@ -0,0 +1,36 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _PressureChannel: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _PressureChannelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_PressureChannel.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CHANNEL_1: _PressureChannel.ValueType # 0 + CHANNEL_2: _PressureChannel.ValueType # 1 + CHANNEL_3: _PressureChannel.ValueType # 2 + CHANNEL_4: _PressureChannel.ValueType # 3 + +class PressureChannel(_PressureChannel, metaclass=_PressureChannelEnumTypeWrapper): ... + +CHANNEL_1: PressureChannel.ValueType # 0 +CHANNEL_2: PressureChannel.ValueType # 1 +CHANNEL_3: PressureChannel.ValueType # 2 +CHANNEL_4: PressureChannel.ValueType # 3 +global___PressureChannel = PressureChannel diff --git a/common/messages/python/argus/temperature/thermocouple_calibration_pb2_grpc.py b/common/messages/python/messages/argus/pressure/pressure_channel_pb2_grpc.py similarity index 88% rename from common/messages/python/argus/temperature/thermocouple_calibration_pb2_grpc.py rename to common/messages/python/messages/argus/pressure/pressure_channel_pb2_grpc.py index 651ecb4..90a9e9e 100644 --- a/common/messages/python/argus/temperature/thermocouple_calibration_pb2_grpc.py +++ b/common/messages/python/messages/argus/pressure/pressure_channel_pb2_grpc.py @@ -17,7 +17,7 @@ if _version_not_supported: raise RuntimeError( f'The grpc package installed is at version {GRPC_VERSION},' - + f' but the generated code in argus/temperature/thermocouple_calibration_pb2_grpc.py depends on' + + f' but the generated code in argus/pressure/pressure_channel_pb2_grpc.py depends on' + f' grpcio>={GRPC_GENERATED_VERSION}.' + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' diff --git a/common/messages/python/messages/argus/pressure/pressure_reading_pb2.py b/common/messages/python/messages/argus/pressure/pressure_reading_pb2.py new file mode 100644 index 0000000..578ce0a --- /dev/null +++ b/common/messages/python/messages/argus/pressure/pressure_reading_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: argus/pressure/pressure_reading.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'argus/pressure/pressure_reading.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from messages.argus import adc_pb2 as argus_dot_adc__pb2 +from messages.argus.pressure import pressure_channel_pb2 as argus_dot_pressure_dot_pressure__channel__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%argus/pressure/pressure_reading.proto\x12(messages.argus.pressure.pressure_reading\x1a\x0f\x61rgus/adc.proto\x1a%argus/pressure/pressure_channel.proto\"\x94\x02\n\x0fPressureReading\x12\x1a\n\rlocal_session\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\nadc_device\x18\x02 \x01(\x0e\x32\x1d.messages.argus.adc.AdcDevice\x12S\n\x10pressure_channel\x18\x03 \x01(\x0e\x32\x39.messages.argus.pressure.pressure_channel.PressureChannel\x12\x13\n\x0brecorded_at\x18\x04 \x01(\x04\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x10\n\x08pressure\x18\x08 \x01(\x01\x12\x13\n\x0btemperature\x18\x06 \x01(\x01\x42\x10\n\x0e_local_sessionb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.pressure.pressure_reading_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_PRESSUREREADING']._serialized_start=140 + _globals['_PRESSUREREADING']._serialized_end=416 +# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/messages/argus/pressure/pressure_reading_pb2.pyi b/common/messages/python/messages/argus/pressure/pressure_reading_pb2.pyi new file mode 100644 index 0000000..6191093 --- /dev/null +++ b/common/messages/python/messages/argus/pressure/pressure_reading_pb2.pyi @@ -0,0 +1,55 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import messages.argus.adc_pb2 +import messages.argus.pressure.pressure_channel_pb2 +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class PressureReading(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_SESSION_FIELD_NUMBER: builtins.int + ADC_DEVICE_FIELD_NUMBER: builtins.int + PRESSURE_CHANNEL_FIELD_NUMBER: builtins.int + RECORDED_AT_FIELD_NUMBER: builtins.int + VOLTAGE_FIELD_NUMBER: builtins.int + PRESSURE_FIELD_NUMBER: builtins.int + TEMPERATURE_FIELD_NUMBER: builtins.int + local_session: builtins.int + """Local session from the device that took the reading""" + adc_device: argus.adc_pb2.AdcDevice.ValueType + """ADC device from which the reading was taken""" + pressure_channel: argus.pressure.pressure_channel_pb2.PressureChannel.ValueType + """Identifier for the pressure within the ADC device""" + recorded_at: builtins.int + """Milliseconds since the board's epoch when the reading was recorded""" + voltage: builtins.float + """Voltage difference measured at the pressure sensor wheatstone bridge in millivolts""" + pressure: builtins.float + """Pressure reading in psi""" + temperature: builtins.float + """Temperature of the manifold from the NTC resistor at the time of the recording in degrees Celsius""" + def __init__( + self, + *, + local_session: builtins.int | None = ..., + adc_device: argus.adc_pb2.AdcDevice.ValueType = ..., + pressure_channel: argus.pressure.pressure_channel_pb2.PressureChannel.ValueType = ..., + recorded_at: builtins.int = ..., + voltage: builtins.float = ..., + pressure: builtins.float = ..., + temperature: builtins.float = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_local_session", b"_local_session", "local_session", b"local_session"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_local_session", b"_local_session", "adc_device", b"adc_device", "local_session", b"local_session", "pressure", b"pressure", "pressure_channel", b"pressure_channel", "recorded_at", b"recorded_at", "temperature", b"temperature", "voltage", b"voltage"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_local_session", b"_local_session"]) -> typing.Literal["local_session"] | None: ... + +global___PressureReading = PressureReading diff --git a/common/messages/python/messages/argus/pressure/pressure_reading_pb2_grpc.py b/common/messages/python/messages/argus/pressure/pressure_reading_pb2_grpc.py new file mode 100644 index 0000000..12dfc27 --- /dev/null +++ b/common/messages/python/messages/argus/pressure/pressure_reading_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.75.1' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in argus/pressure/pressure_reading_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/common/messages/python/argus/service_pb2.py b/common/messages/python/messages/argus/service_pb2.py similarity index 95% rename from common/messages/python/argus/service_pb2.py rename to common/messages/python/messages/argus/service_pb2.py index 1ea8525..e6d6863 100644 --- a/common/messages/python/argus/service_pb2.py +++ b/common/messages/python/messages/argus/service_pb2.py @@ -23,7 +23,7 @@ from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 -from argus import envelope_pb2 as argus_dot_envelope__pb2 +from messages.argus import envelope_pb2 as argus_dot_envelope__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x13\x61rgus/service.proto\x12\x16messages.argus.service\x1a\x1bgoogle/protobuf/empty.proto\x1a\x14\x61rgus/envelope.proto2T\n\x05\x41rgus\x12K\n\x0cSendEnvelope\x12!.messages.argus.envelope.Envelope\x1a\x16.google.protobuf.Empty\"\x00\x62\x06proto3') diff --git a/common/messages/python/argus/service_pb2.pyi b/common/messages/python/messages/argus/service_pb2.pyi similarity index 100% rename from common/messages/python/argus/service_pb2.pyi rename to common/messages/python/messages/argus/service_pb2.pyi diff --git a/common/messages/python/argus/service_pb2_grpc.py b/common/messages/python/messages/argus/service_pb2_grpc.py similarity index 98% rename from common/messages/python/argus/service_pb2_grpc.py rename to common/messages/python/messages/argus/service_pb2_grpc.py index 0237b16..7b49e34 100644 --- a/common/messages/python/argus/service_pb2_grpc.py +++ b/common/messages/python/messages/argus/service_pb2_grpc.py @@ -3,7 +3,7 @@ import grpc import warnings -from argus import envelope_pb2 as argus_dot_envelope__pb2 +from messages.argus import envelope_pb2 as argus_dot_envelope__pb2 from google.protobuf import empty_pb2 as google_dot_protobuf_dot_empty__pb2 GRPC_GENERATED_VERSION = '1.75.1' diff --git a/common/messages/python/messages/argus/strain/__init__.py b/common/messages/python/messages/argus/strain/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/messages/python/messages/argus/strain/strain_channel_pb2.py b/common/messages/python/messages/argus/strain/strain_channel_pb2.py new file mode 100644 index 0000000..836728e --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_channel_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: argus/strain/strain_channel.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'argus/strain/strain_channel.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!argus/strain/strain_channel.proto\x12$messages.argus.strain.strain_channel*K\n\rStrainChannel\x12\r\n\tCHANNEL_1\x10\x00\x12\r\n\tCHANNEL_2\x10\x01\x12\r\n\tCHANNEL_3\x10\x02\x12\r\n\tCHANNEL_4\x10\x03\x62\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.strain.strain_channel_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_STRAINCHANNEL']._serialized_start=75 + _globals['_STRAINCHANNEL']._serialized_end=150 +# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/messages/argus/strain/strain_channel_pb2.pyi b/common/messages/python/messages/argus/strain/strain_channel_pb2.pyi new file mode 100644 index 0000000..924ca8f --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_channel_pb2.pyi @@ -0,0 +1,36 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import builtins +import google.protobuf.descriptor +import google.protobuf.internal.enum_type_wrapper +import sys +import typing + +if sys.version_info >= (3, 10): + import typing as typing_extensions +else: + import typing_extensions + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +class _StrainChannel: + ValueType = typing.NewType("ValueType", builtins.int) + V: typing_extensions.TypeAlias = ValueType + +class _StrainChannelEnumTypeWrapper(google.protobuf.internal.enum_type_wrapper._EnumTypeWrapper[_StrainChannel.ValueType], builtins.type): + DESCRIPTOR: google.protobuf.descriptor.EnumDescriptor + CHANNEL_1: _StrainChannel.ValueType # 0 + CHANNEL_2: _StrainChannel.ValueType # 1 + CHANNEL_3: _StrainChannel.ValueType # 2 + CHANNEL_4: _StrainChannel.ValueType # 3 + +class StrainChannel(_StrainChannel, metaclass=_StrainChannelEnumTypeWrapper): ... + +CHANNEL_1: StrainChannel.ValueType # 0 +CHANNEL_2: StrainChannel.ValueType # 1 +CHANNEL_3: StrainChannel.ValueType # 2 +CHANNEL_4: StrainChannel.ValueType # 3 +global___StrainChannel = StrainChannel diff --git a/common/messages/python/messages/argus/strain/strain_channel_pb2_grpc.py b/common/messages/python/messages/argus/strain/strain_channel_pb2_grpc.py new file mode 100644 index 0000000..3593a49 --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_channel_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.75.1' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in argus/strain/strain_channel_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/common/messages/python/messages/argus/strain/strain_reading_pb2.py b/common/messages/python/messages/argus/strain/strain_reading_pb2.py new file mode 100644 index 0000000..bf8d780 --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_reading_pb2.py @@ -0,0 +1,38 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# NO CHECKED-IN PROTOBUF GENCODE +# source: argus/strain/strain_reading.proto +# Protobuf Python Version: 6.31.1 +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import descriptor_pool as _descriptor_pool +from google.protobuf import runtime_version as _runtime_version +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import builder as _builder +_runtime_version.ValidateProtobufRuntimeVersion( + _runtime_version.Domain.PUBLIC, + 6, + 31, + 1, + '', + 'argus/strain/strain_reading.proto' +) +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + + +from messages.argus import adc_pb2 as argus_dot_adc__pb2 +from messages.argus.strain import strain_channel_pb2 as argus_dot_strain_dot_strain__channel__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n!argus/strain/strain_reading.proto\x12$messages.argus.strain.strain_reading\x1a\x0f\x61rgus/adc.proto\x1a!argus/strain/strain_channel.proto\"\xf3\x01\n\rStrainReading\x12\x1a\n\rlocal_session\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\nadc_device\x18\x02 \x01(\x0e\x32\x1d.messages.argus.adc.AdcDevice\x12K\n\x0estrain_channel\x18\x03 \x01(\x0e\x32\x33.messages.argus.strain.strain_channel.StrainChannel\x12\x13\n\x0brecorded_at\x18\x04 \x01(\x04\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x0e\n\x06strain\x18\x08 \x01(\x01\x42\x10\n\x0e_local_sessionb\x06proto3') + +_globals = globals() +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'argus.strain.strain_reading_pb2', _globals) +if not _descriptor._USE_C_DESCRIPTORS: + DESCRIPTOR._loaded_options = None + _globals['_STRAINREADING']._serialized_start=128 + _globals['_STRAINREADING']._serialized_end=371 +# @@protoc_insertion_point(module_scope) diff --git a/common/messages/python/messages/argus/strain/strain_reading_pb2.pyi b/common/messages/python/messages/argus/strain/strain_reading_pb2.pyi new file mode 100644 index 0000000..655e2a8 --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_reading_pb2.pyi @@ -0,0 +1,51 @@ +""" +@generated by mypy-protobuf. Do not edit manually! +isort:skip_file +""" + +import messages.argus.adc_pb2 +import messages.argus.strain.strain_channel_pb2 +import builtins +import google.protobuf.descriptor +import google.protobuf.message +import typing + +DESCRIPTOR: google.protobuf.descriptor.FileDescriptor + +@typing.final +class StrainReading(google.protobuf.message.Message): + DESCRIPTOR: google.protobuf.descriptor.Descriptor + + LOCAL_SESSION_FIELD_NUMBER: builtins.int + ADC_DEVICE_FIELD_NUMBER: builtins.int + STRAIN_CHANNEL_FIELD_NUMBER: builtins.int + RECORDED_AT_FIELD_NUMBER: builtins.int + VOLTAGE_FIELD_NUMBER: builtins.int + STRAIN_FIELD_NUMBER: builtins.int + local_session: builtins.int + """Local session from the device that took the reading""" + adc_device: argus.adc_pb2.AdcDevice.ValueType + """ADC device from which the reading was taken""" + strain_channel: argus.strain.strain_channel_pb2.StrainChannel.ValueType + """Identifier for the strain gauge within the ADC device""" + recorded_at: builtins.int + """Milliseconds since the board's epoch when the reading was recorded""" + voltage: builtins.float + """Voltage difference measured at the strain wheatstone bridge in millivolts""" + strain: builtins.float + """Strain reading in microstrain""" + def __init__( + self, + *, + local_session: builtins.int | None = ..., + adc_device: argus.adc_pb2.AdcDevice.ValueType = ..., + strain_channel: argus.strain.strain_channel_pb2.StrainChannel.ValueType = ..., + recorded_at: builtins.int = ..., + voltage: builtins.float = ..., + strain: builtins.float = ..., + ) -> None: ... + def HasField(self, field_name: typing.Literal["_local_session", b"_local_session", "local_session", b"local_session"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_local_session", b"_local_session", "adc_device", b"adc_device", "local_session", b"local_session", "recorded_at", b"recorded_at", "strain", b"strain", "strain_channel", b"strain_channel", "voltage", b"voltage"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_local_session", b"_local_session"]) -> typing.Literal["local_session"] | None: ... + +global___StrainReading = StrainReading diff --git a/common/messages/python/messages/argus/strain/strain_reading_pb2_grpc.py b/common/messages/python/messages/argus/strain/strain_reading_pb2_grpc.py new file mode 100644 index 0000000..c648f9c --- /dev/null +++ b/common/messages/python/messages/argus/strain/strain_reading_pb2_grpc.py @@ -0,0 +1,24 @@ +# Generated by the gRPC Python protocol compiler plugin. DO NOT EDIT! +"""Client and server classes corresponding to protobuf-defined services.""" +import grpc +import warnings + + +GRPC_GENERATED_VERSION = '1.75.1' +GRPC_VERSION = grpc.__version__ +_version_not_supported = False + +try: + from grpc._utilities import first_version_is_lower + _version_not_supported = first_version_is_lower(GRPC_VERSION, GRPC_GENERATED_VERSION) +except ImportError: + _version_not_supported = True + +if _version_not_supported: + raise RuntimeError( + f'The grpc package installed is at version {GRPC_VERSION},' + + f' but the generated code in argus/strain/strain_reading_pb2_grpc.py depends on' + + f' grpcio>={GRPC_GENERATED_VERSION}.' + + f' Please upgrade your grpc module to grpcio>={GRPC_GENERATED_VERSION}' + + f' or downgrade your generated code using grpcio-tools<={GRPC_VERSION}.' + ) diff --git a/common/messages/python/messages/argus/temperature/__init__.py b/common/messages/python/messages/argus/temperature/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/common/messages/python/argus/temperature/thermocouple_channel_pb2.py b/common/messages/python/messages/argus/temperature/thermocouple_channel_pb2.py similarity index 100% rename from common/messages/python/argus/temperature/thermocouple_channel_pb2.py rename to common/messages/python/messages/argus/temperature/thermocouple_channel_pb2.py diff --git a/common/messages/python/argus/temperature/thermocouple_channel_pb2.pyi b/common/messages/python/messages/argus/temperature/thermocouple_channel_pb2.pyi similarity index 100% rename from common/messages/python/argus/temperature/thermocouple_channel_pb2.pyi rename to common/messages/python/messages/argus/temperature/thermocouple_channel_pb2.pyi diff --git a/common/messages/python/argus/temperature/thermocouple_channel_pb2_grpc.py b/common/messages/python/messages/argus/temperature/thermocouple_channel_pb2_grpc.py similarity index 100% rename from common/messages/python/argus/temperature/thermocouple_channel_pb2_grpc.py rename to common/messages/python/messages/argus/temperature/thermocouple_channel_pb2_grpc.py diff --git a/common/messages/python/argus/temperature/thermocouple_reading_pb2.py b/common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.py similarity index 92% rename from common/messages/python/argus/temperature/thermocouple_reading_pb2.py rename to common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.py index 7d02690..025cc59 100644 --- a/common/messages/python/argus/temperature/thermocouple_reading_pb2.py +++ b/common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.py @@ -22,8 +22,8 @@ _sym_db = _symbol_database.Default() -from argus import adc_pb2 as argus_dot_adc__pb2 -from argus.temperature import thermocouple_channel_pb2 as argus_dot_temperature_dot_thermocouple__channel__pb2 +from messages.argus import adc_pb2 as argus_dot_adc__pb2 +from messages.argus.temperature import thermocouple_channel_pb2 as argus_dot_temperature_dot_thermocouple__channel__pb2 DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n,argus/temperature/thermocouple_reading.proto\x12/messages.argus.temperature.thermocouple_reading\x1a\x0f\x61rgus/adc.proto\x1a,argus/temperature/thermocouple_channel.proto\"\xe7\x02\n\x13ThermocoupleReading\x12\x1a\n\rlocal_session\x18\x01 \x01(\x05H\x00\x88\x01\x01\x12\x31\n\nadc_device\x18\x02 \x01(\x0e\x32\x1d.messages.argus.adc.AdcDevice\x12\x62\n\x14thermocouple_channel\x18\x03 \x01(\x0e\x32\x44.messages.argus.temperature.thermocouple_channel.ThermocoupleChannel\x12\x13\n\x0brecorded_at\x18\x04 \x01(\x04\x12\x0f\n\x07voltage\x18\x05 \x01(\x02\x12\x1f\n\x17\x63ompensated_temperature\x18\x06 \x01(\x01\x12!\n\x19uncompensated_temperature\x18\x07 \x01(\x01\x12!\n\x19\x63old_junction_temperature\x18\x08 \x01(\x02\x42\x10\n\x0e_local_sessionb\x06proto3') diff --git a/common/messages/python/argus/temperature/thermocouple_reading_pb2.pyi b/common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.pyi similarity index 63% rename from common/messages/python/argus/temperature/thermocouple_reading_pb2.pyi rename to common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.pyi index 3c821d7..91652cb 100644 --- a/common/messages/python/argus/temperature/thermocouple_reading_pb2.pyi +++ b/common/messages/python/messages/argus/temperature/thermocouple_reading_pb2.pyi @@ -3,8 +3,8 @@ isort:skip_file """ -import argus.adc_pb2 -import argus.temperature.thermocouple_channel_pb2 +import messages.argus.adc_pb2 +import messages.argus.temperature.thermocouple_channel_pb2 import builtins import google.protobuf.descriptor import google.protobuf.message @@ -28,9 +28,7 @@ class ThermocoupleReading(google.protobuf.message.Message): """Local session from the device that took the reading""" adc_device: argus.adc_pb2.AdcDevice.ValueType """ADC device from which the reading was taken""" - thermocouple_channel: ( - argus.temperature.thermocouple_channel_pb2.ThermocoupleChannel.ValueType - ) + thermocouple_channel: argus.temperature.thermocouple_channel_pb2.ThermocoupleChannel.ValueType """Identifier for the thermocouple within the ADC device""" recorded_at: builtins.int """Milliseconds since the board's epoch when the reading was recorded""" @@ -54,37 +52,8 @@ class ThermocoupleReading(google.protobuf.message.Message): uncompensated_temperature: builtins.float = ..., cold_junction_temperature: builtins.float = ..., ) -> None: ... - def HasField( - self, - field_name: typing.Literal[ - "_local_session", b"_local_session", "local_session", b"local_session" - ], - ) -> builtins.bool: ... - def ClearField( - self, - field_name: typing.Literal[ - "_local_session", - b"_local_session", - "adc_device", - b"adc_device", - "cold_junction_temperature", - b"cold_junction_temperature", - "compensated_temperature", - b"compensated_temperature", - "local_session", - b"local_session", - "recorded_at", - b"recorded_at", - "thermocouple_channel", - b"thermocouple_channel", - "uncompensated_temperature", - b"uncompensated_temperature", - "voltage", - b"voltage", - ], - ) -> None: ... - def WhichOneof( - self, oneof_group: typing.Literal["_local_session", b"_local_session"] - ) -> typing.Literal["local_session"] | None: ... + def HasField(self, field_name: typing.Literal["_local_session", b"_local_session", "local_session", b"local_session"]) -> builtins.bool: ... + def ClearField(self, field_name: typing.Literal["_local_session", b"_local_session", "adc_device", b"adc_device", "cold_junction_temperature", b"cold_junction_temperature", "compensated_temperature", b"compensated_temperature", "local_session", b"local_session", "recorded_at", b"recorded_at", "thermocouple_channel", b"thermocouple_channel", "uncompensated_temperature", b"uncompensated_temperature", "voltage", b"voltage"]) -> None: ... + def WhichOneof(self, oneof_group: typing.Literal["_local_session", b"_local_session"]) -> typing.Literal["local_session"] | None: ... global___ThermocoupleReading = ThermocoupleReading diff --git a/common/messages/python/argus/temperature/thermocouple_reading_pb2_grpc.py b/common/messages/python/messages/argus/temperature/thermocouple_reading_pb2_grpc.py similarity index 100% rename from common/messages/python/argus/temperature/thermocouple_reading_pb2_grpc.py rename to common/messages/python/messages/argus/temperature/thermocouple_reading_pb2_grpc.py diff --git a/common/messages/python/pyproject.toml b/common/messages/python/pyproject.toml index a5a9895..079d878 100644 --- a/common/messages/python/pyproject.toml +++ b/common/messages/python/pyproject.toml @@ -1,7 +1,3 @@ [project] name = "messages" version = "0.1.0" - -[build-system] -requires = ["setuptools"] -build-backend = "setuptools.build_meta" \ No newline at end of file diff --git a/common/messages/src/argus/mod.rs b/common/messages/src/argus/mod.rs index 2cb5454..c8c18e4 100644 --- a/common/messages/src/argus/mod.rs +++ b/common/messages/src/argus/mod.rs @@ -1,3 +1,5 @@ pub mod adc; pub mod envelope; +pub mod pressure; +pub mod strain; pub mod temperature; diff --git a/common/messages/src/argus/pressure/mod.rs b/common/messages/src/argus/pressure/mod.rs new file mode 100644 index 0000000..91385e7 --- /dev/null +++ b/common/messages/src/argus/pressure/mod.rs @@ -0,0 +1,2 @@ +pub mod pressure_channel; +pub mod pressure_reading; diff --git a/common/messages/src/argus/pressure/pressure_channel.rs b/common/messages/src/argus/pressure/pressure_channel.rs new file mode 100644 index 0000000..9ed46fd --- /dev/null +++ b/common/messages/src/argus/pressure/pressure_channel.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/messages.argus.pressure.pressure_channel.rs")); diff --git a/common/messages/src/argus/pressure/pressure_reading.rs b/common/messages/src/argus/pressure/pressure_reading.rs new file mode 100644 index 0000000..6864697 --- /dev/null +++ b/common/messages/src/argus/pressure/pressure_reading.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/messages.argus.pressure.pressure_reading.rs")); diff --git a/common/messages/src/argus/strain/mod.rs b/common/messages/src/argus/strain/mod.rs new file mode 100644 index 0000000..86b4e60 --- /dev/null +++ b/common/messages/src/argus/strain/mod.rs @@ -0,0 +1,2 @@ +pub mod strain_channel; +pub mod strain_reading; diff --git a/common/messages/src/argus/strain/strain_channel.rs b/common/messages/src/argus/strain/strain_channel.rs new file mode 100644 index 0000000..3dc3ca9 --- /dev/null +++ b/common/messages/src/argus/strain/strain_channel.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/messages.argus.strain.strain_channel.rs")); diff --git a/common/messages/src/argus/strain/strain_reading.rs b/common/messages/src/argus/strain/strain_reading.rs new file mode 100644 index 0000000..566be6c --- /dev/null +++ b/common/messages/src/argus/strain/strain_reading.rs @@ -0,0 +1 @@ +include!(concat!(env!("OUT_DIR"), "/messages.argus.strain.strain_reading.rs")); diff --git a/common/messages/src/argus/temperature/mod.rs b/common/messages/src/argus/temperature/mod.rs index f06a41e..7a072c3 100644 --- a/common/messages/src/argus/temperature/mod.rs +++ b/common/messages/src/argus/temperature/mod.rs @@ -1,3 +1,2 @@ -pub mod thermocouple_calibration; pub mod thermocouple_channel; pub mod thermocouple_reading; diff --git a/common/messages/src/argus/temperature/thermocouple_calibration.rs b/common/messages/src/argus/temperature/thermocouple_calibration.rs deleted file mode 100644 index d8323d0..0000000 --- a/common/messages/src/argus/temperature/thermocouple_calibration.rs +++ /dev/null @@ -1 +0,0 @@ -include!(concat!(env!("OUT_DIR"), "/messages.argus.temperature.thermocouple_calibration.rs")); diff --git a/common/messages/utils/prefix-python-imports.sh b/common/messages/utils/prefix-python-imports.sh new file mode 100755 index 0000000..984807d --- /dev/null +++ b/common/messages/utils/prefix-python-imports.sh @@ -0,0 +1,28 @@ +#!/bin/bash +# Tool to prefix all python imports in a given proto path with a given package prefix. +# Since protobuf currently doesn't support option python_package, this is a workaround to ensure that generated python files are properly namespaced. +set -e + +PROTO_PATH=$1 +PREFIX=$2 + +if [ -z "$PROTO_PATH" ]; then + echo "Usage: $0 "; + exit 1; +fi + +if [ -z "$PREFIX" ]; then + echo "Usage: $0 "; + exit 1; +fi + +cd "$PROTO_PATH" + +for dir in $(find . -type d -maxdepth 1 ! -path "."); do + pkgName="$( echo $dir | perl -pE 's/^\.\///g' | perl -pE 's/\//\./g' )" + echo "$pkgName -> $PREFIX.$pkgName" + pkgRegex="$( echo $pkgName | perl -pE 's/\./\\\./g' )" + find $dir -type f -name "*.py*" | xargs perl -pi -E "s/^from ($pkgName)/from $PREFIX.\$1/g" + find $dir -type f -name "*.py*" | xargs perl -pi -E "s/^import ($pkgName)/import $PREFIX.\$1/g" + find $dir -type f -name "*.pyi" | xargs perl -pi -E "s/\[($pkgName)\./\[$PREFIX.\$1\./g" +done \ No newline at end of file diff --git a/common/straingauge-converter/Cargo.toml b/common/straingauge-converter/Cargo.toml deleted file mode 100644 index c006e44..0000000 --- a/common/straingauge-converter/Cargo.toml +++ /dev/null @@ -1,4 +0,0 @@ -[package] -name = "straingauge-converter" -version = "0.1.1" -edition = "2024" \ No newline at end of file diff --git a/common/straingauge-converter/src/lib.rs b/common/straingauge-converter/src/lib.rs deleted file mode 100644 index fffe184..0000000 --- a/common/straingauge-converter/src/lib.rs +++ /dev/null @@ -1,57 +0,0 @@ -#![no_std] -//! This crate contains code to convert strain gauge voltages to temperatures. - -/// Coefficients for strain gauge conversion -const GAUGE_FACTOR_DEFAULT: f64 = 2.0; -const V_REF: f64 = 5.0; // Output voltage reference - -// Function to convert voltage to strain in quarter bridge configuration -// -// @param voltage: f64 - The voltage output from the strain gauge -// @param gauge_factor: f64 - The gauge factor of the strain gauge default is 2.0 -// @return strain: f64 - The calculated strain value -pub fn voltage_to_strain_quart( - voltage: f64, - gauge_factor: f64, -) -> f64 { - let mut strain = (voltage) / ((0.25) * GAUGE_FACTOR_DEFAULT * V_REF); - // Apply correction factor - if gauge_factor != 0.0 || !gauge_factor.is_nan() { - strain = strain * (GAUGE_FACTOR_DEFAULT / gauge_factor); - } - return strain; -} - -// Function to convert voltage to strain in half bridge configuration -// -// @param voltage: f64 - The voltage output from the strain gauge -// @param gauge_factor: f64 - The gauge factor of the strain gauge default is 2.0 -// @return strain: f64 - The calculated strain value -pub fn voltage_to_strain_half( - voltage: f64, - gauge_factor: f64, -) -> f64 { - let mut strain = (voltage) / ((0.5) * GAUGE_FACTOR_DEFAULT * V_REF); - // Apply correction factor - if gauge_factor != 0.0 || !gauge_factor.is_nan() { - strain = strain * (GAUGE_FACTOR_DEFAULT / gauge_factor); - } - return strain; -} - -// Function to convert voltage to strain in full bridge configuration. -// -// @param voltage: f64 - The voltage output from the strain gauge -// @param gauge_factor: f64 - The gauge factor of the strain gauge -// @return strain: f64 - The calculated strain value -pub fn voltage_to_strain_full( - voltage: f64, - gauge_factor: f64, -) -> f64 { - let mut strain = (voltage) / (GAUGE_FACTOR_DEFAULT * V_REF); - // Apply correction factor - if gauge_factor != 0.0 || !gauge_factor.is_nan() { - strain = strain * (GAUGE_FACTOR_DEFAULT / gauge_factor); - } - return strain; -}