frontend/src/pbHelpers/DeviceAvailability.ts
2026-04-21 16:13:59 -06:00

189 lines
7.6 KiB
TypeScript

import { cloneDeep } from "lodash";
import { Component } from "models";
import { ConfigurablePin } from "pbHelpers/AddressTypes/ConfigurablePinArray";
import { GetProductAvailability } from "products/DeviceProduct";
import { pond } from "protobuf-ts/pond";
import { quack } from "protobuf-ts/quack";
import { or } from "utils/types";
import { FilterSubtypeI2C } from "./ComponentType";
export type ComponentAvailabilityMap = Map<quack.ComponentType, number[]>;
export type DevicePositions = number[] | ComponentAvailabilityMap | ConfigurablePin[];
export type DeviceAvailabilityMap = Map<quack.AddressType, DevicePositions>;
export type OffsetAvailabilityMap = Map<quack.AddressType, number[][]>;
const DefaultPinArray: ConfigurablePin[] = [
{ address: 1, label: "1" },
{ address: 2, label: "2" },
{ address: 4, label: "3" },
{ address: 8, label: "4" },
{ address: 16, label: "5" },
{ address: 32, label: "6" },
{ address: 64, label: "7" },
{ address: 128, label: "8" },
{ address: 256, label: "9" },
{ address: 512, label: "10" },
{ address: 1024, label: "11" },
{ address: 2048, label: "12" },
{ address: 4096, label: "13" },
{ address: 8192, label: "14" }
];
const OffsetArray: number[] = [0, 1, 2, 3, 4, 5, 6, 7];
const OffsetAvailability: OffsetAvailabilityMap = new Map<quack.AddressType, number[][]>([
[quack.AddressType.ADDRESS_TYPE_INVALID, Array(14).fill([])],
[quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, Array(14).fill(OffsetArray)],
[quack.AddressType.ADDRESS_TYPE_I2C, Array(14).fill([0, 1])],
[quack.AddressType.ADDRESS_TYPE_DAC, Array(14).fill([0])],
[quack.AddressType.ADDRESS_TYPE_POWER, Array(14).fill([0])],
[quack.AddressType.ADDRESS_TYPE_GPS, Array(14).fill([0])],
[quack.AddressType.ADDRESS_TYPE_MODEM, Array(14).fill([0])],
[quack.AddressType.ADDRESS_TYPE_I2C_WITH_CHANNELS, Array(14).fill([0])],
[quack.AddressType.ADDRESS_TYPE_INCREMENTAL, Array(14).fill([0])]
]);
const DefaultAvailability: DeviceAvailabilityMap = new Map<quack.AddressType, DevicePositions>([
[quack.AddressType.ADDRESS_TYPE_INVALID, []],
[quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY, DefaultPinArray],
[
quack.AddressType.ADDRESS_TYPE_I2C,
//this will contain all the possible addresses for a component type, if subtypes use specific addresses add a filter to the type describer, see lidar.ts for an example
new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_PRESSURE, [0x18, 0x19, 0x1a]],
[quack.ComponentType.COMPONENT_TYPE_LIGHT, [0x29]],
[quack.ComponentType.COMPONENT_TYPE_CO2, [0x61]],
[quack.ComponentType.COMPONENT_TYPE_STEPPER_MOTOR, [0x14, 0x15, 0x16, 0x17]],
[quack.ComponentType.COMPONENT_TYPE_PH, [0x40]],
[quack.ComponentType.COMPONENT_TYPE_DHT, [0x40, 0x61]],
// [quack.ComponentType.COMPONENT_TYPE_VAPOUR_PRESSURE_DEFICIT, [0x40]], //component type deprecated?
[quack.ComponentType.COMPONENT_TYPE_AIR_QUALITY, [0x58]],
[quack.ComponentType.COMPONENT_TYPE_LIDAR, [0x62, 0x66, 0x10]],
[quack.ComponentType.COMPONENT_TYPE_CAPACITANCE, [0x50]],
[quack.ComponentType.COMPONENT_TYPE_CAPACITOR_CABLE, [0x50]],
[quack.ComponentType.COMPONENT_TYPE_SEN5X, [0x69]],
[quack.ComponentType.COMPONENT_TYPE_AIRFLOW, [0x6c]],
[quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE, [0x71]], // the address cables will use when they are plugged into the expander with V2 device only
[quack.ComponentType.COMPONENT_TYPE_DRAGER_GAS_DONGLE, [0x6d]],
[quack.ComponentType.COMPONENT_TYPE_ANALOG_PRESSURE, [0x6e]]
])
],
[quack.AddressType.ADDRESS_TYPE_DAC, [0, 1]],
[quack.AddressType.ADDRESS_TYPE_POWER, [0]],
[quack.AddressType.ADDRESS_TYPE_GPS, [0]],
[quack.AddressType.ADDRESS_TYPE_MODEM, [0]],
[
quack.AddressType.ADDRESS_TYPE_I2C_WITH_CHANNELS, //this array needs to be built in the component types definition
[
(0x40 << 2) + 0,
(0x40 << 2) + 1,
(0x41 << 2) + 0,
(0x41 << 2) + 1,
(0x44 << 2) + 0,
(0x44 << 2) + 1,
(0x45 << 2) + 0,
(0x45 << 2) + 1
]
],
[quack.AddressType.ADDRESS_TYPE_INCREMENTAL, [1]],
[
quack.AddressType.ADDRESS_TYPE_SPI,
new Map<quack.ComponentType, number[]>([
[quack.ComponentType.COMPONENT_TYPE_VIBRATION_CHAIN, [0x0]]
])
]
]);
export class DeviceAvailability {
availability: DeviceAvailabilityMap;
offsetAvailability: OffsetAvailabilityMap;
constructor(product?: pond.DeviceProduct) {
const ProductAvailability = GetProductAvailability(product);
this.availability = ProductAvailability
? cloneDeep(ProductAvailability)
: cloneDeep(DefaultAvailability);
this.offsetAvailability = cloneDeep(OffsetAvailability);
}
ClaimAddress(
addressType: quack.AddressType,
componentType: quack.ComponentType,
address: number,
expansionLine?: number
) {
let offset = addressType - 8;
if (offset > 0) addressType = addressType - (7 + offset);
else offset = 0;
switch (addressType) {
case quack.AddressType.ADDRESS_TYPE_CONFIGURABLE_PIN_ARRAY:
//if (componentType === quack.ComponentType.COMPONENT_TYPE_GRAIN_CABLE) {
// break;
//}
let arrayPos = Math.log2(address);
let pinArray = cloneDeep(this.availability.get(addressType) as ConfigurablePin[]);
let offsetArray = this.offsetAvailability.get(addressType);
offsetArray = offsetArray ? offsetArray : [];
let newOffsetArray = cloneDeep(offsetArray);
newOffsetArray[arrayPos] = offsetArray[arrayPos]?.filter(o => o !== offset);
this.offsetAvailability.set(addressType, newOffsetArray);
if (offsetArray[arrayPos] ? offsetArray[arrayPos].length < 1 : false) {
let availablePins = pinArray.filter(p => p.address !== address);
this.availability.set(addressType, availablePins);
}
break;
case quack.AddressType.ADDRESS_TYPE_I2C:
case quack.AddressType.ADDRESS_TYPE_SPI:
if(expansionLine){
break;
}
let addressTypePositions = cloneDeep(
this.availability.get(addressType) as ComponentAvailabilityMap
);
let componentPositions = cloneDeep(addressTypePositions.get(componentType) || []);
componentPositions = componentPositions.filter(p => p !== address);
addressTypePositions.set(componentType, componentPositions);
this.availability.set(addressType, addressTypePositions);
break;
case quack.AddressType.ADDRESS_TYPE_INCREMENTAL:
let incrementalPositions = cloneDeep(this.availability.get(addressType) as number[]);
if (address >= incrementalPositions[0]) {
incrementalPositions[0] = address + 1;
}
this.availability.set(addressType, incrementalPositions);
break;
default:
let positions = cloneDeep(this.availability.get(addressType) as number[]);
if (positions) {
positions = positions.filter(p => p !== address);
this.availability.set(addressType, positions);
}
break;
}
}
GetAvailability() {
return this.availability;
}
GetOffsetAvailability() {
return this.offsetAvailability;
}
}
export function FindAvailablePositions(
components: Component[],
product?: pond.DeviceProduct
): DeviceAvailability {
const available = new DeviceAvailability(product);
components.forEach((component: Component) => {
if (component.settings.addressType) {
available.ClaimAddress(
component.settings.addressType,
component.settings.type,
or(component.settings.address, 0),
component.settings.expansionLine
);
}
});
return available;
}