UNPKG

rxpoweredup

Version:

A Typescript RxJS-based library for controlling LEGO Powered UP hubs & peripherals.

135 lines (134 loc) 7.71 kB
import { Observable, filter, last, map, switchMap, take } from 'rxjs'; import { AttachIoEvent } from '../../constants'; export class PortsFeature { portModeReplies; attachedIoReplies; attachedIoCachedReplies; portModeInformationReplies; portValueSetupSingleHandshakeReplies; portInformationRequestMessageFactory; rawPortValueReplies; portModeInformationMessageFactory; portInputFormatSetupMessageFactory; virtualPortSetupMessageFactory; messenger; isDisposed = false; constructor(portModeReplies, attachedIoReplies, attachedIoCachedReplies, portModeInformationReplies, portValueSetupSingleHandshakeReplies, portInformationRequestMessageFactory, rawPortValueReplies, portModeInformationMessageFactory, portInputFormatSetupMessageFactory, virtualPortSetupMessageFactory, messenger) { this.portModeReplies = portModeReplies; this.attachedIoReplies = attachedIoReplies; this.attachedIoCachedReplies = attachedIoCachedReplies; this.portModeInformationReplies = portModeInformationReplies; this.portValueSetupSingleHandshakeReplies = portValueSetupSingleHandshakeReplies; this.portInformationRequestMessageFactory = portInformationRequestMessageFactory; this.rawPortValueReplies = rawPortValueReplies; this.portModeInformationMessageFactory = portModeInformationMessageFactory; this.portInputFormatSetupMessageFactory = portInputFormatSetupMessageFactory; this.virtualPortSetupMessageFactory = virtualPortSetupMessageFactory; this.messenger = messenger; } dispose() { if (this.isDisposed) { throw new Error('PortsFeature already disposed'); } this.isDisposed = true; } onIoAttach(filterOptions = { eventTypes: [AttachIoEvent.Attached, AttachIoEvent.AttachedVirtual], }) { const filters = []; if (typeof filterOptions === 'number') { filters.push(filter((message) => message.portId === filterOptions)); } else { if (filterOptions.ports !== undefined) { const portIdsSet = new Set(filterOptions.ports); filters.push(filter((message) => portIdsSet.has(message.portId))); } if (filterOptions.eventTypes !== undefined) { const eventTypesSet = new Set(filterOptions.eventTypes); filters.push(filter((message) => eventTypesSet.has(message.event))); } if (filterOptions.ioTypes !== undefined) { const ioTypesSet = new Set(filterOptions.ioTypes); filters.push(filter((message) => ioTypesSet.has(message.ioTypeId))); } } return filters.reduce((acc, filterOperator) => acc.pipe(filterOperator), this.attachedIoCachedReplies); } onIoDetach(filterOptions) { const filters = [filter((message) => message.event === AttachIoEvent.Detached)]; if (typeof filterOptions === 'number') { filters.push(filter((message) => message.portId === filterOptions)); } else if (filterOptions?.ports !== undefined) { const portIdsSet = new Set(filterOptions.ports); filters.push(filter((message) => portIdsSet.has(message.portId))); } return filters.reduce((acc, filterOperator) => acc.pipe(filterOperator), this.attachedIoCachedReplies); } getPortValue(portId, modeId, transformer) { const setPortInputFormatMessage = this.portInputFormatSetupMessageFactory.createMessage(portId, modeId, false); const portValueRequestMessage = this.portInformationRequestMessageFactory.createPortValueRequest(portId); const portValueHandshakeReplies$ = this.portValueSetupSingleHandshakeReplies.pipe(filter((r) => r.portId === portId && r.modeId === modeId), take(1)); const portValuesReplies$ = this.rawPortValueReplies.pipe(filter((r) => r.portId === portId)); return this.messenger .sendWithResponse({ message: setPortInputFormatMessage, reply: portValueHandshakeReplies$ }, { message: portValueRequestMessage, reply: portValuesReplies$ }) .pipe(map((r) => (transformer ? transformer.fromRawValue(r.value) : r.value))); } portValueChanges(portId, modeId, deltaThreshold, transformer) { let handShakeMessageSent = false; let subscribersCount = 0; const portValueHandshakeReplies$ = this.portValueSetupSingleHandshakeReplies.pipe(filter((r) => r.portId === portId && r.modeId === modeId), take(1)); const teardownLogic = (sub) => { subscribersCount--; if (!this.isDisposed && subscribersCount === 0) { const disableNotificationsMessage = this.portInputFormatSetupMessageFactory.createMessage(portId, modeId, false); this.messenger.sendWithResponse({ message: disableNotificationsMessage, reply: portValueHandshakeReplies$ }).pipe(take(1)).subscribe(); } sub.unsubscribe(); }; return new Observable((subscriber) => { subscribersCount++; const portValuesReplies$ = this.rawPortValueReplies.pipe(filter((r) => r.portId === portId)); if (!handShakeMessageSent) { const numericDeltaThreshold = transformer ? transformer.toValueThreshold(deltaThreshold) : deltaThreshold; const setPortInputFormatMessage = this.portInputFormatSetupMessageFactory.createMessage(portId, modeId, true, numericDeltaThreshold); const sub = this.messenger .sendWithResponse({ message: setPortInputFormatMessage, reply: portValueHandshakeReplies$ }) .pipe(last(), switchMap(() => portValuesReplies$), map(({ value }) => (transformer ? transformer.fromRawValue(value) : value))) .subscribe(subscriber); handShakeMessageSent = true; return () => teardownLogic(sub); } else { const sub = portValuesReplies$.pipe(map(({ value }) => (transformer ? transformer.fromRawValue(value) : value))).subscribe(subscriber); return () => teardownLogic(sub); } }); } getPortModes(portId) { return this.messenger.sendWithResponse({ message: this.portInformationRequestMessageFactory.createPortModeRequest(portId), reply: this.portModeReplies.pipe(filter((r) => r.portId === portId)), }); } getPortModeInformation(portId, mode, modeInformationType) { const message = this.portModeInformationMessageFactory.createPortModeInformationRequest(portId, mode, modeInformationType); const reply = this.portModeInformationReplies.pipe(filter((r) => r.modeInformationType === modeInformationType && r.portId === portId && r.mode === mode)); return this.messenger.sendWithResponse({ message, reply }); } createVirtualPort(portIdA, portIdB) { const replies = this.attachedIoReplies.pipe(filter((r) => r.event === AttachIoEvent.AttachedVirtual && r.portIdA === portIdA && r.portIdB === portIdB)); return this.messenger.sendWithResponse({ message: this.virtualPortSetupMessageFactory.createVirtualPort(portIdA, portIdB), reply: replies, }); } deleteVirtualPort(virtualPortId) { const replies = this.attachedIoReplies.pipe(filter((r) => r.event === AttachIoEvent.Detached && r.portId === virtualPortId)); return this.messenger.sendWithResponse({ message: this.virtualPortSetupMessageFactory.deleteVirtualPort(virtualPortId), reply: replies, }); } }