rxpoweredup
Version:
A Typescript RxJS-based library for controlling LEGO Powered UP hubs & peripherals.
135 lines (134 loc) • 7.71 kB
JavaScript
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,
});
}
}