Bulk of swift code done for simple controller case.
Just need to get the server running, then can test
This commit is contained in:
@@ -17,12 +17,9 @@ let package = Package(
|
||||
targets: [
|
||||
// Targets are the basic building blocks of a package. A target can define a module or a test suite.
|
||||
// Targets can depend on other targets in this package, and on products in packages which this package depends on.
|
||||
.target(
|
||||
name: "Generated",
|
||||
dependencies: [.product(name: "GRPC", package: "grpc-swift")]),
|
||||
.target(
|
||||
name: "SwiftyCar",
|
||||
dependencies: ["Generated", "SwiftyGPIO"]),
|
||||
dependencies: ["SwiftyGPIO", .product(name: "GRPC", package: "grpc-swift")]),
|
||||
.testTarget(
|
||||
name: "SwiftyCarTests",
|
||||
dependencies: ["SwiftyCar"]),
|
||||
|
||||
57
SwiftyCar/Sources/SwiftyCar/MotorProvider.swift
Normal file
57
SwiftyCar/Sources/SwiftyCar/MotorProvider.swift
Normal file
@@ -0,0 +1,57 @@
|
||||
//
|
||||
// MotorProvider.swift
|
||||
//
|
||||
//
|
||||
// Created by Michael Pivato on 13/5/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import GRPC
|
||||
import NIO
|
||||
import SwiftProtobuf
|
||||
|
||||
class MotorProvider: MotorControl_CarControlProvider{
|
||||
private var vehicle: Vehicle2D
|
||||
|
||||
init(vehicle: Vehicle2D){
|
||||
self.vehicle = vehicle
|
||||
}
|
||||
|
||||
func set_throttle(request: MotorControl_ThrottleRequest, context: StatusOnlyCallContext) -> EventLoopFuture<MotorControl_ThrottleResponse> {
|
||||
self.vehicle.throttle = request.throttle
|
||||
return context.eventLoop.makeSucceededFuture(.with{
|
||||
$0.throttleSet = true
|
||||
})
|
||||
}
|
||||
|
||||
func set_steering(request: MotorControl_SteeringRequest, context: StatusOnlyCallContext) -> EventLoopFuture<MotorControl_SteeringResponse> {
|
||||
self.vehicle.steering = request.steering
|
||||
return context.eventLoop.makeSucceededFuture(.with{
|
||||
$0.steeringSet = true
|
||||
})
|
||||
}
|
||||
|
||||
func stream_vehicle_2d(context: UnaryResponseCallContext<Google_Protobuf_Empty>) -> EventLoopFuture<(StreamEvent<MotorControl_Vehicle2DRequest>) -> Void> {
|
||||
return context.eventLoop.makeSucceededFuture({event in
|
||||
switch event{
|
||||
case .message(let movement):
|
||||
self.vehicle.throttle = movement.throttle.throttle
|
||||
self.vehicle.steering = movement.steering.steering
|
||||
case .end:
|
||||
context.responsePromise.succeed(Google_Protobuf_Empty())
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
func record(request: MotorControl_RecordingReqeust, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
|
||||
// TODO: Recording...
|
||||
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
|
||||
}
|
||||
|
||||
func save_recorded_data(request: MotorControl_SaveRequest, context: StatusOnlyCallContext) -> EventLoopFuture<Google_Protobuf_Empty> {
|
||||
// TODO Recording...
|
||||
return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty())
|
||||
}
|
||||
}
|
||||
|
||||
104
SwiftyCar/Sources/SwiftyCar/PwmWrappers.swift
Normal file
104
SwiftyCar/Sources/SwiftyCar/PwmWrappers.swift
Normal file
@@ -0,0 +1,104 @@
|
||||
//
|
||||
// PwmWrappers.swift
|
||||
//
|
||||
// Simple wrappers for PwmOutput I'll create as I go along, to match gpioZero's implementation I'm currently using.
|
||||
// See gpiozero code here: https://github.com/gpiozero/gpiozero/blob/master/gpiozero/output_devices.py
|
||||
//
|
||||
// Created by Michael Pivato on 9/5/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftyGPIO
|
||||
|
||||
enum ServoError: Error{
|
||||
case invalidMinPulseWidth
|
||||
case invalidMaxPulseWidth
|
||||
}
|
||||
|
||||
class Servo{
|
||||
private (set) var frameWidth : Float
|
||||
private var minDc: Float
|
||||
private var dcRange: Float
|
||||
private var minValue: Float
|
||||
private var valueRange: Float
|
||||
private var internalValue: Float
|
||||
|
||||
// TODO: Use a factory to provide the correct pwm rather than passing the pin in.
|
||||
private var device: PWMOutput
|
||||
private var currentPulseWIdth: Float
|
||||
|
||||
var minPulseWidth: Float {
|
||||
get{
|
||||
return self.minDc * self.frameWidth
|
||||
}
|
||||
}
|
||||
|
||||
var maxPulseWidth: Float{
|
||||
get{
|
||||
return (self.dcRange * self.frameWidth) + self.minPulseWidth
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Also store the pulse width (needs to be within this class)
|
||||
// var pulseWidth: Float{
|
||||
// get{
|
||||
// return self.device//self.pwm_device.pin.state * self.frame_width
|
||||
// }
|
||||
// set (value){
|
||||
//
|
||||
// }
|
||||
// }
|
||||
|
||||
var value: Float{
|
||||
get{
|
||||
internalValue
|
||||
}
|
||||
set(newValue){
|
||||
self.internalValue = newValue
|
||||
self.device.startPWM(period: Int(self.frameWidth),
|
||||
// Multiple by 100 to get into percentage. I don't like that...
|
||||
duty: self.minDc + self.dcRange * ((internalValue - self.minValue) / self.valueRange) * 100)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Change this to nanoseconds to match PWMOutput
|
||||
init?(forPin pin: PWMOutput, startingAt initialValue: Float = 0, withMinPulseWidth minPulseWidth: Float = 1000000,
|
||||
withMaxPulseWidth maxPulseWidth: Float = 2000000, withFrameWidth frameWidth: Float = 20000000) throws {
|
||||
if(minPulseWidth >= maxPulseWidth){
|
||||
throw ServoError.invalidMinPulseWidth
|
||||
}
|
||||
|
||||
if(maxPulseWidth >= frameWidth){
|
||||
throw ServoError.invalidMaxPulseWidth
|
||||
}
|
||||
self.frameWidth = frameWidth
|
||||
self.minDc = minPulseWidth / frameWidth
|
||||
self.dcRange = (maxPulseWidth - minPulseWidth) / frameWidth
|
||||
self.minValue = -1
|
||||
self.valueRange = 2
|
||||
|
||||
self.currentPulseWIdth = 0
|
||||
|
||||
// Initialise pin immediately.
|
||||
self.device = pin
|
||||
self.device.initPWM()
|
||||
self.internalValue = initialValue
|
||||
self.device.startPWM(period: Int(self.frameWidth), duty: self.minDc + self.dcRange * ((internalValue - self.minValue) / self.valueRange) * 100)
|
||||
}
|
||||
|
||||
func min(){
|
||||
self.value = -1
|
||||
}
|
||||
|
||||
func mid(){
|
||||
self.value = 0
|
||||
}
|
||||
|
||||
func max() {
|
||||
self.value = 1
|
||||
}
|
||||
|
||||
func detach(){
|
||||
self.device.stopPWM()
|
||||
}
|
||||
}
|
||||
65
SwiftyCar/Sources/SwiftyCar/Vehicle.swift
Normal file
65
SwiftyCar/Sources/SwiftyCar/Vehicle.swift
Normal file
@@ -0,0 +1,65 @@
|
||||
//
|
||||
// Vehicle.swift
|
||||
//
|
||||
//
|
||||
// Created by Michael Pivato on 8/5/20.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftyGPIO
|
||||
|
||||
protocol Vehicle{
|
||||
mutating func move2D(magnitude: Float, angle: Float)
|
||||
}
|
||||
|
||||
protocol Vehicle2D: Vehicle{
|
||||
var throttle: Float {get set}
|
||||
var steering: Float {get set}
|
||||
}
|
||||
|
||||
class MockVehicle: Vehicle2D{
|
||||
var throttle: Float = 0
|
||||
var steering: Float = 0
|
||||
|
||||
func move2D(magnitude: Float, angle: Float) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class RPiVehicle2D: Vehicle2D{
|
||||
public let pwmThrottle: Servo
|
||||
public let pwmSteering: Servo
|
||||
|
||||
var throttle: Float{
|
||||
get{
|
||||
return pwmThrottle.value
|
||||
}
|
||||
set(value){
|
||||
pwmThrottle.value = value
|
||||
}
|
||||
}
|
||||
|
||||
var steering: Float{
|
||||
get{
|
||||
return pwmSteering.value
|
||||
}
|
||||
set(value){
|
||||
pwmSteering.value = value
|
||||
}
|
||||
}
|
||||
|
||||
init(withThrottlePin: Servo, withSteeringPin: Servo){
|
||||
pwmThrottle = withThrottlePin
|
||||
pwmSteering = withSteeringPin
|
||||
}
|
||||
|
||||
func move2D(magnitude: Float, angle: Float) {
|
||||
|
||||
}
|
||||
|
||||
func stop(){
|
||||
pwmThrottle.detach()
|
||||
pwmSteering.detach()
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,5 +6,6 @@
|
||||
//
|
||||
|
||||
// Entry-Point to the Swift Car Controller
|
||||
print("Hello World!")
|
||||
var text = "Hello World!"
|
||||
print(text)
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ final class SwiftyCarTests: XCTestCase {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct
|
||||
// results.
|
||||
XCTAssertEqual(SwiftyCar().text, "Hello, World!")
|
||||
// XCTAssertEqual(SwiftyCar().text, "Hello, World!")
|
||||
}
|
||||
|
||||
static var allTests = [
|
||||
|
||||
@@ -12,5 +12,5 @@ dependencies {
|
||||
task copySwiftCode(type: Copy, dependsOn: configurations.swift) {
|
||||
// Copy python protobuf code from proto project.
|
||||
from zipTree(configurations.swift.asPath)
|
||||
into './Sources/Generated'
|
||||
into './Sources/SwiftyCar'
|
||||
}
|
||||
Reference in New Issue
Block a user