Simple Controller UI/gRPC working
This commit is contained in:
76
CarControlleriOS/CarController/PiLoader.swift
Normal file
76
CarControlleriOS/CarController/PiLoader.swift
Normal file
@@ -0,0 +1,76 @@
|
||||
//
|
||||
// PiLoader.swift
|
||||
// CarController
|
||||
//
|
||||
// Created by Michael Pivato on 4/5/20.
|
||||
// Copyright © 2020 Michael Pivato. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
import GRPC
|
||||
import NIO
|
||||
|
||||
class PiLoader: ObservableObject {
|
||||
// Find a cleaner way to handle these properties
|
||||
@Published var throttle: Float = 0.5
|
||||
@Published var steering: Float = 0.5
|
||||
let port: Int = 50051
|
||||
var stopped = false
|
||||
|
||||
func makeClient(port: Int, group: EventLoopGroup) -> MotorControl_CarControlClient {
|
||||
let channel = ClientConnection.insecure(group: group)
|
||||
// TODO: Pass the host in based on the settings.
|
||||
.connect(host: "10.0.0.55", port: port)
|
||||
|
||||
return MotorControl_CarControlClient(channel: channel)
|
||||
}
|
||||
|
||||
func startUpdating() {
|
||||
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
||||
defer {
|
||||
try? group.syncShutdownGracefully()
|
||||
}
|
||||
let client = makeClient(port: self.port, group: group)
|
||||
let options = CallOptions(timeout: .seconds(rounding: 10))
|
||||
let call = client.stream_vehicle_2d(callOptions: options)
|
||||
|
||||
call.response.whenFailure { error in
|
||||
print("RecordRoute Failed: \(error)")
|
||||
self.stop()
|
||||
}
|
||||
|
||||
call.status.whenComplete { _ in
|
||||
print("Finished RecordRoute")
|
||||
self.stop()
|
||||
}
|
||||
|
||||
call.response.whenSuccess { summary in
|
||||
print("Finished")
|
||||
self.stop()
|
||||
}
|
||||
|
||||
DispatchQueue.global(qos: .userInteractive).async{
|
||||
// Running in background. Do the update thread stuff.
|
||||
while (!self.stopped){
|
||||
call.sendMessage(self.createProto(), promise: nil)
|
||||
Thread.sleep(forTimeInterval: 0.2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func stop(){
|
||||
stopped = true
|
||||
}
|
||||
|
||||
func createProto() -> MotorControl_Vehicle2DRequest {
|
||||
return .with{
|
||||
$0.throttle = .with{
|
||||
$0.throttle = self.throttle
|
||||
}
|
||||
$0.steering = .with{
|
||||
$0.steering = self.steering
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,9 @@ import Foundation
|
||||
|
||||
final class ServerData: ObservableObject{
|
||||
|
||||
// TODO: Find a way to save/represent this stuff in iOS settings (user can access via settings app).
|
||||
// TODO: Find a way to save/represent this stuff in iOS settings/preferences (user can access via settings app).
|
||||
// Then load the below values ar runtime, from settings.
|
||||
// Ideally want to be able to open this settings page from within the app as well.
|
||||
|
||||
|
||||
@Published var port: Int = 50051
|
||||
|
||||
61
CarControlleriOS/CarController/Settings.bundle/Root.plist
Normal file
61
CarControlleriOS/CarController/Settings.bundle/Root.plist
Normal file
@@ -0,0 +1,61 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>StringsTable</key>
|
||||
<string>Root</string>
|
||||
<key>PreferenceSpecifiers</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSGroupSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Group</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSTextFieldSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Name</string>
|
||||
<key>Key</key>
|
||||
<string>name_preference</string>
|
||||
<key>DefaultValue</key>
|
||||
<string></string>
|
||||
<key>IsSecure</key>
|
||||
<false/>
|
||||
<key>KeyboardType</key>
|
||||
<string>Alphabet</string>
|
||||
<key>AutocapitalizationType</key>
|
||||
<string>None</string>
|
||||
<key>AutocorrectionType</key>
|
||||
<string>No</string>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSToggleSwitchSpecifier</string>
|
||||
<key>Title</key>
|
||||
<string>Enabled</string>
|
||||
<key>Key</key>
|
||||
<string>enabled_preference</string>
|
||||
<key>DefaultValue</key>
|
||||
<true/>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>Type</key>
|
||||
<string>PSSliderSpecifier</string>
|
||||
<key>Key</key>
|
||||
<string>slider_preference</string>
|
||||
<key>DefaultValue</key>
|
||||
<real>0.5</real>
|
||||
<key>MinimumValue</key>
|
||||
<integer>0</integer>
|
||||
<key>MaximumValue</key>
|
||||
<integer>1</integer>
|
||||
<key>MinimumValueImage</key>
|
||||
<string></string>
|
||||
<key>MaximumValueImage</key>
|
||||
<string></string>
|
||||
</dict>
|
||||
</array>
|
||||
</dict>
|
||||
</plist>
|
||||
Binary file not shown.
@@ -10,25 +10,27 @@ import SwiftUI
|
||||
|
||||
struct SimpleControllerView: View {
|
||||
@EnvironmentObject var server: ServerData
|
||||
@State var throttle: Float = 0.5
|
||||
@State var steering: Float = 0.5
|
||||
|
||||
|
||||
// Need lazy so that we can initialise with local properties.
|
||||
@ObservedObject var grpcController: PiLoader = PiLoader()
|
||||
|
||||
var body: some View {
|
||||
HStack{
|
||||
Slider(value: $throttle, in: 0...1){_ in
|
||||
self.throttle = 0.5}
|
||||
Slider(value: $grpcController.throttle, in: 0...1){_ in
|
||||
self.grpcController.throttle = 0.5
|
||||
}
|
||||
.rotationEffect(.degrees(270))
|
||||
|
||||
Slider(value: $steering, in: 0...1){
|
||||
_ in self.steering = 0.5
|
||||
|
||||
Slider(value: $grpcController.steering, in: 0...1){_ in
|
||||
self.grpcController.steering = 0.5
|
||||
}
|
||||
}
|
||||
.onAppear(){
|
||||
// Start the gRPC updater. Should be in a separate class/struct to be easier to implement for SLAM Controller after.
|
||||
self.grpcController.startUpdating()
|
||||
}
|
||||
.onDisappear(){
|
||||
|
||||
// Stop the gRPC updater.
|
||||
self.grpcController.stop()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user