Refactoring: migrating to @kayahr/ed-journal pt2.

This commit is contained in:
marley 2023-05-18 09:48:16 -07:00
parent 36e73b89bc
commit 45533267ce
6 changed files with 411 additions and 207 deletions

259
src/models/CMDR.ts Normal file
View file

@ -0,0 +1,259 @@
import { FSDJump, Journal, JournalEvent, Scan } from '@kayahr/ed-journal';
import * as _ from 'lodash-es';
import { Body } from './Body';
import { Log } from './Log';
import { System } from './System';
const EventEmitter = require('events');
const fs = require('fs');
const { globSync } = require('glob');
const path = require('path');
const reverseLineReader = require('reverse-line-reader');
export class CMDR extends EventEmitter {
location: System;
#journal?: Journal;
navRoute: System[];
constructor(journalPattern: string) {
super();
this.location = new System();
this.navRoute = [];
const latestJournal: string|undefined = this.#getLatestJournal(journalPattern);
if (latestJournal) {
Log.write(`Attempting to find current location.`);
this.#getLastLocation(latestJournal).then(() => {
if (this.location.name !== 'Unknown') {
Log.write('Attempting to find scanned bodies in current system.');
this.#getScannedBodies(latestJournal).then(async () => {
if (this.location.bodies.length > 0) {
Log.write('Scanned bodies found.');
this.emit('BUILD_BODY_LIST');
} else {
Log.write('No scanned bodies found in current system.');
}
this.#journal = await Journal.open({ position: 'end' });
Log.write('Checking for nav route.');
await this.#setNavRoute();
await this.#journal.close();
});
}
});
}
}
/* --------------------------------------------------------------------- #getLatestJournal ---- */
#getLatestJournal(journalPattern: string): string|undefined {
const journals = globSync(journalPattern, { windowsPathsNoEscape: true });
const journalPath: string|undefined = _.maxBy(journals, file => fs.statSync(file).mtime);
if (journalPath) {
Log.write(`New journal file found, now watching ${path.basename(journalPath)}.`);
} else {
Log.write('ERROR: Unable to find latest journal.');
}
return journalPath;
}
/* ---------------------------------------------------------------------- #getLastLocation ---- */
#getLastLocation(latestJournal: string): any {
return reverseLineReader.eachLine(latestJournal, (raw: string, last: boolean) => {
if (raw) {
const line = JSON.parse(raw);
if (line.event === 'FSDJump' || line.event === 'Location') {
this.location = new System(line);
Log.write(`Current location set to ${this.location.name}.`);
this.emit('ENTERED_NEW_SYSTEM');
return false;
} else if (last) {
Log.write('WARNING: Unable to find last known location.');
return false;
}
}
});
}
/* --------------------------------------------------------------------- #getScannedBodies ---- */
#getScannedBodies(latestJournal: string): any {
let dssLine: Scan|null = null;
return reverseLineReader.eachLine(latestJournal, (raw: string) => {
if (raw) {
const line: JournalEvent = JSON.parse(raw);
if (dssLine) {
if (line.event === 'SAAScanComplete') {
this.location.bodies.push(new Body(dssLine, true));
} else {
const dupChecker = { 'BodyName': dssLine.BodyName, 'BodyID': dssLine.BodyID };
const duplicate = _.find(this.location.bodies, dupChecker);
if (duplicate === undefined) {
this.location.bodies.push(new Body(dssLine));
}
}
dssLine = null;
}
if (line.event === 'Scan' && 'ScanType' in line) {
if (line.ScanType === 'Detailed' && !('StarType' in line)) {
dssLine = line as Scan;
} else if ('StarType' in line) {
this.location.bodies.push(new Body(line as Scan));
} else if (line.ScanType == 'AutoScan') {
if ('PlanetClass' in line) {
const dupChecker = {
'BodyName': (line as Scan).BodyName,
'BodyID': (line as Scan).BodyID,
};
const duplicate = _.find(this.location.bodies, dupChecker);
if (duplicate === undefined) {
this.location.bodies.push(new Body(line as Scan));
}
} else { // Asteroid.
this.location.bodies.push(new Body(line as Scan));
}
}
} else if (line.event === 'FSDJump' || line.event === 'Location') {
return false;
}
}
});
}
/* -------------------------------------------------------------------------- #getNavRoute ---- */
async #setNavRoute(): Promise<void> {
this.navRoute = [];
const route = await this.#journal?.readNavRoute();
if (route) {
let push: boolean = false;
route.Route.forEach((system) => {
if (!push && system.SystemAddress === this.location.SystemAddress) {
push = true;
}
if (push && system.SystemAddress !== this.location.SystemAddress) {
this.navRoute?.push(new System(system));
}
});
if (this.navRoute.length > 0) {
Log.write('Nav route set.');
} else {
Log.write('No nav route found.');
}
this.emit('SET_NAV_ROUTE');
}
}
/* --------------------------------------------------------------------------------- track ---- */
async track() {
this.#journal = await Journal.open({ watch: true, position: 'end' });
let dssFlag: boolean = false;
for await (const event of this.#journal) {
switch (event.event) {
case 'StartJump': {
if (event.JumpType === 'Hyperspace') this.emit('ENTERING_WITCH_SPACE');
break;
}
case 'FSDJump': {
this.#hasJumped(event);
break;
}
case 'SAAScanComplete': {
dssFlag = true;
break;
}
case 'Scan': {
this.#hasScanned(event, dssFlag);
dssFlag = false;
break;
}
case 'NavRoute': {
await this.#setNavRoute();
break;
}
case 'NavRouteClear': {
this.navRoute = [];
Log.write('Nav route cleared.');
this.emit('SET_NAV_ROUTE');
break;
}
}
}
}
/* ---------------------------------------------------------------------------- #hasJumped ---- */
#hasJumped(event: FSDJump) {
this.location = new System(event);
Log.write(`FSD Jump detected, current location updated to ${this.location.name}.`);
if (this.navRoute.length > 0) {
_.remove(this.navRoute, (system) => system.SystemAddress === this.location.SystemAddress);
}
this.emit('ENTERED_NEW_SYSTEM');
}
/* --------------------------------------------------------------------------- #hasScanned ---- */
#hasScanned(event: Scan, isDss: boolean) {
const dupChecker = { 'BodyName': event.BodyName, 'BodyID': event.BodyID };
let body: Body|null = null;
if (isDss) {
const bodyIndex = _.findIndex(this.location.bodies, dupChecker);
if (bodyIndex > -1) {
this.location.bodies[bodyIndex].DSSDone = true;
} else {
body = new Body(event, true);
this.location.bodies.push(body);
}
} else {
const duplicate = _.find(this.location.bodies, dupChecker);
if (!duplicate) {
body = new Body(event);
this.location.bodies.push(body);
}
}
Log.write(`Scan detected. Body: ${event.BodyName}.`);
this.emit('BODY_SCANNED', body, isDss);
}
/* ------------------------------------------------------------------------------ shutdown ---- */
shutdown(): void {
this.#journal?.close();
}
}

View file

@ -1,132 +0,0 @@
import type { JournalEvent, Scan } from '@kayahr/ed-journal';
import { Journal as EDJournal } from '@kayahr/ed-journal';
import { Body } from './Body';
import { Log } from './Log';
import { System } from './System';
import * as _ from 'lodash-es';
const EventEmitter = require('events');
const reverseLineReader = require('reverse-line-reader');
export class Journal extends EventEmitter {
readonly #path: string;
location: System;
navRoute: System[];
#journal?: EDJournal;
constructor(journalPath: string) {
super();
this.#path = journalPath;
this.location = new System();
this.navRoute = [];
this.#init();
}
/* --------------------------------------------------------------------------------- #init ---- */
#init(): void {
Log.write(`Journal initialized. Attempting to find current location.`);
this.#getLastLocation().then(() => {
if (this.location.name !== 'Unknown') {
Log.write('Attempting to find scanned bodies in current system.');
this.#getScannedBodies().then(async () => {
if (this.location.bodies.length > 0) {
Log.write('Scanned bodies found.');
this.emit('BUILD_BODY_LIST');
} else {
Log.write('No scanned bodies found in current system.');
}
this.#journal = await EDJournal.open({ watch: true, position: 'end' });
Log.write('Checking for nav route.');
await this.#getNavRoute();
});
}
});
}
/* ---------------------------------------------------------------------- #getLastLocation ---- */
#getLastLocation(): any {
return reverseLineReader.eachLine(this.#path, (raw: string, last: boolean) => {
if (raw) {
const line = JSON.parse(raw);
if (line.event === 'FSDJump' || line.event === 'Location') {
this.location = new System(line);
Log.write(`Current location set to ${this.location.name}.`);
this.emit('ENTERED_NEW_SYSTEM');
return false;
} else if (last) {
Log.write('WARNING: Unable to find last known location.');
return false;
}
}
});
}
/* --------------------------------------------------------------------- #getScannedBodies ---- */
#getScannedBodies(): any {
let dssLine: Scan|null = null;
return reverseLineReader.eachLine(this.#path, (raw: string) => {
if (raw) {
const line: JournalEvent = JSON.parse(raw);
if (dssLine) {
if (line.event === 'SAAScanComplete') {
this.location.bodies.push(new Body(dssLine, true));
} else {
const dupChecker = { 'BodyName': dssLine.BodyName, 'BodyID': dssLine.BodyID };
const duplicate = _.find(this.location.bodies, dupChecker);
if (duplicate === undefined) {
this.location.bodies.push(new Body(dssLine));
}
}
dssLine = null;
}
if (line.event === 'Scan' && 'ScanType' in line) {
if (line.ScanType === 'Detailed' && !('StarType' in line)) {
dssLine = line as Scan;
} else if ('StarType' in line) {
this.location.bodies.push(new Body(line as Scan));
} else if (line.ScanType == 'AutoScan') {
if ('PlanetClass' in line) {
const dupChecker = {
'BodyName': (line as Scan).BodyName,
'BodyID': (line as Scan).BodyID,
};
const duplicate = _.find(this.location.bodies, dupChecker);
if (duplicate === undefined) {
this.location.bodies.push(new Body(line as Scan));
}
} else { // Asteroid.
this.location.bodies.push(new Body(line as Scan));
}
}
} else if (line.event === 'FSDJump' || line.event === 'Location') {
return false;
}
}
});
}
/* -------------------------------------------------------------------------- #getNavRoute ---- */
async #getNavRoute(): Promise<void> {
this.navRoute = [];
}
}

103
src/models/OldSafari.ts Executable file
View file

@ -0,0 +1,103 @@
const chokidar = require('chokidar');
const fs = require('node:fs');
const { globSync } = require('glob');
const os = require('node:os');
const path = require('node:path');
import { Journal } from './Journal';
import { Log } from './Log';
import * as _ from 'lodash-es';
export class Safari {
static #instance: Safari;
readonly #journalDir?: string;
readonly #journalPattern?: string;
journal?: Journal;
#watcher?: any;
private constructor(isPackaged: boolean = false) {
if (!isPackaged && os.platform() === 'linux') {
this.#journalDir = require('app-root-path').resolve('/test_journals');
} else if (os.platform() === 'win32') { // Windows
this.#journalDir = path.join(
os.homedir(),
'Saved Games',
'Frontier Developments',
'Elite Dangerous',
);
} else if (os.platform() === 'linux') { // Linux
this.#journalDir = path.join(
os.homedir(),
'.local',
'share',
'Steam',
'steamapps',
'compatdata',
'359320',
'pfx',
'drive_c',
'steamuser',
'Saved Games',
'Frontier Developments',
'Elite Dangerous',
);
} else {
Log.write(`ERROR: Journal files not found. OS: ${os.platform()}.`);
}
if (this.#journalDir) {
this.#journalPattern = path.join(this.#journalDir, 'Journal.*.log');
this.journal = this.#getLatestJournal();
}
}
static start(isPackaged: boolean = false): Safari {
if (!Safari.#instance) {
Safari.#instance = new Safari(isPackaged);
}
return Safari.#instance;
}
/* ------------------------------------------------------------------- #getLatestJournal ---- */
// https://stackoverflow.com/questions/15696218/get-the-most-recent-file-in-a-directory-node-js
#getLatestJournal(): Journal|undefined {
// @ts-ignore
const journals = globSync(
this.#journalPattern,
{ windowsPathsNoEscape: true },
);
const journalPath: string|undefined = _.maxBy(journals, file => fs.statSync(file).mtime);
if (journalPath) {
Log.write(`New journal file found, now watching ${path.basename(journalPath)}.`);
return new Journal(journalPath);
} else {
Log.write('ERROR: Unable to find latest journal.');
return;
}
}
/* --------------------------------------------------------------------- watchJournalDir ---- */
watchJournalDir(): void {
const options = { usePolling: true, persistent: true, ignoreInitial: true };
// @ts-ignore
this.#watcher = chokidar.watch(this.#journalPattern, options);
this.#watcher.on('ready', () => Log.write('Watching journal folder for changes...'));
this.#watcher.on('add', () => this.journal = this.#getLatestJournal());
}
/* ---------------------------------------------------------------------------- shutdown ---- */
async shutdown(): Promise<void> {
this.journal?.shutdown();
await this.#watcher?.close();
}
}

109
src/models/Safari.ts Executable file → Normal file
View file

@ -1,28 +1,49 @@
const chokidar = require('chokidar');
const fs = require('node:fs');
const { globSync } = require('glob');
const os = require('node:os');
const path = require('node:path');
import { Journal } from './Journal';
import { Log } from './Log';
import * as _ from 'lodash-es';
import { CMDR } from './CMDR';
export class Safari {
const EventEmitter = require('events');
const os = require('os');
const path = require('path');
export class Safari extends EventEmitter {
static #instance: Safari;
readonly #journalDir?: string;
readonly #journalPattern?: string;
journal?: Journal;
#watcher?: any;
CMDR?: CMDR;
private constructor(isPackaged: boolean = false) {
if (!isPackaged && os.platform() === 'linux') {
this.#journalDir = require('app-root-path').resolve('/test_journals');
super();
} else if (os.platform() === 'win32') { // Windows
this.#journalDir = path.join(
this.#journalDir = this.#getJournalDir(isPackaged);
if (this.#journalDir) {
this.#journalPattern = path.join(this.#journalDir, 'Journal.*.log');
this.CMDR = new CMDR(this.#journalPattern as string);
}
Log.write(`Safari initialized.`);
}
/* --------------------------------------------------------------------------------- start ---- */
static start(isPackaged: boolean = false): Safari {
if (!Safari.#instance) {
Safari.#instance = new Safari(isPackaged);
}
return Safari.#instance;
}
/* ------------------------------------------------------------------------ #getJournalDir ---- */
#getJournalDir(isPackaged: boolean): string|undefined {
let dir: string|undefined;
if (!isPackaged && os.platform() === 'linux') {
dir = require('app-root-path').resolve('/test_journals');
} else if (os.platform() === 'win32') {
dir = path.join(
os.homedir(),
'Saved Games',
'Frontier Developments',
@ -30,7 +51,7 @@ export class Safari {
);
} else if (os.platform() === 'linux') { // Linux
this.#journalDir = path.join(
dir = path.join(
os.homedir(),
'.local',
'share',
@ -45,59 +66,11 @@ export class Safari {
'Frontier Developments',
'Elite Dangerous',
);
} else {
Log.write(`ERROR: Journal files not found. OS: ${os.platform()}.`);
}
if (this.#journalDir) {
this.#journalPattern = path.join(this.#journalDir, 'Journal.*.log');
this.journal = this.#getLatestJournal();
}
}
static start(isPackaged: boolean = false): Safari {
if (!Safari.#instance) {
Safari.#instance = new Safari(isPackaged);
}
return Safari.#instance;
}
/* ------------------------------------------------------------------- #getLatestJournal ---- */
// https://stackoverflow.com/questions/15696218/get-the-most-recent-file-in-a-directory-node-js
#getLatestJournal(): Journal|undefined {
// @ts-ignore
const journals = globSync(
this.#journalPattern,
{ windowsPathsNoEscape: true },
);
const journalPath: string|undefined = _.maxBy(journals, file => fs.statSync(file).mtime);
if (journalPath) {
Log.write(`New journal file found, now watching ${path.basename(journalPath)}.`);
return new Journal(journalPath);
} else {
Log.write('ERROR: Unable to find latest journal.');
return;
}
}
/* --------------------------------------------------------------------- watchJournalDir ---- */
watchJournalDir(): void {
const options = { usePolling: true, persistent: true, ignoreInitial: true };
// @ts-ignore
this.#watcher = chokidar.watch(this.#journalPattern, options);
this.#watcher.on('ready', () => Log.write('Watching journal folder for changes...'));
this.#watcher.on('add', () => this.journal = this.#getLatestJournal());
}
/* ---------------------------------------------------------------------------- shutdown ---- */
async shutdown(): Promise<void> {
this.journal?.shutdown();
await this.#watcher?.close();
return dir;
}
}

View file

@ -1,9 +1,10 @@
import type {completeFsdJump, location, navRouteSystem} from '../@types/journalLines';
import { FSDJump } from '@kayahr/ed-journal';
import type { location, navRouteSystem } from '../@types/journalLines';
import * as _ from 'lodash-es';
import {Body} from './Body';
import {EDSM} from './EDSM';
import { Body } from './Body';
import { EDSM } from './EDSM';
export class System {
name: string;
@ -17,7 +18,7 @@ export class System {
estimatedValueMapped?: number;
valuableBodies?: Body[];
constructor(line?: navRouteSystem | completeFsdJump | location) {
constructor(line?: navRouteSystem|FSDJump|location) {
if (!line) {
this.name = 'Unknown';
} else {