From a12cdaf3072d626d4ae7fef4f147a6edc34444d1 Mon Sep 17 00:00:00 2001 From: Gaston Dombiak Date: Mon, 27 Aug 2018 17:24:55 -0700 Subject: [PATCH] Implemented and tested custom controls. Closes #15 --- OctoPod/Base.lproj/Main.storyboard | 10 +++--- OctoPod/Model/Commands/Command.swift | 18 +++++++++- OctoPod/Model/Commands/ControlInput.swift | 10 ++++++ OctoPod/Model/Commands/Script.swift | 14 +++++++- OctoPod/OctoPrint/OctoPrintClient.swift | 19 ++++++++-- .../ExecuteControlViewController.swift | 36 ++++++++++++++++++- 6 files changed, 96 insertions(+), 11 deletions(-) diff --git a/OctoPod/Base.lproj/Main.storyboard b/OctoPod/Base.lproj/Main.storyboard index ed79f47d..05c26f76 100644 --- a/OctoPod/Base.lproj/Main.storyboard +++ b/OctoPod/Base.lproj/Main.storyboard @@ -854,21 +854,21 @@ - + - + - + @@ -909,7 +909,7 @@ - + diff --git a/OctoPod/Model/Commands/Command.swift b/OctoPod/Model/Commands/Command.swift index cc07afc1..843c91ce 100644 --- a/OctoPod/Model/Commands/Command.swift +++ b/OctoPod/Model/Commands/Command.swift @@ -32,6 +32,22 @@ class Command: ExecuteControl { } func executePayload() -> NSDictionary { - return [:] + let result = NSMutableDictionary() + if let commandsArray = commands { + result["commands"] = commandsArray + } else if let singleCommand = command { + result["commands"] = [singleCommand] + } + + var paramsDict: NSMutableDictionary? + if let input = _input { + paramsDict = NSMutableDictionary() + for controlInput in input { + let entry: (key: String, value: Any) = controlInput.executePayload() + paramsDict?[entry.key] = entry.value + } + result["parameters"] = paramsDict! + } + return result } } diff --git a/OctoPod/Model/Commands/ControlInput.swift b/OctoPod/Model/Commands/ControlInput.swift index c7f941f4..8556af69 100644 --- a/OctoPod/Model/Commands/ControlInput.swift +++ b/OctoPod/Model/Commands/ControlInput.swift @@ -19,4 +19,14 @@ class ControlInput { self.parameter = parameter self.hasSlider = false // Assume false until proven otherwise } + + func executePayload() -> (key: String, value: Any) { + if hasSlider { + if let intValue = value as? Int { + return (parameter, intValue) + } + return (parameter, value as! Float) + } + return (parameter, value as! String) + } } diff --git a/OctoPod/Model/Commands/Script.swift b/OctoPod/Model/Commands/Script.swift index 9391427a..0973bb90 100644 --- a/OctoPod/Model/Commands/Script.swift +++ b/OctoPod/Model/Commands/Script.swift @@ -32,6 +32,18 @@ class Script: ExecuteControl { } func executePayload() -> NSDictionary { - return [:] + let result = NSMutableDictionary() + result["script"] = script + + var paramsDict: NSMutableDictionary? + if let input = _input { + paramsDict = NSMutableDictionary() + for controlInput in input { + let entry: (key: String, value: Any) = controlInput.executePayload() + paramsDict?[entry.key] = entry.value + } + result["parameters"] = paramsDict! + } + return result } } diff --git a/OctoPod/OctoPrint/OctoPrintClient.swift b/OctoPod/OctoPrint/OctoPrintClient.swift index 9983208f..5b0fe5ce 100644 --- a/OctoPod/OctoPrint/OctoPrintClient.swift +++ b/OctoPod/OctoPrint/OctoPrintClient.swift @@ -550,8 +550,12 @@ class OctoPrintClient: WebSocketClientDelegate { } } - func executeCustomControl() { - + func executeCustomControl(control: NSDictionary, callback: @escaping (Bool, Error?, HTTPURLResponse) -> Void) { + if let client = httpClient { + client.post("/api/printer/command", json: control, expected: 204) { (result: NSObject?, error: Error?, response: HTTPURLResponse) in + callback(response.statusCode == 204, error, response) + } + } } // MARK: - PSU Control Plugin operations @@ -1044,7 +1048,10 @@ class OctoPrintClient: WebSocketClientDelegate { } else if let gcodeCommands = json["commands"] as? NSArray { var newCommands: Array = Array() for case let gcodeCommand as String in gcodeCommands { - newCommands.append(gcodeCommand) + if !gcodeCommand.isEmpty { + // Ignore empty commands + newCommands.append(gcodeCommand) + } } command.commands = newCommands } else { @@ -1076,16 +1083,22 @@ class OctoPrintClient: WebSocketClientDelegate { controlInput.hasSlider = true if let sliderMax = slider["max"] as? String { controlInput.slider_max = sliderMax + } else if let sliderMax = slider["max"] as? NSNumber { + controlInput.slider_max = sliderMax.stringValue } else { controlInput.slider_max = "255" } if let sliderMin = slider["min"] as? String { controlInput.slider_min = sliderMin + } else if let sliderMin = slider["min"] as? NSNumber { + controlInput.slider_min = sliderMin.stringValue } else { controlInput.slider_min = "0" } if let sliderStep = slider["step"] as? String { controlInput.slider_step = sliderStep + } else if let sliderStep = slider["step"] as? NSNumber { + controlInput.slider_step = sliderStep.stringValue } else { controlInput.slider_step = "1" } diff --git a/OctoPod/Panel UI/Subpanels/Custom Controls/ExecuteControlViewController.swift b/OctoPod/Panel UI/Subpanels/Custom Controls/ExecuteControlViewController.swift index d3e0ae96..3b93f2d5 100644 --- a/OctoPod/Panel UI/Subpanels/Custom Controls/ExecuteControlViewController.swift +++ b/OctoPod/Panel UI/Subpanels/Custom Controls/ExecuteControlViewController.swift @@ -78,10 +78,18 @@ class ExecuteControlViewController: ThemedDynamicUITableViewController { cell.inputLabel?.text = controlInput.name cell.inputLabel?.textColor = Theme.currentTheme().labelColor() + if let text = controlInput.defaultValue as? String { + cell.inputValueField.text = text + } + return cell } } + override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 44 + } + // MARK: - Notifications func valueUpdated(row: Int, value: AnyObject) { @@ -94,7 +102,20 @@ class ExecuteControlViewController: ThemedDynamicUITableViewController { @objc func execute() { let executeBlock = { - self.octoprintClient.executeCustomControl() + let json = self.control.executePayload() + self.octoprintClient.executeCustomControl(control: json, callback: { (requested: Bool, error: Error?, response: HTTPURLResponse) in + if !requested { + // Handle error + NSLog("Error requesting to execute command \(json). HTTP status code \(response.statusCode)") + if response.statusCode == 409 { + self.showAlert("Alert", message: "Command not executed. Printer not operational") + } else if response.statusCode == 404 { + self.showAlert("Alert", message: "Command not executed. Script not found") + } else { + self.showAlert("Alert", message: "Failed to request to execute command") + } + } + }) } if let confirmation = control.confirm() { @@ -121,6 +142,19 @@ class ExecuteControlViewController: ThemedDynamicUITableViewController { }) } + fileprivate func showAlert(_ title: String, message: String) { + let alert = UIAlertController(title: title, message: message, preferredStyle: .alert) + alert.addAction(UIAlertAction(title: "Dismiss", style: .default, handler: { (UIAlertAction) -> Void in + // Nothing to do here + })) + // We are not always on the main thread so present dialog on main thread to prevent crashes + DispatchQueue.main.async { + self.present(alert, animated: true) { () -> Void in + // Nothing to do here + } + } + } + fileprivate func showConfirm(message: String, yes: @escaping (UIAlertAction) -> Void, no: @escaping (UIAlertAction) -> Void) { let alert = UIAlertController(title: "Confirm", message: message, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "Yes", style: .default, handler: yes))