using concurrent::AtomicRef
** Drone config in the Session category.
** Session config relates to the current flight and encapsulates user and application data.
** That is, should the session be changed / reset, the user and application config changes also.
**
** Create a new session by means of:
**
** syntax: fantom
** drone.config.session("My Session Name")
**
** Future code may then access the same session by **not** passing a session name:
**
** syntax: fantom
** drone.config.session.hoveringRange = 1000
**
const class DroneSessionConfig {
private const Log _log := Drone#.pod.log
internal const DroneConfig _config
** Creates a new 'DroneSessionConfig' instance for the given Drone.
** Note this class holds no state.
new make(Drone drone) {
this._config = DroneConfig(drone)
}
// ---- Identity ----
** The current session ID.
**
** Corresponds to the 'CUSTOM:profile_id' configuration key.
Int id {
get { _config._sessId }
private set { }
}
** The current session name.
**
** Corresponds to the 'CUSTOM:profile_desc' configuration key.
Str name {
get { getConfig("CUSTOM:session_desc") }
private set { }
}
** Deletes this session data from the drone.
Void deleteMe() {
id := id
if (id == DroneConfig.defId) {
_log.warn("Will not delete default data!") // don't know what might happen if we try this!?
return
}
_config._delSess("-${id.toHex(8)}")
}
** Deletes **ALL** session data from the drone.
** Use with caution.
Void deleteAll() {
_log.warn("Deleting ALL session data!")
_config._delSess("-all")
}
// ---- Config ----
** Gets or makes user config.
DroneUserConfig user(Str? userName := null) {
_config._userConfig(userName)
}
** Gets or makes application config.
** If 'null' is passed, this just returns the current config.
DroneAppConfig app(Str? appicationName := null) {
_config._appConfig(appicationName)
}
// ---- Other Cmds ----
** Set to 'true' to enable roundel detection and activate a semi-autonomous hover on top of an
** [oriented roundel]`https://bitbucket.org/AlienFactory/afparrotsdk2/downloads/orientedRoundel.png`.
** Setting this also sets the 'detectRoundel' config to 'vertical'.
**
** Note 'HOVER_ON_ROUNDEL' was developed for the 2011 CES autonomous demonstration.
**
** Corresponds to the 'CONTROL:flying_mode' configuration key.
Bool hoverOnRoundel {
// 0 = FREE_FLIGHT // Normal mode, commands are enabled
// 1 = HOVER_ON_ROUNDEL // Commands are disabled, drone hovers on a roundel
// 2 = HOVER_ON_ORIENTED_ROUNDEL // Commands are disabled, drone hovers on an oriented roundel
get { getConfig("CONTROL:flying_mode").toInt == 2 }
set {
if (it == true) {
detectRoundel = VideoCamera.vertical
setConfig("CONTROL:flying_mode", 2)
} else
setConfig("CONTROL:flying_mode", 0)
}
}
** The maximum distance (in millimetres) the drone should hover.
** Used when 'flyingMode' is set to 'HOVER_ON_(ORIENTED_)ROUNDEL'
**
** Corresponds to the 'CONTROL:hovering_range' configuration key.
Int hoverMaxHeight {
get { getConfig("CONTROL:hovering_range").toInt }
set { setConfig("CONTROL:hovering_range", it.toStr) }
}
** Sets the frames per second (fps) of the live video codec.
** Values should be between 15 and 30.
**
** Corresponds to the 'VIDEO:codec_fps' configuration key.
Int videoFps {
get { getConfig("VIDEO:codec_fps").toInt }
set { setConfig("VIDEO:codec_fps", it.toStr) }
}
** Selects which video camera on the drone to stream data back from.
**
** Corresponds to the 'VIDEO:video_channel' configuration key.
VideoCamera videoCamera {
get { VideoCamera.vals[getConfig("VIDEO:video_channel", false)?.toInt ?: 0] }
set { setConfig("VIDEO:video_channel", it.ordinal.toStr) }
}
** Selects the resolution of the video that's streamed back.
**
** It is generally not advised to change this whilst video is already streaming.
**
** Can only be set with a non-default session ID. Throws an Err if this is not the case.
**
** Corresponds to the 'VIDEO:video_codec' configuration key.
VideoResolution videoResolution {
get {
// H264_360P_CODEC = 0x81 // Live stream with 360p H264 hardware encoder. No record stream.
// H264_720P_CODEC = 0x83 // Live stream with 720p H264 hardware encoder. No record stream.
//
// MP4_360P_CODEC = 0x80 // Live stream with MPEG4.2 soft encoder. No record stream.
// MP4_360P_H264_360P_CODEC = 0x82 // Live stream with MPEG4.2 soft encoder. Record stream with 360p H264 hardware encoder.
// MP4_360P_H264_720P_CODEC = 0x88 // Live stream with MPEG4.2 soft encoder. Record stream with 720p H264 hardware encoder.
liveCodec := getConfig("VIDEO:video_codec", false)?.toInt
return (liveCodec == VideoResolution._720p.liveCodec) ? VideoResolution._720p : VideoResolution._360p
}
set {
_checkId("change video codec")
setConfig("VIDEO:video_codec", it.liveCodec.toStr)
}
}
** Maximum bitrate (kilobits per second) the device can decode. This is set as the upper bound for drone bitrate values.
**
** Typical values for Apple iOS Device are:
** - iPhone 4S : 4000 kbps
** - iPhone 4 : 1500 kbps
** - iPhone 3GS : 500 kbps
**
** When using the bitrate control mode in 'VBC_MANUAL', this maximum bitrate is ignored.
**
** When using the bitrate control mode in 'VBC_MODE_DISABLED', the bitrate is fixed to this maximum bitrate.
**
** Can only be set with a non-default session ID. Throws an Err if this is not the case.
**
** Corresponds to the 'VIDEO:max_bitrate' configuration key.
Int videoMaxBitrate {
get { getConfig("VIDEO:max_bitrate").toInt }
set {
_checkId("change video bitrate")
setConfig("VIDEO:max_bitrate", it.toStr)
}
}
** Enables black & white oriented roundel detection on the given camera.
** To receive detection data, enable 'NavOption.visionDetect' in
** [DroneAppConfig.navDataOptions()]`DroneAppConfig.navDataOptions` and inspect the
** 'visionDetect' data in 'NavData'
**
** Set to 'null' to disable detection.
**
** Corresponds to the configuration keys:
** - 'DETECT:detect_type'
** - 'DETECT:detections_select_v'
** - 'DETECT:detections_select_h'
VideoCamera? detectRoundel {
get {
// 3 = CAD_TYPE_NONE : No detections
// 5 = CAD_TYPE_ORIENTED_COCARDE : Detects an oriented roundel under the drone
// 8 = CAD_TYPE_H_ORIENTED_COCARDE : Detects an oriented roundel in front of the drone
// 10 = CAD_TYPE_MULTIPLE_DETECTION_MODE : See following note
// 12 = CAD_TYPE_ORIENTED_COCARDE_BW : Black & White oriented roundel detected on bottom facing camera
type := getConfig("DETECT:detect_type").toInt
if (type == 12)
return VideoCamera.vertical
if (type == 3) {
// Shell=1 | Roundel=2 | BlackRoundel=4 | Stripe=8 | Cap=16 | ShellV2=32 | TowerSide=64 | OrientedRoundel=128
vert := getConfig("DETECT:detections_select_v").toInt
if (vert == 128)
return VideoCamera.vertical
// Shell=1 | Roundel=2 | BlackRoundel=4 | Stripe=8 | Cap=16 | ShellV2=32 | TowerSide=64 | OrientedRoundel=128
hori := getConfig("DETECT:detections_select_h").toInt
if (hori == 128)
return VideoCamera.horizontal
}
return null
}
set {
type := getConfig("DETECT:detect_type").toInt
if (type != 3)
setConfig("DETECT:detect_type", 3)
if (it == null) { // NPE if null used in switch
setConfig("DETECT:detections_select_v", 0)
setConfig("DETECT:detections_select_h", 0)
return
}
switch (it) {
case VideoCamera.vertical:
setConfig("DETECT:detections_select_v", 128)
setConfig("DETECT:detections_select_h", 0)
case VideoCamera.horizontal:
setConfig("DETECT:detections_select_v", 0)
setConfig("DETECT:detections_select_h", 128)
}
}
}
** Detection, by default, runs at 60 frames per section (fps) but by setting this to 'true'
** it is reduced to 30 fps, removing unnecessary CPU load when not needed.
**
** Corresponds to the 'DETECT:detections_select_v_hsync' configuration key.
Bool detectRoundelLoRes {
get { getConfig("DETECT:detections_select_v_hsync").toInt == 30 }
set { setConfig("DETECT:detections_select_v_hsync", it ? 30 : 60) }
}
** The GPS position used for media tagging and userbox recording.
**
** When setting values, all unknown values are ignored.
** To change, pass in a map with just the values you wish to update.
**
** Corresponds to the configuration keys:
** - 'GPS:longitude'
** - 'GPS:latitude'
** - 'GPS:altitude'
Str:Float gpsPosition {
get {
[
"longitude" : getConfig("GPS:longitude").toFloat,
"latitude" : getConfig("GPS:latitude").toFloat,
"altitude" : getConfig("GPS:altitude").toFloat
]
}
set {
setConfig("GPS:longitude" , it["longitude"]?.toStr)
setConfig("GPS:latitude" , it["latitude" ]?.toStr)
setConfig("GPS:altitude" , it["altitude" ]?.toStr)
}
}
** Starts saving navigation data to flash memory.
**
** Creates the binary file '/data/video/boxes/tmp_flight_<dateTime>/userbox_<timestamp>' in flash memory
** containing navigation data where '<timestamp>' is the time since the drone booted.
**
** Corresponds to the 'USERBOX:userbox_cmd' configuration key.
Void userboxStart() {
setConfig("USERBOX:userbox_cmd", [1, DateTime.now.toLocale("YYYYMMDD_hhmmss")])
}
** Stops saving navigation data to flash memory.
**
** Renames the directory containing the binary files to '/data/video/boxes/flight_<dateTime>/'.
**
** Corresponds to the 'USERBOX:userbox_cmd' configuration key.
Void userboxStop() {
setConfig("USERBOX:userbox_cmd", [0])
}
** Stops saving navigation data to flash memory and deletes the temporary files.
**
** Corresponds to the 'USERBOX:userbox_cmd' configuration key.
Void userboxCancel() {
setConfig("USERBOX:userbox_cmd", [3])
}
** Takes photographs with the forward facing front camera and saves it as JPEG images named
** '/data/video/boxes/tmp_flight_<dateTime>/picture_<dateTime>.jpg'.
**
** Note 'delayBetweenPhotos' must be in seconds.
**
** Corresponds to the 'USERBOX:userbox_cmd' configuration key.
Void takePhoto(Int numberOfPhotos := 1, Duration delayBetweenPhotos := 1sec) {
setConfig("USERBOX:userbox_cmd", [2, delayBetweenPhotos.toSec, numberOfPhotos, DateTime.now.toLocale("YYYYMMDD_hhmmss")])
}
** 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 }
width := names.max |p1, p2| { p1.size <=> p2.size }.size
values := fields.map |field, i| { names[i].padr(width, '.') + "...${field.get(this)}" }
dump := values.join("\n")
dump = "SESSION CONFIG\n==============\n" + dump + "\n\n"
dump += user.dump(false)
dump += app.dump(false)
if (dumpToStdOut)
echo(dump)
return dump
}
@NoDoc
override Str toStr() { dump }
internal Void _checkId(Str what) {
if (id == DroneConfig.defId)
throw Err("Can not ${what} with default session ID: $id")
}
private Str? getConfig(Str key, Bool checked := true) {
_config.drone.configMap[key] ?: (checked ? throw UnknownKeyErr(key) : null)
}
private Void setConfig(Str key, Obj? val) {
if (val != null) { // for setting GPS position
_config.sendConfig(key, val)
}
}
}
** The video cameras on the AR Drone.
enum class VideoCamera {
** The forward facing horizontal camera.
horizontal,
** The botton facing vertical camera.
vertical;
}
** A selection of video resolutions.
enum class VideoResolution {
** 640 x 360
_360p(0x81, 0x82),
** 1280 x 720
_720p(0x83, 0x88);
internal const Int liveCodec
internal const Int recordCodec
private new make(Int liveCodec, Int recordCodec) {
this.liveCodec = liveCodec
this.recordCodec = recordCodec
}
}