Something went wrong. Try again.
A functional toolkit for authoring resource-leveling algorithms — compose constraint and scoring blocks, enumerate feasible schedules
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148import { Duration } from "../model/Duration.ts";import { TimeUnit } from "../model/types.ts";
export class MppUtility { static getInt(buffer: Uint8Array, offset: number): number { return new DataView( buffer.buffer, buffer.byteOffset, buffer.byteLength, ).getInt32(offset, true); }
static getUInt(buffer: Uint8Array, offset: number): number { return new DataView( buffer.buffer, buffer.byteOffset, buffer.byteLength, ).getUint32(offset, true); }
static getShort(buffer: Uint8Array, offset: number): number { return new DataView( buffer.buffer, buffer.byteOffset, buffer.byteLength, ).getInt16(offset, true); }
static getUShort(buffer: Uint8Array, offset: number): number { return new DataView( buffer.buffer, buffer.byteOffset, buffer.byteLength, ).getUint16(offset, true); }
static getDouble(buffer: Uint8Array, offset: number): number { return new DataView( buffer.buffer, buffer.byteOffset, buffer.byteLength, ).getFloat64(offset, true); }
static getUnicodeString( buffer: Uint8Array, offset: number, byteLength: number, ): string { const slice = buffer.subarray(offset, offset + byteLength); const decoded = Buffer.from(slice).toString("utf16le"); // NUL is the padding byte in MPP's fixed-width UTF-16LE fields, so // matching it is the point. (oxlint <=1.76 reports this; 1.78 does not — // the suppression keeps the file clean on both.) // oxlint-disable-next-line no-control-regex return decoded.replace(/\0+$/u, ""); }
static getUnicodeStringToEnd(buffer: Uint8Array, offset = 0): string { if (offset >= buffer.length) { return ""; } return ( Buffer.from(buffer.subarray(offset)).toString("utf16le").split("\0")[0] ?? "" ); }
static getAnsiString( buffer: Uint8Array, offset = 0, byteLength?: number, ): string { const slice = byteLength === undefined ? buffer.subarray(offset) : buffer.subarray(offset, offset + byteLength); return Buffer.from(slice).toString("latin1").split("\0")[0] ?? ""; }
static getTimestampValue(buffer: Uint8Array, offset: number): Date | null { if (offset + 4 > buffer.length) { return null; }
const time = this.getUShort(buffer, offset); const days = this.getUShort(buffer, offset + 2); if (days <= 1 || days === 0xffff) { return null; }
// Use UTC arithmetic to avoid DST issues, then construct local Date const baseMs = Date.UTC(1983, 11, 31); const totalSeconds = time * 6; const targetMs = baseMs + days * 86400000 + totalSeconds * 1000; const d = new Date(targetMs); const result = new Date( d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate(), d.getUTCHours(), d.getUTCMinutes(), d.getUTCSeconds(), );
if (days < 100 && result.getSeconds() !== 0) { return null; }
return result; }
static durationFromTenthsOfMinutes( value: number | null, unit: TimeUnit, ): Duration | null { if (value === null) { return null; }
switch (unit) { case TimeUnit.Hours: return new Duration(value / 600, TimeUnit.Hours); case TimeUnit.Days: return new Duration(value / 4800, TimeUnit.Days); case TimeUnit.Minutes: default: return new Duration(value / 10, TimeUnit.Minutes); } }
static isSameMinute(left: Date | null, right: Date | null): boolean { return ( left?.getFullYear() === right?.getFullYear() && left?.getMonth() === right?.getMonth() && left?.getDate() === right?.getDate() && left?.getHours() === right?.getHours() && left?.getMinutes() === right?.getMinutes() ); }
static toHex(buffer: Uint8Array, maxBytes = 64): string { return Array.from(buffer.subarray(0, Math.min(maxBytes, buffer.length))) .map((value) => value.toString(16).padStart(2, "0")) .join(""); }}