parent
a6f21edef4
commit
dd66141a1d
@ -1,14 +1,14 @@
|
|||||||
FROM golang:1.24.1-bullseye
|
FROM golang:1.24.1-bullseye
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY go.mod ./
|
COPY go.mod ./
|
||||||
# COPY go.sum ./ # Uncomment if you have a go.sum file
|
# COPY go.sum ./ # Uncomment if you have a go.sum file
|
||||||
|
|
||||||
RUN go mod download
|
RUN go mod download
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
RUN go build -o track-gopher ./examples/main.go
|
RUN go build -o track-gopher ./examples/main.go
|
||||||
|
|
||||||
ENTRYPOINT ["./track-gopher"]
|
ENTRYPOINT ["./track-gopher"]
|
||||||
@ -1,278 +1,278 @@
|
|||||||
package derby
|
package derby
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"go.bug.st/serial"
|
"go.bug.st/serial"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Result represents a single lane result
|
// Result represents a single lane result
|
||||||
type Result struct {
|
type Result struct {
|
||||||
Lane int
|
Lane int
|
||||||
Time float64
|
Time float64
|
||||||
FinishPlace int
|
FinishPlace int
|
||||||
FinishSymbol string
|
FinishSymbol string
|
||||||
}
|
}
|
||||||
|
|
||||||
// RaceStatus represents the current status of a race
|
// RaceStatus represents the current status of a race
|
||||||
type RaceStatus int
|
type RaceStatus int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
StatusIdle RaceStatus = iota
|
StatusIdle RaceStatus = iota
|
||||||
StatusRunning
|
StatusRunning
|
||||||
StatusFinished
|
StatusFinished
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event types that can be sent on the event channel
|
// Event types that can be sent on the event channel
|
||||||
type EventType int
|
type EventType int
|
||||||
|
|
||||||
const (
|
const (
|
||||||
EventRaceStart EventType = iota
|
EventRaceStart EventType = iota
|
||||||
EventLaneFinish
|
EventLaneFinish
|
||||||
EventRaceComplete
|
EventRaceComplete
|
||||||
)
|
)
|
||||||
|
|
||||||
// Event represents a race event
|
// Event represents a race event
|
||||||
type Event struct {
|
type Event struct {
|
||||||
Type EventType
|
Type EventType
|
||||||
Result *Result // Only populated for EventLaneFinish
|
Result *Result // Only populated for EventLaneFinish
|
||||||
}
|
}
|
||||||
|
|
||||||
// DerbyClock represents the connection to the derby clock device
|
// DerbyClock represents the connection to the derby clock device
|
||||||
type DerbyClock struct {
|
type DerbyClock struct {
|
||||||
port serial.Port
|
port serial.Port
|
||||||
reader *bufio.Reader
|
reader *bufio.Reader
|
||||||
eventChan chan Event
|
eventChan chan Event
|
||||||
status RaceStatus
|
status RaceStatus
|
||||||
results []*Result
|
results []*Result
|
||||||
mu sync.Mutex
|
mu sync.Mutex
|
||||||
stopReading chan struct{}
|
stopReading chan struct{}
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewDerbyClock creates a new connection to the derby clock
|
// NewDerbyClock creates a new connection to the derby clock
|
||||||
func NewDerbyClock(portName string, baudRate int) (*DerbyClock, error) {
|
func NewDerbyClock(portName string, baudRate int) (*DerbyClock, error) {
|
||||||
mode := &serial.Mode{
|
mode := &serial.Mode{
|
||||||
BaudRate: baudRate,
|
BaudRate: baudRate,
|
||||||
DataBits: 8,
|
DataBits: 8,
|
||||||
Parity: serial.NoParity,
|
Parity: serial.NoParity,
|
||||||
StopBits: serial.OneStopBit,
|
StopBits: serial.OneStopBit,
|
||||||
}
|
}
|
||||||
|
|
||||||
port, err := serial.Open(portName, mode)
|
port, err := serial.Open(portName, mode)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("failed to open serial port: %w", err)
|
return nil, fmt.Errorf("failed to open serial port: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
dc := &DerbyClock{
|
dc := &DerbyClock{
|
||||||
port: port,
|
port: port,
|
||||||
reader: bufio.NewReader(port),
|
reader: bufio.NewReader(port),
|
||||||
eventChan: make(chan Event, 10), // Buffer for up to 10 events
|
eventChan: make(chan Event, 10), // Buffer for up to 10 events
|
||||||
status: StatusIdle,
|
status: StatusIdle,
|
||||||
stopReading: make(chan struct{}),
|
stopReading: make(chan struct{}),
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start the reader goroutine
|
// Start the reader goroutine
|
||||||
go dc.readLoop()
|
go dc.readLoop()
|
||||||
|
|
||||||
return dc, nil
|
return dc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Close closes the connection to the derby clock
|
// Close closes the connection to the derby clock
|
||||||
func (dc *DerbyClock) Close() error {
|
func (dc *DerbyClock) Close() error {
|
||||||
// Signal the reading goroutine to stop
|
// Signal the reading goroutine to stop
|
||||||
close(dc.stopReading)
|
close(dc.stopReading)
|
||||||
|
|
||||||
// Close the event channel
|
// Close the event channel
|
||||||
close(dc.eventChan)
|
close(dc.eventChan)
|
||||||
|
|
||||||
// Close the serial port
|
// Close the serial port
|
||||||
return dc.port.Close()
|
return dc.port.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Events returns the channel for race events
|
// Events returns the channel for race events
|
||||||
func (dc *DerbyClock) Events() <-chan Event {
|
func (dc *DerbyClock) Events() <-chan Event {
|
||||||
return dc.eventChan
|
return dc.eventChan
|
||||||
}
|
}
|
||||||
|
|
||||||
// Reset sends the reset command to the derby clock
|
// Reset sends the reset command to the derby clock
|
||||||
func (dc *DerbyClock) Reset() error {
|
func (dc *DerbyClock) Reset() error {
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
defer dc.mu.Unlock()
|
defer dc.mu.Unlock()
|
||||||
|
|
||||||
// Clear any previous results
|
// Clear any previous results
|
||||||
dc.results = nil
|
dc.results = nil
|
||||||
dc.status = StatusIdle
|
dc.status = StatusIdle
|
||||||
|
|
||||||
// Send the reset command
|
// Send the reset command
|
||||||
_, err := dc.port.Write([]byte("R"))
|
_, err := dc.port.Write([]byte("R"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send reset command: %w", err)
|
return fmt.Errorf("failed to send reset command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// ForceEnd sends the force end command to the derby clock
|
// ForceEnd sends the force end command to the derby clock
|
||||||
func (dc *DerbyClock) ForceEnd() error {
|
func (dc *DerbyClock) ForceEnd() error {
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
defer dc.mu.Unlock()
|
defer dc.mu.Unlock()
|
||||||
|
|
||||||
if dc.status != StatusRunning {
|
if dc.status != StatusRunning {
|
||||||
return errors.New("race is not currently running")
|
return errors.New("race is not currently running")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the force end command
|
// Send the force end command
|
||||||
_, err := dc.port.Write([]byte("F"))
|
_, err := dc.port.Write([]byte("F"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fmt.Errorf("failed to send force end command: %w", err)
|
return fmt.Errorf("failed to send force end command: %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns the current race status
|
// Status returns the current race status
|
||||||
func (dc *DerbyClock) Status() RaceStatus {
|
func (dc *DerbyClock) Status() RaceStatus {
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
defer dc.mu.Unlock()
|
defer dc.mu.Unlock()
|
||||||
return dc.status
|
return dc.status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Results returns a copy of the current race results
|
// Results returns a copy of the current race results
|
||||||
func (dc *DerbyClock) Results() []*Result {
|
func (dc *DerbyClock) Results() []*Result {
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
defer dc.mu.Unlock()
|
defer dc.mu.Unlock()
|
||||||
|
|
||||||
// Make a copy of the results
|
// Make a copy of the results
|
||||||
resultsCopy := make([]*Result, len(dc.results))
|
resultsCopy := make([]*Result, len(dc.results))
|
||||||
for i, r := range dc.results {
|
for i, r := range dc.results {
|
||||||
resultCopy := *r
|
resultCopy := *r
|
||||||
resultsCopy[i] = &resultCopy
|
resultsCopy[i] = &resultCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultsCopy
|
return resultsCopy
|
||||||
}
|
}
|
||||||
|
|
||||||
// readLoop continuously reads from the serial port
|
// readLoop continuously reads from the serial port
|
||||||
func (dc *DerbyClock) readLoop() {
|
func (dc *DerbyClock) readLoop() {
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-dc.stopReading:
|
case <-dc.stopReading:
|
||||||
return
|
return
|
||||||
default:
|
default:
|
||||||
line, err := dc.reader.ReadString('\n')
|
line, err := dc.reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if err != io.EOF {
|
if err != io.EOF {
|
||||||
// Only log if it's not EOF
|
// Only log if it's not EOF
|
||||||
fmt.Printf("Error reading from serial port: %v\n", err)
|
fmt.Printf("Error reading from serial port: %v\n", err)
|
||||||
}
|
}
|
||||||
time.Sleep(100 * time.Millisecond)
|
time.Sleep(100 * time.Millisecond)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Process the line
|
// Process the line
|
||||||
dc.processLine(line)
|
dc.processLine(line)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// processLine handles a line of text from the derby clock
|
// processLine handles a line of text from the derby clock
|
||||||
func (dc *DerbyClock) processLine(line string) {
|
func (dc *DerbyClock) processLine(line string) {
|
||||||
// Trim any whitespace and carriage returns
|
// Trim any whitespace and carriage returns
|
||||||
line = strings.TrimSpace(line)
|
line = strings.TrimSpace(line)
|
||||||
|
|
||||||
if line == "C" {
|
if line == "C" {
|
||||||
// Race has started
|
// Race has started
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
dc.status = StatusRunning
|
dc.status = StatusRunning
|
||||||
dc.results = nil
|
dc.results = nil
|
||||||
dc.mu.Unlock()
|
dc.mu.Unlock()
|
||||||
|
|
||||||
// Send race start event
|
// Send race start event
|
||||||
dc.eventChan <- Event{Type: EventRaceStart}
|
dc.eventChan <- Event{Type: EventRaceStart}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if this is a results line
|
// Check if this is a results line
|
||||||
if strings.Contains(line, "=") {
|
if strings.Contains(line, "=") {
|
||||||
results := parseResults(line)
|
results := parseResults(line)
|
||||||
|
|
||||||
dc.mu.Lock()
|
dc.mu.Lock()
|
||||||
// Add results to our stored results
|
// Add results to our stored results
|
||||||
for _, result := range results {
|
for _, result := range results {
|
||||||
dc.results = append(dc.results, result)
|
dc.results = append(dc.results, result)
|
||||||
|
|
||||||
// Send lane finish event
|
// Send lane finish event
|
||||||
dc.eventChan <- Event{
|
dc.eventChan <- Event{
|
||||||
Type: EventLaneFinish,
|
Type: EventLaneFinish,
|
||||||
Result: result,
|
Result: result,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// If we have results, the race is now finished
|
// If we have results, the race is now finished
|
||||||
if len(results) > 0 {
|
if len(results) > 0 {
|
||||||
dc.status = StatusFinished
|
dc.status = StatusFinished
|
||||||
|
|
||||||
// Send race complete event
|
// Send race complete event
|
||||||
dc.eventChan <- Event{Type: EventRaceComplete}
|
dc.eventChan <- Event{Type: EventRaceComplete}
|
||||||
}
|
}
|
||||||
dc.mu.Unlock()
|
dc.mu.Unlock()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// parseResults parses a line of results from the derby clock
|
// parseResults parses a line of results from the derby clock
|
||||||
func parseResults(line string) []*Result {
|
func parseResults(line string) []*Result {
|
||||||
parts := strings.Split(line, " ")
|
parts := strings.Split(line, " ")
|
||||||
results := make([]*Result, 0, len(parts))
|
results := make([]*Result, 0, len(parts))
|
||||||
|
|
||||||
for i, part := range parts {
|
for i, part := range parts {
|
||||||
if !strings.Contains(part, "=") {
|
if !strings.Contains(part, "=") {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format is "n=0.0000c"
|
// Format is "n=0.0000c"
|
||||||
laneStr := strings.Split(part, "=")[0]
|
laneStr := strings.Split(part, "=")[0]
|
||||||
timeAndPlace := strings.Split(part, "=")[1]
|
timeAndPlace := strings.Split(part, "=")[1]
|
||||||
|
|
||||||
if len(timeAndPlace) < 2 {
|
if len(timeAndPlace) < 2 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extract time and place character
|
// Extract time and place character
|
||||||
timeStr := timeAndPlace[:len(timeAndPlace)-1]
|
timeStr := timeAndPlace[:len(timeAndPlace)-1]
|
||||||
placeChar := timeAndPlace[len(timeAndPlace)-1:]
|
placeChar := timeAndPlace[len(timeAndPlace)-1:]
|
||||||
|
|
||||||
// Parse lane number
|
// Parse lane number
|
||||||
lane, err := strconv.Atoi(laneStr)
|
lane, err := strconv.Atoi(laneStr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse time
|
// Parse time
|
||||||
timeVal, err := strconv.ParseFloat(timeStr, 64)
|
timeVal, err := strconv.ParseFloat(timeStr, 64)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Determine place
|
// Determine place
|
||||||
place := i + 1
|
place := i + 1
|
||||||
|
|
||||||
// Create result
|
// Create result
|
||||||
result := &Result{
|
result := &Result{
|
||||||
Lane: lane,
|
Lane: lane,
|
||||||
Time: timeVal,
|
Time: timeVal,
|
||||||
FinishPlace: place,
|
FinishPlace: place,
|
||||||
FinishSymbol: placeChar,
|
FinishSymbol: placeChar,
|
||||||
}
|
}
|
||||||
|
|
||||||
results = append(results, result)
|
results = append(results, result)
|
||||||
}
|
}
|
||||||
|
|
||||||
return results
|
return results
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,124 +1,124 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"bufio"
|
"bufio"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
"strings"
|
"strings"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"track-gopher/derby"
|
"track-gopher/derby"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
// Replace with your actual serial port
|
// Replace with your actual serial port
|
||||||
portName := "/dev/ttyACM0"
|
portName := "/dev/ttyACM0"
|
||||||
if len(os.Args) > 1 {
|
if len(os.Args) > 1 {
|
||||||
portName = os.Args[1]
|
portName = os.Args[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create a new derby clock connection
|
// Create a new derby clock connection
|
||||||
clock, err := derby.NewDerbyClock(portName, 9600)
|
clock, err := derby.NewDerbyClock(portName, 9600)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error opening derby clock: %v\n", err)
|
fmt.Printf("Error opening derby clock: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
defer clock.Close()
|
defer clock.Close()
|
||||||
|
|
||||||
// Set up signal handling for clean shutdown
|
// Set up signal handling for clean shutdown
|
||||||
sigChan := make(chan os.Signal, 1)
|
sigChan := make(chan os.Signal, 1)
|
||||||
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
||||||
|
|
||||||
// Reset the clock to start fresh
|
// Reset the clock to start fresh
|
||||||
if err := clock.Reset(); err != nil {
|
if err := clock.Reset(); err != nil {
|
||||||
fmt.Printf("Error resetting clock: %v\n", err)
|
fmt.Printf("Error resetting clock: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
fmt.Println("Clock reset. Ready to start race.")
|
fmt.Println("Clock reset. Ready to start race.")
|
||||||
|
|
||||||
// Print instructions
|
// Print instructions
|
||||||
fmt.Println("\nCommands:")
|
fmt.Println("\nCommands:")
|
||||||
fmt.Println(" r - Reset the clock")
|
fmt.Println(" r - Reset the clock")
|
||||||
fmt.Println(" f - Force end the race")
|
fmt.Println(" f - Force end the race")
|
||||||
fmt.Println(" q - Quit the program")
|
fmt.Println(" q - Quit the program")
|
||||||
fmt.Println(" ? - Show this help message")
|
fmt.Println(" ? - Show this help message")
|
||||||
|
|
||||||
// Process events from the clock
|
// Process events from the clock
|
||||||
go func() {
|
go func() {
|
||||||
for event := range clock.Events() {
|
for event := range clock.Events() {
|
||||||
switch event.Type {
|
switch event.Type {
|
||||||
case derby.EventRaceStart:
|
case derby.EventRaceStart:
|
||||||
fmt.Println("\n🏁 Race started!")
|
fmt.Println("\n🏁 Race started!")
|
||||||
|
|
||||||
case derby.EventLaneFinish:
|
case derby.EventLaneFinish:
|
||||||
result := event.Result
|
result := event.Result
|
||||||
fmt.Printf("🚗 Lane %d finished in place %d with time %.4f seconds\n",
|
fmt.Printf("🚗 Lane %d finished in place %d with time %.4f seconds\n",
|
||||||
result.Lane, result.FinishPlace, result.Time)
|
result.Lane, result.FinishPlace, result.Time)
|
||||||
|
|
||||||
case derby.EventRaceComplete:
|
case derby.EventRaceComplete:
|
||||||
fmt.Println("\n🏆 Race complete! Final results:")
|
fmt.Println("\n🏆 Race complete! Final results:")
|
||||||
for _, result := range clock.Results() {
|
for _, result := range clock.Results() {
|
||||||
fmt.Printf("Place %d: Lane %d - %.4f seconds\n",
|
fmt.Printf("Place %d: Lane %d - %.4f seconds\n",
|
||||||
result.FinishPlace, result.Lane, result.Time)
|
result.FinishPlace, result.Lane, result.Time)
|
||||||
}
|
}
|
||||||
fmt.Println("\nEnter command (r/f/q/?):")
|
fmt.Println("\nEnter command (r/f/q/?):")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Handle keyboard input
|
// Handle keyboard input
|
||||||
go func() {
|
go func() {
|
||||||
reader := bufio.NewReader(os.Stdin)
|
reader := bufio.NewReader(os.Stdin)
|
||||||
for {
|
for {
|
||||||
fmt.Print("Enter command (r/f/q/?): ")
|
fmt.Print("Enter command (r/f/q/?): ")
|
||||||
input, err := reader.ReadString('\n')
|
input, err := reader.ReadString('\n')
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Error reading input: %v\n", err)
|
fmt.Printf("Error reading input: %v\n", err)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trim whitespace and convert to lowercase
|
// Trim whitespace and convert to lowercase
|
||||||
command := strings.TrimSpace(strings.ToLower(input))
|
command := strings.TrimSpace(strings.ToLower(input))
|
||||||
|
|
||||||
switch command {
|
switch command {
|
||||||
case "r":
|
case "r":
|
||||||
fmt.Println("Resetting clock...")
|
fmt.Println("Resetting clock...")
|
||||||
if err := clock.Reset(); err != nil {
|
if err := clock.Reset(); err != nil {
|
||||||
fmt.Printf("Error resetting clock: %v\n", err)
|
fmt.Printf("Error resetting clock: %v\n", err)
|
||||||
} else {
|
} else {
|
||||||
fmt.Println("Clock reset. Ready to start race.")
|
fmt.Println("Clock reset. Ready to start race.")
|
||||||
}
|
}
|
||||||
|
|
||||||
case "f":
|
case "f":
|
||||||
fmt.Println("Forcing race to end...")
|
fmt.Println("Forcing race to end...")
|
||||||
if err := clock.ForceEnd(); err != nil {
|
if err := clock.ForceEnd(); err != nil {
|
||||||
fmt.Printf("Error forcing race end: %v\n", err)
|
fmt.Printf("Error forcing race end: %v\n", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
case "q":
|
case "q":
|
||||||
fmt.Println("Quitting...")
|
fmt.Println("Quitting...")
|
||||||
sigChan <- syscall.SIGTERM
|
sigChan <- syscall.SIGTERM
|
||||||
return
|
return
|
||||||
|
|
||||||
case "?":
|
case "?":
|
||||||
fmt.Println("\nCommands:")
|
fmt.Println("\nCommands:")
|
||||||
fmt.Println(" r - Reset the clock")
|
fmt.Println(" r - Reset the clock")
|
||||||
fmt.Println(" f - Force end the race")
|
fmt.Println(" f - Force end the race")
|
||||||
fmt.Println(" q - Quit the program")
|
fmt.Println(" q - Quit the program")
|
||||||
fmt.Println(" ? - Show this help message")
|
fmt.Println(" ? - Show this help message")
|
||||||
|
|
||||||
default:
|
default:
|
||||||
if command != "" {
|
if command != "" {
|
||||||
fmt.Println("Unknown command. Type ? for help.")
|
fmt.Println("Unknown command. Type ? for help.")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Wait for signal to exit
|
// Wait for signal to exit
|
||||||
<-sigChan
|
<-sigChan
|
||||||
fmt.Println("Shutting down...")
|
fmt.Println("Shutting down...")
|
||||||
time.Sleep(500 * time.Millisecond) // Give a moment for any pending operations to complete
|
time.Sleep(500 * time.Millisecond) // Give a moment for any pending operations to complete
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in new issue