From 9beecb88a115ee3f7ccdf2588aad87f57ab556a5 Mon Sep 17 00:00:00 2001 From: Michael J Mulligan Date: Mon, 15 Mar 2021 23:32:00 +0000 Subject: [PATCH 1/2] Configurable auto-reconnect and supporting changes. --- debian/local/octoscreen/config | 10 +++++ ui/SplashPanel.go | 39 ++++++++++++------- ui/ui.go | 68 +++++++++++++++++++++++++--------- utils/environment.go | 16 ++++---- utils/globalVars.go | 1 + 5 files changed, 95 insertions(+), 39 deletions(-) diff --git a/debian/local/octoscreen/config b/debian/local/octoscreen/config index 002dd806..57f83ad9 100755 --- a/debian/local/octoscreen/config +++ b/debian/local/octoscreen/config @@ -45,6 +45,16 @@ OCTOSCREEN_LOG_FILE_PATH= OCTOSCREEN_LOG_LEVEL=Error +# Controls automatic reconnect behaviour +# Wether or not OctoScreen should instruct OctoPrint to reconnect to the Printer +# automatically. +# Default setting is TRUE, which will, in effect, prevent you from "Disconecting" +# from your printer via the web interface, as OctoScreen will immediatly attempt +# to reconnect. Set this to FALSE to allow OctoPrint to manage the connection +# internally. +#OCTOSCREEN_AUTO_RECONNECT=FALSE + + # Resolution of the application, and should be configured to the resolution of your # screen, for example 800x480. OCTOSCREEN_RESOLUTION=800x480 diff --git a/ui/SplashPanel.go b/ui/SplashPanel.go index 2e3e80e1..94728510 100755 --- a/ui/SplashPanel.go +++ b/ui/SplashPanel.go @@ -14,12 +14,12 @@ type SplashPanel struct { } func NewSplashPanel(ui *UI) *SplashPanel { - instane := &SplashPanel { + instance := &SplashPanel { CommonPanel: NewCommonPanel(ui, nil), } - instane.initialize() + instance.initialize() - return instane + return instance } func (this *SplashPanel) initialize() { @@ -58,7 +58,7 @@ func (this *SplashPanel) createActionBar() gtk.IWidget { actionBar := utils.MustBox(gtk.ORIENTATION_HORIZONTAL, 5) actionBar.SetHAlign(gtk.ALIGN_END) - this.RetryButton = utils.MustButtonImageStyle("Retry", "refresh.svg", "color2", this.releaseFromHold) + this.RetryButton = utils.MustButtonImageStyle("Reconnect", "refresh.svg", "color2", this.doReconnect) this.RetryButton.SetProperty("width-request", this.Scaled(100)) this.RetryButton.SetProperty("visible", true) actionBar.Add(this.RetryButton) @@ -77,32 +77,43 @@ func (this *SplashPanel) createActionBar() gtk.IWidget { return actionBar } -func (this *SplashPanel) putOnHold() { - logger.TraceEnter("SplashPanel.putOnHold()") +func (this *SplashPanel) displayReconnect() { + logger.TraceEnter("SplashPanel.displayReconnect()") this.RetryButton.Show() ctx, err := this.RetryButton.GetStyleContext() if err != nil { - logger.LogError("SplashPanel.putOnHold()", "RetryButton.GetStyleContext()", err) + logger.LogError("SplashPanel.displayReconnect()", "RetryButton.GetStyleContext()", err) } else { ctx.RemoveClass("hidden") } - this.Label.SetText("Cannot connect to the printer. Tap \"Retry\" to try again.") + this.Label.SetText("Cannot connect to the printer. Tap \"Reconnect\" to try again.") - logger.TraceLeave("SplashPanel.putOnHold()") + logger.TraceLeave("SplashPanel.displayReconnect()") } -func (this *SplashPanel) releaseFromHold() { - logger.TraceEnter("SplashPanel.releaseFromHold()") - +func (this *SplashPanel) hideReconnect() { + logger.TraceEnter("SplashPanel.hideReconnect()") this.RetryButton.Hide() ctx, _ := this.RetryButton.GetStyleContext() ctx.AddClass("hidden") + logger.TraceEnter("SplashPanel.hideReconnect()") +} + +func (this *SplashPanel) doReconnect() { + logger.TraceEnter("SplashPanel.doReconnect()") + + this.hideReconnect() - this.Label.SetText("Loading...") this.UI.connectionAttempts = 0 + + if this.UI.DoReconnect() { + this.Label.SetText("Attempting to reconnect...") + } else { + this.Label.SetText("ERROR: Unable to reconnect...") + } - logger.TraceLeave("SplashPanel.releaseFromHold()") + logger.TraceLeave("SplashPanel.doReconnect()") } func (this *SplashPanel) showNetwork() { diff --git a/ui/ui.go b/ui/ui.go index 03d989ed..a83c70df 100755 --- a/ui/ui.go +++ b/ui/ui.go @@ -3,8 +3,10 @@ package ui import ( "fmt" "strings" + "strconv" "sync" "time" + "os" "github.com/coreos/go-systemd/daemon" "github.com/golang-collections/collections/stack" @@ -19,6 +21,15 @@ import ( "github.com/Z-Bolt/OctoScreen/utils" ) +const ( + connectionAttemptsMax = 8 + connectionAttemptsDisplayRetry = 3 + updateTaskTimer = time.Second * 10 +) + +var ( + errMercyPeriod = time.Second * 10 +) type UI struct { sync.Mutex @@ -44,10 +55,19 @@ type UI struct { height int scaleFactor int connectionAttempts int + + autoReconnect bool } func New(endpoint, key string, width, height int) *UI { logger.TraceEnter("ui.New()") + + autoReconnect := utils.AutoReconnect + + if utils.EnvironmentVariableIsSet(utils.EnvAutoReconnect) { + autoReconnectBool, _ := strconv.ParseBool(os.Getenv(utils.EnvAutoReconnect)) + autoReconnect = autoReconnectBool + } if width == 0 || height == 0 { width = utils.WindowWidth @@ -68,6 +88,8 @@ func New(endpoint, key string, width, height int) *UI { width: width, height: height, + + autoReconnect: autoReconnect, } instance.window.Connect("configure-event", func(win *gtk.Window) { @@ -100,7 +122,7 @@ func New(endpoint, key string, width, height int) *UI { } instance.splashPanel = NewSplashPanel(instance) - instance.backgroundTask = utils.CreateBackgroundTask(time.Second * 10, instance.update) + instance.backgroundTask = utils.CreateBackgroundTask(updateTaskTimer, instance.update) instance.initialize() logger.TraceLeave("ui.New()") @@ -151,8 +173,6 @@ func (this *UI) loadStyle() { logger.TraceLeave("ui.loadStyle()") } -var errMercyPeriod = time.Second * 10 - func (this *UI) verifyConnection() { logger.TraceEnter("ui.verifyConnection()") @@ -215,13 +235,7 @@ func (this *UI) getUiStateAndMessageFromConnectionResponse( case connectionResponse.Current.State.IsOffline(): logger.Debug("ui.getUiStateAndMessageFromConnectionResponse() - the state is now offline and displaying the splash panel") newUIState = "splash" - logger.Info("ui.getUiStateAndMessageFromConnectionResponse() - new UI state is 'splash' and is about to call ConnectRequest.Do()") - if err := (&octoprintApis.ConnectRequest{}).Do(this.Client); err != nil { - logger.LogError("ui.getUiStateAndMessageFromConnectionResponse()", "s.Current.State is IsOffline, and (ConnectRequest)Do(UI.Client)", err) - splashMessage = "Loading..." - } else { - splashMessage = "Printer is offline, now trying to connect..." - } + splashMessage = "Printer is offline, waiting for OctoPrint to connect..." case connectionResponse.Current.State.IsConnecting(): logger.Debug("ui.getUiStateAndMessageFromConnectionResponse() - new state is splash (from IsConnecting)") @@ -261,16 +275,16 @@ func (this *UI) getUiStateAndMessageFromError( logger.Infof("ui.getUiStateAndMessageFromError() - errMessage is: %q", errMessage) if strings.Contains(strings.ToLower(errMessage), "deadline exceeded") { - splashMessage = "Printer is offline (deadline exceeded), retrying to connect..." + splashMessage = "Printer is offline (deadline exceeded), attempting to reconnect..." } else if strings.Contains(strings.ToLower(errMessage), "connection reset by peer") { - splashMessage = "Printer is offline (peer connection reset), retrying to connect..." + splashMessage = "Printer is offline (peer connection reset), attempting to reconnect..." } else if strings.Contains(strings.ToLower(errMessage), "unexpected status code: 403") { - splashMessage = "Printer is offline (403), retrying to connect..." + splashMessage = "Printer is offline (403), attempting to reconnect..." } else { splashMessage = errMessage } } else { - splashMessage = "Printer is offline! (retrying to connect...)" + splashMessage = "Printer is offline! (attempting to reconnect...)" } logger.TraceLeave("ui.getUiStateAndMessageFromError()") @@ -423,14 +437,22 @@ func (this *UI) validateMenuItems(menuItems []dataModels.MenuItem, name string, return true } +func (this *UI) DoReconnect() bool { + logger.Info("UI.DoReconnect() - about to call ConnectRequest.Do()") + if err := (&octoprintApis.ConnectRequest{}).Do(this.Client); err != nil { + logger.LogError("UI.DoReconnect()", "(ConnectRequest)Do(UI.Client)", err) + return false + } else { + return true + } +} + func (this *UI) update() { logger.TraceEnter("ui.update()") - if this.connectionAttempts > 8 { - logger.Info("ui.update() - this.connectionAttempts > 8") - this.splashPanel.putOnHold() + if this.connectionAttempts > connectionAttemptsMax { + logger.Info("ui.update() - this.connectionAttempts > %d", connectionAttemptsMax) this.sdNotify(daemon.SdNotifyWatchdog) - logger.TraceLeave("ui.update()") return } @@ -464,6 +486,16 @@ func (this *UI) update() { } this.verifyConnection() + + if this.UIState == "splash" { + if this.connectionAttempts == connectionAttemptsDisplayRetry { + logger.Info("ui.update() - this.connectionAttempts == %d", connectionAttemptsDisplayRetry) + this.splashPanel.displayReconnect() + } + if this.ConnectionState.IsOffline() && this.autoReconnect { + this.DoReconnect() + } + } logger.TraceLeave("ui.update()") } diff --git a/utils/environment.go b/utils/environment.go index 9fb6a591..cf681b49 100755 --- a/utils/environment.go +++ b/utils/environment.go @@ -26,14 +26,15 @@ const ( EnvLogFilePath = "OCTOSCREEN_LOG_FILE_PATH" EnvResolution = "OCTOSCREEN_RESOLUTION" EnvConfigFile = "OCTOPRINT_CONFIG_FILE" + EnvAutoReconnect = "OCTOPRINT_AUTO_RECONNECT" ) func RequiredEnvironmentVariablesAreSet(apiKey string) bool { - if( !environmentVariableIsSet(EnvStylePath) ) { + if( !EnvironmentVariableIsSet(EnvStylePath) ) { return false } - if( !environmentVariableIsSet(EnvBaseURL) ) { + if( !EnvironmentVariableIsSet(EnvBaseURL) ) { return false } @@ -45,7 +46,7 @@ func RequiredEnvironmentVariablesAreSet(apiKey string) bool { // and due to GoLang's rules, /main/utils doesn't have access to globals in /main, // so APIKey has to be passed into RequiredEnvironmentVariablesAreSet(). // - // if( !environmentVariableIsSet(EnvAPIKey) ) { + // if( !EnvironmentVariableIsSet(EnvAPIKey) ) { // return false // } if apiKey == "" { @@ -55,16 +56,16 @@ func RequiredEnvironmentVariablesAreSet(apiKey string) bool { return true } -func environmentVariableIsSet(environmentVariable string) bool { +func EnvironmentVariableIsSet(environmentVariable string) bool { return os.Getenv(environmentVariable) != "" } func NameOfMissingRequiredEnvironmentVariable(apiKey string) string { - if( !environmentVariableIsSet(EnvStylePath) ) { + if( !EnvironmentVariableIsSet(EnvStylePath) ) { return EnvStylePath } - if( !environmentVariableIsSet(EnvBaseURL) ) { + if( !EnvironmentVariableIsSet(EnvBaseURL) ) { return EnvBaseURL } @@ -72,7 +73,7 @@ func NameOfMissingRequiredEnvironmentVariable(apiKey string) string { // Since the runtime value of APIKey is set in main.init(), and can be set by either // being defined in OctoScreen's config file or in OctoPrint's config file, // the value needs to be passed into NameOfMissingRequiredEnvironmentVariable(). - // if( !environmentVariableIsSet(EnvAPIKey) ) { + // if( !EnvironmentVariableIsSet(EnvAPIKey) ) { // return EnvAPIKey // } if apiKey == "" { @@ -110,6 +111,7 @@ func DumpEnvironmentVariables() { dumpEnvironmentVariable(EnvLogFilePath) dumpEnvironmentVariable(EnvLogLevel) dumpEnvironmentVariable(EnvResolution) + dumpEnvironmentVariable(EnvAutoReconnect) // EnvResolution is optional. If not set, the window size will // default to the values defined in globalVars.go. } diff --git a/utils/globalVars.go b/utils/globalVars.go index a036df35..823ed152 100755 --- a/utils/globalVars.go +++ b/utils/globalVars.go @@ -6,6 +6,7 @@ var ( WindowName = "OctoScreen" WindowWidth = 800 WindowHeight = 480 + AutoReconnect = true ) const ( From d70622d9bde4b89f849f6f524567c18d238b54ae Mon Sep 17 00:00:00 2001 From: Michael Mulligan Date: Fri, 19 Mar 2021 08:37:36 -0400 Subject: [PATCH 2/2] Update README.md --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 3b0c3bab..3f9a9386 100755 --- a/README.md +++ b/README.md @@ -153,6 +153,8 @@ The basic configuration is handled via environment variables, if you are using t - `OCTOSCREEN_RESOLUTION` - Resolution of the application, and should be configured to the resolution of your screen. Optimal resolution for OctoScreen is no less than 800x480, so if the physical resolution of your screen is 480x320, it's recommended to set the software resolution 800x533. If you are using Raspbian you can do it by changing [`hdmi_cvt`](https://www.raspberrypi.org/documentation/configuration/config-txt/video.md) param in `/boot/config.txt` file. Please see [Setting Up OctoScreen and Your Display](https://github.com/Z-Bolt/OctoScreen/wiki/Setting-Up-OctoScreen-and-Your-Display) and [Installing OctoScreen with a 3.5" 480x320 TFT screen](https://github.com/Z-Bolt/OctoScreen/wiki/Installing-OctoScreen-with-a-3.5%22-480x320-TFT-screen) for more information. +- `OCTOSCREEN_AUTO_RECONNECT` - Controls wether OctoScreen automatically triggers a Connect attempt when it detects the Printer is not connected. Defaults to `TRUE`. Dissabling this feature releases control of Printer connection to OctoPrint, and may provide better compatibility when flashing or interfacing with the printer controller via an external device. +