259 lines
6.2 KiB
TypeScript
259 lines
6.2 KiB
TypeScript
/**
|
|
* @author Martin Karkowski
|
|
* @email m.karkowski@zema.de
|
|
* @create date 2022-01-11 17:38:19
|
|
* @modify date 2022-01-14 08:29:02
|
|
* @desc [description]
|
|
*/
|
|
|
|
import { avgOfArray, maxOfArray, minOfArray } from "../../helpers/arrayMethods";
|
|
import { getNopeLogger } from "../../logger/getLogger";
|
|
import { INopeDispatcher } from "../../types/nope";
|
|
|
|
const logger = getNopeLogger("baseService");
|
|
|
|
/**
|
|
* Generate and registers a ping service.
|
|
*
|
|
* @author M.Karkowski
|
|
* @export
|
|
* @param {INopeDispatcher} dispatcher
|
|
* @return {*} The function to ping all dispatchers.
|
|
*/
|
|
export async function generatePingServices(dispatcher: INopeDispatcher) {
|
|
// Name of the Service
|
|
const serviceName = `nope/baseService/ping`;
|
|
|
|
const ping = async () => {
|
|
return {
|
|
dispatcherId: dispatcher.id,
|
|
timestamp: dispatcher.connectivityManager.now,
|
|
};
|
|
};
|
|
|
|
// Registers the Ping Method at the Dispatcher.
|
|
await dispatcher.rpcManager.registerService(ping, {
|
|
id: serviceName,
|
|
schema: {
|
|
inputs: [],
|
|
outputs: {
|
|
type: "object",
|
|
properties: {
|
|
dispatcherId: {
|
|
type: "string",
|
|
description: "Id of the responding Dispatcher",
|
|
},
|
|
timestamp: {
|
|
type: "number",
|
|
description: "UTC-Timestamp of the system which is responding",
|
|
},
|
|
},
|
|
},
|
|
type: "function",
|
|
description: "Ping",
|
|
},
|
|
});
|
|
|
|
return await generatePingAccessors(dispatcher);
|
|
}
|
|
|
|
/**
|
|
* Helper to generate the Accessors to ping dispatchers or ping all
|
|
* systems
|
|
*
|
|
* @author M.Karkowski
|
|
* @export
|
|
* @param {INopeDispatcher} dispatcher The Dispatcher, used to perform the calls.
|
|
* @return {*}
|
|
*/
|
|
export async function generatePingAccessors(dispatcher: INopeDispatcher) {
|
|
const serviceName = `nope/baseService/ping`;
|
|
|
|
// Function to determine the ping in the services.
|
|
const determinePing = async (target: string) => {
|
|
// Call the Pings
|
|
const start = dispatcher.connectivityManager.now;
|
|
const result: {
|
|
requestId: string;
|
|
dispatcherId: string;
|
|
timestamp: number;
|
|
isMaster: boolean;
|
|
} = await dispatcher.rpcManager.performCall(serviceName, [], { target });
|
|
const delay = dispatcher.connectivityManager.now;
|
|
|
|
const ping = delay - start;
|
|
|
|
return {
|
|
ping,
|
|
...result,
|
|
};
|
|
};
|
|
|
|
// Function to Ping all Services
|
|
const pingAll = async () => {
|
|
const dispatchers = Array.from(
|
|
dispatcher.connectivityManager.dispatchers.reverseSimplified.get(
|
|
serviceName
|
|
)
|
|
);
|
|
const promises = dispatchers.map((target) => {
|
|
return determinePing(target);
|
|
});
|
|
const pings = await promises;
|
|
const avg = avgOfArray(pings, "ping");
|
|
const max = maxOfArray(pings, "ping");
|
|
const min = minOfArray(pings, "ping");
|
|
|
|
return {
|
|
pings,
|
|
avg,
|
|
max,
|
|
min,
|
|
};
|
|
};
|
|
|
|
return {
|
|
determinePing,
|
|
pingAll,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Registers a sync service, which will syncronize the time
|
|
*
|
|
* @author M.Karkowski
|
|
* @export
|
|
* @param {INopeDispatcher} dispatcher
|
|
* @return {*} The Sync-Method to call
|
|
*/
|
|
export async function enableTimeSyncing(dispatcher: INopeDispatcher) {
|
|
if (!dispatcher.rpcManager.serviceExists(`nope/baseService/ping`)) {
|
|
await generatePingServices(dispatcher);
|
|
}
|
|
|
|
const determinePing = (await generatePingAccessors(dispatcher)).determinePing;
|
|
|
|
// Function to determine the ping in the services.
|
|
const manualSyncTime = async () => {
|
|
const masterId = dispatcher.connectivityManager.master.id;
|
|
const ping = await determinePing(masterId);
|
|
|
|
// Now use the delay to synchronize the time.
|
|
dispatcher.connectivityManager.syncTime(ping.timestamp, ping.ping / 2);
|
|
};
|
|
|
|
dispatcher.connectivityManager.dispatchers.onChange.subscribe((changes) => {
|
|
if (dispatcher.disposing) {
|
|
return;
|
|
}
|
|
|
|
if (!dispatcher.connectivityManager.isMaster) {
|
|
manualSyncTime()
|
|
.catch((e) => {
|
|
logger.error("Failed synchronizing time");
|
|
logger.error(e);
|
|
})
|
|
.then((_) => {
|
|
logger.info(
|
|
`Synchronized time with master=${dispatcher.connectivityManager.master.id}`
|
|
);
|
|
});
|
|
}
|
|
});
|
|
|
|
return {
|
|
manualSyncTime,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Helper, that lets enables waiting for a required dispatcher.
|
|
*
|
|
* @author M.Karkowski
|
|
* @export
|
|
* @param {INopeDispatcher} dispatcher
|
|
* @return {*}
|
|
*/
|
|
export async function waitForDispatcher(
|
|
dispatcher: INopeDispatcher,
|
|
name: string
|
|
) {
|
|
dispatcher.ready.getter = (value) => {
|
|
return (
|
|
dispatcher.connectivityManager.dispatchers.data
|
|
.getContent()
|
|
.includes(name) && value
|
|
);
|
|
};
|
|
|
|
return {};
|
|
}
|
|
|
|
/**
|
|
* A Helper to create a Service to manually define a master.
|
|
*
|
|
* @author M.Karkowski
|
|
* @export
|
|
* @param {INopeDispatcher} dispatcher The Dispatcher to use.
|
|
* @return {*}
|
|
*/
|
|
export async function generateDefineMaster(dispatcher: INopeDispatcher) {
|
|
/**
|
|
* Create a Ping service.
|
|
*
|
|
* @author M.Karkowski
|
|
* @param {string} requestId
|
|
* @return {*}
|
|
*/
|
|
const defineAsMaster = async (value = true) => {
|
|
dispatcher.connectivityManager.isMaster = value;
|
|
};
|
|
|
|
// Registers the Ping Method at the Dispatcher.
|
|
await dispatcher.rpcManager.registerService(defineAsMaster, {
|
|
id: `nope/baseService/defineAsMaster`,
|
|
schema: {
|
|
description:
|
|
"Defines the desired Node as Master. Propagets, that this system is a master",
|
|
inputs: [
|
|
{
|
|
name: "value",
|
|
optional: true,
|
|
schema: {
|
|
type: "boolean",
|
|
},
|
|
},
|
|
],
|
|
outputs: [],
|
|
},
|
|
});
|
|
|
|
const setMaster = async () => {
|
|
// Set the Master to True
|
|
dispatcher.connectivityManager.isMaster = true;
|
|
// Get the Master ID
|
|
const masterId = dispatcher.id;
|
|
const service = `nope/baseService/defineAsMaster`;
|
|
|
|
// Get the Matching Dispatchers.
|
|
const dispatchers =
|
|
dispatcher.connectivityManager.dispatchers.reverseSimplified.get(
|
|
`nope/baseService/defineAsMaster`
|
|
);
|
|
dispatchers.delete(masterId);
|
|
const targets = Array.from(dispatchers);
|
|
|
|
await dispatcher.rpcManager.performCall(
|
|
targets.map((_) => service),
|
|
[false],
|
|
targets.map((target) => {
|
|
return { target, timeout: 200 };
|
|
})
|
|
);
|
|
};
|
|
|
|
return {
|
|
setMaster,
|
|
};
|
|
}
|