Skip to content

Commit

Permalink
Merge branch 'flybywiresim:main' into mcdu-adjustments
Browse files Browse the repository at this point in the history
  • Loading branch information
ssewell authored Nov 29, 2023
2 parents 07b87af + 6c20075 commit 20bec8a
Show file tree
Hide file tree
Showing 25 changed files with 21,316 additions and 31,759 deletions.
4 changes: 2 additions & 2 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ jobs:
BUILD_DIR_NAME: release
steps:
- name: Checkout source
uses: actions/checkout@v3
uses: actions/checkout@v4
- name: set Node version
uses: actions/setup-node@v3
with:
node-version: 16
node-version: 18
- name: Build Local API
run: |
npm install
Expand Down
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
engine-strict=true
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ This repo contains the source code to SimBridge

Please make sure you have:

NodeJS 16 - [Homepage](https://nodejs.org/en/)
NodeJS 18 - [Homepage](https://nodejs.org/en/)

```bash
# Install all dependencies
Expand Down
16 changes: 12 additions & 4 deletions apps/mcdu/src/App.jsx
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import './assets/css/App.css';
import React, { useEffect, useState } from 'react';
import React, { useEffect, useRef, useState } from 'react';
import useWebSocket, { ReadyState } from 'react-use-websocket';
import { McduScreen } from './components/McduScreen';
import { McduButtons } from './components/McduButtons';
import { WebsocketContext } from './WebsocketContext';
import { McduKeyboardEvents } from './McduKeyboardEvents';
import darkBg from './assets/images/mcdu-a32nx-dark.png';
import bg from './assets/images/mcdu-a32nx.png';

Expand Down Expand Up @@ -43,7 +44,7 @@ const App = () => {
// automaticaly upgrate to wss if the page is served over https
if (window.location.protocol === 'https:') {
socketUrl = socketUrl.replace('ws', 'wss');
};
}

const [content, setContent] = useState(
{
Expand Down Expand Up @@ -79,6 +80,13 @@ const App = () => {
reconnectInterval: 500,
});

const { onKeyboardInput } = new McduKeyboardEvents(sendMessage);
const rootPanelRef = useRef(null);

useEffect(() => {
rootPanelRef.current.focus();
}, []);

useEffect(() => {
if (readyState === ReadyState.OPEN) {
sendMessage('requestUpdate');
Expand All @@ -100,7 +108,7 @@ const App = () => {
}

return (
<>
<div ref={rootPanelRef} tabIndex={-1} onKeyDown={onKeyboardInput}>
{!fullscreen && (
<>
<div className="normal">
Expand Down Expand Up @@ -151,7 +159,7 @@ const App = () => {
</div>
</>
)}
</>
</div>
);
};

Expand Down
71 changes: 71 additions & 0 deletions apps/mcdu/src/McduKeyboardEvents.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
const McduFunctionalKeys = {
Tab: 'DIR',
Insert: 'PROG',
Home: 'PERF',
PageUp: 'INIT',
Enter: 'DATA',
NumpadEnter: 'DATA',
Delete: 'FPLN',
End: 'RAD',
PageDown: 'FUEL',
Escape: 'MENU',
ShiftLeft: 'AIRPORT',
ArrowLeft: 'PREVPAGE',
ArrowRight: 'NEXTPAGE',
ArrowUp: 'UP',
ArrowDown: 'DOWN',
Backspace: 'CLR',
Space: 'SP',
Minus: 'PLUSMINUS',
NumpadSubtract: 'PLUSMINUS',
NumpadAdd: 'PLUSMINUS',
Period: 'DOT',
NumpadDecimal: 'DOT',
NumpadDivide: 'DIV',
Slash: 'DIV',
NumpadMultiply: 'OVFY',
};

export class McduKeyboardEvents {
constructor(socketSender) {
this.socketSender = socketSender;
this.mcduFunctionalKeys = McduFunctionalKeys;
}

getMcduKey = (keyEvent) => {
// match mcdu L/R row input for F keys
if (keyEvent.code.match(/F\d+/)) {
const fn = parseInt(keyEvent.code.replace('F', ''));
return fn <= 6 ? `L${fn}` : `R${fn - 6}`;
}

// match a-z
if (keyEvent.code.match(/Key[A-Z]/)) {
return keyEvent.code.replace('Key', '').toLocaleUpperCase();
}

// match 0-9
if (keyEvent.code.match(/(Digit|Numpad)\d/i)) {
return keyEvent.code.replace(/Digit|Numpad/i, '');
}

// match mcdu function keys
return this.mcduFunctionalKeys[keyEvent.code];
}

onKeyboardInput = (keyEvent) => {
//console.log('event', { key: keyEvent.key, code: keyEvent.code });
const key = this.getMcduKey(keyEvent);

if (key) {
keyEvent.preventDefault();
keyEvent.stopPropagation();
} else {
return;
}

//console.log(`mcdu key: ${key}`);

this.socketSender(`event:left:${key}`);
}
}
3 changes: 3 additions & 0 deletions apps/mcdu/src/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@
<div id="root">
<script type='text/javascript' src="index.js"></script>
<script>
// window close prompt
window.onbeforeunload = () => { return false; }

// Workaround for a webkit "feature" where 100vh is not actually 100vh
function appHeight() {
const doc = document.documentElement;
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/config/printer.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { registerAs } from '@nestjs/config';
import { getExecutablePath } from 'apps/server/src/utilities/pathUtil';
import { readFileSync } from 'fs';
import { join } from 'path';

const CONFIG_FILENAME = 'resources/properties.json';

export default registerAs('printer', () => {
const configPath = join(process.cwd(), CONFIG_FILENAME);
const configPath = join(getExecutablePath(), CONFIG_FILENAME);
const properties = JSON.parse(readFileSync(configPath, 'utf8'));

return {
Expand Down
3 changes: 2 additions & 1 deletion apps/server/src/config/server.config.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { registerAs } from '@nestjs/config';
import { getExecutablePath } from 'apps/server/src/utilities/pathUtil';
import { readFileSync } from 'fs';
import { join } from 'path';

const CONFIG_FILENAME = 'resources/properties.json';

export default registerAs('server', () => {
const configPath = join(process.cwd(), CONFIG_FILENAME);
const configPath = join(getExecutablePath(), CONFIG_FILENAME);
const properties = JSON.parse(readFileSync(configPath, 'utf8'));

return { port: properties.server.port, hidden: properties.server.hidden, closeWithMSFS: properties.server.closeWithMSFS };
Expand Down
5 changes: 4 additions & 1 deletion apps/server/src/config/winston.service.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
import { Injectable } from '@nestjs/common';
import { getExecutablePath } from 'apps/server/src/utilities/pathUtil';
import { WinstonModuleOptions, WinstonModuleOptionsFactory, utilities as nestWinstonModuleUtilities } from 'nest-winston';
import { join } from 'path';
import * as winston from 'winston';
import 'winston-daily-rotate-file';

const consoleTransport = new winston.transports.Console({
level: 'info',
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.ms(),
nestWinstonModuleUtilities.format.nestLike('FBW-SimBridge', { prettyPrint: true }),
Expand All @@ -16,7 +19,7 @@ const consoleTransport = new winston.transports.Console({
const fileTransport = new winston.transports.DailyRotateFile({
frequency: '24h',
filename: 'fbw-simbridge-%DATE%.log',
dirname: 'resources/logs',
dirname: `${join(getExecutablePath(), 'resources/logs')}`,
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
maxSize: '20m',
Expand Down
13 changes: 7 additions & 6 deletions apps/server/src/interfaces/mcdu.gateway.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { NetworkService } from '../utilities/network.service';
@WebSocketGateway({
cors: { origin: '*' },
path: '/interfaces/v1/mcdu',
})
})
export class McduGateway implements OnGatewayInit, OnGatewayConnection {
constructor(
@Inject(serverConfig.KEY) private serverConf: ConfigType<typeof serverConfig>,
Expand All @@ -29,17 +29,18 @@ export class McduGateway implements OnGatewayInit, OnGatewayConnection {

handleConnection(client: WebSocket) {
this.logger.log('Client connected');
client.on('message', (message: String) => {
if (message === 'mcduConnected') {
client.on('message', (message: Buffer) => {
const messageString = message.toString();
if (messageString === 'mcduConnected') {
this.logger.log('Simulator connected');
}
this.server.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(message);
client.send(messageString);
}
});
if (message.startsWith('print:')) {
const { lines } = JSON.parse(message.substring(6));
if (messageString.startsWith('print:')) {
const { lines } = JSON.parse(messageString.substring(6));
this.printerService.print(lines);
}
});
Expand Down
4 changes: 3 additions & 1 deletion apps/server/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import { ConfigService } from '@nestjs/config';
import { NestFactory } from '@nestjs/core';
import { platform } from 'os';
import { hideConsole } from 'node-hide-console-window';
import * as path from 'path';
import { getExecutablePath } from 'apps/server/src/utilities/pathUtil';
import { ShutDownService } from './utilities/shutdown.service';
import { AppModule } from './app.module';
import { NetworkService } from './utilities/network.service';
Expand Down Expand Up @@ -75,7 +77,7 @@ bootstrap();
function generateResourceFolders() {
dirs.forEach((dir) => {
access(dir, (error) => {
if (error) mkdirSync(dir, { recursive: true });
if (error) mkdirSync(path.join(getExecutablePath(), dir), { recursive: true });
});
});
}
4 changes: 3 additions & 1 deletion apps/server/src/terrain/processing/maphandler.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { GPU, IKernelRunShortcut, Texture } from 'gpu.js';
import { readFile } from 'fs/promises';
import { join } from 'path';
import { getExecutablePath } from 'apps/server/src/utilities/pathUtil';
import {
AircraftStatus,
ElevationProfile,
Expand Down Expand Up @@ -168,7 +170,7 @@ export class MapHandler {

private async readTerrainMap(): Promise<TerrainMap | undefined> {
try {
const buffer = await readFile('./terrain/terrain.map');
const buffer = await readFile(join(getExecutablePath(), './terrain/terrain.map'));
this.logging.info(`Read MB of terrainmap: ${(Buffer.byteLength(buffer) / (1024 * 1024)).toFixed(2)}`);
return new TerrainMap(buffer);
} catch (err) {
Expand Down
40 changes: 27 additions & 13 deletions apps/server/src/utilities/file.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ import { readFileSync, lstatSync } from 'fs';
import * as xml2js from 'xml2js';
import { getDocument, PDFDocumentProxy } from 'pdfjs-dist/legacy/build/pdf';
import { join } from 'path';
import { getExecutablePath } from './pathUtil';
import { pdfToPng } from './pdfConversion';

const pdfjsLib = require('pdfjs-dist/legacy/build/pdf.js');

pdfjsLib.GlobalWorkerOptions.workerSrc = join(process.cwd(), 'node_modules', 'pdfjs-dist', 'build', 'pdf.worker.min.js');
pdfjsLib.GlobalWorkerOptions.workerSrc = join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'build', 'pdf.worker.min.js');

@Injectable()
export class FileService {
Expand All @@ -21,7 +22,7 @@ export class FileService {
async getFileCount(directory: string): Promise<number> {
try {
this.logger.debug(`Retrieving number of files in folder: ${directory}`);
const dir = join(process.cwd(), directory);
const dir = join(getExecutablePath(), directory);
this.checkFilePathSafety(dir);
const retrievedDir = await readdir(dir, { withFileTypes: true });
const fileNames = retrievedDir.filter((dir) => dir.isFile()).map((dir) => dir.name);
Expand All @@ -37,7 +38,7 @@ export class FileService {
try {
this.logger.debug(`Reading all files in directory: ${directory}`);

const dir = join(process.cwd(), directory);
const dir = join(getExecutablePath(), directory);
this.checkFilePathSafety(dir);
const fileNames = (await readdir(dir, { withFileTypes: true })).filter((dir) => dir.isFile()).map((dir) => dir.name);

Expand All @@ -53,10 +54,10 @@ export class FileService {
}
}

async getFolderFilenames(directory: string): Promise<string[]> {
async getFilenames(directory: string): Promise<string[]> {
try {
this.logger.debug(`Reading all files in directory: ${directory}`);
const dir = join(process.cwd(), directory);
const dir = join(getExecutablePath(), directory);
this.checkFilePathSafety(dir);
return (await readdir(dir, { withFileTypes: true })).filter((dir) => dir.isFile()).map((dir) => dir.name);
} catch (err) {
Expand All @@ -66,11 +67,24 @@ export class FileService {
}
}

async getFoldernames(directory: string): Promise<string[]> {
try {
this.logger.debug(`Reading all Dirs in directory: ${directory}`);
const dir = join(getExecutablePath(), directory);
this.checkFilePathSafety(dir);
return (await readdir(dir, { withFileTypes: true })).filter((dir) => dir.isDirectory()).map((dir) => dir.name);
} catch (err) {
const message = `Error reading directory: ${directory}`;
this.logger.error(message, err);
throw new HttpException(message, HttpStatus.NOT_FOUND);
}
}

async getFile(directory: string, fileName: string): Promise<Buffer> {
try {
this.logger.debug(`Retrieving file: ${fileName} in folder: ${directory}`);

const path = join(process.cwd(), directory, fileName);
const path = join(getExecutablePath(), directory, fileName);
this.checkFilePathSafety(path);

if (!lstatSync(path).isFile()) {
Expand All @@ -85,8 +99,8 @@ export class FileService {
}
}

async getNumberOfPdfPages(fileName: string): Promise<number> {
const retrievedFile = await this.getFile('resources\\pdfs\\', fileName);
async getNumberOfPdfPages(Directory: string, fileName: string): Promise<number> {
const retrievedFile = await this.getFile(Directory, fileName);
return getDocument({ data: retrievedFile }).promise.then((document) => document.numPages);
}

Expand All @@ -99,21 +113,21 @@ export class FileService {
throw new HttpException('Unexpected null byte encountered', HttpStatus.UNPROCESSABLE_ENTITY);
}

if (filePath.indexOf(process.cwd()) !== 0) {
if (filePath.indexOf(getExecutablePath()) !== 0) {
throw new HttpException('Unacceptable file path', HttpStatus.UNPROCESSABLE_ENTITY);
}
}

async getConvertedPdfFile(fileName: string, pageNumber: number, scale: number = 4): Promise<StreamableFile> {
async getConvertedPdfFile(directory:string, fileName: string, pageNumber: number, scale: number = 4): Promise<StreamableFile> {
// Some PDFs need external cmaps.
const CMAP_URL = `${join(process.cwd(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`;
const CMAP_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'cmaps')}/`;
const CMAP_PACKED = true;

// Where the standard fonts are located.
const STANDARD_FONT_DATA_URL = `${join(process.cwd(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`;
const STANDARD_FONT_DATA_URL = `${join(getExecutablePath(), 'node_modules', 'pdfjs-dist', 'standard_fonts')}/`;

try {
const conversionFilePath = join(process.cwd(), 'resources', 'pdfs', fileName);
const conversionFilePath = join(getExecutablePath(), directory, fileName);

this.checkFilePathSafety(conversionFilePath);

Expand Down
2 changes: 1 addition & 1 deletion apps/server/src/utilities/network.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ export class NetworkService implements OnApplicationShutdown {
*/
async getLocalIp(defaultToLocalhost = false): Promise<string | undefined> {
return new Promise<string | undefined>((resolve) => {
const conn = createConnection({ host: 'api.flybywiresim.com', port: 443, timeout: 1000 })
const conn = createConnection({ host: 'api.flybywiresim.com', port: 443, timeout: 1000, family: 4 })
.on('connect', () => {
const { address } = conn.address() as AddressInfo;
// Calling destroy on every event to make sure simbridge can shut down cleanly
Expand Down
Loading

0 comments on commit 20bec8a

Please sign in to comment.