{"version":3,"sources":["jsdelivr-header.js","/npm/dcs-bios-api-mazeppa@0.1.0/lib/dcs-bios-export-parser.js"],"names":[],"mappings":"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","file":"/npm/dcs-bios-api-mazeppa@0.1.0/lib/dcs-bios-export-parser.js","sourceRoot":"","sourcesContent":["/**\n * Minified by jsDelivr using Terser v5.39.0.\n * Original file: /npm/dcs-bios-api-mazeppa@0.1.0/lib/dcs-bios-export-parser.js\n *\n * Do NOT use SRI with dynamically generated files! More information: https://www.jsdelivr.com/using-sri-with-dynamic-files\n */\n","const EventEmitter = require('events');\nconst path = require('path');\nconst glob = require('glob');\nconst JsonParser = require('./dcs-bios-json-parser');\nconst UdpClient = require('./udp-multicast-client');\nconst BufferReader = require('buffer-utils').BufferReader;\nconst log = require('./logger');\n\nconst MAX_DATA_SIZE = 65536;\n\nconst defaultOptions = {\n  receivePort: 5010,\n  sendPort: 7778,\n  multicastAddress: '239.255.50.10',\n  emitAllUpdates: false,\n  logLevel: 'NONE',\n  autoStartListening: true\n};\n\nclass DcsBiosExportParser extends EventEmitter {\n  /**\n   * DCS BIOS export data parser. Takes messages emitted from the UDP client and parses them.\n   * @param {Object} options - configuration options\n   * @example\n   * // possible options and their defaults\n   * new DcsBiosExportParser({\n   *   receivePort: 5010,\n   *   sendPort: 7778,\n   *   multicastAddress: '239.255.50.10',\n   *   emitAllUpdates: false,\n   *   logLevel: 'NONE'\n   * });\n   */\n  constructor(options = {}) {\n    super();\n    // Set the options from the constructor param.\n    this._options = Object.assign(defaultOptions, options);\n    this.setLogLevel(this._options.logLevel);\n\n    // Parse all the JSON files in the json folder.\n    var files = glob.sync(path.join(__dirname, '/../docs/json/*.json'));\n    var { aircraftData, controls, addressLookup } = JsonParser.parseFiles(files);\n    this._aircraftData = aircraftData;\n    this._controls = controls;\n    this._addressLookup = addressLookup;\n\n    // Create the UDP client.\n    this._client = new UdpClient(this._options);\n\n    // Create a buffer to hold all the incoming data from the DCS BIOS exporter. It's mainly used to re-construct\n    // strings when there's only a partial string update.\n    this._data = Buffer.alloc(MAX_DATA_SIZE);\n\n    // Queue of controls that need to emit an update once we receive a sync command.\n    this._emitQueue = [];\n\n    this._client.on('message', (message) => {\n      this._parseMessage(message);\n    });\n\n    if (options.autoStartListening) {\n      this.startListening();\n    }\n  }\n\n  /**\n   * Start listening for new UDP messages from the DCS BIOS exporter.\n   * @returns {Promise} when the client has started listening\n   */\n  startListening() {\n    // Don't do anything if the UDP client is already listening.\n    if (this._isListening) { return; }\n\n    return this._client.start().then(() => this._isListening = true);\n  }\n\n  /**\n   * Stop listening for new UDP messages from the DCS BIOS exporter.\n   */\n  stopListening() {\n    // Don't do anything if the UDP client has already stopped listening.\n    if (!this._isListening) { return; }\n\n    return this._client.stop().then(() => this._isListening = false);\n  }\n\n  /**\n   * Get the value of a control.\n   * @param {string} controlId - control identifier\n   * @param {string} outputSuffix - output suffix, if the control has more than one output\n   * @returns {string|Number} value of the control that was requested\n   */\n  getControlValue(controlId, outputSuffix) {\n    var control = this._controls[controlId];\n\n    // If no control was found, return undefined.\n    if (!control) {\n      return undefined;\n    }\n    // If the control only has one output, return its value.\n    if (control.outputs.length == 1) {\n      return control.outputs[0].value;\n    }\n    // If the control has more than one output, return the value of the one with the provided suffix.\n    var output = control.outputs.find((output) => output.suffix == outputSuffix);\n    return output ? output.value : undefined;\n  }\n\n  /**\n   * Get the aircraft data. Contains every aircraft with its categories, controls, outputs, and values.\n   * @returns {Object} aircraft object\n   */\n  get aircraftData() {\n    return this._aircraftData;\n  }\n\n  /**\n   * Set the logging level.\n   * @param {String} level - log level\n   */\n  setLogLevel(level) {\n    log.setLogLevel(level);\n  }\n\n  /**\n   * Send a message to the DCS BIOS importer.\n   * @returns {Promise} when the message has finished sending\n   */\n  sendMessage(message) {\n    return this._client.sendMessage(message.trim() + '\\n');\n  }\n\n  /**\n   * Remove all control listeners.\n   */\n  removeControlListeners() {\n    this.removeAllListeners();\n  }\n\n  /** Parse the DCS BIOS export message from the UDP client.\n   * @param {Buffer} message - DCS BIOS export message\n   */\n  _parseMessage(message) {\n    var reader = new BufferReader(message);\n\n    if (reader.bytesLeft() < 4) {\n      log.warn('incomplete message received from UDP client: length is < 4');\n      return;\n    }\n\n    // Go through each update block in the message.\n    while (reader.bytesLeft()) {\n      var address = reader.readUInt16LE();\n      var count = reader.readUInt16LE();\n\n      // EARLY OUT: If this is a sync command, we're safe to emit. Process the emit queue.\n      if (address == 0x5555 && count == 0x5555) {\n        this._processEmitQueue();\n        continue;\n      }\n      else {\n        while (count) {\n          var controlAddress = address;\n          var addressValue = reader.readUInt16LE();\n          count = count - 2;\n          address = address + 2;\n          // Save the address value into the data buffer that holds all the data.\n          this._data.writeUInt16LE(addressValue, controlAddress);\n\n          // Find the closest address that has a control. Need to do this because string updates can be partial updates\n          // and the address that the exporter gives us will not correspond to a control in the address lookup.\n          while (!this._addressLookup[controlAddress]) {\n            controlAddress = controlAddress - 2;\n          }\n\n          // For each control in the address, process the value and add the control to the update queue.\n          this._addressLookup[controlAddress].forEach((control) => {\n            control.outputs.forEach((output) => {\n              var controlValue;\n\n              // This is a string, get the string using the starting address and its maximum length.\n              if (output.type == 'string') {\n                controlValue = this._data.toString('utf8', controlAddress, controlAddress + output.max_length);\n              }\n              // This is a number, get the value using the mask and the bit shift amount.\n              else {\n                controlValue = (addressValue & output.mask) >> output.shift_by;\n              }\n\n              var valueHasChanged = (this._options.emitAllUpdates || output.value != controlValue);\n              var isAlreadyAdded = this._emitQueue.some((item) => item.control == control && item.output == output);\n\n              // Don't add to the emit queue if the value hasn't changed (can be overriden by emitAllUpdates), or if the\n              // emit queue already has the control and output queued up for an emit.\n              if (valueHasChanged && !isAlreadyAdded) {\n                // Save the value on the output object.\n                output.value = controlValue;\n                this._emitQueue.push({ output, control });\n              }\n            });\n          });\n        }\n      }\n    }\n  }\n\n  /**\n   * Process the controls that need to trigger events.\n   */\n  _processEmitQueue() {\n    // Emit all the updated controls. Useful if someone wants to process all the updates as an array.\n    this.emit('update', this._emitQueue);\n    // Emit each updated control as a separate event. Useful if someone only cares about a single control.\n    this._emitQueue.forEach((item) => {\n      var identifier = item.control.identifier + item.output.suffix;\n      log.debug('emit:', identifier, item.output.value);\n      // Emit the name of the control, i.e. 'ACFT_NAME'\n      this.emit(identifier, item.output.value, item.control, item.output);\n      // Emit the name of the control with its value, i.e. 'ACFT_NAME:Ka-50'\n      this.emit(identifier + ':' + item.output.value, item.control, item.output);\n    });\n\n    this._emitQueue = [];\n  }\n}\n\nmodule.exports = DcsBiosExportParser;\n"]}