doc: add and improve comments

This commit is contained in:
Sebastian Mark 2024-10-30 07:37:14 +01:00
parent d83acc77b2
commit 83a3a3b052
12 changed files with 67 additions and 41 deletions

View file

@ -14,35 +14,42 @@ import (
) )
var ( var (
// Define CLI flags // define CLI flags
listenAddress = flag.String("listenAddress", shared.DefaultServerConfig.ListenAddress, "IP address to listen on") listenAddress = flag.String("listenAddress", shared.DefaultServerConfig.ListenAddress, "IP address to listen on")
listenPort = flag.Int("listenPort", shared.DefaultServerConfig.ListenPort, "Port to listen on") listenPort = flag.Int("listenPort", shared.DefaultServerConfig.ListenPort, "Port to listen on")
password = flag.String("password", "", "Control password for pomodoro session (optional)") password = flag.String("password", "", "Control password for pomodoro session (optional)")
showVersionFlag = flag.Bool("version", false, "Output version") showVersionFlag = flag.Bool("version", false, "Output version")
) )
// Start the pomodoro server
func Start() { func Start() {
flag.Parse() flag.Parse()
// show server and protocl version and exit
if *showVersionFlag { if *showVersionFlag {
fmt.Println("App-Version:", metadata.GoTomatoVersion) fmt.Println("Server-Version:", metadata.GoTomatoVersion)
fmt.Println("Protocol-Version:", metadata.ProtocolVersion) fmt.Println("Protocol-Version:", metadata.ProtocolVersion)
os.Exit(0) os.Exit(0)
} }
// set server config
serverConfig := models.ServerConfig{ serverConfig := models.ServerConfig{
ListenAddress: *listenAddress, ListenAddress: *listenAddress,
ListenPort: *listenPort, ListenPort: *listenPort,
} }
shared.PomodoroPassword = *password shared.PomodoroPassword = *password
// define listen address for websocket
listen := fmt.Sprintf("%s:%d", serverConfig.ListenAddress, serverConfig.ListenPort) listen := fmt.Sprintf("%s:%d", serverConfig.ListenAddress, serverConfig.ListenPort)
// start connection handler and broadcast
http.HandleFunc("/ws", websocket.HandleConnection) http.HandleFunc("/ws", websocket.HandleConnection)
go websocket.SendPermanentBroadCastMessage() go websocket.SendPermanentBroadCastMessage()
log.Info("GoTomato started", "version", metadata.GoTomatoVersion) log.Info("GoTomato started", "version", metadata.GoTomatoVersion)
log.Info("Websocket listening", "address", listen) log.Info("Websocket listening", "address", listen)
// start the listener
err := http.ListenAndServe(listen, nil) err := http.ListenAndServe(listen, nil)
if err != nil { if err != nil {
log.Fatal("Error starting server:", "msg", err) log.Fatal("Error starting server:", "msg", err)

View file

@ -2,5 +2,5 @@ package metadata
import "strings" import "strings"
const GoTomatoVersion = "v0.0.4" // The GoTomato Version const GoTomatoVersion = "v0.0.4" // The GoTomato version
var ProtocolVersion = strings.Split(GoTomatoVersion, ".")[0] var ProtocolVersion = strings.Split(GoTomatoVersion, ".")[0] // The protocol version

View file

@ -11,6 +11,8 @@ import (
var mu sync.Mutex // to synchronize access to shared state var mu sync.Mutex // to synchronize access to shared state
var timer Timer var timer Timer
// Update `State` with the remaining seconds of the passed timer
// until the timer sends an End or Abort signal
func waitForTimer(t Timer) bool { func waitForTimer(t Timer) bool {
for { for {
select { select {
@ -24,7 +26,7 @@ func waitForTimer(t Timer) bool {
} }
} }
// RunPomodoro iterates the Pomodoro work/break sessions. // RunPomodoro iterates the Pomodoro work/break sessions
func RunPomodoro() { func RunPomodoro() {
mu.Lock() mu.Lock()
shared.State.Ongoing = true shared.State.Ongoing = true
@ -38,12 +40,14 @@ func RunPomodoro() {
shared.State.Session = session shared.State.Session = session
// Work
shared.State.Mode = "Work" shared.State.Mode = "Work"
go timer.Start(pomodoroConfig.Work) go timer.Start(pomodoroConfig.Work)
if !waitForTimer(timer) { if !waitForTimer(timer) {
break break
} }
// Breaks
if session < pomodoroConfig.Sessions { if session < pomodoroConfig.Sessions {
shared.State.Mode = "ShortBreak" shared.State.Mode = "ShortBreak"
go timer.Start(pomodoroConfig.ShortBreak) go timer.Start(pomodoroConfig.ShortBreak)
@ -56,6 +60,8 @@ func RunPomodoro() {
if !waitForTimer(timer) { if !waitForTimer(timer) {
break break
} }
// send "End" state for one second
shared.State.Mode = "End" shared.State.Mode = "End"
time.Sleep(time.Second) time.Sleep(time.Second)
} }
@ -92,13 +98,13 @@ func ResumePomodoro() {
func IsPomodoroOngoing() bool { func IsPomodoroOngoing() bool {
mu.Lock() mu.Lock()
defer mu.Unlock() // Ensures that the mutex is unlocked after the function is done defer mu.Unlock()
return shared.State.Ongoing return shared.State.Ongoing
} }
func IsPomodoroPaused() bool { func IsPomodoroPaused() bool {
mu.Lock() mu.Lock()
defer mu.Unlock() // Ensures that the mutex is unlocked after the function is done defer mu.Unlock()
return shared.State.Paused return shared.State.Paused
} }

View file

@ -2,15 +2,17 @@ package pomodoro
import "time" import "time"
// Represents a timer
type Timer struct { type Timer struct {
TimeLeft chan int // time left TimeLeft chan int // signals time left on every second
End chan bool // signal on successful end End chan bool // signal on successful end
Abort chan bool // signal on premature end Abort chan bool // signal on premature end
stop chan bool stop chan bool // internal channel
wait chan bool wait chan bool // internal channel
resume chan bool resume chan bool // internal channel
} }
// Initializes a new timer with fresh channels
func (t Timer) Init() Timer { func (t Timer) Init() Timer {
return Timer{ return Timer{
TimeLeft: make(chan int), TimeLeft: make(chan int),
@ -22,8 +24,10 @@ func (t Timer) Init() Timer {
} }
} }
// Start the timer
func (t *Timer) Start(duration int) { func (t *Timer) Start(duration int) {
tick := time.NewTicker(time.Second) tick := time.NewTicker(time.Second)
go func() {
for timeLeft := duration; timeLeft > 0; { for timeLeft := duration; timeLeft > 0; {
select { select {
case <-t.stop: case <-t.stop:
@ -41,6 +45,7 @@ func (t *Timer) Start(duration int) {
t.TimeLeft <- 0 t.TimeLeft <- 0
t.End <- true t.End <- true
}()
} }
func (t *Timer) Stop() { func (t *Timer) Stop() {

View file

@ -2,11 +2,13 @@ package shared
import "git.smsvc.net/pomodoro/GoTomato/pkg/models" import "git.smsvc.net/pomodoro/GoTomato/pkg/models"
// The default server config if nothing else is set
var DefaultServerConfig = models.ServerConfig{ var DefaultServerConfig = models.ServerConfig{
ListenAddress: "0.0.0.0", ListenAddress: "0.0.0.0",
ListenPort: 8080, ListenPort: 8080,
} }
// The default pomodoro config if nothing else is set
var DefaultPomodoroConfig = models.PomodoroConfig{ var DefaultPomodoroConfig = models.PomodoroConfig{
Work: 25 * 60, Work: 25 * 60,
ShortBreak: 5 * 60, ShortBreak: 5 * 60,

View file

@ -5,6 +5,7 @@ import (
"git.smsvc.net/pomodoro/GoTomato/pkg/models" "git.smsvc.net/pomodoro/GoTomato/pkg/models"
) )
// The global state of the pomodoro
var State = models.ServerMessage{ var State = models.ServerMessage{
Mode: "Idle", Mode: "Idle",
Settings: DefaultPomodoroConfig, Settings: DefaultPomodoroConfig,
@ -15,4 +16,5 @@ var State = models.ServerMessage{
ProtocolVersion: metadata.ProtocolVersion, ProtocolVersion: metadata.ProtocolVersion,
} }
// The password needed to execute client commands or change the pomodoro config
var PomodoroPassword string var PomodoroPassword string

View file

@ -9,7 +9,7 @@ import (
"git.smsvc.net/pomodoro/GoTomato/internal/shared" "git.smsvc.net/pomodoro/GoTomato/internal/shared"
) )
// sends continous messages to all connected WebSocket clients. // Sends continous messages to all connected WebSocket clients
func SendPermanentBroadCastMessage() { func SendPermanentBroadCastMessage() {
tick := time.NewTicker(time.Second) tick := time.NewTicker(time.Second)
for { for {

View file

@ -10,7 +10,7 @@ import (
"git.smsvc.net/pomodoro/GoTomato/pkg/models" "git.smsvc.net/pomodoro/GoTomato/pkg/models"
) )
// handleClientCommands listens for commands from WebSocket clients // Listens for commands from a client and handles them
func handleClientCommands(ws *websocket.Conn) { func handleClientCommands(ws *websocket.Conn) {
for { for {
var clientCommand models.ClientCommand var clientCommand models.ClientCommand

View file

@ -13,12 +13,12 @@ import (
var Clients = make(map[*websocket.Conn]*models.Client) var Clients = make(map[*websocket.Conn]*models.Client)
var mu sync.Mutex // Mutex to protect access to the Clients map var mu sync.Mutex // Mutex to protect access to the Clients map
// Upgrader to upgrade HTTP requests to WebSocket connections // Upgrade HTTP requests to WebSocket connections
var upgrader = websocket.Upgrader{ var upgrader = websocket.Upgrader{
CheckOrigin: func(r *http.Request) bool { return true }, CheckOrigin: func(r *http.Request) bool { return true },
} }
// HandleConnection upgrades HTTP requests to WebSocket connections and manages the client lifecycle. // Upgrades HTTP requests to WebSocket connections and manages the client lifecycle
func HandleConnection(w http.ResponseWriter, r *http.Request) { func HandleConnection(w http.ResponseWriter, r *http.Request) {
// Upgrade initial GET request to a WebSocket // Upgrade initial GET request to a WebSocket
ws, err := upgrader.Upgrade(w, r, nil) ws, err := upgrader.Upgrade(w, r, nil)

View file

@ -6,19 +6,21 @@ import (
"sync" "sync"
) )
// ClientCommand represents a command from the client (start/stop). // Represents a command from the client (start/stop)
type ClientCommand struct { type ClientCommand struct {
Command string `json:"command"` // comman send to the server Command string `json:"command"` // Command send to the server
Password string `json:"password"` // pomodoro control password Password string `json:"password"` // Pomodoro control password
Settings PomodoroConfig `json:"settings"` // pomodoro config Settings PomodoroConfig `json:"settings"` // Pomodoro config
} }
// Represents a single client
type Client struct { type Client struct {
Conn *websocket.Conn Conn *websocket.Conn // Websocket connection of the client
Mutex sync.Mutex Mutex sync.Mutex // Mutex used to lock
} }
// It automatically locks and unlocks the mutex to ensure that only one goroutine can write at a time. // Sends a message to the websocket.
// Automatically locks and unlocks the client mutex, to ensure that only one goroutine can write at a time.
func (c *Client) SendMessage(messageType int, data []byte) error { func (c *Client) SendMessage(messageType int, data []byte) error {
c.Mutex.Lock() c.Mutex.Lock()
defer c.Mutex.Unlock() defer c.Mutex.Unlock()

View file

@ -1,12 +1,14 @@
package models package models
// Represents the configuration of a pomodoro
type PomodoroConfig struct { type PomodoroConfig struct {
Work int `json:"work"` // Length of work sessions in seconds Work int `json:"work"` // Length of work sessions in seconds
ShortBreak int `json:"shortBreak"` // Length of short break in seconds ShortBreak int `json:"shortBreak"` // Length of short break in seconds
LongBreak int `json:"longBreak"` // Length if ling break in seconds LongBreak int `json:"longBreak"` // Length of long break in seconds
Sessions int `json:"sessions"` // Number of total sessions Sessions int `json:"sessions"` // Number of total sessions
} }
// Represents the server configuration
type ServerConfig struct { type ServerConfig struct {
ListenAddress string `json:"listenAddress"` // Server listen address ListenAddress string `json:"listenAddress"` // Server listen address
ListenPort int `json:"listenPort"` // Server listen port ListenPort int `json:"listenPort"` // Server listen port

View file

@ -1,12 +1,12 @@
package models package models
// ServerMessage represents the data sent to the client via WebSocket. // Represents the data sent to the client via WebSocket
type ServerMessage struct { type ServerMessage struct {
Mode string `json:"mode"` // "Idle", "Work", "ShortBreak", "LongBreak" or "End" Mode string `json:"mode"` // "Idle", "Work", "ShortBreak", "LongBreak" or "End"
Settings PomodoroConfig `json:"settings"` // The currrent pomodoro settings Settings PomodoroConfig `json:"settings"` // The currrent pomodoro settings
Session int `json:"session"` // Current session number Session int `json:"session"` // Current session number
TimeLeft int `json:"time_left"` // Remaining time in seconds TimeLeft int `json:"time_left"` // Remaining time in seconds
Ongoing bool `json:"ongoing"` // Ongoing pomodoro Ongoing bool `json:"ongoing"` // Pomodoro ongoing
Paused bool `json:"paused"` // Is timer paused Paused bool `json:"paused"` // Is timer paused
ProtocolVersion string `json:"version"` // Version of the server ProtocolVersion string `json:"version"` // Version of the protocol
} }