mirror of
https://github.com/Xevion/HATray.git
synced 2025-12-06 11:15:15 -06:00
feat: initial state as paused, remove Start/Stop methods, goroutine start in service handler
This commit is contained in:
@@ -8,9 +8,10 @@ import (
|
|||||||
|
|
||||||
// App represents the main application layer that is generic and cross-platform
|
// App represents the main application layer that is generic and cross-platform
|
||||||
type App struct {
|
type App struct {
|
||||||
logger *slog.Logger
|
logger *slog.Logger
|
||||||
mu sync.RWMutex
|
mu sync.RWMutex
|
||||||
state AppState
|
state AppState
|
||||||
|
hasStarted bool // true only if the application has ever been started (i.e. has been resumed from initial paused state)
|
||||||
}
|
}
|
||||||
|
|
||||||
// AppState represents the current state of the application
|
// AppState represents the current state of the application
|
||||||
@@ -19,50 +20,17 @@ type AppState string
|
|||||||
const (
|
const (
|
||||||
StateRunning AppState = "running"
|
StateRunning AppState = "running"
|
||||||
StatePaused AppState = "paused"
|
StatePaused AppState = "paused"
|
||||||
StateStopped AppState = "stopped"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// NewApp creates a new application instance
|
// NewApp creates a new application instance
|
||||||
func NewApp(logger *slog.Logger) *App {
|
func NewApp(logger *slog.Logger) *App {
|
||||||
return &App{
|
return &App{
|
||||||
logger: logger,
|
logger: logger,
|
||||||
state: StateStopped,
|
state: StatePaused,
|
||||||
|
hasStarted: false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start transitions the application from Stopped or Paused to Running
|
|
||||||
func (app *App) Start() error {
|
|
||||||
app.mu.Lock()
|
|
||||||
defer app.mu.Unlock()
|
|
||||||
|
|
||||||
switch app.state {
|
|
||||||
case StateRunning:
|
|
||||||
return fmt.Errorf("application is already running")
|
|
||||||
case StateStopped, StatePaused:
|
|
||||||
// valid states to start from, do nothing
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("cannot start application from state: %s", app.state)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.logger.Info("starting application",
|
|
||||||
"action", "start",
|
|
||||||
"previous_state", app.state,
|
|
||||||
"new_state", StateRunning)
|
|
||||||
|
|
||||||
// TODO: Implement actual start logic
|
|
||||||
// - Connect to Home Assistant WebSocket
|
|
||||||
// - Start background tasks
|
|
||||||
// - Start sensor monitoring
|
|
||||||
|
|
||||||
app.state = StateRunning
|
|
||||||
|
|
||||||
app.logger.Info("started successfully",
|
|
||||||
"action", "start",
|
|
||||||
"state", app.state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pause disconnects from the server and ceases any background tasks
|
// Pause disconnects from the server and ceases any background tasks
|
||||||
func (app *App) Pause() error {
|
func (app *App) Pause() error {
|
||||||
app.mu.Lock()
|
app.mu.Lock()
|
||||||
@@ -71,8 +39,10 @@ func (app *App) Pause() error {
|
|||||||
switch app.state {
|
switch app.state {
|
||||||
case StatePaused:
|
case StatePaused:
|
||||||
return fmt.Errorf("application is already paused")
|
return fmt.Errorf("application is already paused")
|
||||||
case StateStopped:
|
case StateRunning:
|
||||||
return fmt.Errorf("cannot pause application when stopped")
|
// valid state to pause from, do nothing
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("unexpected state encountered while pausing application: %s", app.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.logger.Info("pausing application",
|
app.logger.Info("pausing application",
|
||||||
@@ -102,18 +72,18 @@ func (app *App) Resume() error {
|
|||||||
switch app.state {
|
switch app.state {
|
||||||
case StateRunning:
|
case StateRunning:
|
||||||
return fmt.Errorf("application is already running")
|
return fmt.Errorf("application is already running")
|
||||||
case StateStopped:
|
|
||||||
return fmt.Errorf("cannot resume application when stopped, instead, start the application")
|
|
||||||
case StatePaused:
|
case StatePaused:
|
||||||
// valid state to resume from, do nothing
|
// valid state to resume from, do nothing
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot resume application from state: %s", app.state)
|
return fmt.Errorf("unexpected state encountered while resuming application: %s", app.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
app.logger.Info("resuming application",
|
app.logger.Info("resuming application",
|
||||||
"action", "resume",
|
"action", "resume",
|
||||||
"previous_state", app.state,
|
"previous_state", app.state,
|
||||||
"new_state", StateRunning)
|
"new_state", StateRunning,
|
||||||
|
"has_started", app.hasStarted,
|
||||||
|
)
|
||||||
|
|
||||||
// TODO: Implement actual resume logic
|
// TODO: Implement actual resume logic
|
||||||
// - Connect to Home Assistant WebSocket
|
// - Connect to Home Assistant WebSocket
|
||||||
@@ -121,6 +91,7 @@ func (app *App) Resume() error {
|
|||||||
// - Resume sensor monitoring
|
// - Resume sensor monitoring
|
||||||
|
|
||||||
app.state = StateRunning
|
app.state = StateRunning
|
||||||
|
app.hasStarted = true
|
||||||
|
|
||||||
app.logger.Info("resumed successfully",
|
app.logger.Info("resumed successfully",
|
||||||
"action", "resume",
|
"action", "resume",
|
||||||
@@ -135,14 +106,12 @@ func (a *App) Reload() error {
|
|||||||
defer a.mu.Unlock()
|
defer a.mu.Unlock()
|
||||||
|
|
||||||
switch a.state {
|
switch a.state {
|
||||||
case StateStopped:
|
|
||||||
return fmt.Errorf("cannot reload application when stopped")
|
|
||||||
case StatePaused:
|
case StatePaused:
|
||||||
return fmt.Errorf("cannot reload application when paused")
|
return fmt.Errorf("cannot reload application when paused")
|
||||||
case StateRunning:
|
case StateRunning:
|
||||||
// valid state to reload from, do nothing
|
// valid state to reload from, do nothing
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("cannot reload application from state: %s", a.state)
|
return fmt.Errorf("unexpected state encountered while reloading application: %s", a.state)
|
||||||
}
|
}
|
||||||
|
|
||||||
a.logger.Info("starting application reload",
|
a.logger.Info("starting application reload",
|
||||||
@@ -193,36 +162,3 @@ func (a *App) GetState() AppState {
|
|||||||
defer a.mu.RUnlock()
|
defer a.mu.RUnlock()
|
||||||
return a.state
|
return a.state
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stop stops the application completely
|
|
||||||
func (app *App) Stop() error {
|
|
||||||
app.mu.Lock()
|
|
||||||
defer app.mu.Unlock()
|
|
||||||
|
|
||||||
switch app.state {
|
|
||||||
case StateStopped:
|
|
||||||
return fmt.Errorf("application is already stopped")
|
|
||||||
case StatePaused, StateRunning:
|
|
||||||
// valid state to stop from, do nothing
|
|
||||||
default:
|
|
||||||
return fmt.Errorf("unexpected state encountered while stopping application: %s", app.state)
|
|
||||||
}
|
|
||||||
|
|
||||||
app.logger.Info("stopping application",
|
|
||||||
"action", "stop",
|
|
||||||
"previous_state", app.state,
|
|
||||||
"new_state", StateStopped)
|
|
||||||
|
|
||||||
// TODO: Implement actual stop logic
|
|
||||||
// - Disconnect from all services
|
|
||||||
// - Clean up resources
|
|
||||||
// - Stop all background tasks
|
|
||||||
|
|
||||||
app.state = StateStopped
|
|
||||||
|
|
||||||
app.logger.Info("application stopped successfully",
|
|
||||||
"action", "stop",
|
|
||||||
"state", app.state)
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -80,9 +80,17 @@ func (handler *serviceHandler) Execute(args []string, r <-chan winsvc.ChangeRequ
|
|||||||
const cmdsAccepted = winsvc.AcceptStop | winsvc.AcceptShutdown | winsvc.AcceptPauseAndContinue
|
const cmdsAccepted = winsvc.AcceptStop | winsvc.AcceptShutdown | winsvc.AcceptPauseAndContinue
|
||||||
changes <- winsvc.Status{State: winsvc.StartPending}
|
changes <- winsvc.Status{State: winsvc.StartPending}
|
||||||
|
|
||||||
handler.service.logger.Info("service starting")
|
handler.service.logger.Info("starting service")
|
||||||
changes <- winsvc.Status{State: winsvc.Running, Accepts: cmdsAccepted}
|
changes <- winsvc.Status{State: winsvc.Running, Accepts: cmdsAccepted}
|
||||||
|
|
||||||
|
// Start the application; backgrounded so that the service can still respond to Windows control requests (the app layer can handle concurrent requests)
|
||||||
|
go func() {
|
||||||
|
// TODO: This has no true error handling, retry mechanism, or timeout mechanism. If this fails, then the service will be stuck in the 'StartPending' state.
|
||||||
|
if err := handler.service.app.Resume(); err != nil {
|
||||||
|
handler.service.logger.Error("failed to start (resume) app layer", "error", err)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
// Service heartbeat
|
// Service heartbeat
|
||||||
ticker := time.NewTicker(30 * time.Second)
|
ticker := time.NewTicker(30 * time.Second)
|
||||||
defer ticker.Stop()
|
defer ticker.Stop()
|
||||||
@@ -106,8 +114,8 @@ func (handler *serviceHandler) Execute(args []string, r <-chan winsvc.ChangeRequ
|
|||||||
|
|
||||||
handler.service.logger.Info("service stopping", "shutdown", c.Cmd == winsvc.Shutdown)
|
handler.service.logger.Info("service stopping", "shutdown", c.Cmd == winsvc.Shutdown)
|
||||||
|
|
||||||
if err := handler.service.app.Stop(); err != nil {
|
if err := handler.service.app.Pause(); err != nil {
|
||||||
handler.service.logger.Error("Failed to stop app layer", "error", err)
|
handler.service.logger.Error("failed to pause app layer", "error", err)
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
case winsvc.Pause:
|
case winsvc.Pause:
|
||||||
@@ -115,14 +123,14 @@ func (handler *serviceHandler) Execute(args []string, r <-chan winsvc.ChangeRequ
|
|||||||
|
|
||||||
handler.service.logger.Info("service pausing")
|
handler.service.logger.Info("service pausing")
|
||||||
if err := handler.service.app.Pause(); err != nil {
|
if err := handler.service.app.Pause(); err != nil {
|
||||||
handler.service.logger.Error("Failed to pause app layer", "error", err)
|
handler.service.logger.Error("failed to pause app layer", "error", err)
|
||||||
}
|
}
|
||||||
case winsvc.Continue:
|
case winsvc.Continue:
|
||||||
changes <- winsvc.Status{State: winsvc.Running, Accepts: cmdsAccepted}
|
changes <- winsvc.Status{State: winsvc.Running, Accepts: cmdsAccepted}
|
||||||
|
|
||||||
handler.service.logger.Info("service continuing")
|
handler.service.logger.Info("service continuing")
|
||||||
if err := handler.service.app.Resume(); err != nil {
|
if err := handler.service.app.Resume(); err != nil {
|
||||||
handler.service.logger.Error("Failed to resume app layer", "error", err)
|
handler.service.logger.Error("failed to resume app layer", "error", err)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
// Log the error to the event log & service logger
|
// Log the error to the event log & service logger
|
||||||
|
|||||||
Reference in New Issue
Block a user