You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
190 lines
4.9 KiB
190 lines
4.9 KiB
package main
|
|
|
|
import (
|
|
"bufio"
|
|
"flag"
|
|
"fmt"
|
|
"os"
|
|
"os/signal"
|
|
"strings"
|
|
"sync"
|
|
"syscall"
|
|
"time"
|
|
|
|
"track-gopher/derby"
|
|
"track-gopher/web"
|
|
)
|
|
|
|
func main() {
|
|
// Parse command line flags
|
|
portName := flag.String("port", "/dev/ttyACM0", "Serial port for the derby clock")
|
|
baudRate := flag.Int("baud", 19200, "Baud rate for the serial port")
|
|
webPort := flag.Int("web-port", 8080, "Port for the web server")
|
|
noWeb := flag.Bool("no-web", false, "Disable web interface")
|
|
noTerminal := flag.Bool("no-terminal", false, "Disable terminal interface")
|
|
flag.Parse()
|
|
|
|
// Create a new derby clock connection
|
|
clock, err := derby.NewDerbyClock(*portName, *baudRate)
|
|
if err != nil {
|
|
fmt.Printf("Error opening derby clock: %v\n", err)
|
|
os.Exit(1)
|
|
}
|
|
defer clock.Close()
|
|
|
|
// Set up signal handling for clean shutdown
|
|
sigChan := make(chan os.Signal, 1)
|
|
signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM)
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
// Create a fan-out mechanism for events
|
|
eventBroadcaster := make(chan derby.Event, 10)
|
|
|
|
// Start the event broadcaster
|
|
go func() {
|
|
for event := range clock.Events() {
|
|
eventBroadcaster <- event
|
|
}
|
|
close(eventBroadcaster)
|
|
}()
|
|
|
|
// Start web interface if enabled
|
|
if !*noWeb {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
startWebInterface(clock, eventBroadcaster, *webPort, sigChan)
|
|
}()
|
|
}
|
|
|
|
// Start terminal interface if enabled
|
|
if !*noTerminal {
|
|
wg.Add(1)
|
|
go func() {
|
|
defer wg.Done()
|
|
startTerminalInterface(clock, eventBroadcaster, sigChan)
|
|
}()
|
|
}
|
|
|
|
// Wait for signal to exit
|
|
<-sigChan
|
|
fmt.Println("Shutting down...")
|
|
time.Sleep(500 * time.Millisecond) // Give a moment for any pending operations to complete
|
|
|
|
// Wait for all interfaces to shut down
|
|
wg.Wait()
|
|
}
|
|
|
|
// startWebInterface initializes and runs the web interface
|
|
func startWebInterface(clock *derby.DerbyClock, events <-chan derby.Event, webPort int, sigChan chan os.Signal) {
|
|
// Create and start the web server
|
|
server, err := web.NewServer(clock, events, webPort)
|
|
if err != nil {
|
|
fmt.Printf("Error creating web server: %v\n", err)
|
|
sigChan <- syscall.SIGTERM
|
|
return
|
|
}
|
|
|
|
fmt.Printf("Web interface available at http://localhost:%d\n", webPort)
|
|
|
|
// Start the web server
|
|
if err := server.Start(); err != nil {
|
|
fmt.Printf("Web server error: %v\n", err)
|
|
sigChan <- syscall.SIGTERM
|
|
}
|
|
}
|
|
|
|
// startTerminalInterface initializes and runs the terminal interface
|
|
func startTerminalInterface(clock *derby.DerbyClock, events <-chan derby.Event, sigChan chan os.Signal) {
|
|
fmt.Println("Terminal interface started")
|
|
|
|
// Reset the clock to start fresh
|
|
if err := clock.Reset(); err != nil {
|
|
fmt.Printf("Error resetting clock: %v\n", err)
|
|
sigChan <- syscall.SIGTERM
|
|
return
|
|
}
|
|
fmt.Println("Clock reset. Ready to start race.")
|
|
|
|
// Print instructions
|
|
fmt.Println("\nCommands:")
|
|
fmt.Println(" r - Reset the clock")
|
|
fmt.Println(" f - Force end the race")
|
|
fmt.Println(" q - Quit the program")
|
|
fmt.Println(" ? - Show this help message")
|
|
|
|
// Process events from the clock
|
|
go func() {
|
|
raceResults := make([]*derby.Result, 0)
|
|
for event := range events {
|
|
switch event.Type {
|
|
case derby.EventRaceStart:
|
|
fmt.Println("\n🏁 Race started!")
|
|
|
|
case derby.EventLaneFinish:
|
|
result := event.Result
|
|
fmt.Printf("🚗 Lane %d finished in place %d with time %.4f seconds\n",
|
|
result.Lane, result.FinishPlace, result.Time)
|
|
raceResults = append(raceResults, result)
|
|
|
|
case derby.EventRaceComplete:
|
|
fmt.Println("\n🏆 Race complete! Final results:")
|
|
for _, result := range raceResults {
|
|
fmt.Printf("Place %d: Lane %d - %.4f seconds\n",
|
|
result.FinishPlace, result.Lane, result.Time)
|
|
}
|
|
fmt.Println("\nEnter command (r/f/q/?):")
|
|
raceResults = nil
|
|
}
|
|
}
|
|
}()
|
|
|
|
// Handle keyboard input
|
|
reader := bufio.NewReader(os.Stdin)
|
|
for {
|
|
fmt.Print("Enter command (r/f/q/?): ")
|
|
input, err := reader.ReadString('\n')
|
|
if err != nil {
|
|
fmt.Printf("Error reading input: %v\n", err)
|
|
continue
|
|
}
|
|
|
|
// Trim whitespace and convert to lowercase
|
|
command := strings.TrimSpace(strings.ToLower(input))
|
|
|
|
switch command {
|
|
case "r":
|
|
fmt.Println("Resetting clock...")
|
|
if err := clock.Reset(); err != nil {
|
|
fmt.Printf("Error resetting clock: %v\n", err)
|
|
} else {
|
|
fmt.Println("Clock reset. Ready to start race.")
|
|
}
|
|
|
|
case "f":
|
|
fmt.Println("Forcing race to end...")
|
|
if err := clock.ForceEnd(); err != nil {
|
|
fmt.Printf("Error forcing race end: %v\n", err)
|
|
}
|
|
|
|
case "q":
|
|
fmt.Println("Quitting...")
|
|
sigChan <- syscall.SIGTERM
|
|
return
|
|
|
|
case "?":
|
|
fmt.Println("\nCommands:")
|
|
fmt.Println(" r - Reset the clock")
|
|
fmt.Println(" f - Force end the race")
|
|
fmt.Println(" q - Quit the program")
|
|
fmt.Println(" ? - Show this help message")
|
|
|
|
default:
|
|
if command != "" {
|
|
fmt.Println("Unknown command. Type ? for help.")
|
|
}
|
|
}
|
|
}
|
|
}
|