diff --git a/.vscode/launch.json b/.vscode/launch.json index ffe4adf..7c79804 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -10,9 +10,9 @@ "request": "launch", "module": "car", "env": { - "CAR_LIDAR": "LIDAR_RPLIDAR", + "CAR_LIDAR": "LIDAR_MOCK", "CAR_VEHICLE": "CAR_MOCK", - "LIDAR_DEVICE": "/dev/tty.usbserial-0001" + // "LIDAR_DEVICE": "/dev/tty.usbserial-0001" } }, { diff --git a/CarControlleriOS/CarController.xcodeproj/project.pbxproj b/CarControlleriOS/CarController.xcodeproj/project.pbxproj index ab9af95..bfa7185 100644 --- a/CarControlleriOS/CarController.xcodeproj/project.pbxproj +++ b/CarControlleriOS/CarController.xcodeproj/project.pbxproj @@ -21,7 +21,9 @@ 5A9EB280240100970053D3CF /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5A9EB27F240100970053D3CF /* 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 */; }; - 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 */ /* Begin PBXFileReference section */ @@ -41,6 +43,8 @@ 5A9EB282240100970053D3CF /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; 5A9EB285240100970053D3CF /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 5A9EB287240100970053D3CF /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5AC1297A245EF22700CC19C3 /* Settings.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; path = Settings.bundle; sourceTree = ""; }; + 5AC1297C245FB95000CC19C3 /* PiLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiLoader.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -48,7 +52,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( - 5AE45131245CFF2800D82BAF /* BuildFile in Frameworks */, + 5AE45131245CFF2800D82BAF /* GRPC in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -122,6 +126,8 @@ 5A9EB281240100970053D3CF /* Preview Content */, 5A9C27122443F52500DBDF12 /* SimpleControllerView.swift */, 5A9C27142443F5B500DBDF12 /* ServerData.swift */, + 5AC1297A245EF22700CC19C3 /* Settings.bundle */, + 5AC1297C245FB95000CC19C3 /* PiLoader.swift */, ); path = CarController; sourceTree = ""; @@ -151,7 +157,7 @@ ); name = CarController; packageProductDependencies = ( - 5AE45130245CFF2800D82BAF /* SwiftPackageProductDependency */, + 5AE45130245CFF2800D82BAF /* GRPC */, ); productName = CarController; productReference = 5A9EB276240100960053D3CF /* CarController.app */; @@ -182,7 +188,7 @@ ); mainGroup = 5A9EB26D240100950053D3CF; packageReferences = ( - 5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */, + 5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */, ); productRefGroup = 5A9EB277240100960053D3CF /* Products */; projectDirPath = ""; @@ -198,6 +204,7 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 5AC1297B245EF22700CC19C3 /* Settings.bundle in Resources */, 5A9EB286240100970053D3CF /* LaunchScreen.storyboard in Resources */, 5A9EB283240100970053D3CF /* Preview Assets.xcassets in Resources */, 5A9EB280240100970053D3CF /* Assets.xcassets in Resources */, @@ -216,6 +223,7 @@ 5A6B34552459AFCE0000E6FC /* SlamController.grpc.swift in Sources */, 5A6B34562459AFCE0000E6FC /* SlamController.pb.swift in Sources */, 5A6B34592459AFCE0000E6FC /* motorService.grpc.swift in Sources */, + 5AC1297D245FB95000CC19C3 /* PiLoader.swift in Sources */, 5A9EB27A240100960053D3CF /* AppDelegate.swift in Sources */, 5A6B345A2459AFCE0000E6FC /* motorService.pb.swift in Sources */, 5A6B34582459AFCE0000E6FC /* lidar_tracker.grpc.swift in Sources */, @@ -417,7 +425,7 @@ /* End XCConfigurationList section */ /* Begin XCRemoteSwiftPackageReference section */ - 5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */ = { + 5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */ = { isa = XCRemoteSwiftPackageReference; repositoryURL = "https://github.com/grpc/grpc-swift.git"; requirement = { @@ -428,9 +436,9 @@ /* End XCRemoteSwiftPackageReference section */ /* Begin XCSwiftPackageProductDependency section */ - 5AE45130245CFF2800D82BAF /* SwiftPackageProductDependency */ = { + 5AE45130245CFF2800D82BAF /* GRPC */ = { isa = XCSwiftPackageProductDependency; - package = 5AE4512F245CFF2800D82BAF /* RemoteSwiftPackageReference */; + package = 5AE4512F245CFF2800D82BAF /* XCRemoteSwiftPackageReference "grpc-swift" */; productName = GRPC; }; /* End XCSwiftPackageProductDependency section */ diff --git a/CarControlleriOS/CarController.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/CarControlleriOS/CarController.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved index c90f41d..12fe0a3 100644 --- a/CarControlleriOS/CarController.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved +++ b/CarControlleriOS/CarController.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -24,8 +24,8 @@ "repositoryURL": "https://github.com/apple/swift-nio.git", "state": { "branch": null, - "revision": "e876fb37410e0036b98b5361bb18e6854739572b", - "version": "2.16.0" + "revision": "40bdad80882d307abe2c0bb36cf3bd4d3e03fe04", + "version": "2.16.1" } }, { diff --git a/CarControlleriOS/CarController/ContentView.swift b/CarControlleriOS/CarController/ContentView.swift index 5f8de90..7044469 100644 --- a/CarControlleriOS/CarController/ContentView.swift +++ b/CarControlleriOS/CarController/ContentView.swift @@ -15,16 +15,26 @@ struct ContentView: View { NavigationLink(destination: SimpleControllerView()){ Text("Simple Controller") } - // TODO: Change these when other functionality is implemented + NavigationLink(destination: SimpleControllerView()){ + Text("Gamepad controller") + } NavigationLink(destination: SimpleControllerView()){ Text("SLAM Controller") } 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()) } } diff --git a/CarControlleriOS/CarController/PiLoader.swift b/CarControlleriOS/CarController/PiLoader.swift new file mode 100644 index 0000000..1c42a58 --- /dev/null +++ b/CarControlleriOS/CarController/PiLoader.swift @@ -0,0 +1,75 @@ +// +// 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 + var stopped = false + + func startUpdating(forPort port: Int, atHost host: String) { + DispatchQueue.global(qos: .background).async{ + let group = PlatformSupport.makeEventLoopGroup(loopCount: 1) + defer { + try? group.syncShutdownGracefully() + } + let client = makeClient(port: port, host: host, group: group) + let options = CallOptions(timeout: .seconds(rounding: 10)) + let call = client.stream_vehicle_2d(callOptions: options) + + call.response.whenFailure { error in + print("Failed: \(error)") + self.stop() + } + + call.status.whenComplete { _ in + print("Finished") + self.stop() + } + + call.response.whenSuccess { summary in + print("Finished") + self.stop() + } + // 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 + } + } + } +} + +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) +} diff --git a/CarControlleriOS/CarController/ServerData.swift b/CarControlleriOS/CarController/ServerData.swift index 816144a..c049235 100644 --- a/CarControlleriOS/CarController/ServerData.swift +++ b/CarControlleriOS/CarController/ServerData.swift @@ -8,19 +8,18 @@ import Foundation +struct UserKeys{ + static let host = "host" + static let port = "port" +} + final class ServerData: ObservableObject{ - // TODO: Find a way to save/represent this stuff in iOS settings (user can access via settings app). - // Then load the below values ar runtime, from settings. - - - @Published var port: Int = 50051 - @Published var grpcPort: Int = 50050 - @Published var host: String = "10.0.0.53" + @Published var host: String = UserDefaults.standard.string(forKey: UserKeys.host) ?? "10.0.0.53" + @Published var grpcPort: Int = UserDefaults.standard.integer(forKey: UserKeys.port) func load(){ - // Load the server values from settings, if they had been - // previously saved. + // Load the server values from settings, if they had been previously saved. } func save(){ diff --git a/CarControlleriOS/CarController/Settings.bundle/Root.plist b/CarControlleriOS/CarController/Settings.bundle/Root.plist new file mode 100644 index 0000000..1261bd8 --- /dev/null +++ b/CarControlleriOS/CarController/Settings.bundle/Root.plist @@ -0,0 +1,41 @@ + + + + + StringsTable + Root + PreferenceSpecifiers + + + Type + PSTextFieldSpecifier + Title + Host + Key + host + DefaultValue + 10.0.0.53 + KeyboardType + Alphabet + AutocapitalizationType + None + AutocorrectionType + No + + + Type + PSTextFieldSpecifier + Title + Port + Key + port + DefaultValue + 50051 + IsSecure + + KeyboardType + NumberPad + + + + diff --git a/CarControlleriOS/CarController/Settings.bundle/en.lproj/Root.strings b/CarControlleriOS/CarController/Settings.bundle/en.lproj/Root.strings new file mode 100644 index 0000000..8cd87b9 Binary files /dev/null and b/CarControlleriOS/CarController/Settings.bundle/en.lproj/Root.strings differ diff --git a/CarControlleriOS/CarController/SimpleControllerView.swift b/CarControlleriOS/CarController/SimpleControllerView.swift index a394d1f..fa866ec 100644 --- a/CarControlleriOS/CarController/SimpleControllerView.swift +++ b/CarControlleriOS/CarController/SimpleControllerView.swift @@ -10,14 +10,33 @@ import SwiftUI struct SimpleControllerView: View { @EnvironmentObject var server: ServerData - @State var throttle: Float = 0 - @State var steering: Float = 0 + @ObservedObject var grpcController: PiLoader = PiLoader() var body: some View { - HStack{ - Text("Opened Simple Controller!") - Slider(value: $throttle) - Slider(value: $steering) + VStack (alignment: .trailing, spacing: 0){ + Spacer() + HStack{ + // Move this up a bit, due to being rotated. + Slider(value: self.$grpcController.throttle, in: 0...1){_ in + self.grpcController.throttle = 0.5 + } + .offset(x: 200) + .frame(width: 300) + .rotationEffect(.degrees(270)) + Spacer() + Slider(value: self.$grpcController.steering, in: 0...1){_ in + self.grpcController.steering = 0.5 + } + .frame(width:300) + .offset(x: -50, y: -200) + .padding() + } + .onAppear(){ + self.grpcController.startUpdating(forPort: self.server.grpcPort, atHost: self.server.host) + } + .onDisappear(){ + self.grpcController.stop() + } } } } diff --git a/CarControlleriOS/build.gradle b/CarControlleriOS/build.gradle index ea6f5da..039b69b 100644 --- a/CarControlleriOS/build.gradle +++ b/CarControlleriOS/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 './CarController/CarController' + into './CarController' } \ No newline at end of file