import { NodeIQQNTWrapperSession } from '@/core/wrapper'; import { randomUUID } from 'crypto'; import { ListenerNamingMapping, ServiceNamingMapping } from '@/core'; interface InternalMapKey { timeout: number; createtime: number; func: (...arg: any[]) => any; checker: ((...args: any[]) => boolean) | undefined; } type EnsureFunc = T extends (...args: any) => any ? T : never; type FuncKeys = Extract< { [K in keyof T]: EnsureFunc extends never ? never : K; }[keyof T], string >; export type ListenerClassBase = Record; export class NTEventWrapper { private readonly WrapperSession: NodeIQQNTWrapperSession | undefined; //WrapperSession private readonly listenerManager: Map = new Map(); //ListenerName-Unique -> Listener实例 private readonly EventTask = new Map>>(); //tasks ListenerMainName -> ListenerSubName-> uuid -> {timeout,createtime,func} constructor( wrapperSession: NodeIQQNTWrapperSession, ) { this.WrapperSession = wrapperSession; } createProxyDispatch(ListenerMainName: string) { const dispatcherListenerFunc = this.dispatcherListener.bind(this); return new Proxy( {}, { get(target: any, prop: any, receiver: any) { if (typeof target[prop] === 'undefined') { // 如果方法不存在,返回一个函数,这个函数调用existentMethod return (...args: any[]) => { dispatcherListenerFunc(ListenerMainName, prop, ...args).then(); }; } // 如果方法存在,正常返回 return Reflect.get(target, prop, receiver); }, }, ); } createEventFunction< Service extends keyof ServiceNamingMapping, ServiceMethod extends FuncKeys, T extends (...args: any) => any = EnsureFunc, >(eventName: `${Service}/${ServiceMethod}`): T | undefined { const eventNameArr = eventName.split('/'); type eventType = { [key: string]: () => { [key: string]: (...params: Parameters) => Promise> }; }; if (eventNameArr.length > 1) { const serviceName = 'get' + eventNameArr[0].replace('NodeIKernel', ''); const eventName = eventNameArr[1]; const services = (this.WrapperSession as unknown as eventType)[serviceName](); let event = services[eventName]; //重新绑定this event = event.bind(services); if (event) { return event as T; } return undefined; } } createListenerFunction(listenerMainName: string, uniqueCode: string = ''): T { const existListener = this.listenerManager.get(listenerMainName + uniqueCode); if (!existListener) { const Listener = this.createProxyDispatch(listenerMainName); const ServiceSubName = /^NodeIKernel(.*?)Listener$/.exec(listenerMainName)![1]; const Service = `NodeIKernel${ServiceSubName}Service/addKernel${ServiceSubName}Listener`; // eslint-disable-next-line // @ts-ignore this.createEventFunction(Service)(Listener as T); this.listenerManager.set(listenerMainName + uniqueCode, Listener); return Listener as T; } return existListener as T; } //统一回调清理事件 async dispatcherListener(ListenerMainName: string, ListenerSubName: string, ...args: any[]) { this.EventTask.get(ListenerMainName) ?.get(ListenerSubName) ?.forEach((task, uuid) => { if (task.createtime + task.timeout < Date.now()) { this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.delete(uuid); return; } if (task?.checker?.(...args)) { task.func(...args); } }); } async callNoListenerEvent< Service extends keyof ServiceNamingMapping, ServiceMethod extends FuncKeys, EventType extends (...args: any) => any = EnsureFunc, >( serviceAndMethod: `${Service}/${ServiceMethod}`, ...args: Parameters ): Promise>> { return (this.createEventFunction(serviceAndMethod))!(...args); } async registerListen< Listener extends keyof ListenerNamingMapping, ListenerMethod extends FuncKeys, ListenerType extends (...args: any) => any = EnsureFunc, >( listenerAndMethod: `${Listener}/${ListenerMethod}`, checker: (...args: Parameters) => boolean, waitTimes = 1, timeout = 5000, ) { return new Promise>((resolve, reject) => { const ListenerNameList = listenerAndMethod.split('/'); const ListenerMainName = ListenerNameList[0]; const ListenerSubName = ListenerNameList[1]; const id = randomUUID(); let complete = 0; let retData: Parameters | undefined = undefined; function sendDataCallback() { if (complete == 0) { reject(new Error(' ListenerName:' + listenerAndMethod + ' timeout')); } else { resolve(retData!); } } const timeoutRef = setTimeout(sendDataCallback, timeout); const eventCallback = { timeout: timeout, createtime: Date.now(), checker: checker, func: (...args: Parameters) => { complete++; retData = args; if (complete >= waitTimes) { clearTimeout(timeoutRef); sendDataCallback(); } }, }; if (!this.EventTask.get(ListenerMainName)) { this.EventTask.set(ListenerMainName, new Map()); } if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) { this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map()); } this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback); this.createListenerFunction(ListenerMainName); }); } async callNormalEventV2< Service extends keyof ServiceNamingMapping, ServiceMethod extends FuncKeys, Listener extends keyof ListenerNamingMapping, ListenerMethod extends FuncKeys, EventType extends (...args: any) => any = EnsureFunc, ListenerType extends (...args: any) => any = EnsureFunc >( serviceAndMethod: `${Service}/${ServiceMethod}`, listenerAndMethod: `${Listener}/${ListenerMethod}`, args: Parameters, checkerEvent: (ret: Awaited>) => boolean = () => true, checkerListener: (...args: Parameters) => boolean = () => true, callbackTimesToWait = 1, timeout = 5000, ) { const id = randomUUID(); let complete = 0; let retData: Parameters | undefined = undefined; let retEvent: any = {}; function sendDataCallback(resolve: any, reject: any) { if (complete == 0) { reject( new Error( 'Timeout: NTEvent serviceAndMethod:' + serviceAndMethod + ' ListenerName:' + listenerAndMethod + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n', ), ); } else { resolve([retEvent as Awaited>, ...retData!]); } } const ListenerNameList = listenerAndMethod.split('/'); const ListenerMainName = ListenerNameList[0]; const ListenerSubName = ListenerNameList[1]; return new Promise<[EventRet: Awaited>, ...Parameters]>( (resolve, reject) => { const timeoutRef = setTimeout(() => sendDataCallback(resolve, reject), timeout); const eventCallback = { timeout: timeout, createtime: Date.now(), checker: checkerListener, func: (...args: any[]) => { complete++; retData = args as Parameters; if (complete >= callbackTimesToWait) { clearTimeout(timeoutRef); sendDataCallback(resolve, reject); } }, }; if (!this.EventTask.get(ListenerMainName)) { this.EventTask.set(ListenerMainName, new Map()); } if (!this.EventTask.get(ListenerMainName)?.get(ListenerSubName)) { this.EventTask.get(ListenerMainName)?.set(ListenerSubName, new Map()); } this.EventTask.get(ListenerMainName)?.get(ListenerSubName)?.set(id, eventCallback); this.createListenerFunction(ListenerMainName); this.createEventFunction(serviceAndMethod)!(...(args)) .then((eventResult: any) => { retEvent = eventResult; if (!checkerEvent(retEvent) && timeoutRef.hasRef()) { clearTimeout(timeoutRef); reject( new Error( 'EventChecker Failed: NTEvent serviceAndMethod:' + serviceAndMethod + ' ListenerName:' + listenerAndMethod + ' EventRet:\n' + JSON.stringify(retEvent, null, 4) + '\n', ), ); } }) .catch(reject); }, ); } }