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; export type DevicePositions = number[] | ComponentAvailabilityMap | ConfigurablePin[]; export type DeviceAvailabilityMap = Map; export type OffsetAvailabilityMap = Map; 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.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.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.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.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; }