Skip to content

Commit

Permalink
TEL - Cart Changes (#124)
Browse files Browse the repository at this point in the history
  • Loading branch information
davidbeechey authored Jul 1, 2024
1 parent 2e2ba1b commit 52eb714
Show file tree
Hide file tree
Showing 14 changed files with 9,271 additions and 7,133 deletions.
109 changes: 107 additions & 2 deletions telemetry/packages/constants/src/pods/pods.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,117 @@ import {
levitationHeightCommon,
} from './common';

export const POD_IDS = ['pod_1', 'pod_2024'] as const;
export const POD_IDS = ['pod_1', 'pod_2024', 'cart_2024'] as const;
export type PodId = (typeof POD_IDS)[number];
export type Pods = Record<PodId, Pod>;

export const pods: Pods = {
cart_2024: {
id: 'cart_2024',
name: 'Cart 2024',
operationMode: 'LIM_ONLY',
measurements: {
// ************************************ ACCELEROMETER ************************************ //
optical_flow_x: {
name: 'Optical Flow x-axis',
key: 'optical_flow_x',
format: 'float',
type: 'velocity',
unit: 'm/s',
limits: {
critical: {
low: 0,
high: 10,
},
},
rms_noise: 0,
sampling_time: 500,
},
optical_flow_y: {
name: 'Optical Flow y-axis',
key: 'optical_flow_y',
format: 'float',
type: 'velocity',
unit: 'm/s',
limits: {
critical: {
low: 0,
high: 10,
},
},
rms_noise: 0,
sampling_time: 500,
},
// ************************************ ACCELEROMETER ************************************ //
accelerometer_x: {
name: 'Accelerometer x-axis',
key: 'accelerometer_x',
...accelerometerCommon,
},
accelerometer_y: {
name: 'Accelerometer y-axis',
key: 'accelerometer_y',
...accelerometerCommon,
},
accelerometer_z: {
name: 'Accelerometer z-axis',
key: 'accelerometer_z',
...accelerometerCommon,
},
// ************************************ NAVIGATION ************************************ //
displacement: {
name: 'Displacement',
key: 'displacement',
format: 'float',
type: 'motion',
unit: 'm',
limits: {
critical: {
low: 0,
high: 100,
},
},
rms_noise: 0,
sampling_time: accelerometerCommon.sampling_time,
},
velocity: {
name: 'Velocity',
key: 'velocity',
format: 'float',
type: 'motion',
unit: 'm/s',
limits: {
critical: {
low: 0,
high: 50,
},
},
rms_noise: accelerometerCommon.rms_noise,
sampling_time: accelerometerCommon.sampling_time,
},
acceleration: {
name: 'Acceleration',
key: 'acceleration',
format: 'float',
type: 'motion',
unit: 'm/s²',
limits: {
critical: {
low: 0,
high: 5,
},
},
rms_noise: accelerometerCommon.rms_noise,
sampling_time: accelerometerCommon.sampling_time,
},
// ************************************ KEYENCE ************************************ //
keyence: {
name: 'Keyence',
key: 'keyence',
...keyenceCommon,
},
},
},
pod_1: {
id: 'pod_1',
name: 'Pod Ness',
Expand Down Expand Up @@ -428,7 +534,6 @@ export const pods: Pods = {
},
},
},

pod_2024: {
id: 'pod_2024',
name: 'Poddington',
Expand Down
45 changes: 24 additions & 21 deletions telemetry/packages/constants/src/pods/states.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,30 @@
export type PodStateType = keyof typeof ALL_POD_STATES;
/* eslint-disable @typescript-eslint/no-unsafe-argument */
export type PodStateType = (typeof ALL_POD_STATES)[keyof typeof ALL_POD_STATES];

export const FAILURE_STATES = {
FAILURE_BRAKING: 'FAILURE_BRAKING',
FAILURE_BRAKING: 'kFailureBrake',
FAILURE: 'kFailure',
} as const;

export const PASSIVE_STATES = {
IDLE: 'IDLE',
CALIBRATE: 'CALIBRATE',
SAFE: 'SAFE',
IDLE: 'kIdle',
CALIBRATE: 'kCalibrate',
SAFE: 'kSafe',
} as const;

export const ACTIVE_STATES = {
PRECHARGE: 'PRECHARGE',
READY_FOR_LEVITATION: 'READY_FOR_LEVITATION',
BEGIN_LEVITATION: 'BEGIN_LEVITATION',
READY_FOR_LAUNCH: 'READY_FOR_LAUNCH',
ACCELERATE: 'ACCELERATE',
LIM_BRAKE: 'LIM_BRAKE',
FRICTION_BRAKE: 'FRICTION_BRAKE',
STOP_LEVITATION: 'STOP_LEVITATION',
STOPPED: 'STOPPED',
BATTERY_RECHARGE: 'BATTERY_RECHARGE',
CAPACITOR_DISCHARGE: 'CAPACITOR_DISCHARGE',
PRECHARGE: 'kPrecharge',
READY_FOR_LEVITATION: 'kReadyForLevitation',
BEGIN_LEVITATION: 'kBeginLevitation',
LEVITATING: 'kLevitating',
READY_FOR_LAUNCH: 'kReady',
ACCELERATE: 'kAccelerate',
LIM_BRAKE: 'kLimBrake',
FRICTION_BRAKE: 'kFrictionBrake',
STOP_LEVITATION: 'kStopLevitation',
STOPPED: 'kStopped',
BATTERY_RECHARGE: 'kBatteryRecharge',
CAPACITOR_DISCHARGE: 'kCapacitorDischarge',
} as const;

export const NULL_STATES = {
Expand All @@ -45,11 +48,11 @@ export const ALL_POD_STATE_TYPES = [
export type PodStateCategoryType = (typeof ALL_POD_STATE_TYPES)[number];

export const getStateType = (
state: string,
state: any,
): (typeof ALL_POD_STATE_TYPES)[number] => {
if (FAILURE_STATES[state as keyof typeof FAILURE_STATES]) return 'FAILURE';
if (PASSIVE_STATES[state as keyof typeof PASSIVE_STATES]) return 'PASSIVE';
if (ACTIVE_STATES[state as keyof typeof ACTIVE_STATES]) return 'ACTIVE';
if (NULL_STATES[state as keyof typeof NULL_STATES]) return 'NULL';
if (Object.values(FAILURE_STATES).includes(state)) return 'FAILURE';
if (Object.values(PASSIVE_STATES).includes(state)) return 'PASSIVE';
if (Object.values(ACTIVE_STATES).includes(state)) return 'ACTIVE';
if (Object.values(NULL_STATES).includes(state)) return 'NULL';
throw new Error(`Unknown state: ${state}`);
};
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,10 @@ export function zodEnumFromObjKeys<K extends string>(
const [firstKey, ...otherKeys] = Object.keys(obj) as K[];
return z.enum([firstKey, ...otherKeys]);
}

export function zodEnumFromObjValues<V extends string>(
obj: Record<string, V>,
): z.ZodEnum<[V, ...V[]]> {
const [firstValue, ...otherValues] = Object.values(obj);
return z.enum([firstValue, ...otherValues]);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable, LoggerService, Inject } from '@nestjs/common';
import { MqttService } from 'nest-mqtt';
import { Logger } from '@/modules/logger/Logger.decorator';
import { ALL_POD_STATES } from '@hyped/telemetry-constants';

@Injectable()
export class PodControlsService {
Expand All @@ -17,10 +18,28 @@ export class PodControlsService {
* @returns True if the message was sent successfully, false otherwise
*/
async sendControlMessage(control: string, podId: string) {
await this.mqttService.publish(
`hyped/${podId}/controls/${control}`,
control,
);
switch (control) {
case 'start':
await this.mqttService.publish(`hyped/${podId}/state/state_request`, {
state: ALL_POD_STATES.ACCELERATE,
});
break;
case 'stop':
await this.mqttService.publish(`hyped/${podId}/state/state_request`, {
state: ALL_POD_STATES.LIM_BRAKE,
});
break;
case 'levitate':
await this.mqttService.publish(`hyped/${podId}/state/state_request`, {
state: ALL_POD_STATES.BEGIN_LEVITATION,
});
break;
default:
await this.mqttService.publish(
`hyped/${podId}/controls/${control}`,
control,
);
}
this.logger.log(
`Control message "${control}" sent to pod "${podId}"`,
PodControlsService.name,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,49 +1,129 @@
import { Injectable } from '@nestjs/common';
import { Injectable, LoggerService } from '@nestjs/common';
import { Params, Payload, Subscribe } from 'nest-mqtt';
import { MeasurementService } from '@/modules/measurement/Measurement.service';
import { currentTime } from '@influxdata/influxdb-client';
import { StateService } from '@/modules/state/State.service';
import { MqttIngestionError } from './errors/MqttIngestionError';
import { POD_IDS, PodId, PodStateType } from '@hyped/telemetry-constants';
import { Logger } from '@/modules/logger/Logger.decorator';

@Injectable()
export class MqttIngestionService {
constructor(
private measurementService: MeasurementService,
private stateService: StateService,
@Logger()
private readonly logger: LoggerService,
) {}

@Subscribe('hyped/+/measurement/+')
async getMeasurementReading(
@Params() rawParams: string[],
@Payload() rawValue: number, // TODOLater: check that this is correct
@Payload() rawValue: any, // TODOLater: check that this is correct
) {
const timestamp = currentTime.nanos();
const podId = rawParams[0];
const measurementKey = rawParams[1];
const value = rawValue;

this.validateMqttMessage({ podId, measurementKey, value });
// If it's not the cart, do what we normally do
this.validatePodId(podId);
if (podId !== 'cart_2024') {
const value = rawValue as number;
this.validateMqttMessage({ podId, measurementKey, value });

await this.measurementService.addMeasurementReading({
podId,
measurementKey,
value,
timestamp,
});
await this.measurementService.addMeasurementReading({
podId,
measurementKey,
value,
timestamp,
});
}

// If it's the cart, add the readings manually
this.logger.debug(rawValue);

switch (measurementKey) {
case 'accelerometer':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'accelerometer_x',
value: rawValue.x as number,
timestamp: timestamp,
});
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'accelerometer_y',
value: rawValue.y as number,
timestamp: timestamp,
});
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'accelerometer_z',
value: rawValue.z as number,
timestamp: timestamp,
});
return;
case 'acceleration':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'acceleration',
value: rawValue.acceleration as number,
timestamp: timestamp,
});
return;
case 'velocity':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'velocity',
value: rawValue.velocity as number,
timestamp: timestamp,
});
return;
case 'displacement':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'displacement',
value: rawValue.displacement as number,
timestamp: timestamp,
});
return;
case 'optical_flow':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: `optical_flow_x`,
value: rawValue.x as number,
timestamp: timestamp,
});
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: `optical_flow_y`,
value: rawValue.y as number,
timestamp: timestamp,
});
return;
case 'keyence':
await this.measurementService.addMeasurementReading({
podId: 'cart_2024',
measurementKey: 'keyence',
value: rawValue.count as number,
timestamp: timestamp,
});
return;
}
}

@Subscribe('hyped/+/state')
@Subscribe('hyped/+/state/state')
getStateReading(
@Params() rawParams: string[],
@Payload() rawValue: PodStateType,
@Payload()
rawValue: {
state: PodStateType;
},
) {
const timestamp = currentTime.nanos();
const podId = rawParams[0];
const value = rawValue;
const value = rawValue.state;

this.validateMqttMessage({ podId, measurementKey: 'state', value });
this.validatePodId(podId);

this.stateService.addStateReading({
Expand Down
Loading

0 comments on commit 52eb714

Please sign in to comment.