diff --git a/.gitignore b/.gitignore index bd3e4d6..fa18d1a 100644 --- a/.gitignore +++ b/.gitignore @@ -28,6 +28,7 @@ SwiftyCar/Packages SwiftyCar/*.xcodeproj SwiftyCar/Sources/Generated SwiftyCar/xcuserdata/ +SwiftyCar/Sources/SwiftyCar/car xcuserdata/ .settings .project diff --git a/SwiftyCar/Package.swift b/SwiftyCar/Package.swift index 99a62f3..93417da 100644 --- a/SwiftyCar/Package.swift +++ b/SwiftyCar/Package.swift @@ -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"]), diff --git a/SwiftyCar/Sources/SwiftyCar/MotorProvider.swift b/SwiftyCar/Sources/SwiftyCar/MotorProvider.swift new file mode 100644 index 0000000..fadc40a --- /dev/null +++ b/SwiftyCar/Sources/SwiftyCar/MotorProvider.swift @@ -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 { + self.vehicle.throttle = request.throttle + return context.eventLoop.makeSucceededFuture(.with{ + $0.throttleSet = true + }) + } + + func set_steering(request: MotorControl_SteeringRequest, context: StatusOnlyCallContext) -> EventLoopFuture { + self.vehicle.steering = request.steering + return context.eventLoop.makeSucceededFuture(.with{ + $0.steeringSet = true + }) + } + + func stream_vehicle_2d(context: UnaryResponseCallContext) -> EventLoopFuture<(StreamEvent) -> 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 { + // TODO: Recording... + return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty()) + } + + func save_recorded_data(request: MotorControl_SaveRequest, context: StatusOnlyCallContext) -> EventLoopFuture { + // TODO Recording... + return context.eventLoop.makeSucceededFuture(Google_Protobuf_Empty()) + } +} + diff --git a/SwiftyCar/Sources/SwiftyCar/PwmWrappers.swift b/SwiftyCar/Sources/SwiftyCar/PwmWrappers.swift new file mode 100644 index 0000000..688351e --- /dev/null +++ b/SwiftyCar/Sources/SwiftyCar/PwmWrappers.swift @@ -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() + } +} diff --git a/SwiftyCar/Sources/SwiftyCar/Vehicle.swift b/SwiftyCar/Sources/SwiftyCar/Vehicle.swift new file mode 100644 index 0000000..957bc15 --- /dev/null +++ b/SwiftyCar/Sources/SwiftyCar/Vehicle.swift @@ -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() + } + +} diff --git a/SwiftyCar/Sources/SwiftyCar/main.swift b/SwiftyCar/Sources/SwiftyCar/main.swift index 115b18d..6572f74 100644 --- a/SwiftyCar/Sources/SwiftyCar/main.swift +++ b/SwiftyCar/Sources/SwiftyCar/main.swift @@ -6,5 +6,6 @@ // // Entry-Point to the Swift Car Controller -print("Hello World!") +var text = "Hello World!" +print(text) diff --git a/SwiftyCar/Tests/SwiftyCarTests/SwiftyCarTests.swift b/SwiftyCar/Tests/SwiftyCarTests/SwiftyCarTests.swift index 2d727ae..bebfbd1 100644 --- a/SwiftyCar/Tests/SwiftyCarTests/SwiftyCarTests.swift +++ b/SwiftyCar/Tests/SwiftyCarTests/SwiftyCarTests.swift @@ -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 = [ diff --git a/SwiftyCar/build.gradle b/SwiftyCar/build.gradle index 158e34f..38b27e0 100644 --- a/SwiftyCar/build.gradle +++ b/SwiftyCar/build.gradle @@ -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' } \ No newline at end of file