8a89b578c0e76acaa2e0343e7bc6a68d5c04d4ff
[e-mobility-charging-stations-simulator.git] / src / utils / Utils.ts
1 import Configuration from './Configuration';
2 import { WebSocketCloseEventStatusString } from '../types/WebSocket';
3 import { WorkerProcessType } from '../types/Worker';
4 import crypto from 'crypto';
5 import { v4 as uuid } from 'uuid';
6
7 export default class Utils {
8 public static logPrefix(prefixString = ''): string {
9 return new Date().toLocaleString() + prefixString;
10 }
11
12 public static generateUUID(): string {
13 return uuid();
14 }
15
16 public static async sleep(milliSeconds: number): Promise<NodeJS.Timeout> {
17 return new Promise((resolve) => setTimeout(resolve, milliSeconds));
18 }
19
20 public static secondsToHHMMSS(seconds: number): string {
21 return Utils.milliSecondsToHHMMSS(seconds * 1000);
22 }
23
24 public static milliSecondsToHHMMSS(milliSeconds: number): string {
25 return new Date(milliSeconds).toISOString().substr(11, 8);
26 }
27
28 public static removeExtraEmptyLines(tab: string[]): void {
29 // Start from the end
30 for (let i = tab.length - 1; i > 0; i--) {
31 // Two consecutive empty lines?
32 if (tab[i].length === 0 && tab[i - 1].length === 0) {
33 // Remove the last one
34 tab.splice(i, 1);
35 }
36 // Check last line
37 if (i === 1 && tab[i - 1].length === 0) {
38 // Remove the first one
39 tab.splice(i - 1, 1);
40 }
41 }
42 }
43
44 public static convertToDate(value: unknown): Date {
45 // Check
46 if (!value) {
47 return value as Date;
48 }
49 // Check Type
50 if (!(value instanceof Date)) {
51 return new Date(value as string);
52 }
53 return value;
54 }
55
56 public static convertToInt(value: unknown): number {
57 let changedValue: number = value as number;
58 if (!value) {
59 return 0;
60 }
61 if (Number.isSafeInteger(value)) {
62 return value as number;
63 }
64 // Check
65 if (Utils.isString(value)) {
66 // Create Object
67 changedValue = parseInt(value as string);
68 }
69 return changedValue;
70 }
71
72 public static convertToFloat(value: unknown): number {
73 let changedValue: number = value as number;
74 if (!value) {
75 return 0;
76 }
77 // Check
78 if (Utils.isString(value)) {
79 // Create Object
80 changedValue = parseFloat(value as string);
81 }
82 return changedValue;
83 }
84
85 public static convertToBoolean(value: unknown): boolean {
86 let result = false;
87 // Check boolean
88 if (value) {
89 // Check the type
90 if (typeof value === 'boolean') {
91 // Already a boolean
92 result = value;
93 } else {
94 // Convert
95 result = (value === 'true');
96 }
97 }
98 return result;
99 }
100
101 public static getRandomFloat(max: number, min = 0): number {
102 return (crypto.randomBytes(4).readUInt32LE() / 0xffffffff) * (max - min) + min;
103 }
104
105 public static getRandomInt(max: number, min = 0): number {
106 if (min) {
107 return Math.floor(Utils.secureRandom() * (max - min + 1)) + min;
108 }
109 return Math.floor(Utils.secureRandom() * (max + 1));
110 }
111
112 public static roundTo(numberValue: number, scale: number): number {
113 const roundPower = Math.pow(10, scale);
114 return Math.round(numberValue * roundPower) / roundPower;
115 }
116
117 public static truncTo(numberValue: number, scale: number): number {
118 const truncPower = Math.pow(10, scale);
119 return Math.trunc(numberValue * truncPower) / truncPower;
120 }
121
122 public static getRandomFloatRounded(max: number, min = 0, scale = 2): number {
123 if (min) {
124 return Utils.roundTo(Utils.getRandomFloat(max, min), scale);
125 }
126 return Utils.roundTo(Utils.getRandomFloat(max), scale);
127 }
128
129 public static getRandomFloatFluctuatedRounded(staticValue: number, fluctuationPercent: number, scale = 2): number {
130 if (fluctuationPercent === 0) {
131 return Utils.roundTo(staticValue, scale);
132 }
133 const fluctuationRatio = fluctuationPercent / 100;
134 return Utils.getRandomFloatRounded(staticValue * (1 + fluctuationRatio), staticValue * (1 - fluctuationRatio), scale);
135 }
136
137 public static cloneObject<T>(object: T): T {
138 return JSON.parse(JSON.stringify(object)) as T;
139 }
140
141 public static isIterable<T>(obj: T): boolean {
142 if (obj) {
143 return typeof obj[Symbol.iterator] === 'function';
144 }
145 return false;
146 }
147
148 public static isEmptyJSon(document: unknown): boolean {
149 // Empty?
150 if (!document) {
151 return true;
152 }
153 // Check type
154 if (typeof document !== 'object') {
155 return true;
156 }
157 // Check
158 return Object.keys(document).length === 0;
159 }
160
161 public static isString(value: unknown): boolean {
162 return typeof value === 'string';
163 }
164
165 public static isUndefined(value: unknown): boolean {
166 return typeof value === 'undefined';
167 }
168
169 public static isNullOrUndefined(value: unknown): boolean {
170 // eslint-disable-next-line no-eq-null, eqeqeq
171 if (value == null) {
172 return true;
173 }
174 return false;
175 }
176
177 public static isEmptyArray(object: unknown): boolean {
178 if (!object) {
179 return true;
180 }
181 if (Array.isArray(object) && object.length > 0) {
182 return false;
183 }
184 return true;
185 }
186
187 public static isEmptyObject(obj: Record<string, unknown>): boolean {
188 return !Object.keys(obj).length;
189 }
190
191 public static insertAt = (str: string, subStr: string, pos: number): string => `${str.slice(0, pos)}${subStr}${str.slice(pos)}`;
192
193 /**
194 * @param [retryNumber=0]
195 * @returns delay in milliseconds
196 */
197 public static exponentialDelay(retryNumber = 0): number {
198 const delay = Math.pow(2, retryNumber) * 100;
199 const randomSum = delay * 0.2 * Utils.secureRandom(); // 0-20% of the delay
200 return delay + randomSum;
201 }
202
203 /**
204 * Convert websocket error code to human readable string message
205 *
206 * @param code websocket error code
207 * @returns human readable string message
208 */
209 public static getWebSocketCloseEventStatusString(code: number): string {
210 if (code >= 0 && code <= 999) {
211 return '(Unused)';
212 } else if (code >= 1016) {
213 if (code <= 1999) {
214 return '(For WebSocket standard)';
215 } else if (code <= 2999) {
216 return '(For WebSocket extensions)';
217 } else if (code <= 3999) {
218 return '(For libraries and frameworks)';
219 } else if (code <= 4999) {
220 return '(For applications)';
221 }
222 }
223 if (!Utils.isUndefined(WebSocketCloseEventStatusString[code])) {
224 return WebSocketCloseEventStatusString[code] as string;
225 }
226 return '(Unknown)';
227 }
228
229 public static workerPoolInUse(): boolean {
230 return [WorkerProcessType.DYNAMIC_POOL, WorkerProcessType.STATIC_POOL].includes(Configuration.getWorkerProcess());
231 }
232
233 public static workerDynamicPoolInUse(): boolean {
234 return Configuration.getWorkerProcess() === WorkerProcessType.DYNAMIC_POOL;
235 }
236
237 /**
238 * Generate a cryptographically secure random number in the [0,1[ range
239 *
240 * @returns
241 */
242 public static secureRandom(): number {
243 return crypto.randomBytes(4).readUInt32LE() / 0x100000000;
244 }
245 }