260 lines
8.1 KiB
TypeScript
260 lines
8.1 KiB
TypeScript
import { cloneDeep } from "lodash";
|
|
import { pond } from "protobuf-ts/pond";
|
|
import { quack } from "protobuf-ts/quack";
|
|
import { User } from "models";
|
|
import moment from "moment";
|
|
import { describeMeasurement } from "pbHelpers/MeasurementDescriber";
|
|
import { or } from "utils";
|
|
|
|
export interface MeasurementsFor {
|
|
type: quack.MeasurementType;
|
|
label: string;
|
|
values: number[];
|
|
unit: string;
|
|
colour: string;
|
|
error: boolean;
|
|
}
|
|
|
|
export interface convertedUnitMeasurement {
|
|
timestamp: string;
|
|
measurementsFor: MeasurementsFor[];
|
|
}
|
|
|
|
export class UnitMeasurement {
|
|
public componentId: string = "";
|
|
public type: quack.MeasurementType = 0;
|
|
public label: string = "";
|
|
public values: pond.ValueArray[] = [];
|
|
public timestamps: string[] = [];
|
|
public colour: string = "";
|
|
public unit: string = "";
|
|
|
|
public static create(pb?: pond.UnitMeasurementsForComponent, user?: User): UnitMeasurement {
|
|
let my = new UnitMeasurement();
|
|
if (pb) {
|
|
let describer = describeMeasurement(pb.type, pb.componentType);
|
|
let values = pb.values;
|
|
if (user) {
|
|
values = unitConversion(pb.values, pb.type, user);
|
|
}
|
|
my.componentId = pb.componentId;
|
|
my.type = pb.type;
|
|
my.values = values;
|
|
my.timestamps = pb.timestamps;
|
|
my.colour = describer.colour();
|
|
my.unit = describer.GetUnit();
|
|
my.label = describer.label();
|
|
}
|
|
return my;
|
|
}
|
|
|
|
public static clone(other?: UnitMeasurement): UnitMeasurement {
|
|
if (other) {
|
|
return UnitMeasurement.create(
|
|
pond.UnitMeasurementsForComponent.fromObject(
|
|
cloneDeep({
|
|
componentId: other.componentId,
|
|
label: other.label,
|
|
type: other.type,
|
|
values: other.values,
|
|
timestamps: other.timestamps,
|
|
colour: other.colour,
|
|
unit: other.unit
|
|
})
|
|
)
|
|
);
|
|
}
|
|
return UnitMeasurement.create();
|
|
}
|
|
|
|
public static any(data: any, user?: User): UnitMeasurement {
|
|
let my = UnitMeasurement.create(
|
|
pond.UnitMeasurementsForComponent.fromObject(cloneDeep(data)),
|
|
user
|
|
);
|
|
return my;
|
|
}
|
|
|
|
/**
|
|
* takes in the unitMeasurementsForComponents from list and re-structures them into an array of objects that have the timestamp
|
|
* and all measurements at that timestamp in one object
|
|
* the converted measurement is meant to be used with the measurement summary
|
|
* @param toConvert
|
|
* @returns
|
|
*/
|
|
public static convertMeasurements(toConvert: UnitMeasurement[]): convertedUnitMeasurement[] {
|
|
let map: Map<string, convertedUnitMeasurement> = new Map<string, convertedUnitMeasurement>();
|
|
let converted: convertedUnitMeasurement[] = [];
|
|
toConvert.forEach((um: UnitMeasurement) => {
|
|
um.values.forEach((vals, i) => {
|
|
let current = map.get(um.timestamps[i]);
|
|
//checks if there is a converted measurement with that timestamp yet, if there is add the new one to it
|
|
if (current) {
|
|
current.measurementsFor.push({
|
|
type: um.type,
|
|
label: um.label,
|
|
values: vals.values,
|
|
colour: um.colour,
|
|
unit: um.unit,
|
|
error: vals.error
|
|
});
|
|
// if there is not then create a new one and put it in the map
|
|
} else {
|
|
let converted: convertedUnitMeasurement = {
|
|
timestamp: um.timestamps[i],
|
|
measurementsFor: [
|
|
{
|
|
type: um.type,
|
|
label: um.label,
|
|
values: vals.values,
|
|
colour: um.colour,
|
|
unit: um.unit,
|
|
error: vals.error
|
|
}
|
|
]
|
|
};
|
|
map.set(um.timestamps[i], converted);
|
|
}
|
|
});
|
|
});
|
|
converted = Array.from(map.values());
|
|
return converted;
|
|
}
|
|
|
|
//similar to the above however it only returns the converted measurement with the most recent timestamp
|
|
public static convertLastMeasurement(toConvert: UnitMeasurement[]): convertedUnitMeasurement {
|
|
let converted: convertedUnitMeasurement = {
|
|
measurementsFor: [],
|
|
timestamp: ""
|
|
};
|
|
toConvert.forEach((um: UnitMeasurement) => {
|
|
if (um.values.length > 0 && um.timestamps.length > 0) {
|
|
let recentPos = 0;
|
|
let timeVal = 0;
|
|
um.timestamps.forEach((timestamp, pos) => {
|
|
if (moment(timestamp).valueOf() > timeVal) {
|
|
recentPos = pos;
|
|
timeVal = moment(timestamp).valueOf();
|
|
}
|
|
});
|
|
converted.timestamp = um.timestamps[recentPos];
|
|
converted.measurementsFor.push({
|
|
type: um.type,
|
|
label: um.label,
|
|
values: um.values[recentPos].values,
|
|
colour: um.colour,
|
|
unit: um.unit,
|
|
error: um.values[recentPos].error
|
|
});
|
|
}
|
|
});
|
|
return converted;
|
|
}
|
|
|
|
/**
|
|
* will return a converted unit measurement where the values in the the measurementsFor array only contain the number for the requested node,
|
|
* if the requested node is outside the array (component doesn't have that node) the array will simply be empty
|
|
* @param toConvert
|
|
* @param node
|
|
* @returns
|
|
*/
|
|
public static convertLastMeasurementByNode(
|
|
toConvert: UnitMeasurement[],
|
|
node: number
|
|
): convertedUnitMeasurement {
|
|
let converted: convertedUnitMeasurement = {
|
|
measurementsFor: [],
|
|
timestamp: ""
|
|
};
|
|
toConvert.forEach((um: UnitMeasurement) => {
|
|
if (um.values.length > 0 && um.timestamps.length > 0) {
|
|
let recentPos = 0;
|
|
let timeVal = 0;
|
|
um.timestamps.forEach((timestamp, pos) => {
|
|
if (moment(timestamp).valueOf() > timeVal) {
|
|
recentPos = pos;
|
|
timeVal = moment(timestamp).valueOf();
|
|
}
|
|
});
|
|
converted.timestamp = um.timestamps[recentPos];
|
|
converted.measurementsFor.push({
|
|
type: um.type,
|
|
label: um.label,
|
|
values: or([um.values[recentPos].values[node]], []),
|
|
colour: um.colour,
|
|
unit: um.unit,
|
|
error: um.values[recentPos].error
|
|
});
|
|
}
|
|
});
|
|
return converted;
|
|
}
|
|
|
|
public getRecentForNode(node: number): number | undefined {
|
|
if (this.values.length > 0) {
|
|
if (this.values[this.values.length - 1].values[node]) {
|
|
return this.values[this.values.length - 1].values[node];
|
|
}
|
|
}
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* for handling the conversion of values based on user preferences, this is run for each unit measurement when it is created from the data from protobuf
|
|
* @param values
|
|
* @param type
|
|
* @param user
|
|
* @returns
|
|
*/
|
|
function unitConversion(
|
|
values: pond.ValueArray[],
|
|
type: quack.MeasurementType,
|
|
user: User
|
|
): pond.ValueArray[] {
|
|
let newVals = values;
|
|
if (
|
|
type === quack.MeasurementType.MEASUREMENT_TYPE_TEMPERATURE &&
|
|
user.settings.temperatureUnit === pond.TemperatureUnit.TEMPERATURE_UNIT_FAHRENHEIT
|
|
) {
|
|
newVals.forEach((val, i) => {
|
|
val.values.forEach((v, j) => {
|
|
newVals[i].values[j] = Math.round((v * 1.8 + 32) * 100) / 100;
|
|
});
|
|
});
|
|
}
|
|
if (type === quack.MeasurementType.MEASUREMENT_TYPE_PRESSURE) {
|
|
if (user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_KILOPASCALS) {
|
|
newVals.forEach((val, i) => {
|
|
val.values.forEach((v, j) => {
|
|
newVals[i].values[j] = v / 1000;
|
|
});
|
|
});
|
|
//} else if (user.settings.pressureUnit === pond.PressureUnit.PRESSURE_UNIT_INCHES_OF_WATER) {
|
|
} else {
|
|
//by default if there is nothing set use iwg
|
|
newVals.forEach((val, i) => {
|
|
val.values.forEach((v, j) => {
|
|
newVals[i].values[j] = Math.round((v / 248.8) * 100) / 100;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
if (type === quack.MeasurementType.MEASUREMENT_TYPE_DISTANCE_CM) {
|
|
if (user.settings.distanceUnit === pond.DistanceUnit.DISTANCE_UNIT_FEET) {
|
|
newVals.forEach((val, i) => {
|
|
val.values.forEach((v, j) => {
|
|
newVals[i].values[j] = Math.round((v / 30.48) * 100) / 100;
|
|
});
|
|
});
|
|
} else {
|
|
//use meters as the default for users that have none set yet
|
|
newVals.forEach((val, i) => {
|
|
val.values.forEach((v, j) => {
|
|
newVals[i].values[j] = v / 100;
|
|
});
|
|
});
|
|
}
|
|
}
|
|
return newVals;
|
|
}
|