Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
94 changes: 94 additions & 0 deletions scripts/daq/heartbeat.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
package main

import (
"bytes"
"crypto/rand"
"encoding/hex"
"encoding/json"
"fmt"
"net"
"net/http"
"os"
"time"

"go.uber.org/zap"
)

type HeartbeatHandler struct {
serverURL string
vehicleID string
sessionID string
can0 net.Conn
can1 net.Conn
logger *zap.Logger
}

func NewHeartbeatHandler(can0, can1 net.Conn, logger *zap.Logger) *HeartbeatHandler {

// Configuring Vehicle ID
vehicleID := os.Getenv("VEHICLE_ID")
if vehicleID == "" {
logger.Error("VEHICLE_ID not found, using default.")
vehicleID = "default"
}

// Generating Session ID
sessionBytes := make([]byte, 16)
_, err := rand.Read(sessionBytes)
var sessionID string
if err != nil {
logger.Warn("Failed to generate session ID, defaulting to time based session ID.")
sessionID = fmt.Sprintf("fallback-%d", time.Now().UnixNano())
} else {
sessionID = hex.EncodeToString(sessionBytes)
}

serverURL := os.Getenv("SERVER_URL")
if serverURL == "" {
logger.Error("SERVER_URL for heartbeatnot found")
}

return &HeartbeatHandler{
serverURL: serverURL,
vehicleID: vehicleID,
sessionID: sessionID,
can0: can0,
can1: can1,
logger: logger,
}
}

func (h *HeartbeatHandler) SendHeartbeat() error {
can0Active, can1Active := h.checkCAN()

payload := map[string]interface{}{
"timestamp": time.Now().UnixMilli(),
"vehicle_id": h.vehicleID,
"session_id": h.sessionID,
"can_status": map[string]bool{
"can0": can0Active,
"can1": can1Active,
},
}

jsonPayload, err := json.Marshal(payload)
if err != nil {
h.logger.Error("Failed to convert heartbeat payload to JSON", zap.Error(err))
return err
}

response, err := http.Post(h.serverURL, "application/json", bytes.NewBuffer(jsonPayload))
if err != nil {
h.logger.Error("Failed to send heartbeat", zap.Error(err))
return err
}
defer response.Body.Close()

return nil
}

func (h *HeartbeatHandler) checkCAN() (bool, bool) {
can0Active := h.can0 != nil
can1Active := h.can1 != nil
return can0Active, can1Active
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I haven't touched go in a while but can you confirm if this actually checks if the can channels are active? (assuming that this is the goal)

are we certain that if HeartbeatHandler -> can0 : net.Conn is simply initialized that it would mean its active/online?

Again I'm not really sure how this works so just lmk if its right or not.

11 changes: 11 additions & 0 deletions scripts/daq/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@ func main() {

logger, _ := zap.NewDevelopment()

heartbeat := NewHeartbeatHandler(can0, can1, logger)

telemetry, err := NewTelemetryHandler("./can_cache.sqlite")
if err != nil {
panic(err)
Expand All @@ -41,13 +43,22 @@ func main() {
manager1.Start(context.Background())

uploadTimer := time.NewTimer(time.Second)

heartbeatInterval := time.NewTicker(3 * time.Second)
defer heartbeatInterval.Stop()

for {
select {
case <-uploadTimer.C:
err = telemetry.Upload()
if err != nil {
fmt.Printf("failed to upload telemetry data: %v\n", err)
}
case <-heartbeatInterval.C:
err = heartbeat.SendHeartbeat()
if err != nil {
logger.Error("Failed to send heartbeat", zap.Error(err))
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we name both to Interval? Feels weird to have one be uploadTimer and heartbeatInterval

Suggested change
}
uploadInterval := time.NewTimer(time.Second)
heartbeatInterval := time.NewTicker(3 * time.Second)
defer heartbeatInterval.Stop()
for {
select {
case <-uploadInterval.C:
err = telemetry.Upload()
if err != nil {
fmt.Printf("failed to upload telemetry data: %v\n", err)
}
case <-heartbeatInterval.C:
err = heartbeat.SendHeartbeat()
if err != nil {
logger.Error("Failed to send heartbeat", zap.Error(err))
}
}
}

}