sourceafParrotSdk2::DroneConfig.fan


** Drone config in the Common category.
** This contains config in the *Common* category. See `session` for other config.
** 
** 'DroneConfig' instances may be obtained from the Drone class:
** 
**   syntax: fantom
**   config := drone.config() 
** 
const class DroneConfig {
    ** The default ID for session, user, and application when the drone is first turned on.
    static const    Int     defId   := 0

    ** The wrapped drone.
    const Drone     drone

    ** Creates a new 'DroneConfig' instance for the given Drone. 
    ** Note this class holds no state.
    new make(Drone drone) {
        this.drone = drone
    }

    ** Resets the session, user, and application profiles back to the default id of '00000000'.
    Void reset() {
        drone.sendConfig("CUSTOM:application_id", defId.toHex(8).upper)
        drone.sendConfig("CUSTOM:profile_id", defId.toHex(8).upper)
        drone.sendConfig("CUSTOM:session_id", defId.toHex(8).upper)
        drone.configRefresh
    }
    
    ** If the done contains data for the given 'sessionName' then it is loaded. 
    ** If not, then default data for the new session is created.  
    ** 
    ** If session name is 'null', or matches the current session, then the current session config is returned.
    ** 
    ** See `DroneSessionConfig` for details.
    DroneSessionConfig session(Str? sessionName := null) {
        sessConfig := DroneSessionConfig(drone)
        if (sessionName != null) {
            //id := Int.random.and(0xFFFFFFFF).toHex(8).upper   // I got too many sessions!
            id := sessionName.toBuf.crc("CRC-32-Adler")
            if (id != _sessId) {
                sendConfig("CUSTOM:session_id", id.toHex(8).upper)
                sendConfig("CUSTOM:session_desc", sessionName)
                drone.configRefresh
            }
        }
        return sessConfig
    }
    
    ** Gets or makes user config.
    internal DroneUserConfig _userConfig(Str? userName) {
        userConfig := DroneUserConfig(drone)
        if (userName != null) {
            id := userName.toBuf.crc("CRC-32-Adler")
            if (id != _userId) {
                sendConfig("CUSTOM:profile_id", id.toHex(8).upper)
                sendConfig("CUSTOM:profile_desc", userName)
                drone.configRefresh
            }
        }
        return userConfig
    }

    ** Gets or makes application config.
    ** If 'null' is passed, this just returns the current config.
    internal DroneAppConfig _appConfig(Str? appicationName) {
        appConfig := DroneAppConfig(drone)
        if (appicationName != null) {
            id := appicationName.toBuf.crc("CRC-32-Adler")
            if (id != _appId) {
                sendConfig("CUSTOM:application_id", id.toHex(8).upper)
                sendConfig("CUSTOM:application_desc", appicationName)
                drone.configRefresh
            }
        }
        return appConfig
    }
    
    
    
    // ---- Common Configuration ----
    
    ** Returns the version of the drone's configuration subsystem, motherboard, and firmware.
    ** 
    **   syntax: fantom
    **   versions()  // --> [config:1, firmware:2.4.8, motherboard:34]
    ** 
    ** Corresponds to the configuration keys:
    **   - 'GENERAL:num_version_config'
    **   - 'GENERAL:num_version_mb'
    **   - 'GENERAL:num_version_soft'
    Str:Version versions() {
        [
            "config"        : Version(getConfig("GENERAL:num_version_config")),
            "motherboard"   : Version(getConfig("GENERAL:num_version_mb")),
            "firmware"      : Version(getConfig("GENERAL:num_version_soft"))
        ]
    }
    
    ** Serial number of the drone.
    ** 
    ** Corresponds to the 'GENERAL:drone_serial' configuration key.
    Str serialNumber {
        get { getConfig("GENERAL:drone_serial") }
        private set { }
    }
    
    ** Date of drone firmware compilation.
    ** 
    ** Corresponds to the 'GENERAL:soft_build_date' configuration key.
    DateTime firmwareBuildDate {
        get { DateTime.fromLocale(getConfig("GENERAL:soft_build_date"), "YYYY-MM-DD hh:mm") }
        private set { }
    }
    
    ** Returns the versions of the drone's motor's hardware, software, and supplier.
    ** 
    **   syntax: fantom
    **   versions()  // --> [hardware:6.0, software:1.43, supplier:1.1]
    ** 
    ** The motor number should be between 1 and 4.
    ** 
    ** Corresponds to the configuration keys:
    **   - 'GENERAL:motorX_hard'
    **   - 'GENERAL:motorX_soft'
    **   - 'GENERAL:motorX_supplier'
    Str:Version motorVersions(Int num) {
        if (num < 1 || num > 4)
            throw ArgErr("Motor num should be between 1 - 4, not ${num}")
        return [
            "hardware"  : Version(getConfig("GENERAL:motor${num}_hard")),
            "software"  : Version(getConfig("GENERAL:motor${num}_soft")),
            "supplier"  : Version(getConfig("GENERAL:motor${num}_supplier"))
        ]
    }
    
    ** Name of the drone. This name may be used by video games developer to assign a default
    ** name to a player. Note this is not related to the Wi-Fi SSID name.
    ** 
    ** Corresponds to the 'GENERAL:ardrone_name' configuration key.
    Str droneName {
        get { getConfig("GENERAL:ardrone_name") }
        set { setConfig("GENERAL:ardrone_name", it) }
    }

    ** Time spent by the drone in a flying state in its whole lifetime.
    ** 
    ** Corresponds to the 'GENERAL:flying_time' configuration key.
    Duration totalFlightTime {
        get { 1sec * getConfig("GENERAL:flying_time").toInt }
        private set { }     
    }
    
    ** Set to 'true' to have the drone send 'NavData' at a full 200 times a second (approx). 
    ** When 'false' (the default) 'NavData' is sent 15 times a second (approx).
    ** 
    ** Corresponds to the 'GENERAL:navdata_demo' configuration key.
    Bool hiResNavData {
        get { Bool(getConfig("GENERAL:navdata_demo").lower).not }
        set { setConfig("GENERAL:navdata_demo", it.not) }       
    }

    ** Minimum battery level before the drone lands automatically.
    ** 
    ** Corresponds to the 'GENERAL:vbat_min' configuration key.
    Int minBatteryLevel {
        get { getConfig("GENERAL:vbat_min").toInt }
        set { setConfig("GENERAL:vbat_min", it.toStr) }     
    }

    ** Maximum drone altitude in meters.
    ** A typical value for *unlimited* altitude is 100 meters.
    **
    ** Corresponds to the 'CONTROL:altitude_max' configuration key.
    Float maxAltitude {
        get { getConfig("CONTROL:altitude_max").toInt / 1000f }
        set { setConfig("CONTROL:altitude_max", (it * 1000).toInt.toStr) }      
    }

    ** Minimum drone altitude in meters.
    ** Should be left to the default value, for control stabilities issues.
    **
    ** Corresponds to the 'CONTROL:altitude_min' configuration key.
    Float minAltitude {
        get { getConfig("CONTROL:altitude_min").toInt / 1000f }
        set { setConfig("CONTROL:altitude_min", (it * 1000).toInt.toStr) }      
    }

    ** Sets the sensitivity profile for indoor / outdoor use. Loads values for:
    **  - 'indoor/outdoor_euler_angle_max'
    **  - 'indoor/outdoor_control_vz_max'
    **  - 'indoor/outdoor_control_yaw'
    **
    ** See `DroneUserConfig` for details.
    **  
    ** Note this also enables the wind estimator of the AR Drone 2.0, and thus should always be 
    ** enabled when flying outside.
    **  
    ** Corresponds to the 'CONTROL:outdoor' configuration key.
    Bool useOutdoorProfile {
        get { Bool(getConfig("CONTROL:outdoor").lower) }
        set { setConfig("CONTROL:outdoor", it.toStr.upper) }            
    }

    ** Tells the drone it is wearing the outdoor shell so flight optimisations can be made.
    ** Deactivate this when flying with the indoor shell.
    ** 
    ** This setting is not linked with 'useOutdoorProfile', both have different effects on the drone.
    **  
    ** Corresponds to the 'CONTROL:flight_without_shell' configuration key.
    Bool useOutdoorShell {
        get { Bool(getConfig("CONTROL:flight_without_shell").lower) }
        set { setConfig("CONTROL:flight_without_shell", it.toStr.upper) }           
    }

    ** The network SSID. Changes are applied on reboot.
    **  
    ** Corresponds to the 'NETWORK:ssid_single_player' configuration key.
    Str networkSsid {
        get { getConfig("NETWORK:ssid_single_player") }
        set { setConfig("NETWORK:ssid_single_player", it) }
    }

    ** Mode of the Wi-Fi network. Possible values are:
    ** 
    **   0 = The drone is the access point of the network
    **   1 = The drone creates (or join) the network in Ad-Hoc mode
    **   2 = The drone tries to join the network as a station
    **  
    ** Corresponds to the 'NETWORK:wifi_mode' configuration key.
    Int networkWifiMode {
        get { getConfig("NETWORK:wifi_mode").toInt }
        set { setConfig("NETWORK:wifi_mode", it.toStr) }        
    }

    ** Mac address paired with the drone. Set to '00:00:00:00:00:00' to un-pair with the drone.
    **  
    ** Corresponds to the 'NETWORK:owner_mac' configuration key.
    Str networkPairedMac {
        get { getConfig("NETWORK:owner_mac") }
        set { setConfig("NETWORK:owner_mac", it) }
    }
    
    ** Ultrasound frequency used to measure altitude.
    ** Using two different frequencies for two drones can significantly reduce the ultrasound 
    ** perturbations.
    ** 
    **   7 = Ultrasound at 22.22 Hz
    **   8 = Ultrasound at 25.00 Hz
    **  
    ** Corresponds to the 'PIC:ultrasound_freq' configuration key.
    Int picUltrasoundFreq {
        get { getConfig("PIC:ultrasound_freq").toInt }
        set { setConfig("PIC:ultrasound_freq", it.toStr) }      
    }

    ** The software version of the Nav-board.
    ** 
    ** Corresponds to the 'PIC:pic_version' configuration key.
    Version picVersion {
        get { Version(getConfig("PIC:pic_version")) }
        private set { }
    }

    ** Returns read only video info. Returned keys are:
    ** 
    **  - 'fps' - Current FPS of the video interface. This may be different than the actual framerate.
    **  - 'bufferDepth' - Buffer depth for the video interface.
    **  - 'noOfTrackers' - Number of tracking points used for optical speed estimation.
    **  - 'storageSpace' - Size of the wifi video record buffer.
    **  - 'fileIndex' - Number of the last recorded video ( 'video_XXX.mp4' ) on USB key.
    ** 
    **   syntax: fantom
    **   videoInfo()  // --> ["fps": 30, "bufferDepth": 2, "noOfTrackers": 12, "storageSpace": 20480, "fileIndex": 1]
    Str:Int videoInfo() {
        [
            "fps"           : getConfig("VIDEO:camif_fps").toInt,
            "bufferDepth"   : getConfig("VIDEO:camif_buffers").toInt,
            "noOfTrackers"  : getConfig("VIDEO:num_trackers").toInt,
            "storageSpace"  : getConfig("VIDEO:video_storage_space").toInt,
            "fileIndex"     : getConfig("VIDEO:video_file_index").toInt
        ]
    }

    ** If a USB key with > 100Mb of freespace is connected, the video stream will be recorded on the USB key.
    **  
    ** In all other cases, the record stream is sent to the controlling device, which will be in 
    ** charge of the actual recording.
    ** 
    ** Corresponds to the 'VIDEO:video_on_usb' configuration key.
    Bool videoOnUsb {
        get { Bool(getConfig("VIDEO:video_on_usb").lower) }
        set { setConfig("VIDEO:video_on_usb", it.toStr.upper) }         
    }

    // remove stuff I can't / won't use
    
//  ** The color of the hulls you want to detect. Possible values are:
//  ** 
//  **   1 = Green
//  **   2 = Yellow
//  **   3 = Blue
//  ** 
//  ** Note : This config is only used for standard tag / hull detection. 
//  ** Roundel detection doesn't use it.
//  ** 
//  ** Corresponds to the 'DETECT:enemy_colors' configuration key.
//  Int detectEmemyColours {
//      get { getConfig("DETECT:enemy_colors").toInt }
//      set { setConfig("DETECT:enemy_colors", it.toStr) }      
//  }
//
//  ** Activate to detect outdoor hulls. Deactivate to detect indoor hulls.
//  **  
//  ** Corresponds to the 'DETECT:enemy_without_shell' configuration key.
//  Bool detectEmemyOutdoorShell {
//      get { getConfig("DETECT:enemy_without_shell").toInt == 0 }
//      set { setConfig("DETECT:enemy_without_shell", it ? "0" : "1") }         
//  }

    ** Dumps all fields to debug string.
    Str dump(Bool dumpToStdOut := true) {
        fields := typeof.fields.findAll { it.isPublic && it.parent == this.typeof }
        names  := (Str[]) fields.map { it.name.toDisplayName }.add("motorVersions(x)")
        width  := names.max |p1, p2| { p1.size <=> p2.size }.size
        values := fields.map |field, i| { names[i].padr(width, '.') + "..." + field.get(this).toStr }
        meths  := Str:Str[:] { it.ordered = true }
            .add("versions",            versions.toStr)
            .add("motorVersions(1)",    motorVersions(1).toStr)
            .add("motorVersions(2)",    motorVersions(2).toStr)
            .add("motorVersions(3)",    motorVersions(3).toStr)
            .add("motorVersions(4)",    motorVersions(4).toStr)
            .add("videoInfo",           videoInfo.toStr)
        methVal := meths.map |val, key| { key.padr(width, '.') + "..." + val }.vals
        dump    := methVal.addAll(values).join("\n")
        
        dump = "COMMON CONFIG\n=============\n" + dump + "\n\n"
        dump += session.dump(false)
        
        if (dumpToStdOut)
            echo(dump)
        return dump
    }
    
    ** Sends config to the drone, using the current Session, User, and Application IDs.
    ** This method blocks until the config has been acknowledged. 
    Void sendConfig(Str key, Obj val) {
        if (_sessId != defId || _userId != defId || _appId != defId)
            drone.sendConfig(key, val, _sessId, _userId, _appId)
        else
            drone.sendConfig(key, val)
        drone._updateConfig(key, val)
    }

    @NoDoc
    override Str toStr() { dump }

    // ---- Internal Methods ----

    internal Int _sessId() {
        getConfig("CUSTOM:session_id").toInt(16)
    }

    internal Int _userId() {
        getConfig("CUSTOM:profile_id").toInt(16)
    }

    internal Int _appId() {
        getConfig("CUSTOM:application_id").toInt(16)
    }

    internal Void _delSess(Str id) {
        drone.sendConfig("CUSTOM:session_id", id)
        drone.sendConfig("CUSTOM:session_id", defId.toHex(8))
        drone.configRefresh
    }

    internal Void _delUser(Str id) {
        drone.sendConfig("CUSTOM:profile_id", id)
        drone.sendConfig("CUSTOM:profile_id", defId.toHex(8))
        drone.configRefresh
    }
    
    internal Void _delApp(Str id) {
        drone.sendConfig("CUSTOM:application_id", id)
        drone.sendConfig("CUSTOM:application_id", defId.toHex(8))
        drone.configRefresh
    }
    
    private Str getConfig(Str key) {
        drone.configMap[key] ?: throw UnknownKeyErr(key)
    }
    
    private Void setConfig(Str key, Obj val) {
        sendConfig(key, val)
    }
}