Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions server/app.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
const express = require('express');
const {logInfoMsgPrefix, logWarnMsgPrefix, logErrorMsgPrefix} = require('./utils/utils');

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'logErrorMsgPrefix' is assigned a value but never used

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (18.x)

'logWarnMsgPrefix' is assigned a value but never used

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (22.x)

'logErrorMsgPrefix' is assigned a value but never used

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (22.x)

'logWarnMsgPrefix' is assigned a value but never used

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'logErrorMsgPrefix' is assigned a value but never used

Check warning on line 2 in server/app.js

View workflow job for this annotation

GitHub Actions / build (20.x)

'logWarnMsgPrefix' is assigned a value but never used
const packageLock = require('./package-lock.json');
const { wellsMeasurements } = require('./measurements/payloadMeasurements');
const packageLock = require('./package-lock.json');

// route imports, see ./routes folder
const payloadRouter = require('./routes/payloadRoutes');
const adcsController = require('./routes/adcsRoutes');

const app = express();

Expand Down Expand Up @@ -50,5 +50,6 @@

// routes for each endpoint, see ./routes folder
app.use('/payload', payloadRouter);
app.use('/adcs', adcsController);

module.exports = app;
184 changes: 184 additions & 0 deletions server/controllers/adcsController.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
const { logInfoMsgPrefix, logWarnMsgPrefix, logErrorMsgPrefix } = require('../utils/utils');
const angVelocityMeasurements = require('../measurements/adcsMeasurements');
const magFieldMeasurements = require('../measurements/adcsMeasurements');
/**
* adcs.js
* @brief This file contains the controller functions for the ADCS API
*/

const getMagFieldData = async (req, res) => {
console.log(logInfoMsgPrefix('ADCS magnetic field check'), 'request_body:', req.body);
const variant = req.params.variant;
let { start, end } = req.query; // extract query parameters

// check if variant is provided, shouldn't happen
if (!variant) {
console.log(logWarnMsgPrefix('Variant number not provided'));
res.status(400).json({ error: 'Variant number not provided' });
return;
}

// check if variant is valid
if (variant < 1 || variant > 2) {
console.log(logWarnMsgPrefix(`Invalid variant number provided: ${variant}`));
res.status(400).json({ error: 'Invalid variant number provided, must be 1 or 2' });
return;
}

// TODO: should be written into utils/utils.js
// check if start and end are provided, if not, set them to null
// time stamp format is in RFC3339 format, e.g. 2023-10-01T00:00:00Z
if (!start || !end) {
console.log(logWarnMsgPrefix('Start or end time not provided'));
res.status(400).json({ error: 'Start and end time must be provided' });
return;
} else {
// check if start and end are in valid RFC3339 format
const dateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/; // RFC3339 format: YYYY-MM-DDTHH:MM:SSZ
if (!dateRegex.test(start) || !dateRegex.test(end)) {
console.log(logWarnMsgPrefix(`Invalid date format provided: start=${start}, end=${end}`));
res.status(400).json({ error: 'Invalid date format provided, must be in RFC3339 format' });
return;
}

// convert start and end to Date objects to validate
const startDate = new Date(start);
const endDate = new Date(end);
// end date must be after start date
if (endDate <= startDate) {
console.log(logWarnMsgPrefix(`End date must be after start date: start=${start}, end=${end}`));
res.status(400).json({ error: 'End date must be after start date' });
return;
}
}

try {
const queryResult = await magFieldMeasurements.magFieldMeasurements(variant, start, end);

const merged = {};

queryResult.forEach(item => {
const time = item._time;

if (!merged[time]) {
merged[time] = {
variant: item.variant,
timestamp: time,
LSB: null,
X: null,
Y: null,
Z: null
};
}

const axis = item._field.toUpperCase();
merged[time][axis] = item._value;

});

const finalOutput = Object.values(merged);
console.log(logInfoMsgPrefix('ADCS magnetic field data fetched successfully'));

res.status(200).json(finalOutput);

} catch (error) {
console.error(logErrorMsgPrefix('Error fetching ADCS magnetic field data'), error);
res.status(500).json({ error: 'Internal server error' });
}
};

const getAngVelocityData = async (req, res) => {
console.log(logInfoMsgPrefix('ADCS angular velocity check'), 'request_body:', req.body);
const variant = req.params.variant;
let { start, end } = req.query; // extract query parameters

// check if variant is provided, shouldn't happen
if (!variant) {
console.log(logWarnMsgPrefix('Variant number not provided'));
res.status(400).json({ error: 'Variant number not provided' });
return;
}

// check if variant is valid
if (variant < 1 || variant > 2) {
console.log(logWarnMsgPrefix(`Invalid variant number provided: ${variant}`));
res.status(400).json({ error: 'Invalid variant number provided, must be between 1 or 2' });
return;
}

// TODO: should be written into utils/utils.js
// check if start and end are provided, if not, set them to null
// time stamp format is in RFC3339 format, e.g. 2023-10-01T00:00:00Z
if (!start || !end) {
console.log(logWarnMsgPrefix('Start or end time not provided'));
res.status(400).json({ error: 'Start and end time must be provided' });
return;
} else {
// check if start and end are in valid RFC3339 format
const dateRegex = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}Z$/; // RFC3339 format: YYYY-MM-DDTHH:MM:SSZ
if (!dateRegex.test(start) || !dateRegex.test(end)) {
console.log(logWarnMsgPrefix(`Invalid date format provided: start=${start}, end=${end}`));
res.status(400).json({ error: 'Invalid date format provided, must be in RFC3339 format' });
return;
}

// convert start and end to Date objects to validate
const startDate = new Date(start);
const endDate = new Date(end);
// end date must be after start date
if (endDate <= startDate) {
console.log(logWarnMsgPrefix(`End date must be after start date: start=${start}, end=${end}`));
res.status(400).json({ error: 'End date must be after start date' });
return;
}
}

try {
const queryResult = await angVelocityMeasurements.angVelocityMeasurements(variant, start, end);

const merged = {};

queryResult.forEach(item => {
const time = item._time;

if (!merged[time]) {
merged[time] = {
variant: item.variant,
timestamp: time,
X: null,
Y: null,
Z: null
};
}

const axis = item._field.toUpperCase();
merged[time][axis] = item._value;

});

const finalOutput = Object.values(merged);
console.log(logInfoMsgPrefix('ADCS angular velocity data fetched successfully'));

res.status(200).json(finalOutput);

} catch (error) {
console.error(logErrorMsgPrefix('Error fetching ADCS angular velocity data'), error);
res.status(500).json({ error: 'Internal server error' });
}
};

/**
* @brief This function is used to get the measurement of the magnetic field, the data is fetched from the database
* @param variant The variant number
* @query start The start time of the period to get the data
* @query end The end time of the period to get the data
*/
exports.magFieldMeasurements = async (req, res) => getMagFieldData(req, res);

/**
* @brief This function is used to get the measurement of the angular velocity, the data is fetched from the database
* @param variant The variant number
* @query start The start time of the period to get the data
* @query end The end time of the period to get the data
*/
exports.angVelocityMeasurements = async (req, res) => getAngVelocityData(req, res);
2 changes: 1 addition & 1 deletion server/controllers/payloadController.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ const getWellData = async (req, res, field) => {
timestamp: item._time,
[field]: item._value
}));

res.status(200).json(formattedResult);
console.log(logInfoMsgPrefix(`Well ${field} data fetched successfully: ${wellNum}`));
} catch (error) {
Expand Down
38 changes: 38 additions & 0 deletions server/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,41 @@
- end
- end time of the query range
- must in seconds-only RFC3339 date format

### ADCS

#### Magnetic Field

- /adcs/magf/{:variant number}?{start}&{end}
- must have start and end query parameters with seconds-only RFC3339 date format (no fractional seconds)
- will return magnetic field telemetry of the given period
- includes:
- XYZ LSBs (00XXYYZZ)
- X-value MSB
- Y-value MSB
- Z-value MSB
- query parameters
- start
- start time of the query range
- must in seconds-only RFC3339 date format
- end
- end time of the query range
- must in seconds-only RFC3339 date format

#### Angular Velocity

- /adcs/angv/{:variant number}?{start}&{end}
- must have start and end query parameters with seconds-only RFC3339 date format (no fractional seconds)
- will return angular velocity telemetry of the given period
- includes:
- X-value
- Y-value
- Z-value
- query parameters
- start
- start time of the query range
- must in seconds-only RFC3339 date format
- end
- end time of the query range
- must in seconds-only RFC3339 date format

89 changes: 89 additions & 0 deletions server/measurements/adcsMeasurements.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
const { queryApi } = require('../db/dbSingleton');
const { db_bucket } = require('../config/env');
const { logInfoMsgPrefix, logWarnMsgPrefix, logErrorMsgPrefix } = require('../utils/utils');

/**
* adcsMeasurements.js
* @brief This file contains the functions to fetch ADCS related measurements
*/

// Tag for the ADCS
const adcsTags = 'ADCS';
// Tag for the magnetic field measurements
const magFieldMeasurementsTag = 'magField';
// fields for the magnetic field measurements
const magFieldMeasurementsFields = {
LSB: 'LSB',
X: 'X',
Y: 'Y',
Z: 'Z'
};
// Tag for the angular velocity measurements
const angVelocityMeasurementsTag = 'angVelocity';
// fields for the angular velocity measurements
const angVelocityMeasurementsFields = {
X: 'X',
Y: 'Y',
Z: 'Z'
};

async function magFieldMeasurements(variant_num, start, end) {
const query = `from(bucket: "${db_bucket}")
|> range(start: ${start}, stop: ${end})
|> filter(fn: (r) => r["_measurement"] == "${magFieldMeasurementsTag}")
|> filter(fn: (r) => r["host"] == "${adcsTags}")
|> filter(fn: (r) => r["variant"] == "${variant_num}")
|> keep(columns: ["_time", "_value", "variant", "_field"])
`;
return new Promise((resolve, reject) => {
let result = [];
queryApi.queryRows(query, {
next(row, tableMeta) {
const o = tableMeta.toObject(row);
result.push(o);
},
error(error) {
reject(error);
},
complete() {
resolve(result);
}
});
});
}

async function angVelocityMeasurements(variant_num, start, end) {
const query = `from(bucket: "${db_bucket}")
|> range(start: ${start}, stop: ${end})
|> filter(fn: (r) => r["_measurement"] == "${angVelocityMeasurementsTag}")
|> filter(fn: (r) => r["host"] == "${adcsTags}")
|> filter(fn: (r) => r["variant"] == "${variant_num}")
|> keep(columns: ["_time", "_value", "variant", "_field"])
|> group(columns: ["_time"])`;

return new Promise((resolve, reject) => {
let result = [];
queryApi.queryRows(query, {
next(row, tableMeta) {
const o = tableMeta.toObject(row);
result.push(o);
},
error(error) {
reject(error);
},
complete() {
resolve(result);
}
})
})
}

module.exports = {
magFieldMeasurements,
angVelocityMeasurements,
adcsTags,
magFieldMeasurementsTag,
magFieldMeasurementsFields,
angVelocityMeasurementsTag,
angVelocityMeasurementsFields
};
27 changes: 27 additions & 0 deletions server/routes/adcsRoutes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
const express = require('express');
const adcsController = require('../controllers/adcsController');

const router = express.Router();

/**
* adcsRoutes.js
* @brief This file contains the routes for the ADCS API
*/

/**
* @brief This route is used to get the magnetic field data of a variant
* @param variant The variant number
* @query start The start time of the period to get the data
* @query end The end time of the period to get the data
*/
router.route('/magf/:variant').get(adcsController.magFieldMeasurements);

/**
* @brief This route is used to get the angular velocity data of a variant
* @param variant The variant number
* @query start The start time of the period to get the data
* @query end The end time of the period to get the data
*/
router.route('/angv/:variant').get(adcsController.angVelocityMeasurements);

module.exports = router;
5 changes: 4 additions & 1 deletion tools/telemetryInputCli/.env
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ DB_PASSWORD = "adminPassword"
DB_TOKEN = "umsatsAdminToken"
PAYLOAD_TAG = "Payload"
WELL_TEMP_FIELD = "temp"
WELL_LUMIN_FIELD = "lumin"
WELL_LUMIN_FIELD = "lumin"
ADCS_TAG = "ADCS"
MAG_FIELD = "magfield"
ANG_VEL = "angvel"
4 changes: 3 additions & 1 deletion tools/telemetryInputCli/.gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
*.csv

!examples/data_temp.csv
!examples/data_lumin.csv
!examples/data_lumin.csv
!examples/data_angvel.csv
!examples/data_magField.csv
Loading
Loading