features.js

/*
 * Copyright 2017-2019 Tom Swindell
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * 
 */

const FEATURE_IDS = {
  0x00: 'common',
  0x01: 'ardrone3',
  0x04: 'skyctrl',
  0x85: 'generic',
  0x86: 'followme',
  0x87: 'wifi',
  0x88: 'rc',
  0x89: 'drone_manager',
  0x8a: 'mapper',
  0x8b: 'debug',
  0x8c: 'controller_info',
  0x8f: 'camera',
  0x90: 'animation',
  0x91: 'user_storage',
  0x92: 'rth',
  0x94: 'gimbal',
  0x95: 'battery',
  0x96: 'mediastore',
  0x97: 'precise_home',
}

const FEATURE_CLASSES = {
  0x01: ARDrone3,
  0x04: SkyController,
  0x88: RC,
  0x89: DroneManager,
}

/**
 * @public
 * 
 * @param {*} __uid 
 * @param {*} __device 
 */
function Feature (__fId, __device) {
  this.uid = __device.uid + '/' + FEATURE_IDS[__fId] || 'unknown'
  this.type = this.constructor.name
  this.device = __device
}

/**
 * 
 */
Feature.prototype.init = async function () { }

/**
 * 
 */
Feature.prototype.toJSON = function () {
  return { uid: this.uid, type: this.type, navlink: this.device.uid }
}

/**
 * @public
 * 
 * @param {*} __device 
 */
function ARDrone3 (__device) {
  const __feature_id = 0x01

  Feature.call(this, __feature_id, __device)

  this.type = 'ardrone3'

  const __state = {}

  const onMessage = (message) => {
    if (message.featureId !== __feature_id) return
    __state[message.path.split('.').slice(1).join('.')] = message.params
  }

  this.init = async () => {
    // Request SkyController state and settings.
    await Promise.all([
        __device.message('common.Common.AllStates'),
        __device.message('common.Settings.AllSettings')
      ])
      .then(([ states, settings ]) => {
        states.forEach(onMessage)
        settings.forEach(onMessage)

        __device.on('message', onMessage)
      })
  }

  /**
   * 
   */
  this.toJSON = () => (
    Object.assign(Feature.prototype.toJSON.call(this), __state)
  )
}
ARDrone3.prototype = Object.create(Feature.prototype)

/**
 * @public
 * 
 * @param {*} __device 
 */
function SkyController (__device) {
  const __feature_id = 0x04

  Feature.call(this, __feature_id, __device)

  this.type = 'SkyController'

  const __state = {}

  /**
   * 
   * @param {*} message 
   */
  const onMessage = (message) => {
    if (message.featureId !== __feature_id) return
    __state[message.path.split('.').slice(1).join('.')] = message.params
  }

  /**
   * 
   */
  this.init = async () => {
    // Request SkyController state and settings.
    await Promise.all([
        __device.message('skyctrl.Common.AllStates'),
        __device.message('skyctrl.Settings.AllSettings')
      ])
      .then(([states, settings]) => {
        states.forEach(onMessage)
        settings.forEach(onMessage)

        __device.on('message', onMessage)
      })
      .catch(e => console.error(e))
  }

  /**
   * 
   */
  this.toJSON = () => (
    Object.assign(Feature.prototype.toJSON.call(this), __state)
  )
}
SkyController.prototype = Object.create(Feature.prototype)

/**
 * @public
 * 
 * @param {*} __device 
 */
function RC (__device) {
  const __feature_id = 0x88

  Feature.call(this, __feature_id, __device)

  this.type = 'RC'

  const __state = {}

  const onMessage = (message) => {
    if (message.featureId !== __feature_id) return
    __state[message.path.split('.').slice(1).join('.')] = message.params
  }

  this.init = async () => {
    __device.on('message', onMessage)
  }
}
RC.prototype = Object.create(Feature.prototype)

/**
 * @public
 * 
 * @param {*} __device 
 */
function DroneManager (__device) {
  const __feature_id = 0x89

  Feature.call(this, __feature_id, __device)

  this.type = 'DroneManager'

  const __state = {}

  /**
   * 
   * @param {*} message 
   */
  const onMessage = (mesg) => {
    if (mesg.featureId !== __feature_id) return
    const minfo = mesg.info
    const prop = mesg.path.split('.')[2]

    if (minfo.messageType === 'event' && minfo.event.type &&
        minfo.event.type.startsWith('MAP_ITEM:')) {
      const key = mesg.info.event.type.split(':')[1]

      if (!__state[prop]) __state[prop] = {}

      __state[prop][mesg.params[key]] = mesg.params
      return
    }

    __state[prop] = mesg.params
  }

  /**
   * 
   */
  this.init = async () => {
    __device.on('message', onMessage)
  }

  /**
   * 
   */
  this.toJSON = () => (
    Object.assign(Feature.prototype.toJSON.call(this), __state)
  )
}
DroneManager.prototype = Object.create(Feature.prototype)

module.exports = {
  FEATURE_CLASSES,
}