From cb805dd4aea0c4160493a21fd5b378e350584fbd Mon Sep 17 00:00:00 2001 From: y3n Date: Tue, 17 Dec 2024 12:47:08 +0100 Subject: [PATCH] Refactor DIS6 PDU handling: rename DIS6_PduFactory to PduFactory, update imports, and enhance EntityType with enums for better clarity. Add SubsurfacePlatformAppearance and SurfacePlatformAppearance classes for detailed appearance management. --- .../SubsurfacePlatformAppearance.js | 161 +++++++++++++++++ .../Appearance/SurfacePlatformAppearance.js | 161 +++++++++++++++++ src/dis6/EntityType.js | 70 +++++++- src/dis6/Pdu/EntityStatePdu.js | 163 ++++++------------ .../{DIS6_PduFactory.js => PduFactory.js} | 4 +- src/dis6/{DIS6_PduMap.js => PduMap.js} | 0 src/index.js | 8 +- 7 files changed, 448 insertions(+), 119 deletions(-) create mode 100644 src/dis6/Appearance/SubsurfacePlatformAppearance.js create mode 100644 src/dis6/Appearance/SurfacePlatformAppearance.js rename src/dis6/{DIS6_PduFactory.js => PduFactory.js} (71%) rename src/dis6/{DIS6_PduMap.js => PduMap.js} (100%) diff --git a/src/dis6/Appearance/SubsurfacePlatformAppearance.js b/src/dis6/Appearance/SubsurfacePlatformAppearance.js new file mode 100644 index 0000000..a71cb9b --- /dev/null +++ b/src/dis6/Appearance/SubsurfacePlatformAppearance.js @@ -0,0 +1,161 @@ +/** + * @typedef {import("../common/InputStream.js").default} InputStream + * @typedef {import("../common/OutputStream.js").default} OutputStream + */ + +/** + * @typedef {number} PaintScheme + * @typedef {number} Damage + * @typedef {number} Hatch + * @typedef {number} State + **/ + +class SubsurfacePlatformAppearance { + /** + * Bit masks for each property. + */ + static BIT_MASKS = { + paintScheme: 0b1 << 0, // Paint Scheme (Bit 0) + mobilityKilled: 0b1 << 1, // Mobility Killed (Bit 1) + damage: 0b11 << 3, // Damage (Bits 3-4) + isSmokeEmanating: 0b1 << 5, // Is Smoke Emanating (Bit 5) + isEngineEmittingSmoke: 0b1 << 6, // Is Engine Emitting Smoke (Bit 6) + hatch: 0b111 << 9, // Hatch (Bits 9-11) + runningLightsOn: 0b1 << 12, // Running Lights On (Bit 12) + isFlaming: 0b1 << 15, // Is Flaming (Bit 15) + isFrozen: 0b1 << 21, // Is Frozen (Bit 21) + powerPlantOn: 0b1 << 22, // Power Plant On (Bit 22) + state: 0b1 << 23, // State (Bit 23) + }; + + /** + * @readonly + * @enum {PaintScheme} + */ + static PAINT_SCHEME = { + CAMOUFLAGE: 0, + COLOR: 1, + }; + + /** + * @readonly + * @enum {Damage} + */ + static DAMAGE = { + NO_DAMAGE: 0, + SLIGHT_DAMAGE: 1, + MODERATE_DAMAGE: 2, + DESTROYED: 3, + }; + + /** + * @readonly + * @enum {Hatch} + */ + static HATCH = { + NOT_APPLICABLE: 0, + CLOSED: 1, + OPEN: 4, + }; + + /** + * @readonly + * @enum {State} + */ + static STATE = { + ACTIVE: 0, + DEACTIVATED: 1, + }; + + /** @type {PaintScheme} */ + paintScheme = SubsurfacePlatformAppearance.PAINT_SCHEME.CAMOUFLAGE; + + /** @type {boolean} */ + mobilityKilled = false; + + /** @type {Damage} */ + damage = SubsurfacePlatformAppearance.DAMAGE.NO_DAMAGE; + + /** @type {boolean} */ + isSmokeEmanating = false; + + /** @type {boolean} */ + isEngineEmittingSmoke = false; + + /** @type {Hatch} */ + hatch = SubsurfacePlatformAppearance.HATCH.NOT_APPLICABLE; + + /** @type {boolean} */ + runningLightsOn = false; + + /** @type {boolean} */ + isFlaming = false; + + /** @type {boolean} */ + isFrozen = false; + + /** @type {boolean} */ + powerPlantOn = false; + + /** @type {State} */ + state = SubsurfacePlatformAppearance.STATE.ACTIVE; + + /** + * Write the appearance to an int32 using BIT_MASKS + */ + toUInt32() { + let result = 0; + + result |= (this.paintScheme & 0b1) << 0; + result |= (this.mobilityKilled ? 1 : 0) << 1; + result |= (this.damage & 0b11) << 3; + result |= (this.isSmokeEmanating ? 1 : 0) << 5; + result |= (this.isEngineEmittingSmoke ? 1 : 0) << 6; + result |= (this.hatch & 0b111) << 9; + result |= (this.runningLightsOn ? 1 : 0) << 12; + result |= (this.isFlaming ? 1 : 0) << 15; + result |= (this.isFrozen ? 1 : 0) << 21; + result |= (this.powerPlantOn ? 1 : 0) << 22; + result |= (this.state & 0b1) << 23; + + return result; + } + + /** + * Populate the appearance properties from a uint32 + * @param {number} uint32 + */ + fromUInt32(uint32) { + this.paintScheme = (uint32 >> 0) & 0b1; + this.mobilityKilled = ((uint32 >> 1) & 0b1) === 1; + this.damage = (uint32 >> 3) & 0b11; + this.isSmokeEmanating = ((uint32 >> 5) & 0b1) === 1; + this.isEngineEmittingSmoke = ((uint32 >> 6) & 0b1) === 1; + this.hatch = (uint32 >> 9) & 0b111; + this.runningLightsOn = ((uint32 >> 12) & 0b1) === 1; + this.isFlaming = ((uint32 >> 15) & 0b1) === 1; + this.isFrozen = ((uint32 >> 21) & 0b1) === 1; + this.powerPlantOn = ((uint32 >> 22) & 0b1) === 1; + this.state = (uint32 >> 23) & 0b1; + } + + /** + * Utility method to reset all properties to defaults + */ + reset() { + this.paintScheme = SubsurfacePlatformAppearance.PAINT_SCHEME.CAMOUFLAGE; + this.mobilityKilled = false; + this.damage = SubsurfacePlatformAppearance.DAMAGE.NO_DAMAGE; + this.isSmokeEmanating = false; + this.isEngineEmittingSmoke = false; + this.hatch = SubsurfacePlatformAppearance.HATCH.NOT_APPLICABLE; + this.runningLightsOn = false; + this.isFlaming = false; + this.isFrozen = false; + this.powerPlantOn = false; + this.state = SubsurfacePlatformAppearance.STATE.ACTIVE; + } +} + +export default SubsurfacePlatformAppearance; +export { SubsurfacePlatformAppearance }; \ No newline at end of file diff --git a/src/dis6/Appearance/SurfacePlatformAppearance.js b/src/dis6/Appearance/SurfacePlatformAppearance.js new file mode 100644 index 0000000..f621617 --- /dev/null +++ b/src/dis6/Appearance/SurfacePlatformAppearance.js @@ -0,0 +1,161 @@ +/** + * @typedef {import("../common/InputStream.js").default} InputStream + * @typedef {import("../common/OutputStream.js").default} OutputStream + */ + +/** + * @typedef {number} PaintScheme + * @typedef {number} Damage + * @typedef {number} State + **/ + +class SurfacePlatformAppearance { + /** + * @readonly + * @enum {PaintScheme} + */ + static PAINT_SCHEME = { + CAMOUFLAGE: 0, + COLOR: 1, + }; + + /** + * @readonly + * @enum {Damage} + */ + static DAMAGE = { + NO_DAMAGE: 0, + SLIGHT_DAMAGE: 1, + MODERATE_DAMAGE: 2, + DESTROYED: 3, + }; + + /** + * @readonly + * @enum {State} + */ + static STATE = { + ACTIVE: 0, + DEACTIVATED: 1, + }; + + /** @type {PaintScheme} */ + paintScheme = SurfacePlatformAppearance.PAINT_SCHEME.CAMOUFLAGE; + + /** @type {boolean} */ + mobilityKilled = false; + + /** @type {Damage} */ + damage = SurfacePlatformAppearance.DAMAGE.NO_DAMAGE; + + /** @type {boolean} */ + isSmokeEmanating = false; + + /** @type {boolean} */ + isEngineEmittingSmoke = false; + + /** @type {number} */ + wakeSize = 0; + + /** @type {boolean} */ + runningLightsOn = false; + + /** @type {boolean} */ + isFlaming = false; + + /** @type {boolean} */ + isAccomodationLadderLowered = false; + + /** @type {boolean} */ + isFenceRaised = false; + + /** @type {boolean} */ + isFlagRaised = false; + + /** @type {boolean} */ + isFrozen = false; + + /** @type {boolean} */ + powerPlantOn = false; + + /** @type {State} */ + state = SurfacePlatformAppearance.STATE.ACTIVE; + + /** @type {boolean} */ + spotLightsOn = false; + + /** @type {boolean} */ + interiorLightsOn = false; + + /** + * Write the appearance to an int32 + */ + toUInt32() { + let result = 0; + result |= (this.paintScheme & 0b1) << 0; + result |= (this.mobilityKilled ? 1 : 0) << 1; + result |= (this.damage & 0b11) << 3; + result |= (this.isSmokeEmanating ? 1 : 0) << 5; + result |= (this.isEngineEmittingSmoke ? 1 : 0) << 6; + result |= (this.wakeSize & 0b11) << 7; + result |= (this.runningLightsOn ? 1 : 0) << 12; + result |= (this.isFlaming ? 1 : 0) << 15; + result |= (this.isAccomodationLadderLowered ? 1 : 0) << 16; + result |= (this.isFenceRaised ? 1 : 0) << 17; + result |= (this.isFlagRaised ? 1 : 0) << 18; + result |= (this.isFrozen ? 1 : 0) << 21; + result |= (this.powerPlantOn ? 1 : 0) << 22; + result |= (this.state & 0b1) << 23; + result |= (this.spotLightsOn ? 1 : 0) << 28; + result |= (this.interiorLightsOn ? 1 : 0) << 29; + return result; + } + + /** + * Populate the appearance properties from a uint32 + * @param {number} uint32 + */ + fromUInt32(uint32) { + this.paintScheme = (uint32 >> 0) & 0b1; + this.mobilityKilled = ((uint32 >> 1) & 0b1) === 1; + this.damage = (uint32 >> 3) & 0b11; + this.isSmokeEmanating = ((uint32 >> 5) & 0b1) === 1; + this.isEngineEmittingSmoke = ((uint32 >> 6) & 0b1) === 1; + this.wakeSize = (uint32 >> 7) & 0b11; + this.runningLightsOn = ((uint32 >> 12) & 0b1) === 1; + this.isFlaming = ((uint32 >> 15) & 0b1) === 1; + this.isAccomodationLadderLowered = ((uint32 >> 16) & 0b1) === 1; + this.isFenceRaised = ((uint32 >> 17) & 0b1) === 1; + this.isFlagRaised = ((uint32 >> 18) & 0b1) === 1; + this.isFrozen = ((uint32 >> 21) & 0b1) === 1; + this.powerPlantOn = ((uint32 >> 22) & 0b1) === 1; + this.state = (uint32 >> 23) & 0b1; + this.spotLightsOn = ((uint32 >> 28) & 0b1) === 1; + this.interiorLightsOn = ((uint32 >> 29) & 0b1) === 1; + } + + /** + * Utility method to reset all properties to defaults + */ + reset() { + this.paintScheme = SurfacePlatformAppearance.PAINT_SCHEME.CAMOUFLAGE; + this.mobilityKilled = false; + this.damage = SurfacePlatformAppearance.DAMAGE.NO_DAMAGE; + this.isSmokeEmanating = false; + this.isEngineEmittingSmoke = false; + this.wakeSize = 0; + this.runningLightsOn = false; + this.isFlaming = false; + this.isAccomodationLadderLowered = false; + this.isFenceRaised = false; + this.isFlagRaised = false; + this.isFrozen = false; + this.powerPlantOn = false; + this.state = SurfacePlatformAppearance.STATE.ACTIVE; + this.spotLightsOn = false; + this.interiorLightsOn = false; + } +} + +export default SurfacePlatformAppearance; +export { SurfacePlatformAppearance }; diff --git a/src/dis6/EntityType.js b/src/dis6/EntityType.js index a27859f..b17dbde 100644 --- a/src/dis6/EntityType.js +++ b/src/dis6/EntityType.js @@ -7,24 +7,32 @@ * @typedef {import("../common/OutputStream.js").default} OutputStream */ +/** + * @typedef {number} EntityKind + * @typedef {number} Domain + * @typedef {number} MunitionDomain + * @typedef {number} LandCategory + * @typedef {number} Country + **/ + class EntityType { /** * Kind of entity. - * @type {number} + * @type {EntityKind} */ - entityKind = 0; + entityKind = EntityType.Domain.OTHER; /** - * Domain of entity (air, surface, subsurface, space, etc). - * @type {number} + * Domain of entity (air, surface, subsurface, space, etc.). + * @type {Domain|MunitionDomain} */ - domain = 0; + domain = EntityType.Domain.OTHER /** * Country to which the design of the entity is attributed. - * @type {number} + * @type {Country} */ - country = 0; + country = 71; // France /** * Category of entity. @@ -77,6 +85,54 @@ class EntityType { outputStream.writeUByte(this.spec); outputStream.writeUByte(this.extra); } + + /** + * @readonly + * @enum {EntityKind} + */ + static EntityKind = { + OTHER: 0, + PLATFORM: 1, + MUNITION: 2, + LIFE_FORM: 3, + ENVIRONMENTAL: 4, + CULTURAL_FEATURE: 5, + SUPPLY: 6, + }; + + /** + * @readonly + * @enum {MunitionDomain} + */ + static MunitionDomain = { + OTHER: 0, + ANTI_AIR: 1, + ANTI_ARMOR: 2, + ANTI_GUIDED_WEAPON: 3, + ANTI_RADAR: 4, + ANTI_SATELLITE: 5, + ANTI_SHIP: 6, + ANTI_SUBMARINE: 7, + ANTI_PERSONNEL: 8, + BATTLEFIELD_SUPPORT: 9, + STRATEGIC: 10, + TACTICAL: 11, + DIRECTED_ENERGY_WEAPON: 12, + }; + + /** + * @readonly + * @enum {Domain} + */ + static Domain = { + OTHER: 0, + LAND: 1, + AIR: 2, + SURFACE: 3, + SUBSURFACE: 4, + SPACE: 5, + NON_COMBATANT: 6, + }; } export default EntityType; diff --git a/src/dis6/Pdu/EntityStatePdu.js b/src/dis6/Pdu/EntityStatePdu.js index 762f844..260f671 100644 --- a/src/dis6/Pdu/EntityStatePdu.js +++ b/src/dis6/Pdu/EntityStatePdu.js @@ -17,13 +17,60 @@ import ArticulationParameter from "../ArticulationParameter.js"; /** * @typedef {import("../../common/InputStream.js").default} InputStream + * @typedef {import("../../common/OutputStream.js").default} OutputStream + */ + +/** + * @typedef {number} ForceID */ class EntityStatePdu { + /** + * @readonly + */ + static pduType = 1; + + /** + * @readonly + * @enum {ForceID} + */ + static FORCE_ID = { + OTHER: 0, + FRIENDLY: 1, + OPPOSING: 2, + NEUTRAL: 3, + FRIENDLY_2: 4, + OPPOSING_2: 5, + NEUTRAL_2: 6, + FRIENDLY_3: 7, + OPPOSING_3: 8, + NEUTRAL_3: 9, + FRIENDLY_4: 10, + OPPOSING_4: 11, + NEUTRAL_4: 12, + FRIENDLY_5: 13, + OPPOSING_5: 14, + NEUTRAL_5: 15, + FRIENDLY_6: 16, + OPPOSING_6: 17, + NEUTRAL_6: 18, + FRIENDLY_7: 19, + OPPOSING_7: 20, + NEUTRAL_7: 21, + FRIENDLY_8: 22, + OPPOSING_8: 23, + NEUTRAL_8: 24, + FRIENDLY_9: 25, + OPPOSING_9: 26, + NEUTRAL_9: 27, + FRIENDLY_10: 28, + OPPOSING_10: 29, + NEUTRAL_10: 30, + }; + constructor() { this.protocolVersion = 6; this.exerciseID = 0; - this.pduType = 1; this.protocolFamily = 1; this.timestamp = 0; this.pduLength = 0; @@ -64,7 +111,7 @@ class EntityStatePdu { this.entityLinearVelocity.initFromBinary(inputStream); this.entityLocation.initFromBinary(inputStream); this.entityOrientation.initFromBinary(inputStream); - this.entityAppearance = inputStream.readInt(); + this.entityAppearance = inputStream.readUInt(); this.deadReckoningParameters.initFromBinary(inputStream); this.marking.initFromBinary(inputStream); this.capabilities = inputStream.readInt(); @@ -76,10 +123,14 @@ class EntityStatePdu { } } + /** + * + * @param {OutputStream} outputStream + */ encodeToBinary(outputStream) { outputStream.writeUByte(this.protocolVersion); outputStream.writeUByte(this.exerciseID); - outputStream.writeUByte(this.pduType); + outputStream.writeUByte(EntityStatePdu.pduType); outputStream.writeUByte(this.protocolFamily); outputStream.writeUInt(this.timestamp); outputStream.writeUShort(this.pduLength); @@ -92,7 +143,7 @@ class EntityStatePdu { this.entityLinearVelocity.encodeToBinary(outputStream); this.entityLocation.encodeToBinary(outputStream); this.entityOrientation.encodeToBinary(outputStream); - outputStream.writeInt(this.entityAppearance); + outputStream.writeUInt(this.entityAppearance); this.deadReckoningParameters.encodeToBinary(outputStream); this.marking.encodeToBinary(outputStream); outputStream.writeInt(this.capabilities); @@ -108,110 +159,6 @@ class EntityStatePdu { _setBits(mask, shift, val) { this.entityAppearance = (this.entityAppearance & ~mask) | (val << shift); } - - getPaintScheme() { - return this._extractBits(0x1, 0); - } - - setPaintScheme(val) { - this._setBits(0x1, 0, val); - } - - getMobility() { - return this._extractBits(0x2, 1); - } - - setMobility(val) { - this._setBits(0x2, 1, val); - } - - getFirepower() { - return this._extractBits(0x4, 2); - } - - setFirepower(val) { - this._setBits(0x4, 2, val); - } - - getDamage() { - return this._extractBits(0x18, 3); - } - - setDamage(val) { - this._setBits(0x18, 3, val); - } - - getSmoke() { - return this._extractBits(0x60, 5); - } - - setSmoke(val) { - this._setBits(0x60, 5, val); - } - - getTrailingEffects() { - return this._extractBits(0x180, 7); - } - - setTrailingEffects(val) { - this._setBits(0x180, 7, val); - } - - getHatch() { - return this._extractBits(0xe00, 9); - } - - setHatch(val) { - this._setBits(0xe00, 9, val); - } - - getHeadlights() { - return this._extractBits(0x1000, 12); - } - - setHeadlights(val) { - this._setBits(0x1000, 12, val); - } - - getTailLights() { - return this._extractBits(0x2000, 13); - } - - setTailLights(val) { - this._setBits(0x2000, 13, val); - } - - getBrakeLights() { - return this._extractBits(0x4000, 14); - } - - setBrakeLights(val) { - this._setBits(0x4000, 14, val); - } - - getFlaming() { - return this._extractBits(0x8000, 15); - } - - setFlaming(val) { - this._setBits(0x8000, 15, val); - } - - getLauncher() { - return this._extractBits(0x10000, 16); - } - - setLauncher(val) { - this._setBits(0x10000, 16, val); - } - - getCamouflageType() { - return this._extractBits(0x60000, 17); - } - - setCamouflageType(val) { - this._setBits(0x60000, 17, val); - } } export default EntityStatePdu; diff --git a/src/dis6/DIS6_PduFactory.js b/src/dis6/PduFactory.js similarity index 71% rename from src/dis6/DIS6_PduFactory.js rename to src/dis6/PduFactory.js index c3a45d1..011db9f 100644 --- a/src/dis6/DIS6_PduFactory.js +++ b/src/dis6/PduFactory.js @@ -1,11 +1,11 @@ import PduFactory from "../common/PduFactory.js"; -import DIS6_PDU_MAP from "./DIS6_PduMap.js"; +import PDU_MAP from "./PduMap.js"; class DIS6_PduFactory extends PduFactory { constructor() { super(); - this.pduMap = DIS6_PDU_MAP; + this.pduMap = PDU_MAP; } } diff --git a/src/dis6/DIS6_PduMap.js b/src/dis6/PduMap.js similarity index 100% rename from src/dis6/DIS6_PduMap.js rename to src/dis6/PduMap.js diff --git a/src/index.js b/src/index.js index bf8dd93..d05c0d0 100644 --- a/src/index.js +++ b/src/index.js @@ -1,3 +1,7 @@ -export { DIS6_PduFactory } from './dis6/DIS6_PduFactory.js'; -export { DIS6_PDU_MAP } from './dis6/DIS6_PduMap.js'; +export { DIS6_PduFactory } from './dis6/PduFactory.js'; +export { DIS6_PDU_MAP } from './dis6/PduMap.js'; +export { EntityStatePdu as DIS6_EntityStatePdu } from './dis6/Pdu/EntityStatePdu.js'; +export { EntityType as DIS6_EntityType } from './dis6/EntityType.js'; +export { SurfacePlatformAppearance as DIS6_SurfacePlatformAppearance } from './dis6/Appearance/SurfacePlatformAppearance.js'; +export { SubsurfacePlatformAppearance as DIS6_SubsurfacePlatformAppearance } from './dis6/Appearance/SubsurfacePlatformAppearance.js'; export { CoordinateConverter } from './common/CoordinateConverter.js'; \ No newline at end of file