Skip to content

Commit

Permalink
Started adding API endpoints, worked on race condition issues
Browse files Browse the repository at this point in the history
  • Loading branch information
jm20122012 committed Jul 3, 2024
1 parent d79a555 commit f1b6c88
Show file tree
Hide file tree
Showing 7 changed files with 136 additions and 33 deletions.
14 changes: 10 additions & 4 deletions controller/cmd/controllerservice/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -62,14 +62,20 @@ func main() {
cancel()
}(cancel)

var apiHndlr api.IApiHandler
wg := &sync.WaitGroup{}

// Instantiate the API and start it
wg.Add(1)
api := api.NewApi(ctx, logger, wg)
go api.Run()

var apiHndlr cs.IApiHandler
if cfg.AppConfig.DryRun {
apiHndlr = api.NewDryRunApiHandler(ctx, logger, cfg.AppConfig.ApiUrl)
apiHndlr = cs.NewDryRunApiHandler(ctx, logger, cfg.AppConfig.ApiUrl)
} else {
apiHndlr = api.NewApiHandler(ctx, logger, cfg.AppConfig.ApiUrl)
apiHndlr = cs.NewApiHandler(ctx, logger, cfg.AppConfig.ApiUrl)
}

wg := &sync.WaitGroup{}
wg.Add(1)
controllerService := cs.NewControllerService(
ctx,
Expand Down
86 changes: 74 additions & 12 deletions controller/internal/api/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,30 +2,92 @@ package api

import (
"context"
"encoding/json"
"fmt"
"log/slog"
"sprinkler-controller-service/internal/config"
"net/http"
"sync"
"time"
)

type IApiHandler interface {
SendSprinklerEventRequest(event *config.ScheduleItem) error
type StatusResponse struct {
MicrocontrollerStatus string `json:"microcontrollerStatus"`
SprinklerStatus []SprinklerStatus `json:"sprinklerStatus"`
}

type ApiHandler struct {
type SprinklerStatus struct {
ZoneName string `json:"zoneName"`
IsActive bool `json:"isActive"`
}
type Api struct {
Ctx context.Context
Logger *slog.Logger
ApiUrl string
Wg *sync.WaitGroup
Mux *http.ServeMux
}

func NewApiHandler(ctx context.Context, logger *slog.Logger, apiUrl string) IApiHandler {
return &ApiHandler{
func NewApi(ctx context.Context, logger *slog.Logger, wg *sync.WaitGroup) *Api {
mux := http.NewServeMux()
mux.HandleFunc("GET /ping", pingHandler)
mux.HandleFunc("GET /system-status", getSystemStatus)

return &Api{
Ctx: ctx,
Mux: mux,
Logger: logger,
ApiUrl: apiUrl,
Wg: wg,
}
}

func (a *Api) Run() {
server := &http.Server{
Addr: ":8080",
Handler: a.Mux,
}

a.Logger.Info("Starting API...")
err := server.ListenAndServe()
if err != nil {
a.Logger.Error("Error starting API", "error", err)
}

<-a.Ctx.Done()

a.Logger.Info("Done context signal received in API - exiting")
shutdownCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

err = server.Shutdown(shutdownCtx)
if err != nil {
a.Logger.Error("Error shutting down http server", "error", err)
}

a.Wg.Done()
}

func pingHandler(w http.ResponseWriter, r *http.Request) {
resp := map[string]string{
"ping": "OK",
}
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(resp)
}

func (a *ApiHandler) SendSprinklerEventRequest(event *config.ScheduleItem) error {
a.Logger.Info("Sending event to API", "event", event)
func getSystemStatus(w http.ResponseWriter, r *http.Request) {

resp := StatusResponse{
MicrocontrollerStatus: "OKAY",
}

for i := 0; i < 5; i++ {
s := SprinklerStatus{
ZoneName: fmt.Sprintf("zone%d", i),
IsActive: false,
}
resp.SprinklerStatus = append(resp.SprinklerStatus, s)
}

return nil
w.Header().Set("Content-Type", "application-json")
w.WriteHeader(http.StatusOK)
json.NewEncoder(w).Encode(resp)
}
1 change: 1 addition & 0 deletions controller/internal/api/control.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package api
1 change: 1 addition & 0 deletions controller/internal/api/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package api
31 changes: 31 additions & 0 deletions controller/internal/controllerservice/apihandler.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package controllerservice

import (
"context"
"log/slog"
"sprinkler-controller-service/internal/config"
)

type IApiHandler interface {
SendSprinklerEventRequest(event *config.ScheduleItem) error
}

type ApiHandler struct {
Ctx context.Context
Logger *slog.Logger
ApiUrl string
}

func NewApiHandler(ctx context.Context, logger *slog.Logger, apiUrl string) IApiHandler {
return &ApiHandler{
Ctx: ctx,
Logger: logger,
ApiUrl: apiUrl,
}
}

func (a *ApiHandler) SendSprinklerEventRequest(event *config.ScheduleItem) error {
a.Logger.Info("Sending event to API", "event", event)

return nil
}
34 changes: 18 additions & 16 deletions controller/internal/controllerservice/controllerservice.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package controllerservice
import (
"context"
"log/slog"
"sprinkler-controller-service/internal/api"
"sprinkler-controller-service/internal/config"
"sync"
"time"
Expand All @@ -19,17 +18,17 @@ type ControllerService struct {
Wg *sync.WaitGroup
Logger *slog.Logger
Config *config.Config
ApiHandler api.IApiHandler
ApiHandler IApiHandler
LastResetDate time.Time
Mutex sync.Mutex
Mutex sync.RWMutex
TaskQueue chan *config.ScheduleItem
}

func NewControllerService(ctx context.Context,
wg *sync.WaitGroup,
logger *slog.Logger,
cfg *config.Config,
apiHdnlr api.IApiHandler,
apiHdnlr IApiHandler,
) *ControllerService {
return &ControllerService{
Ctx: ctx,
Expand All @@ -38,7 +37,7 @@ func NewControllerService(ctx context.Context,
Config: cfg,
ApiHandler: apiHdnlr,
LastResetDate: time.Now(),
Mutex: sync.Mutex{},
Mutex: sync.RWMutex{},
TaskQueue: make(chan *config.ScheduleItem, 100),
}
}
Expand Down Expand Up @@ -80,30 +79,31 @@ func (c *ControllerService) Run() {
for zoneName, zoneInfo := range c.Config.ZoneList {
c.Logger.Debug("Checking zone schedule", "zone", zoneName)
for idx := range zoneInfo.Schedule {
scheduleItem := zoneInfo.Schedule[idx]
currentTime := time.Now()

c.Logger.Debug("Comparing current and start times", "zone", zoneName, "current", currentTime, "startTime", scheduleItem.StartTime)
c.Logger.Debug("Comparing current and start times", "zone", zoneName, "current", currentTime, "startTime", zoneInfo.Schedule[idx].StartTime)

startTime, err := time.Parse(time.TimeOnly, scheduleItem.StartTime)
startTime, err := time.Parse(time.TimeOnly, zoneInfo.Schedule[idx].StartTime)
if err != nil {
c.Logger.Error("Error parsing start time", "startTime", scheduleItem.StartTime, "error", err)
c.Logger.Error("Error parsing start time", "startTime", zoneInfo.Schedule[idx].StartTime, "error", err)
}

duration := time.Duration(scheduleItem.DurationMinutes)
duration := time.Duration(zoneInfo.Schedule[idx].DurationMinutes)
endTime := startTime.Add(duration * time.Minute)

if currentTime.After(startTime) && !scheduleItem.Active {
c.Mutex.RLock()
if currentTime.After(startTime) && !zoneInfo.Schedule[idx].Active {
c.Logger.Debug("Zone is not active and current time exceeds start time for zone schedule item", "zone", zoneName, "currentTime", time.Now(), "startTime", startTime)
c.Logger.Info("Starting sprinkler event", "zoneName", zoneName, "currentTime", currentTime, "startTime", scheduleItem.StartTime, "endTime", endTime, "durationMinutes", scheduleItem.DurationMinutes)
c.TaskQueue <- &scheduleItem
c.Logger.Info("Starting sprinkler event", "zoneName", zoneName, "currentTime", currentTime, "startTime", zoneInfo.Schedule[idx].StartTime, "endTime", endTime, "durationMinutes", zoneInfo.Schedule[idx].DurationMinutes)
c.TaskQueue <- &zoneInfo.Schedule[idx]
}

if currentTime.After(endTime) && scheduleItem.Active {
if currentTime.After(endTime) && zoneInfo.Schedule[idx].Active {
c.Logger.Debug("Zone is active and current time exceeds end time for zone schedule item", "zone", zoneName, "currentTime", time.Now(), "startTime", startTime)
c.Logger.Info("Stopping sprinkler event", "zoneName", zoneName, "currentTime", currentTime, "startTime", scheduleItem.StartTime, "endTime", endTime, "durationMinutes", scheduleItem.DurationMinutes)
c.TaskQueue <- &scheduleItem
c.Logger.Info("Stopping sprinkler event", "zoneName", zoneName, "currentTime", currentTime, "startTime", zoneInfo.Schedule[idx].StartTime, "endTime", endTime, "durationMinutes", zoneInfo.Schedule[idx].DurationMinutes)
c.TaskQueue <- &zoneInfo.Schedule[idx]
}
c.Mutex.RUnlock()
}
}
}
Expand All @@ -126,9 +126,11 @@ func (c *ControllerService) TaskProcessor(wg *sync.WaitGroup, ctx context.Contex
c.Logger.Error("API request error", "event", t)
continue
}
c.Mutex.Lock()
t.Mutex.Lock()
t.Active = true
t.Mutex.Unlock()
c.Mutex.Unlock()
}
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package api
package controllerservice

import (
"context"
Expand Down

0 comments on commit f1b6c88

Please sign in to comment.