diff --git a/Modules/RobotKit/Examples/RobotKitExample/Sources/MainView.swift b/Modules/RobotKit/Examples/RobotKitExample/Sources/MainView.swift index b6ff96b85a..6140995c9f 100644 --- a/Modules/RobotKit/Examples/RobotKitExample/Sources/MainView.swift +++ b/Modules/RobotKit/Examples/RobotKitExample/Sources/MainView.swift @@ -2,6 +2,7 @@ // Copyright 2023 APF France handicap // SPDX-License-Identifier: Apache-2.0 +import RobotKit import SwiftUI struct MainView: View { @@ -24,7 +25,7 @@ struct MainView: View { NavigationLink( destination: { - RobotControlView() + RobotControlView(viewModel: RobotControlViewModel(robot: Robot.shared)) }, label: { Text("Go to robot control") diff --git a/Modules/RobotKit/Examples/RobotKitExample/Sources/Models/RobotControlViewModel.swift b/Modules/RobotKit/Examples/RobotKitExample/Sources/Models/RobotControlViewModel.swift new file mode 100644 index 0000000000..01c2c09282 --- /dev/null +++ b/Modules/RobotKit/Examples/RobotKitExample/Sources/Models/RobotControlViewModel.swift @@ -0,0 +1,39 @@ +// Leka - iOS Monorepo +// Copyright 2023 APF France handicap +// SPDX-License-Identifier: Apache-2.0 + +import Combine +import RobotKit +import SwiftUI + +class RobotControlViewModel: ObservableObject { + + @Published var magicCard: MagicCard = .none + @Published var magicCardImage: Image = Image(systemName: "photo") + + private let robot: Robot + private var cancellables: Set = [] + + init(robot: Robot) { + self.robot = robot + self.robot.onMagicCard() + .receive(on: DispatchQueue.main) + .sink { + self.magicCard = $0 + self.magicCardImage = { + switch $0 { + case .none: + Image(systemName: "cross") + case .emergency_stop: + Image(systemName: "exclamationmark.octagon") + case .dice_roll: + Image(systemName: "dice") + default: + Image(systemName: "photo") + } + }($0) + } + .store(in: &cancellables) + } + +} diff --git a/Modules/RobotKit/Examples/RobotKitExample/Sources/Views/RobotControlView.swift b/Modules/RobotKit/Examples/RobotKitExample/Sources/Views/RobotControlView.swift index 23b95942e1..bc83bc1071 100644 --- a/Modules/RobotKit/Examples/RobotKitExample/Sources/Views/RobotControlView.swift +++ b/Modules/RobotKit/Examples/RobotKitExample/Sources/Views/RobotControlView.swift @@ -2,10 +2,20 @@ // Copyright 2023 APF France handicap // SPDX-License-Identifier: Apache-2.0 +import Combine +import RobotKit import SwiftUI struct RobotControlView: View { + @StateObject var viewModel: RobotControlViewModel + + private let robot = Robot.shared + + init(viewModel: RobotControlViewModel) { + self._viewModel = StateObject(wrappedValue: viewModel) + } + var body: some View { VStack { VStack(alignment: .leading, spacing: 30) { @@ -14,21 +24,21 @@ struct RobotControlView: View { .font(.title) HStack { RobotControlActionButton(title: "Move forward", image: "arrow.up", tint: .orange) { - // TODO(@ladislas): Add command + robot.move(.forward, speed: 1.0) } RobotControlActionButton(title: "Move backward", image: "arrow.down", tint: .green) { - // TODO(@ladislas): Add command + robot.move(.backward, speed: 0.5) } RobotControlActionButton(title: "Spin clockwise", image: "arrow.clockwise", tint: .indigo) { - // TODO(@ladislas): Add command + robot.spin(.clockwise, speed: 0.7) } RobotControlActionButton( title: "Spin counterclockwise", image: "arrow.counterclockwise", tint: .teal ) { - // TODO(@ladislas): Add command + robot.spin(.counterclockwise, speed: 1.0) } RobotControlActionButton(title: "Stop motion", image: "xmark", tint: .red) { - // TODO(@ladislas): Add command + robot.stopMotion() } } } @@ -38,19 +48,19 @@ struct RobotControlView: View { .font(.title) HStack { RobotControlActionButton(title: "Individual LEDs", image: "light.max", tint: .orange) { - // TODO(@ladislas): Add command + robot.shine(.spot(ids: [0, 4, 8, 10, 12]), color: .red) } RobotControlActionButton(title: "Quarters", image: "light.max", tint: .green) { - // TODO(@ladislas): Add command + robot.shine(.quarterBackLeft, color: .blue) } RobotControlActionButton(title: "Halves", image: "light.max", tint: .indigo) { - // TODO(@ladislas): Add command + robot.shine(.halfRight, color: .green) } - RobotControlActionButton(title: "Full counterclockwise", image: "light.max", tint: .teal) { - // TODO(@ladislas): Add command + RobotControlActionButton(title: "Full", image: "light.max", tint: .teal) { + robot.shine(.full, color: .blue) } RobotControlActionButton(title: "Turn off lights", image: "xmark", tint: .red) { - // TODO(@ladislas): Add command + robot.stopLights() } } } @@ -60,16 +70,16 @@ struct RobotControlView: View { .font(.title) HStack { RobotControlActionButton(title: "Rainbow", image: "number.circle", tint: .orange) { - // TODO(@ladislas): Add command + robot.run(.rainbow) } RobotControlActionButton(title: "Fire", image: "number.circle", tint: .green) { - // TODO(@ladislas): Add command + robot.run(.fire) } RobotControlActionButton(title: "Spin 1", image: "number.circle", tint: .indigo) { - // TODO(@ladislas): Add command + robot.run(.spinBlinkGreenOff) } RobotControlActionButton(title: "Spin 2", image: "number.circle", tint: .teal) { - // TODO(@ladislas): Add command + robot.run(.spinBlinkBlueViolet) } } } @@ -78,10 +88,9 @@ struct RobotControlView: View { Text("Magic Cards") .font(.title) HStack(alignment: .center, spacing: 30) { - Text("ID: 0x\(String(0xDEAD_BEEF, radix: 16, uppercase: true))") + Text("ID: 0x\(String(format: "%04X", viewModel.magicCard.id))") .monospacedDigit() - // TODO(@ladislas): Display image of magic card read by the robot - Image(systemName: "photo") + viewModel.magicCardImage .resizable() .aspectRatio(contentMode: .fit) .frame(height: 180) @@ -100,8 +109,7 @@ struct RobotControlView: View { var stopButton: some View { Button { - print("STOP ROBOT") - // TODO(@ladislas): Add command + robot.stop() } label: { Image(systemName: "exclamationmark.octagon.fill") Text("STOP") @@ -113,7 +121,9 @@ struct RobotControlView: View { } #Preview { - NavigationStack { - RobotControlView() + let viewModel = RobotControlViewModel(robot: Robot.shared) + + return NavigationStack { + RobotControlView(viewModel: viewModel) } }