package main import ( "bufio" "context" "flag" "fmt" "log" "os" "strconv" "strings" "time" "spektr/internal/config" "spektr/internal/discovery" grpcclient "spektr/internal/grpc" pb "spektr/internal/grpc/pb" ) func main() { configPath := flag.String("config", "configs/config.yaml", "path to config file") addrOverride := flag.String("addr", "192.168.1.10:50051", "explicit gRPC address") flag.Parse() cfg, err := config.Load(*configPath) if err != nil { log.Fatal(err) } scanner := bufio.NewScanner(os.Stdin) for { addr := resolveAddr(cfg, *addrOverride) client, err := waitForClient(addr) if err != nil { log.Fatal(err) } fmt.Println("Подключено к", addr) printHelp() lostConnection := false for { fmt.Print("> ") if !scanner.Scan() { _ = client.Close() return } line := strings.TrimSpace(scanner.Text()) switch { case line == "", line == "help": printHelp() case line == "exit": _ = client.Close() return case line == "av": lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_Av{Av: &pb.AvCommand{}}}) case line == "get": lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_GetFrequency{GetFrequency: &pb.GetFrequencyCommand{}}}) case strings.HasPrefix(line, "af "): value := strings.TrimSpace(strings.TrimPrefix(line, "af ")) mhz, err := strconv.ParseUint(value, 10, 32) if err != nil { fmt.Println("invalid frequency") continue } lostConnection = !sendAndPrint(client, &pb.ClientMessage{RequestId: requestID(), Payload: &pb.ClientMessage_SetFrequency{SetFrequency: &pb.SetFrequencyCommand{Frequency: uint32(mhz)}}}) default: fmt.Println("unknown command") } if lostConnection { _ = client.Close() fmt.Println("Соединение потеряно, ожидаю демон...") time.Sleep(2 * time.Second) break } } } } func resolveAddr(cfg config.Config, override string) string { if override != "" { return override } ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) defer cancel() discovered, err := discovery.Discover(ctx, cfg.DiscoveryPort) if err == nil && discovered != "" { return discovered } return cfg.GRPCAdvertiseAddr } func waitForClient(addr string) (*grpcclient.Client, error) { for { dialCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second) client, err := grpcclient.Dial(dialCtx, addr) cancel() if err == nil { return client, nil } fmt.Println("Демон недоступен, жду подключение...") time.Sleep(2 * time.Second) } } func printHelp() { fmt.Println("Доступные команды:") fmt.Println(" av - проверить связь с устройством") fmt.Println(" af <мгц> - установить частоту, например: af 1880") fmt.Println(" get - показать текущую частоту") fmt.Println(" help - показать список команд") fmt.Println(" exit - выйти из CLI") } func sendAndPrint(client *grpcclient.Client, msg *pb.ClientMessage) bool { resp, err := client.Send(msg) if err != nil { fmt.Println("error:", err) return false } switch payload := resp.GetPayload().(type) { case *pb.ServerMessage_Av: fmt.Printf("HW: %s FW: %s\n", payload.Av.GetHw(), payload.Av.GetSw()) case *pb.ServerMessage_SetFrequency: fmt.Println("ok") case *pb.ServerMessage_GetFrequency: fmt.Printf("%s\n", formatFrequencyResponse(payload.GetFrequency.GetFrequency())) case *pb.ServerMessage_Error: fmt.Println("error:", payload.Error.GetMessage()) default: fmt.Println("unknown response") } return true } func requestID() string { return strconv.FormatInt(time.Now().UnixNano(), 10) } func formatFrequencyResponse(freq uint32) string { return fmt.Sprintf("f%07d", freq*100) }