在最新的苹果官方发布会上,苹果和法国无人机厂商Parrot公司共同为其旗下的多款无人机推出了Playgrounds编程教育项目。这些Playgrounds Book可以引导学习者使用Playgrounds来即时操作无人机。在现在的Playgrounds Store里面可以看到两个有关项目,一个是Parrot模板,一个是Parrot Education项目。今天,我们主要将挖掘挖掘Parrot Education项目中没有教的,但却写在源码里的一些指令。
首先先要看一看Parrot Education适配的机型。无论是从项目封面还是进入项目后的介绍图都可以明显看出该项目的最适配机型是Parrot Mambo。
事实上,Parrot Mambo确实是最合适的机型,项目有部分内容是Parrot Mambo所独有的功能,如使用持力夹,发射加农炮弹等。但如果仔细翻相关的源代码就会发现,项目提供的机型支持还是蛮多的。
首先进入项目的辅助源文件(工具 - 高级 - 查看辅助源文件)。进入Contents目录,在进入Sources目录,可以看到一列表的Swift文件。各个文件的作用我们稍后会提及到,现在我们需要查看DataStorage.swift文件。打开文件之后,重点看前三个代码块,分别是枚举类Model,枚举类SubModel和结构体DroneModel。先看最后一个结构体DroneModel,很简单就能推断出一个Drone对象有一个Model和一个SubModel构成。所以查看其Model和SubModel的枚举种类就可以知道总共的类型数量。那么在Model类中,一共有四个枚举对象:rollingSpider,cargo,night,mambo。由此可见,Playgrounds支持了四个大类的产品。再看到另一个枚举类SubModel,里面一共有6个枚举对象,但与Model又不太相同。阅读注释不难发现,前两个对象travis和mars是cargo系列下的子产品。对象swat,maclane和blaze则是属于night系列的(这里原版注释有拼写错误,将Model中的night标成了light)。而由于rollingSpider和mambo系列都只有一个产品,所以有一个none对象,及没有SubModel。
public enum Model: UInt8 {
case rollingSpider = 0x00
case cargo = 0x09
case night = 0x07
case mambo = 0x0B
}
// list of sub models
public enum SubModel: UInt8 {
// cargo
case travis = 0
case mars = 1
// light
case swat = 2
case maclane = 3
case blaze = 4
// mambo and rolling spinder dones't have sub-models
case none = 128
}
public struct DroneModel {
public let model: Model
public let subModel: SubModel
}
综上所述,往细里算,Parrot Education支持了7款产品。不过除了mambo的其他产品不能适应项目的所有特性。因此,本文将以mambo为主机体展开讲述。
知道了适配的机型,下面我们来看看项目教了哪些内容。从目录中看出,项目教得较为细致,内容包括简单的飞行指令以及复杂的特技指令,同时也包括了配件的使用,如拍照,使用持力夹等。最后也教学习者如何用iPad实时控制无人机。由于项目叙述的较为详细,这里也不再多提,我们主要看看辅助源文件里有的,但没写在教程里的功能。仔细观察发现,除了通用指令外,mambo自带了持力夹和bb弹加农炮两个配件。而教程中提及了操作持力夹开关的指令,却没有一点与加农炮有关的说明,这就很令人疑惑了。将加农炮装到mambo上,再与iPad配对之后可以发现,Playgrounds可以检测到装上的Cannon配件。同时在上述的DataStorage.swift文件中也可以发现检测两个配件的代码块。于是乎,我们可以大胆推测,在相关的文件中,应该有操作加农炮的指令。
var myCannonAccessoryId: UInt8? {
get {
if case let .integer(cannonId)? = PlaygroundKeyValueStore.current["com.parrot.CannonAccessoryId"] {
return UInt8(cannonId)
} else {
return nil
}
}
set {
let value: PlaygroundValue?
if let newValue = newValue {
value = .integer(Int(newValue))
} else {
value = nil
}
PlaygroundKeyValueStore.current["com.parrot.CannonAccessoryId"] = value
}
}
var myGrabberAccessoryId: UInt8? {
get {
if case let .integer(cannonId)? = PlaygroundKeyValueStore.current["com.parrot.GrabberAccessoryId"] {
return UInt8(cannonId)
} else {
return nil
}
}
set {
let value: PlaygroundValue?
if let newValue = newValue {
value = .integer(Int(newValue))
} else {
value = nil
}
PlaygroundKeyValueStore.current["com.parrot.GrabberAccessoryId"] = value
}
}
遍历一遍整个列表的文件名,Assessor.swift文件吸引了我们的注意。Assessor是Assessable的名词形式,意思与“集合”,“聚合”有关,于是这次我们点开Assessor.swift文件,看看是不是指指令的集合。很幸运的是,在枚举类Action中,我们发现了13个枚举对象,分别对应着“起飞”,“降落”,“调整速度”,“移动”,“复杂移动”,“转动”,“翻滚”,“打开持力夹”,“关闭持力夹”,“发射加农炮”,“拍照”和两个没有明确指代的指令(可以先忽略,并不重要)。其中,我们找到了操作加农炮的指令,即fireCannon的action。由此可见,Parrot公司确实将完整的操作代码写了出来,但可能因为发射加农炮有宣扬*暴力*的因素,并没有写进教程当中去。
// Assessable action
public enum Action {
case takeOff
case land
case speed(UInt?)
case move(direction: MoveDirection?, duration: Int?)
case complexMove(MoveParams?, duration: UInt?)
case turn(direction: TurnDirection?, angle: UInt?)
case flip(direction: FlipDirection?)
case openGrabber
case closeGrabber
case fireCannon
case takePicture
// for expected action only: a set of action present is any order
case allAnyOrder([Action])
// for expected action only: a list of action in a single Assessment
case all([Action])
}
这时候,我们尝试将fireCannon写进一段代码中可以发现,mambo确实使用了bb弹加农炮发射了一次,这也说明了Parrot公司提供了具体的实现方法。至于各个指令的具体实现方法,我们就要等到下期再说了。