iOS simple controller works
This commit is contained in:
4
.vscode/launch.json
vendored
4
.vscode/launch.json
vendored
@@ -10,9 +10,9 @@
|
|||||||
"request": "launch",
|
"request": "launch",
|
||||||
"module": "car",
|
"module": "car",
|
||||||
"env": {
|
"env": {
|
||||||
"CAR_LIDAR": "LIDAR_RPLIDAR",
|
"CAR_LIDAR": "LIDAR_MOCK",
|
||||||
"CAR_VEHICLE": "CAR_MOCK",
|
"CAR_VEHICLE": "CAR_MOCK",
|
||||||
"LIDAR_DEVICE": "/dev/tty.usbserial-0001"
|
// "LIDAR_DEVICE": "/dev/tty.usbserial-0001"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -24,8 +24,8 @@
|
|||||||
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
"repositoryURL": "https://github.com/apple/swift-nio.git",
|
||||||
"state": {
|
"state": {
|
||||||
"branch": null,
|
"branch": null,
|
||||||
"revision": "e876fb37410e0036b98b5361bb18e6854739572b",
|
"revision": "40bdad80882d307abe2c0bb36cf3bd4d3e03fe04",
|
||||||
"version": "2.16.0"
|
"version": "2.16.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -15,16 +15,26 @@ struct ContentView: View {
|
|||||||
NavigationLink(destination: SimpleControllerView()){
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
Text("Simple Controller")
|
Text("Simple Controller")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Change these when other functionality is implemented
|
// TODO: Change these when other functionality is implemented
|
||||||
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
|
Text("Gamepad controller")
|
||||||
|
}
|
||||||
NavigationLink(destination: SimpleControllerView()){
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
Text("SLAM Controller")
|
Text("SLAM Controller")
|
||||||
}
|
}
|
||||||
NavigationLink(destination: SimpleControllerView()){
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
Text("Tracking Controller")
|
Text("Lidar Tracking Controller")
|
||||||
|
}
|
||||||
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
|
Text("Mono Camera Tracking Controller")
|
||||||
|
}
|
||||||
|
NavigationLink(destination: SimpleControllerView()){
|
||||||
|
Text("Hybrid Lidar Camera Tracking Controller")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.navigationBarTitle(Text("Controllers"))
|
||||||
}
|
}
|
||||||
|
.navigationViewStyle(StackNavigationViewStyle())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,33 +15,25 @@ class PiLoader: ObservableObject {
|
|||||||
// Find a cleaner way to handle these properties
|
// Find a cleaner way to handle these properties
|
||||||
@Published var throttle: Float = 0.5
|
@Published var throttle: Float = 0.5
|
||||||
@Published var steering: Float = 0.5
|
@Published var steering: Float = 0.5
|
||||||
let port: Int = 50051
|
|
||||||
var stopped = false
|
var stopped = false
|
||||||
|
|
||||||
func makeClient(port: Int, group: EventLoopGroup) -> MotorControl_CarControlClient {
|
func startUpdating(forPort port: Int, atHost host: String) {
|
||||||
let channel = ClientConnection.insecure(group: group)
|
DispatchQueue.global(qos: .background).async{
|
||||||
// TODO: Pass the host in based on the settings.
|
let group = PlatformSupport.makeEventLoopGroup(loopCount: 1)
|
||||||
.connect(host: "10.0.0.55", port: port)
|
|
||||||
|
|
||||||
return MotorControl_CarControlClient(channel: channel)
|
|
||||||
}
|
|
||||||
|
|
||||||
func startUpdating() {
|
|
||||||
let group = MultiThreadedEventLoopGroup(numberOfThreads: 1)
|
|
||||||
defer {
|
defer {
|
||||||
try? group.syncShutdownGracefully()
|
try? group.syncShutdownGracefully()
|
||||||
}
|
}
|
||||||
let client = makeClient(port: self.port, group: group)
|
let client = makeClient(port: port, host: host, group: group)
|
||||||
let options = CallOptions(timeout: .seconds(rounding: 10))
|
let options = CallOptions(timeout: .seconds(rounding: 10))
|
||||||
let call = client.stream_vehicle_2d(callOptions: options)
|
let call = client.stream_vehicle_2d(callOptions: options)
|
||||||
|
|
||||||
call.response.whenFailure { error in
|
call.response.whenFailure { error in
|
||||||
print("RecordRoute Failed: \(error)")
|
print("Failed: \(error)")
|
||||||
self.stop()
|
self.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
call.status.whenComplete { _ in
|
call.status.whenComplete { _ in
|
||||||
print("Finished RecordRoute")
|
print("Finished")
|
||||||
self.stop()
|
self.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,8 +41,6 @@ class PiLoader: ObservableObject {
|
|||||||
print("Finished")
|
print("Finished")
|
||||||
self.stop()
|
self.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
DispatchQueue.global(qos: .userInteractive).async{
|
|
||||||
// Running in background. Do the update thread stuff.
|
// Running in background. Do the update thread stuff.
|
||||||
while (!self.stopped){
|
while (!self.stopped){
|
||||||
call.sendMessage(self.createProto(), promise: nil)
|
call.sendMessage(self.createProto(), promise: nil)
|
||||||
@@ -74,3 +64,12 @@ class PiLoader: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func makeClient(port: Int, host: String, group: EventLoopGroup) -> MotorControl_CarControlClient {
|
||||||
|
let channel = ClientConnection.insecure(group: group)
|
||||||
|
// TODO: Pass the host in based on the settings.
|
||||||
|
.connect(host: host, port: port)
|
||||||
|
|
||||||
|
|
||||||
|
return MotorControl_CarControlClient(channel: channel)
|
||||||
|
}
|
||||||
|
|||||||
@@ -8,20 +8,18 @@
|
|||||||
|
|
||||||
import Foundation
|
import Foundation
|
||||||
|
|
||||||
|
struct UserKeys{
|
||||||
|
static let host = "host"
|
||||||
|
static let port = "port"
|
||||||
|
}
|
||||||
|
|
||||||
final class ServerData: ObservableObject{
|
final class ServerData: ObservableObject{
|
||||||
|
|
||||||
// TODO: Find a way to save/represent this stuff in iOS settings/preferences (user can access via settings app).
|
@Published var host: String = UserDefaults.standard.string(forKey: UserKeys.host) ?? "10.0.0.53"
|
||||||
// Then load the below values ar runtime, from settings.
|
@Published var grpcPort: Int = UserDefaults.standard.integer(forKey: UserKeys.port)
|
||||||
// Ideally want to be able to open this settings page from within the app as well.
|
|
||||||
|
|
||||||
|
|
||||||
@Published var port: Int = 50051
|
|
||||||
@Published var grpcPort: Int = 50050
|
|
||||||
@Published var host: String = "10.0.0.53"
|
|
||||||
|
|
||||||
func load(){
|
func load(){
|
||||||
// Load the server values from settings, if they had been
|
// Load the server values from settings, if they had been previously saved.
|
||||||
// previously saved.
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func save(){
|
func save(){
|
||||||
|
|||||||
@@ -6,23 +6,15 @@
|
|||||||
<string>Root</string>
|
<string>Root</string>
|
||||||
<key>PreferenceSpecifiers</key>
|
<key>PreferenceSpecifiers</key>
|
||||||
<array>
|
<array>
|
||||||
<dict>
|
|
||||||
<key>Type</key>
|
|
||||||
<string>PSGroupSpecifier</string>
|
|
||||||
<key>Title</key>
|
|
||||||
<string>Group</string>
|
|
||||||
</dict>
|
|
||||||
<dict>
|
<dict>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSTextFieldSpecifier</string>
|
<string>PSTextFieldSpecifier</string>
|
||||||
<key>Title</key>
|
<key>Title</key>
|
||||||
<string>Name</string>
|
<string>Host</string>
|
||||||
<key>Key</key>
|
<key>Key</key>
|
||||||
<string>name_preference</string>
|
<string>host</string>
|
||||||
<key>DefaultValue</key>
|
<key>DefaultValue</key>
|
||||||
<string></string>
|
<string>10.0.0.53</string>
|
||||||
<key>IsSecure</key>
|
|
||||||
<false/>
|
|
||||||
<key>KeyboardType</key>
|
<key>KeyboardType</key>
|
||||||
<string>Alphabet</string>
|
<string>Alphabet</string>
|
||||||
<key>AutocapitalizationType</key>
|
<key>AutocapitalizationType</key>
|
||||||
@@ -32,29 +24,17 @@
|
|||||||
</dict>
|
</dict>
|
||||||
<dict>
|
<dict>
|
||||||
<key>Type</key>
|
<key>Type</key>
|
||||||
<string>PSToggleSwitchSpecifier</string>
|
<string>PSTextFieldSpecifier</string>
|
||||||
<key>Title</key>
|
<key>Title</key>
|
||||||
<string>Enabled</string>
|
<string>Port</string>
|
||||||
<key>Key</key>
|
<key>Key</key>
|
||||||
<string>enabled_preference</string>
|
<string>port</string>
|
||||||
<key>DefaultValue</key>
|
<key>DefaultValue</key>
|
||||||
<true/>
|
<string>50051</string>
|
||||||
</dict>
|
<key>IsSecure</key>
|
||||||
<dict>
|
<false/>
|
||||||
<key>Type</key>
|
<key>KeyboardType</key>
|
||||||
<string>PSSliderSpecifier</string>
|
<string>NumberPad</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>
|
</dict>
|
||||||
</array>
|
</array>
|
||||||
</dict>
|
</dict>
|
||||||
|
|||||||
@@ -10,30 +10,36 @@ import SwiftUI
|
|||||||
|
|
||||||
struct SimpleControllerView: View {
|
struct SimpleControllerView: View {
|
||||||
@EnvironmentObject var server: ServerData
|
@EnvironmentObject var server: ServerData
|
||||||
|
|
||||||
// Need lazy so that we can initialise with local properties.
|
|
||||||
@ObservedObject var grpcController: PiLoader = PiLoader()
|
@ObservedObject var grpcController: PiLoader = PiLoader()
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
VStack (alignment: .trailing, spacing: 0){
|
||||||
|
Spacer()
|
||||||
HStack{
|
HStack{
|
||||||
Slider(value: $grpcController.throttle, in: 0...1){_ in
|
// Move this up a bit, due to being rotated.
|
||||||
|
Slider(value: self.$grpcController.throttle, in: 0...1){_ in
|
||||||
self.grpcController.throttle = 0.5
|
self.grpcController.throttle = 0.5
|
||||||
}
|
}
|
||||||
|
.offset(x: 200)
|
||||||
|
.frame(width: 300)
|
||||||
.rotationEffect(.degrees(270))
|
.rotationEffect(.degrees(270))
|
||||||
|
Spacer()
|
||||||
Slider(value: $grpcController.steering, in: 0...1){_ in
|
Slider(value: self.$grpcController.steering, in: 0...1){_ in
|
||||||
self.grpcController.steering = 0.5
|
self.grpcController.steering = 0.5
|
||||||
}
|
}
|
||||||
|
.frame(width:300)
|
||||||
|
.offset(x: -50, y: -200)
|
||||||
|
.padding()
|
||||||
}
|
}
|
||||||
.onAppear(){
|
.onAppear(){
|
||||||
self.grpcController.startUpdating()
|
self.grpcController.startUpdating(forPort: self.server.grpcPort, atHost: self.server.host)
|
||||||
}
|
}
|
||||||
.onDisappear(){
|
.onDisappear(){
|
||||||
// Stop the gRPC updater.
|
|
||||||
self.grpcController.stop()
|
self.grpcController.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct SimpleControllerView_Previews: PreviewProvider {
|
struct SimpleControllerView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
|||||||
Reference in New Issue
Block a user