Skip to content

Instantly share code, notes, and snippets.

@sanekgusev
Created January 6, 2017 03:52
Show Gist options
  • Save sanekgusev/475a8bf7d55a95aaf834652f3a6cad2d to your computer and use it in GitHub Desktop.
Save sanekgusev/475a8bf7d55a95aaf834652f3a6cad2d to your computer and use it in GitHub Desktop.
Robotized API
// MARK: Interface
public protocol ConnectionState: RawRepresentable {
var rawValue: String { get }
init?(rawValue: String)
}
public struct SensorReading<U: Unit> {
let name: String
let measurement: Measurement<U>
}
extension SensorReading: Hashable {
public var hashValue: Int {
return name.hashValue ^ measurement.hashValue
}
}
public func ==<U>(lhs: SensorReading<U>, rhs: SensorReading<U>) -> Bool {
return lhs.name == rhs.name && lhs.measurement == rhs.measurement
}
public protocol CompoundSensorReading: Hashable {
var name: String { get }
var readings: Set<SensorReading<Unit>> { get }
}
extension CompoundSensorReading {
public var hashValue: Int {
return name.hashValue ^ readings.hashValue
}
}
public func ==<T: CompoundSensorReading>(lhs: T, rhs: T) -> Bool {
return lhs.name == rhs.name && lhs.readings == rhs.readings
}
public struct AnyCompoundSensorReading: CompoundSensorReading {
private let nameClosure: () -> String
private let readingsClosure: () -> Set<SensorReading<Unit>>
public init<CompoundSensorReadingType: CompoundSensorReading>(reading: CompoundSensorReadingType) {
nameClosure = { reading.name }
readingsClosure = { reading.readings }
}
public var name: String {
return nameClosure()
}
public var readings: Set<SensorReading<Unit>> {
return readingsClosure()
}
}
public protocol SensorSet {
var timestamp: Date { get }
var readings: Set<AnyCompoundSensorReading> { get }
}
public enum CommandArgumentValue<CustomArgumentType: Hashable, UnitType: Unit> {
case customType(CustomArgumentType)
case measurement(Measurement<UnitType>)
}
extension CommandArgumentValue: Hashable {
public var hashValue: Int {
switch self {
case let .customType(type):
return type.hashValue
case let .measurement(measurement):
return measurement.hashValue
}
}
}
public func ==<CustomArgumentType: Hashable, UnitType: Unit>(lhs: CommandArgumentValue<CustomArgumentType, UnitType>, rhs: CommandArgumentValue<CustomArgumentType, UnitType>) -> Bool {
switch (lhs, rhs) {
case let (.customType(t1), .customType(t2)) where t1 == t2:
return true
case let (.measurement(m1), .measurement(m2)) where m1 == m2:
return true
}
return false
}
public struct CommandArgument<CustomArgumentType: Hashable, UnitType: Unit> {
let name: String
let value: CommandArgumentValue<CustomArgumentType, UnitType>
}
extension CommandArgument: Hashable {
public var hashValue: Int {
return name.hashValue
}
}
public func ==<T, U: Unit>(lhs: CommandArgument<T, U>, rhs: CommandArgument<T, U>) -> Bool {
return lhs.name == rhs.name && lhs.value == rhs.value
}
public protocol Command: Hashable {
var name: String { get }
var arguments: Set<CommandArgument<AnyHashable, Unit>> { get }
}
extension Command {
public var hashValue: Int {
return name.hashValue ^ arguments.hashValue
}
}
public func ==<CommandType: Command>(lhs: CommandType, rhs: CommandType) -> Bool {
return lhs.name == rhs.name && lhs.arguments == rhs.arguments
}
public struct AnyCommand: Command {
private let nameClosure: () -> String
private let argumentsClosure: () -> Set<CommandArgument<AnyHashable, Unit>>
public init<CommandType: Command>(command: CommandType) {
nameClosure = { command.name }
argumentsClosure = { command.arguments }
}
public var name: String {
return nameClosure()
}
public var arguments: Set<CommandArgument<AnyHashable, Unit>> {
return argumentsClosure()
}
}
public protocol CommandSet {
var commands: Set<AnyCommand> { get }
}
public protocol Robot {
associatedtype SensorSetType: SensorSet
associatedtype ConnectionStateType: ConnectionState
associatedtype CommandSetType: CommandSet
var name: String { get }
var type: String { get }
var connectionState: ConnectionStateType { get }
var commands: CommandSetType { get }
var sensorHistory: AnyCollection<SensorSetType> { get }
}
// MARK: Implementation
public enum SampleConnectionState: String, ConnectionState {
case unknown
case lost
case discovered
case connecting
case connected
case disconnected
}
@sanekgusev
Copy link
Author

sanekgusev commented Jan 6, 2017

The idea is to create concrete implementations of SensorSet (with a bunch of specific CompoundSensorReading implementations as its properties), ConnectionState, and CommandSet (with a bunch of specific commands as properties that all will conform to Command) for each implementation of Robot.

Different models of robots would be represented by different Robot implementations.

An AnyRobot type-erasing wrapper can also be created, in the same fashion as AnyCompoundSensorReading and AnyCommand, if at some point we'll need to treat different robots as being of same, more generic, type.

This approach is also meant to make it easier to add support for new commands and sensor readings.

And yes, no callback/notification mechanism for now. 😞

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment