Simple Controller UI/gRPC working
This commit is contained in:
@@ -21,7 +21,9 @@
|
|||||||
5A9EB280240100970053D3CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB27F240100970053D3CF /* Assets.xcassets */; };
|
5A9EB280240100970053D3CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB27F240100970053D3CF /* Assets.xcassets */; };
|
||||||
5A9EB283240100970053D3CF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB282240100970053D3CF /* Preview Assets.xcassets */; };
|
5A9EB283240100970053D3CF /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB282240100970053D3CF /* Preview Assets.xcassets */; };
|
||||||
5A9EB286240100970053D3CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB284240100970053D3CF /* LaunchScreen.storyboard */; };
|
5A9EB286240100970053D3CF /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB284240100970053D3CF /* LaunchScreen.storyboard */; };
|
||||||
5AE45131245CFF2800D82BAF /* BuildFile in Frameworks */ = {isa = PBXBuildFile; productRef = 5AE45130245CFF2800D82BAF /* SwiftPackageProductDependency */; };
|
5AC1297B245EF22700CC19C3 /* Settings.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 5AC1297A245EF22700CC19C3 /* Settings.bundle */; };
|
||||||
|
5AC1297D245FB95000CC19C3 /* PiLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5AC1297C245FB95000CC19C3 /* PiLoader.swift */; };
|
||||||
|
5AE45131245CFF2800D82BAF /* GRPC in Frameworks */ = {isa = PBXBuildFile; productRef = 5AE45130245CFF2800D82BAF /* GRPC */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
@@ -41,6 +43,8 @@
|
|||||||
5A9EB282240100970053D3CF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
5A9EB282240100970053D3CF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = "<group>"; };
|
||||||
5A9EB285240100970053D3CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
5A9EB285240100970053D3CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||||
5A9EB287240100970053D3CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
5A9EB287240100970053D3CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||||
|
5AC1297A245EF22700CC19C3 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = "<group>"; };
|
||||||
|
5AC1297C245FB95000CC19C3 /* PiLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiLoader.swift; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -48,7 +52,7 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
5AE45131245CFF2800D82BAF /* BuildFile in Frameworks */,
|
5AE45131245CFF2800D82BAF /* GRPC in Frameworks */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -122,6 +126,8 @@
|
|||||||
5A9EB281240100970053D3CF /* Preview Content */,
|
5A9EB281240100970053D3CF /* Preview Content */,
|
||||||
5A9C27122443F52500DBDF12 /* SimpleControllerView.swift */,
|
5A9C27122443F52500DBDF12 /* SimpleControllerView.swift */,
|
||||||
5A9C27142443F5B500DBDF12 /* ServerData.swift */,
|
5A9C27142443F5B500DBDF12 /* ServerData.swift */,
|
||||||
|
5AC1297A245EF22700CC19C3 /* Settings.bundle */,
|
||||||
|
5AC1297C245FB95000CC19C3 /* PiLoader.swift */,
|
||||||
);
|
);
|
||||||
path = CarController;
|
path = CarController;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -151,7 +157,7 @@
|
|||||||
);
|
);
|
||||||
name = CarController;
|
name = CarController;
|
||||||
packageProductDependencies = (
|
packageProductDependencies = (
|
||||||
5AE45130245CFF2800D82BAF /* SwiftPackageProductDependency */,
|
5AE45130245CFF2800D82BAF /* GRPC */,
|
||||||
);
|
);
|
||||||
productName = CarController;
|
productName = CarController;
|
||||||
productReference = 5A9EB276240100960053D3CF /* CarController.app */;
|
productReference = 5A9EB276240100960053D3CF /* CarController.app */;
|
||||||
@@ -182,7 +188,7 @@
|
|||||||
);
|
);
|
||||||
mainGroup = 5A9EB26D240100950053D3CF;
|
mainGroup = 5A9EB26D240100950053D3CF;
|
||||||
packageReferences = (
|
packageReferences = (
|
||||||
5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */,
|
5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */,
|
||||||
);
|
);
|
||||||
productRefGroup = 5A9EB277240100960053D3CF /* Products */;
|
productRefGroup = 5A9EB277240100960053D3CF /* Products */;
|
||||||
projectDirPath = "";
|
projectDirPath = "";
|
||||||
@@ -198,6 +204,7 @@
|
|||||||
isa = PBXResourcesBuildPhase;
|
isa = PBXResourcesBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
5AC1297B245EF22700CC19C3 /* Settings.bundle in Resources */,
|
||||||
5A9EB286240100970053D3CF /* LaunchScreen.storyboard in Resources */,
|
5A9EB286240100970053D3CF /* LaunchScreen.storyboard in Resources */,
|
||||||
5A9EB283240100970053D3CF /* Preview Assets.xcassets in Resources */,
|
5A9EB283240100970053D3CF /* Preview Assets.xcassets in Resources */,
|
||||||
5A9EB280240100970053D3CF /* Assets.xcassets in Resources */,
|
5A9EB280240100970053D3CF /* Assets.xcassets in Resources */,
|
||||||
@@ -216,6 +223,7 @@
|
|||||||
5A6B34552459AFCE0000E6FC /* SlamController.grpc.swift in Sources */,
|
5A6B34552459AFCE0000E6FC /* SlamController.grpc.swift in Sources */,
|
||||||
5A6B34562459AFCE0000E6FC /* SlamController.pb.swift in Sources */,
|
5A6B34562459AFCE0000E6FC /* SlamController.pb.swift in Sources */,
|
||||||
5A6B34592459AFCE0000E6FC /* motorService.grpc.swift in Sources */,
|
5A6B34592459AFCE0000E6FC /* motorService.grpc.swift in Sources */,
|
||||||
|
5AC1297D245FB95000CC19C3 /* PiLoader.swift in Sources */,
|
||||||
5A9EB27A240100960053D3CF /* AppDelegate.swift in Sources */,
|
5A9EB27A240100960053D3CF /* AppDelegate.swift in Sources */,
|
||||||
5A6B345A2459AFCE0000E6FC /* motorService.pb.swift in Sources */,
|
5A6B345A2459AFCE0000E6FC /* motorService.pb.swift in Sources */,
|
||||||
5A6B34582459AFCE0000E6FC /* lidar_tracker.grpc.swift in Sources */,
|
5A6B34582459AFCE0000E6FC /* lidar_tracker.grpc.swift in Sources */,
|
||||||
@@ -417,7 +425,7 @@
|
|||||||
/* End XCConfigurationList section */
|
/* End XCConfigurationList section */
|
||||||
|
|
||||||
/* Begin XCRemoteSwiftPackageReference section */
|
/* Begin XCRemoteSwiftPackageReference section */
|
||||||
5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */ = {
|
5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */ = {
|
||||||
isa = XCRemoteSwiftPackageReference;
|
isa = XCRemoteSwiftPackageReference;
|
||||||
repositoryURL = "https://github.com/grpc/grpc-swift.git";
|
repositoryURL = "https://github.com/grpc/grpc-swift.git";
|
||||||
requirement = {
|
requirement = {
|
||||||
@@ -428,9 +436,9 @@
|
|||||||
/* End XCRemoteSwiftPackageReference section */
|
/* End XCRemoteSwiftPackageReference section */
|
||||||
|
|
||||||
/* Begin XCSwiftPackageProductDependency section */
|
/* Begin XCSwiftPackageProductDependency section */
|
||||||
5AE45130245CFF2800D82BAF /* SwiftPackageProductDependency */ = {
|
5AE45130245CFF2800D82BAF /* GRPC */ = {
|
||||||
isa = XCSwiftPackageProductDependency;
|
isa = XCSwiftPackageProductDependency;
|
||||||
package = 5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */;
|
package = 5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */;
|
||||||
productName = GRPC;
|
productName = GRPC;
|
||||||
};
|
};
|
||||||
/* End XCSwiftPackageProductDependency section */
|
/* End XCSwiftPackageProductDependency section */
|
||||||
|
|||||||
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{
|
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.
|
// 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
|
@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 {
|
struct SimpleControllerView: View {
|
||||||
@EnvironmentObject var server: ServerData
|
@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 {
|
var body: some View {
|
||||||
HStack{
|
HStack{
|
||||||
Slider(value: $throttle, in: 0...1){_ in
|
Slider(value: $grpcController.throttle, in: 0...1){_ in
|
||||||
self.throttle = 0.5}
|
self.grpcController.throttle = 0.5
|
||||||
|
}
|
||||||
.rotationEffect(.degrees(270))
|
.rotationEffect(.degrees(270))
|
||||||
|
|
||||||
Slider(value: $steering, in: 0...1){
|
Slider(value: $grpcController.steering, in: 0...1){_ in
|
||||||
_ in self.steering = 0.5
|
self.grpcController.steering = 0.5
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear(){
|
.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(){
|
.onDisappear(){
|
||||||
|
// Stop the gRPC updater.
|
||||||
|
self.grpcController.stop()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,5 +12,5 @@ dependencies {
|
|||||||
task copySwiftCode(type: Copy, dependsOn: configurations.swift) {
|
task copySwiftCode(type: Copy, dependsOn: configurations.swift) {
|
||||||
// Copy python protobuf code from proto project.
|
// Copy python protobuf code from proto project.
|
||||||
from zipTree(configurations.swift.asPath)
|
from zipTree(configurations.swift.asPath)
|
||||||
into './CarController/CarController'
|
into './CarController'
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user