{"version":3,"sources":["communication-hub.ts"],"sourcesContent":["import type { dxb_header } from \"../utils/global_types.ts\";\nimport { Endpoint, BROADCAST, LOCAL_ENDPOINT } from \"../types/addressing.ts\";\nimport { CommunicationInterface, CommunicationInterfaceSocket, ConnectedCommunicationInterfaceSocket } from \"./communication-interface.ts\";\nimport { Disjunction } from \"../types/logic.ts\";\nimport \"../utils/auto_map.ts\";\nimport { InterfaceDirection } from \"./communication-interface.ts\";\nimport { ESCAPE_SEQUENCES, Logger } from \"../utils/logger.ts\";\nimport { NetworkError } from \"../types/errors.ts\";\nimport { Compiler } from \"../compiler/compiler.ts\";\nimport { Runtime } from \"../runtime/runtime.ts\";\nimport { ProtocolDataType } from \"../compiler/protocol_types.ts\";\nimport { Crypto } from \"../runtime/crypto.ts\";\nimport { IOHandler } from \"../runtime/io_handler.ts\";\nimport { DATEX_ERROR } from \"../types/error_codes.ts\";\nimport { LocalLoopbackInterface, LocalLoopbackInterfaceSocket } from  \"./communication-interfaces/local-loopback-interface.ts\";\nimport { WindowInterface } from  \"./communication-interfaces/window-interface.ts\";\n\nimport { Datex } from \"../mod.ts\";\nimport { Supranet } from \"./supranet.ts\";\nimport { DisposableCallbackHandler } from \"../utils/disposable-callback-handler.ts\";\n\nexport type DatexInData = {\n    dxb: ArrayBuffer|ReadableStreamDefaultReader<Uint8Array>,\n    socket: CommunicationInterfaceSocket\n}\n\nexport type DatexOutData = {\n    dxb: ArrayBuffer,\n    receivers: Endpoint|Disjunction<Endpoint>, // @@any for broadcasts\n    socket: CommunicationInterfaceSocket\n}\n\n/**\n * Callback for handling incoming datex blocks\n */\nexport type DatexInHandler = (data: DatexInData) => Promise<dxb_header>\n\n/**\n * Public communication access point for managing\n * CommunicationInterfaces\n */\nexport class CommunicationHub {\n\n    // singleton\n    private constructor() {}\n    static #instance?: CommunicationHub\n    static get() {\n        if (!this.#instance) this.#instance = new CommunicationHub()\n        return this.#instance;\n    }\n\n    get connected() {\n        return this.handler.connected;\n    }\n\n    get defaultSocket() {\n        return this.handler.defaultSocket;\n    }\n\n    public clear() {\n        return this.handler.clear();\n    }\n\n    /**\n     * Registers a new CommunicationInterface and initializes it\n     * @param comInterface - CommunicationInterface to add\n     * @param setAsDefault - set as default interface for sending DATEX messages\n     * @param timeout - timeout in ms for interface initialization (default: no timeout)\n     * @returns true if the interface was successfully initialized, false if connection could not be established after timeout if specified\n     */\n    public addInterface(comInterface: CommunicationInterface, setAsDefault = false, timeout?: number) {\n        return this.handler.addInterface(comInterface, setAsDefault, timeout);\n    }\n\n    public removeInterface(comInterface: CommunicationInterface) {\n        return this.handler.removeInterface(comInterface);\n    }\n\n    /**\n     * Prints the status of all connected interfaces and attached sockets\n     */\n    public printStatus() {\n        return this.handler.printStatus();\n    }\n\n    /**\n     * Lists all available sockets for an endpoint, ordered by relevance\n     */\n    public printEndpointSockets(endpoint: Endpoint) {\n        return this.handler.printEndpointSockets(endpoint);\n    }\n\n\n    /**\n     * Adds a handler that is called when the online state of the communication hub changes\n     * @param handler \n     */\n    public onOnlineStateChange(handler: (online: boolean) => any) {\n        return this.handler.onOnlineStateChange(handler);\n    }\n\n    /**\n     * @private\n     */\n    handler = new CommunicationHubHandler()\n\n}\n\n\nexport const COM_HUB_SECRET = Symbol(\"COM_HUB_SECRET\")\n\ntype DynamicProperties = {\n    knownSince: number,\n    distance: number\n}\n\n/**\n * Internal handler for managing CommunicationInterfaces\n */\nexport class CommunicationHubHandler {\n\n    /**\n     * Interval for checking online status for indirect/direct socket endpoints\n     */\n    readonly CLEANUP_INTERVAL = 10 * 60 * 1000; // 10min\n    #connected = false;\n    get connected() {\n        return this.#connected;\n    }\n    private updateConnectionStatus() {\n        const isConnected = this.isConnected();\n        if (isConnected !== this.connected) {\n            this.#connected = isConnected;\n            Supranet._setConnected(this.#connected);\n            this.#logger.debug(`Connection status was changed. This endpoint (${Datex.Runtime.endpoint}) is ${isConnected ? \"online\" : \"offline\"}!`);\n            if (this.#connected)\n                this.onlineEvents.forEach(e => e());\n            this.onlineStateChangeCallbackHandler.trigger(this.#connected);\n        }\n    }\n    public async clear() {\n        for (const iterf of this.#interfaces) {\n            if (!(iterf instanceof LocalLoopbackInterface) /*&& !(iterf instanceof WindowInterface)*/) {\n                await this.removeInterface(iterf);\n                this.#logger.warn(\"Removing interface \" + iterf);\n            }\n        }\n    }\n\n    private onlineStateChangeCallbackHandler = new DisposableCallbackHandler<(online: boolean) => any>()\n    \n    private onlineEvents = new Set<() => unknown>();\n    public addOnlineHandler(method: () => unknown) {\n        this.onlineEvents.add(method);\n    }\n    public removeOnlineHandler(method: () => unknown) {\n        this.onlineEvents.delete(method);\n    }\n\n    public onOnlineStateChange(handler: (online: boolean) => any) {\n        return this.onlineStateChangeCallbackHandler.add(handler);\n    }\n\n\n    private isConnected() {\n        if (this.#defaultInterface?.getSockets().size) {\n            return true;\n        }\n        for (const socket of this.iterateSockets(true)) {\n            if (!(socket instanceof LocalLoopbackInterfaceSocket)) {\n                return true;\n            }\n        }\n        return false;\n    }\n\n    #logger = new Logger(\"CommunicationHub\")\n    \n    #interfaces = new Set<CommunicationInterface>()\n    // CommunicationInterfaceSockets are ordered, most recent last\n    #endpointSockets = new Map<Endpoint, Map<ConnectedCommunicationInterfaceSocket, DynamicProperties>>().setAutoDefault(Map).enableAutoRemove()\n    #registeredSockets = new Map<ConnectedCommunicationInterfaceSocket, Set<Endpoint>>().setAutoDefault(Set).enableAutoRemove()\n\n    get registeredSockets() {\n        return this.#registeredSockets;\n    }\n    get endpointSockets() {\n        return this.#endpointSockets;\n    }\n    get interfaces() {\n        return this.#interfaces;\n    }\n\n    // maps main endpoints to a list of instance endpoints that are currently connected via sockets\n    #activeEndpointInstances = new Map<Endpoint, Set<Endpoint>>().setAutoDefault(Set).enableAutoRemove()\n\n    #defaultInterface?: CommunicationInterface\n    #defaultSocket?: ConnectedCommunicationInterfaceSocket\n\n    set defaultSocket(socket: ConnectedCommunicationInterfaceSocket|undefined) {\n        this.#defaultSocket = socket;\n        if (this.#defaultSocket) Runtime.setMainNode(this.#defaultSocket.endpoint);\n    }\n    get defaultSocket() {\n        return this.#defaultSocket;\n    }\n\n    #datexInHandler?: DatexInHandler\n\n    directionSymbols = {\n        [InterfaceDirection.IN]: \"◀──\",\n        [InterfaceDirection.OUT]: \"──▶\",\n        [InterfaceDirection.IN_OUT]: \"◀─▶\"\n    }\n\n    constructor() {\n        this.startCleanupInterval();\n    }\n\n    /** Public facing methods: **/\n\n    public async addInterface(comInterface: CommunicationInterface, setAsDefault = false, timeout?: number) {\n        this.#interfaces.add(comInterface)\n        const connected = await comInterface.init(COM_HUB_SECRET, timeout);\n        if (connected && setAsDefault) this.setDefaultInterface(comInterface)\n        if (!connected) this.#interfaces.delete(comInterface);\n        this.updateConnectionStatus();\n        return connected\n    }\n\n    public async removeInterface(comInterface: CommunicationInterface) {\n        this.#interfaces.delete(comInterface)\n        await comInterface.deinit(COM_HUB_SECRET);\n        this.updateConnectionStatus();\n    }\n\n    public printStatus() {\n        console.log(this.getStatus())\n    }\n\n    public getStatus() {\n        let string = \"\";\n        string += ESCAPE_SEQUENCES.BOLD + \"DATEX Communication Hub\\n\\n\" + ESCAPE_SEQUENCES.RESET;\n        string += `Local Endpoint: ${Runtime.endpoint}\\n`\n        string += `Registered Interfaces: ${this.#interfaces.size}\\n`\n        string += `Connected Sockets: ${this.#registeredSockets.size}\\n\\n`\n\n        const mapping = new Map<string, Map<CommunicationInterfaceSocket, {directEndpoint?: Endpoint, directEndpointDynamicProperties?: DynamicProperties, indirectEndpoints: Map<Endpoint, DynamicProperties>}>>()\n\n        const endpointPreferredSockets = new Map<Endpoint, CommunicationInterfaceSocket|undefined>()\n\n        // interfaces with direct sockets\n        for (const comInterface of this.#interfaces) {\n            const sockets = new Set(\n                [...comInterface.getSockets()]\n                    .map(socket => [socket.endpoint, socket] as [Endpoint, ConnectedCommunicationInterfaceSocket])\n            )\n            const identifier = comInterface.toString()\n            if (!mapping.has(identifier)) mapping.set(identifier, new Map())\n            sockets.forEach(([endpoint, socket]) => mapping.get(identifier)!.set(socket, {directEndpoint: endpoint, directEndpointDynamicProperties: this.#endpointSockets.get(endpoint)?.get(socket), indirectEndpoints: new Map()}))\n        }\n\n        // indirect connections\n        for (const [endpoint, sockets] of this.#endpointSockets) {\n            for (const [socket, dynamicProperties] of sockets) {\n                // check if endpoint is indirect\n                if (socket.endpoint !== endpoint) {\n                    if (!socket.interfaceProperties) {\n                        console.warn(\"Invalid socket, missing interfaceProperties\", socket);\n                        continue;\n                    }\n                    const identifier = socket.toString();\n                    if (!mapping.has(identifier)) mapping.set(identifier, new Map());\n                    if (!mapping.get(identifier)!.has(socket)) mapping.get(identifier)!.set(socket, {indirectEndpoints: new Map});\n\n                    mapping.get(identifier)!.get(socket)!.indirectEndpoints.set(endpoint, dynamicProperties);\n                }\n            }\n        }\n \n        string += \"Default interface: \" + (this.#defaultInterface ? this.#defaultInterface.toString() : \"none\") + \"\\n\";\n\n        const COLORS = {\n            DARK_GREEN: [41, 120, 83],\n            DARK_RED: [120, 41, 53],\n            DARK_GREY: [110, 110, 110],\n        }\n        const DARK_GREEN = `\\x1b[38;2;${COLORS.DARK_GREEN.join(';')}m`\n        const DARK_GREY = `\\x1b[38;2;${COLORS.DARK_GREY.join(';')}m`\n        const DARK_RED = `\\x1b[38;2;${COLORS.DARK_RED.join(';')}m`\n\n        const getFormattedSocketString = (socket: CommunicationInterfaceSocket, endpoint: Endpoint, dynamicProperties: DynamicProperties) => {\n            if (socket.interfaceProperties?.noContinuousConnection && endpoint == BROADCAST) return \"\";\n\n            if (!endpointPreferredSockets.has(endpoint)) endpointPreferredSockets.set(endpoint, this.getPreferredSocketForEndpoint(endpoint));\n            const isPreferred = endpointPreferredSockets.get(endpoint)! === socket;\n\n            const directionSymbolColor = isPreferred ? ESCAPE_SEQUENCES.BOLD : ESCAPE_SEQUENCES.GREY;\n            const directionSymbol =  directionSymbolColor + this.directionSymbols[socket.interfaceProperties?.direction as InterfaceDirection] ?? \"?\";\n            const isDirect = socket.endpoint === endpoint;\n            const color = socket.connected ? \n                (\n                    socket.endpoint ? \n                    (isDirect ? ESCAPE_SEQUENCES.UNYT_GREEN : DARK_GREEN) :\n                    (isDirect ? ESCAPE_SEQUENCES.UNYT_GREY : DARK_GREY)\n                ) : \n                (isDirect ? ESCAPE_SEQUENCES.UNYT_RED : DARK_RED)\n            const connectedState = `${color}⬤${ESCAPE_SEQUENCES.RESET}`\n            const knownSince = (Date.now()-dynamicProperties.knownSince)/1000;\n            const distance = dynamicProperties.distance\n            return `  ${connectedState} ${directionSymbol}${isDirect?'':' (indirect)'}${isDirect&&this.defaultSocket==socket?' (default)':''} ${endpoint??'unknown endpoint'}${ESCAPE_SEQUENCES.GREY} (distance:${distance < 0 ? 'unknown' : distance}, knownSince:${knownSince < 0 ? 'unknown' : knownSince.toFixed(2)+'s'})${ESCAPE_SEQUENCES.RESET}\\n`\n        }\n\n        // print\n        for (const [identifier, sockets] of mapping) {\n            string += `\\n${ESCAPE_SEQUENCES.BOLD}${identifier}${ESCAPE_SEQUENCES.RESET}\\n`\n            for (const [socket, {directEndpoint, directEndpointDynamicProperties, indirectEndpoints}] of sockets) {\n                if (directEndpoint) string += getFormattedSocketString(socket, directEndpoint, directEndpointDynamicProperties!)\n                for (const [endpoint, dynamicProperties] of indirectEndpoints) {\n                    string += getFormattedSocketString(socket, endpoint, dynamicProperties)\n                }\n            }\n        }\n\n        return string;\n    }\n\n    public getEndpointSockets(endpoint: Endpoint|string) {\n        endpoint = endpoint instanceof Endpoint ? endpoint : Endpoint.get(endpoint) as Endpoint;\n\n        let string = \"\";\n        string += `Available sockets for ${endpoint}:\\n`\n\n        for (const socket of this.iterateEndpointSockets(endpoint, false, true)) {\n            string += \"  - \" + socket.toString() + \"\\n\";\n        }\n\n        return string;\n    }\n\n    public printEndpointSockets(endpoint: Endpoint|string) {\n        console.log(this.getEndpointSockets(endpoint))\n    }\n\n\n    /** Internal methods: */\n\n    public registerSocket(socket: ConnectedCommunicationInterfaceSocket, endpoint: Endpoint|undefined = socket.endpoint, dynamicProperties: DynamicProperties) {\n        if (this.#endpointSockets.get(endpoint)?.has(socket)) return;\n\n        if (!endpoint) throw new Error(\"Cannot register socket to communication hub without endpoint.\")\n        if (!socket.connected || !socket.endpoint || !socket.interfaceProperties) throw new Error(\"Cannot register disconnected or uninitialized socket.\")\n\n        const isDirect = socket.endpoint==endpoint;\n\n        // set default socket\n        if (isDirect && socket.toString()==this.#defaultInterface?.toString() && socket.canSend) {\n            this.#logger.debug(\"Setting default socket \" + socket.toString() + \" (endpoint \" + endpoint.toString()+\")\")\n            this.defaultSocket = socket;\t\t\n        }\n\n\n        this.#logger.debug(\"Added new\" + (isDirect?'':' indirect') + \" socket \" + socket.toString() + \" for endpoint \" + endpoint.toString())\n        this.#registeredSockets.getAuto(socket).add(endpoint);\n        this.#endpointSockets.getAuto(endpoint).set(socket, dynamicProperties);\n        // add to instances map if not main endpoint\n        if (endpoint.main !== endpoint)\tthis.#activeEndpointInstances.getAuto(endpoint.main).add(endpoint);\n        this.sortSockets(endpoint);\n        this.updateConnectionStatus();\n    }\n\n    public unregisterSocket(socket: CommunicationInterfaceSocket, endpoint: Endpoint|undefined = socket.endpoint) {\n        const connectedSocket = socket as ConnectedCommunicationInterfaceSocket;\n        if (!endpoint) throw new Error(\"Cannot unregister socket from communication hub without endpoint.\")\n        if (!this.#endpointSockets.has(endpoint)) throw new Error(\"Cannot unregister socket, not registered for endpoint.\")\n        if (!this.#registeredSockets.has(connectedSocket)) throw new Error(\"Cannot unregister socket, not registered.\")\n\n        const isDirect = connectedSocket.endpoint==endpoint;\n\n        // remove default socket\n        if (isDirect && connectedSocket === this.defaultSocket) {\n            this.defaultSocket = undefined\n        }\n\n        this.#logger.debug(\"Removed\" + (isDirect?'':' indirect') + \" socket \" + connectedSocket.toString() + \" for endpoint \" + endpoint.toString())\n\n        const endpointSockets = this.#endpointSockets.get(endpoint)!;\n        const socketEndpoints = this.#registeredSockets.get(connectedSocket)!;\n\n        // remove own socket endpoint\n        endpointSockets.delete(connectedSocket)\n        socketEndpoints.delete(endpoint)\n        this.safeDeleteEndpointInstance(endpoint)\n        \n        // direct socket removed, also remove all indirect sockets\n        if (isDirect) {\n            for (const indirectEndpoint of socketEndpoints) {\n                this.#endpointSockets.get(indirectEndpoint)?.delete(connectedSocket)\n                this.safeDeleteEndpointInstance(indirectEndpoint)\n            }\n            this.#registeredSockets.delete(connectedSocket)\n        }\n        this.updateConnectionStatus();\n    }\n\n    /**\n     * Only completely remove endpoint instance from main->instance mapping if no more sockets are registered for the instance\n     * @param endpoint \n     */\n    private safeDeleteEndpointInstance(endpoint: Endpoint) {\n        if (!this.#endpointSockets.get(endpoint)?.size) {\n            this.#activeEndpointInstances.get(endpoint.main)?.delete(endpoint)\n        }\n    }\n\n    public setDatexInHandler(handler: DatexInHandler) {\n        this.#datexInHandler = handler\n    }\n\n\n    private startCleanupInterval() {\n        setInterval(() => {\n            for (const endpoint of this.#endpointSockets.keys()) {\n                endpoint.isOnline();\n            }\n        }, this.CLEANUP_INTERVAL)\n    }\n\n    /**\n     * @private\n     */\n    async init() {\n        let lastEndpointGooodbyeMessage = await this.compileGoodbyeMessage();\n        let lastEndpoint = Runtime.endpoint;\n        Runtime.onEndpointChanged(async (endpoint) => {\n            this.#logger.debug(\"Endpoint changed to \" + endpoint.toString() + (lastEndpoint ? \" (previous: \" + lastEndpoint + \")\" : \"\"));\n\n            // send GOODBYE for previous endpoint\n            if (lastEndpointGooodbyeMessage /*  && lastEndpoint.main !== lastEndpoint */) {\n                this.#logger.debug(`Broadcasting GOODBYE for previous endpoint ${lastEndpoint} over all sockets`);\n\n                // iterate direct outgoing sockets\n                for (const socket of this.iterateSockets()) {\n                    socket.sendGoodbye(lastEndpointGooodbyeMessage)\n                }\n            } else if (lastEndpoint) this.#logger.debug(`Skipping GOODBYE message for ${lastEndpoint}`);\n\n            lastEndpointGooodbyeMessage = await this.compileGoodbyeMessage();\n            lastEndpoint = endpoint;\n            await sleep(1000);\n\n            // iterate direct outgoing sockets\n            const helloMessage = await this.compileHelloMessage(); // TODO: set ttl to 1?\n            if (helloMessage) {\n                for (const socket of this.iterateSockets()) {\n                    socket.sendHello(helloMessage)\n                }\n            }\n        })\n    }\n\n    public compileGoodbyeMessage() {\n        if (!Runtime.endpoint || Runtime.endpoint == LOCAL_ENDPOINT) return;\n        this.#logger.debug(\"Compiled goodbye message for\", Runtime.endpoint);\n        return Compiler.compile(\"\", [], {type:ProtocolDataType.GOODBYE, sign:true, flood:true, __routing_ttl:1}) as Promise<ArrayBuffer>\n    }\n\n    public compileHelloMessage(ttl = 6) {\n        if (!Runtime.endpoint || Runtime.endpoint == LOCAL_ENDPOINT) return;\n        const keys = Crypto.getOwnPublicKeysExported();\n        return Compiler.compile('?', [keys], {type:ProtocolDataType.HELLO, sign:false, flood:true, __routing_ttl:ttl}) as Promise<ArrayBuffer>;\n    }\n    \n\n    private setDefaultInterface(defaultInterface: CommunicationInterface) {\n        this.#defaultInterface = defaultInterface\n        this.defaultSocket = defaultInterface.getSockets().values().next().value;\n    }\n\n    /**\n     * Returns true when the socket is registered.\n     * Returns true when the endpoint is registered for the socket (if an endpoint is provided).\n     */\n    public hasSocket(socket: CommunicationInterfaceSocket, endpoint?: Endpoint) {\n        if (endpoint) return this.#registeredSockets.get(socket as ConnectedCommunicationInterfaceSocket)?.has(endpoint)\n        else return this.#registeredSockets.has(socket as ConnectedCommunicationInterfaceSocket)\n    }\n\n    public handleOfflineEndpoint(endpoint: Endpoint) {\n        for (const socket of this.iterateEndpointSockets(endpoint, false, false)) {\n            // direct connection endpoint offline, should not happen\n            if (socket.endpoint == endpoint) {\n                // this.#logger.error(\"Direct socket endpoint \"+endpoint.toString()+\" is not reachable, but socket is still connected.\");\n                // (socket as CommunicationInterfaceSocket).connected = false;\n            }\n            // indirect endpoint offline, remove socket registration\n            else {\n                this.#logger.debug(\"Indirect socket endpoint \"+endpoint.toString()+\" is offline, removing socket registration.\");\n                this.unregisterSocket(socket, endpoint);\n            }\n        }\n    }\n\n    /**\n     * Returns true when the endpoint or a matching instance endpoint is directly registered for the socket.\n     */\n    public hasDirectSocket(endpoint: Endpoint) {\n        // check if exact endpoint instance is registered\n        if (this._hasDirectSocket(endpoint)) return true;\n\n        // find socket that matches instance if main endpoint\n        if (endpoint.main === endpoint) {\n            const instances = [...this.#activeEndpointInstances.get(endpoint)??[]];\n            for (const instance of instances) {\n                if (this._hasDirectSocket(instance)) return true;\n            }\n        }\n\n        return false\n    }\n\n     private _hasDirectSocket(endpoint: Endpoint) {\n        // check if exact endpoint instance is registered as direct socket\n        return this.iterateEndpointSockets(endpoint, true, false)\n            .next().done === false;\n    }\n\n    /**\n     * Method called by CommunicationInterfaceSockets when they receive a datex block\n     * @param data\n     */\n\n    public datexIn(data: DatexInData) {\n        if (!this.#datexInHandler) throw new Error(\"No datexInHandler set\")\n        return this.#datexInHandler(data)\n    }\n\n    /**\n     * Sort available sockets for endpoint:\n     * - direct sockets first\n     * - then sort by channel channelFactor (latency,bandwidth)\n     * - then sort by socket connectTimestamp\n     */\n    private sortSockets(endpoint: Endpoint) {\n        const sockets = this.#endpointSockets.get(endpoint)\n        if (!sockets) throw new Error(\"No sockets for endpoint \" + endpoint);\n        const sortedSockets = \n            new Map(\n                    // sort by direct/indirect, direct first\n                    this.sortGrouped(sockets, ([socket]) => socket.endpoint === endpoint ? 0 : 1, 1)\n                .map(sockets => \n                    // sort by distance, smallest first\n                    this.sortGrouped(sockets, ([_, {distance}]) => distance, 1)\n                .map(sockets => \n                    // sort by channelFactor, highest first\n                    this.sortGrouped(sockets, ([socket]) => socket.channelFactor, -1)\n                .map(sockets => \n                    // sort by knownSince, newest (highest) first\n                    this.sortGrouped(sockets, ([_, {knownSince}]) => knownSince, -1)\n                )))\n            .flat(4)\n            )\n        this.#endpointSockets.set(endpoint, sortedSockets)\n    }\n\n    private sortGrouped<I extends Iterable<unknown>>(iterable: I, groupBy: (item: I extends Iterable<infer Item> ? Item : unknown) => number, sortDirection = 1) {\n        return Object\n            // group by channelFactor\n            .entries(Object.groupBy(iterable as any, groupBy))\n            // sort by channelFactor\n            .toSorted(([a], [b]) => (Number(a) - Number(b)) * sortDirection)\n            .map(([_, values]) => values!)\n    }\n\n\n    private getPreferredSocketForEndpoint(endpoint: Endpoint, excludeSocket?: CommunicationInterfaceSocket) {\n        \n        // find socket that matches endpoint instance exactly\n        const socket = this.findMatchingEndpointSocket(endpoint, excludeSocket);\n\n        if (socket) return socket;\n\n        // find socket that matches instance if main endpoint\n        if (endpoint.main === endpoint) {\n            const instances = [...this.#activeEndpointInstances.get(endpoint)??[]];\n            for (let i=instances.length-1; i>=0; i--) {\n                const socket = this.findMatchingEndpointSocket(instances[i], excludeSocket);\n                if (socket) return socket;\n            }\n        }\n\n        if (this.defaultSocket !== excludeSocket)\n            return this.defaultSocket;\n    }\n\n    private findMatchingEndpointSocket(endpoint: Endpoint, excludeSocket?: CommunicationInterfaceSocket) {\n        for (const socket of this.iterateEndpointSockets(endpoint, false, true)) {\n            if (socket === excludeSocket) continue;\n            return socket;\n        }\n    }\n    \n\n    private *iterateEndpointSockets(endpoint: Endpoint, onlyDirect = true, onlyOutgoing = true) {\n        for (const socket of this.#endpointSockets.get(endpoint)?.keys() ?? []) {\n            if (onlyDirect && socket.endpoint !== endpoint) continue;\n            if (onlyOutgoing && !socket.canSend) continue;\n            yield socket;\n        }\n    }\n\n    private *iterateSockets(onlyOutgoing = true) {\n        for (const socket of this.#registeredSockets.keys()) {\n            if (onlyOutgoing && !socket.canSend) continue;\n            yield socket;\n        }\n    }\n\n    /**\n     * Method called to send a datex block to a receiver (or as a broadcast) \n     * @param dxb \n     */\n    public async datexOut(data: DatexOutData):Promise<void> {\n        \n        this.updateTTL(data.dxb, -1) // decrement TTL\n\n        // broadcast\n        if (data.receivers == BROADCAST) return this.datexBroadcastOut(data);\n\n        const receivers = data.receivers instanceof Endpoint ? [data.receivers] : [...data.receivers];\n        const outGroups = receivers.length == 1 ? \n        \n            // single endpoint shortcut\n            new Map([[this.getPreferredSocketForEndpoint(receivers[0], data.socket), new Disjunction(receivers[0])]]) :\n            \n            // group for multiple endpoints\n            new Map(\n                // group receivers by socket\n                [...Map.groupBy(\n                        // map receivers to sockets\n                        receivers.map(r => ({endpoint: r, socket: this.getPreferredSocketForEndpoint(r, data.socket)}),\n                    ), ({socket}) => socket\n                    ).entries()\n                ]\n                // map endpoint object arrays to Set<Endpoint>\n                .map(([k, v]) => [k, new Disjunction(...v.map(({endpoint}) => endpoint))] as const)\n            );\n\n\n        const promises = []\n\n        for (const [socket, endpoints] of outGroups) {\n            if (!socket) continue;\n            promises.push(this.sendAddressedBlockToReceivers(data.dxb, endpoints, socket));\n        }\n\n        // throw error if message could not be sent to some receivers\n        if (outGroups.has(undefined)) {\n            const endpointsString = [...outGroups.get(undefined)!].map(e => e.toString()).join(\", \")\n            throw new NetworkError(\"No socket for \" + endpointsString);\n        } \n\n        await Promise.all(promises);\n    }\n\n    public datexBroadcastOut(data: DatexOutData) {\n        const reachedEndpoints = new Set<Endpoint>()\n\n        for (const socket of this.iterateSockets()) {\n            if (data.socket === socket) continue;\n            if (reachedEndpoints.has(socket.endpoint)) continue;\n            if (socket.interfaceProperties.allowRedirects === false) continue;\n            reachedEndpoints.add(socket.endpoint);\n\n            socket.sendBlock(data.dxb).catch(console.error);\n        }\n    }\n\n    public async sendAddressedBlockToReceivers(dxb: ArrayBuffer, receivers: Disjunction<Endpoint>, destSocket: CommunicationInterfaceSocket) {\n        const addressedDXB = Compiler.updateHeaderReceiver(dxb, receivers);\n        if ((dxb as any)._is_stream) {\n            (addressedDXB as any)._is_stream = true\n        }\n        if (!addressedDXB) throw new Error(\"Failed to update header receivers\");\n\n        IOHandler.handleDatexSent(addressedDXB, receivers, destSocket)\n\n        const success = await destSocket.sendBlock(addressedDXB);\n        if (!success) {\n            this.#logger.warn(\"Failed to send block to \" + receivers.toString() + \" via \" + destSocket.toString() + \", retrying\");\n            this.updateTTL(dxb, 1) // reset TTL to original\n            return this.datexOut({\n                dxb,\n                receivers,\n                socket: destSocket\n            })\n        }\n    }\n\n    private updateTTL(dxb: ArrayBuffer, delta = -1) {\n        const uint8 = new Uint8Array(dxb);\n        const currentTTL = uint8[5];\n        // console.log(\"currentTTL\", currentTTL, delta);\n\n        // too many redirects (ttl is 0)\n        if (currentTTL <= 0) throw new NetworkError(DATEX_ERROR.TOO_MANY_REDIRECTS);\n\n        uint8[5] = currentTTL + delta;\n    }\n\n}\n\n\nexport const communicationHub = CommunicationHub.get()\n"],"names":["Endpoint","BROADCAST","LOCAL_ENDPOINT","Disjunction","InterfaceDirection","ESCAPE_SEQUENCES","Logger","NetworkError","Compiler","Runtime","ProtocolDataType","Crypto","IOHandler","DATEX_ERROR","LocalLoopbackInterface","LocalLoopbackInterfaceSocket","Datex","Supranet","DisposableCallbackHandler","CommunicationHub","get","connected","handler","defaultSocket","clear","addInterface","comInterface","setAsDefault","timeout","removeInterface","printStatus","printEndpointSockets","endpoint","onOnlineStateChange","CommunicationHubHandler","COM_HUB_SECRET","Symbol","CLEANUP_INTERVAL","updateConnectionStatus","isConnected","_setConnected","debug","onlineEvents","forEach","e","onlineStateChangeCallbackHandler","trigger","iterf","warn","Set","addOnlineHandler","method","add","removeOnlineHandler","delete","getSockets","size","socket","iterateSockets","Map","setAutoDefault","enableAutoRemove","registeredSockets","endpointSockets","interfaces","setMainNode","directionSymbols","IN","OUT","IN_OUT","constructor","startCleanupInterval","init","setDefaultInterface","deinit","console","log","getStatus","string","BOLD","RESET","mapping","endpointPreferredSockets","sockets","map","identifier","toString","has","set","directEndpoint","directEndpointDynamicProperties","indirectEndpoints","dynamicProperties","interfaceProperties","DARK_GREEN","DARK_GREY","DARK_RED","getFormattedSocketString","noContinuousConnection","getPreferredSocketForEndpoint","directionSymbol","directionSymbolColor","isPreferred","GREY","direction","isDirect","color","UNYT_GREEN","UNYT_GREY","UNYT_RED","connectedState","knownSince","Date","now","distance","toFixed","getEndpointSockets","iterateEndpointSockets","registerSocket","Error","canSend","getAuto","main","sortSockets","unregisterSocket","connectedSocket","undefined","socketEndpoints","safeDeleteEndpointInstance","indirectEndpoint","setDatexInHandler","setInterval","keys","isOnline","lastEndpointGooodbyeMessage","compileGoodbyeMessage","lastEndpoint","onEndpointChanged","sendGoodbye","sleep","helloMessage","compileHelloMessage","sendHello","compile","type","GOODBYE","sign","flood","__routing_ttl","ttl","getOwnPublicKeysExported","HELLO","defaultInterface","values","next","value","hasSocket","handleOfflineEndpoint","hasDirectSocket","_hasDirectSocket","instance","done","datexIn","data","sortedSockets","sortGrouped","_","channelFactor","flat","iterable","groupBy","sortDirection","Object","entries","toSorted","a","b","Number","excludeSocket","findMatchingEndpointSocket","instances","i","length","onlyDirect","onlyOutgoing","datexOut","updateTTL","dxb","receivers","datexBroadcastOut","outGroups","r","k","v","promises","endpoints","push","sendAddressedBlockToReceivers","join","Promise","all","reachedEndpoints","allowRedirects","sendBlock","catch","error","destSocket","addressedDXB","updateHeaderReceiver","_is_stream","handleDatexSent","delta","uint8","Uint8Array","currentTTL","TOO_MANY_REDIRECTS","communicationHub"],"mappings":"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","sourceRoot":"/lib/datex-core-js-legacy-0.3.15/network/","file":"communication-hub.ts"}