From e440f3489b3d30b4ee7c0f1ab0bd31301057f2d7 Mon Sep 17 00:00:00 2001 From: maddiebaka Date: Tue, 27 Jun 2023 18:41:22 -0400 Subject: [PATCH] Socket logic force disconnect/reconnect when server doesn't properly close it --- Soyuz.xcodeproj/project.pbxproj | 4 + Soyuz/ViewModels/BonjourBrowser.swift | 83 ++++++++++++++++--- Soyuz/ViewModels/MoonrakerSocketManager.swift | 28 +++++-- Soyuz/Views/PrinterConfigView.swift | 8 +- Soyuz/Views/SoyuzMenuBarExtraView.swift | 7 -- 5 files changed, 105 insertions(+), 25 deletions(-) diff --git a/Soyuz.xcodeproj/project.pbxproj b/Soyuz.xcodeproj/project.pbxproj index 5bb98dc..2d64dd0 100755 --- a/Soyuz.xcodeproj/project.pbxproj +++ b/Soyuz.xcodeproj/project.pbxproj @@ -9,6 +9,7 @@ /* Begin PBXBuildFile section */ C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = C15B60A72A2423760052F712 /* UserNotificationHandler.swift */; }; C15F06A62A20198300C14CD8 /* Soyuz.help in Resources */ = {isa = PBXBuildFile; fileRef = C15F06A42A20171E00C14CD8 /* Soyuz.help */; }; + C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */ = {isa = PBXBuildFile; fileRef = C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */; }; E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */; }; E16378B429A491E6002F05E9 /* MoonrakerSocketManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */; }; E180B5E92992CD9100425DB0 /* SoyuzApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E180B5E82992CD9100425DB0 /* SoyuzApp.swift */; }; @@ -47,6 +48,7 @@ /* Begin PBXFileReference section */ C15B60A72A2423760052F712 /* UserNotificationHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserNotificationHandler.swift; sourceTree = ""; }; C15F06A42A20171E00C14CD8 /* Soyuz.help */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = Soyuz.help; sourceTree = ""; }; + C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerNative.swift; sourceTree = ""; }; E124B9D72993FE5500C0D2D2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; }; E124B9D829941A4D00C0D2D2 /* PrinterConfigView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PrinterConfigView.swift; sourceTree = ""; }; E16378B329A491E6002F05E9 /* MoonrakerSocketManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoonrakerSocketManagerTests.swift; sourceTree = ""; }; @@ -175,6 +177,7 @@ E180B6212993256E00425DB0 /* MoonrakerSocketManager.swift */, E1A93C6629C932E200BAE750 /* BonjourBrowser.swift */, C15B60A72A2423760052F712 /* UserNotificationHandler.swift */, + C1A11C172A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift */, ); path = ViewModels; sourceTree = ""; @@ -318,6 +321,7 @@ files = ( E180B61D2992D53700425DB0 /* PrinterObjectsQuery.swift in Sources */, E180B5F52992CD9200425DB0 /* KlipperMon.xcdatamodeld in Sources */, + C1A11C182A4B5F67006524B8 /* MoonrakerSocketManagerNative.swift in Sources */, E124B9D929941A4D00C0D2D2 /* PrinterConfigView.swift in Sources */, E180B5F22992CD9200425DB0 /* Persistence.swift in Sources */, C15B60A82A2423760052F712 /* UserNotificationHandler.swift in Sources */, diff --git a/Soyuz/ViewModels/BonjourBrowser.swift b/Soyuz/ViewModels/BonjourBrowser.swift index 6227237..1d7be8b 100755 --- a/Soyuz/ViewModels/BonjourBrowser.swift +++ b/Soyuz/ViewModels/BonjourBrowser.swift @@ -35,23 +35,27 @@ extension NWBrowser: NetworkDiscoveryEngine { // MARK: BonjourBrowser class BonjourBrowser: ObservableObject { - @Published var NDEngineResults: [NWBrowser.Result] = [] + @Published var networkResults: [NWBrowser.Result] = [] - private let nwBrowser: NetworkDiscoveryEngine - var connection: NWConnection! + private var nwBrowser: NWBrowser! + private var connection: NWConnection! - // TEMPORARY -// var bonjourListener: NWListener? - - init(browser: NetworkDiscoveryEngine = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)) { - nwBrowser = browser + + init() { + if nwBrowser == nil { + setup() + } + } + + func setup() { + nwBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp) // Bonjour browser results changed handler nwBrowser.setBrowseResultsChangedHandler({ (newResults, changes) in print("[update] Results changed.") - self.NDEngineResults.removeAll() + self.networkResults.removeAll() newResults.forEach { result in print(result) - self.NDEngineResults.append(result) + self.networkResults.append(result) } }) @@ -72,4 +76,63 @@ class BonjourBrowser: ObservableObject { nwBrowser.startScan(queue: DispatchQueue.main) } + func enableScan(_ queue: DispatchQueue) { + if(nwBrowser.state == .cancelled) { + self.setup() + } + nwBrowser.start(queue: queue) + } + + func disableScan() { + if(nwBrowser.state != .cancelled) { + nwBrowser.cancel() + } + } } + +//class BonjourBrowser: ObservableObject { +// @Published var NDEngineResults: [NWBrowser.Result] = [] +// +// private let nwBrowser: NetworkDiscoveryEngine +// var connection: NWConnection! +// +// // TEMPORARY +//// var bonjourListener: NWListener? +// +// init(browser: NetworkDiscoveryEngine = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp)) { +// nwBrowser = browser +// // Bonjour browser results changed handler +// nwBrowser.setBrowseResultsChangedHandler({ (newResults, changes) in +// print("[update] Results changed.") +// self.NDEngineResults.removeAll() +// newResults.forEach { result in +// print(result) +// self.NDEngineResults.append(result) +// } +// }) +// +// // Bonjour browser state update handler +// nwBrowser.setStateUpdateHandler({ newState in +// switch newState { +// case .failed(let error): +// print("[error] nwbrowser: \(error)") +// case .ready: +// print("[ready] nwbrowser") +// case .setup: +// print("[setup] nwbrowser") +// default: +// break +// } +// }) +// +// nwBrowser.startScan(queue: DispatchQueue.main) +// } +// +// func startScanning(queue: DispatchQueue.main) { +// if(self.nwBrowser.state == NWBrowser.State.cancelled) { +// +// } +// self.nwBrowser.startScan(queue) +// } +// +//} diff --git a/Soyuz/ViewModels/MoonrakerSocketManager.swift b/Soyuz/ViewModels/MoonrakerSocketManager.swift index 5d77303..dc429a1 100755 --- a/Soyuz/ViewModels/MoonrakerSocketManager.swift +++ b/Soyuz/ViewModels/MoonrakerSocketManager.swift @@ -74,7 +74,7 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate { // socket?.disconnect() // } // - if connection == nil { + if connection == nil || connection?.state == .cancelled { connection = NWConnection(to: endpoint, using: .tcp) } @@ -112,25 +112,29 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate { func disconnect() { print("disconnect() called") - isConnected = false - //connection?.cancel() - connection = nil + self.isConnected = false socket?.disconnect() + socket = nil } // MARK: Private functions // Opens the websocket connection - private func openWebsocket() { + func openWebsocket() { // Exit function if there is no server to connect to if socketHost.isEmpty || socketPort.isEmpty { return } + if socket != nil { + socket!.connect() + return + } + lastPingDate = Date.now - var request = URLRequest(url: URL(string: "http://\(socketHost):\(socketPort)/websocket")!) + var request = URLRequest(url: URL(string: "ws://\(socketHost):\(socketPort)/websocket")!) request.timeoutInterval = 30 socket = WebSocket(request: request, engine: starscreamEngine) socket!.delegate = self @@ -154,7 +158,8 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate { switch(notification.name) { case NSWorkspace.screensDidSleepNotification: print("Screen slept. Disconnecting..") - socket?.disconnect() + self.disconnect() + //socket?.disconnect() case NSWorkspace.screensDidWakeNotification: print("Screen awoke. Opening websocket..") self.openWebsocket() @@ -216,6 +221,15 @@ class MoonrakerSocketManager: ObservableObject, WebSocketDelegate { case .error(let error): isConnected = false print("[error] Starscream: \(error.debugDescription)") + switch(error) { + case .some(HTTPUpgradeError.notAnUpgrade(200)): + print("[debug] Starscream: Forcing disconnect and reconnect..") + self.socket?.forceDisconnect() + self.socket = nil + self.openWebsocket() + default: + break + } } } diff --git a/Soyuz/Views/PrinterConfigView.swift b/Soyuz/Views/PrinterConfigView.swift index d3fdcc6..949499e 100755 --- a/Soyuz/Views/PrinterConfigView.swift +++ b/Soyuz/Views/PrinterConfigView.swift @@ -13,6 +13,8 @@ struct PrinterConfigView: View { @ObservedObject var printerManager: MoonrakerSocketManager @ObservedObject var bonjourBrowser = BonjourBrowser() + //@State var bonjourBrowser = NWBrowser(for: .bonjourWithTXTRecord(type: "_moonraker._tcp", domain: "local."), using: .tcp) + @Environment(\.openURL) private var openURL @@ -61,7 +63,7 @@ struct PrinterConfigView: View { }.buttonStyle(PlainButtonStyle()) } - ForEach(bonjourBrowser.NDEngineResults , id: \.hashValue) { result in + ForEach(bonjourBrowser.networkResults, id: \.hashValue) { result in HStack { Text(result.endpoint.toFriendlyString()) Button { @@ -79,6 +81,10 @@ struct PrinterConfigView: View { } .onAppear { NSApplication.shared.activate(ignoringOtherApps: true) + bonjourBrowser.enableScan(DispatchQueue.main) + } + .onDisappear { + bonjourBrowser.disableScan() } } } diff --git a/Soyuz/Views/SoyuzMenuBarExtraView.swift b/Soyuz/Views/SoyuzMenuBarExtraView.swift index 0793ab1..67916f1 100755 --- a/Soyuz/Views/SoyuzMenuBarExtraView.swift +++ b/Soyuz/Views/SoyuzMenuBarExtraView.swift @@ -86,13 +86,6 @@ struct SoyuzMenuBarExtraView: View { Text("Printers") .foregroundColor(Color("ButtonForegroundColor")) } - /* Debugging Stuff */ - Button { - notification.sendNotification(.printComplete) - } label: { - Text("Notify") - } - /* Debugging Stuff */ Spacer() if(printerManager.isConnected) { Image(systemName: "network")