package main import ( "context" "errors" "fmt" "log" "strconv" "sync" "time" "spektr/internal/config" "spektr/internal/discovery" grpcclient "spektr/internal/grpc" pb "spektr/internal/grpc/pb" "github.com/wailsapp/wails/v2/pkg/runtime" ) const HardAddr = "192.168.1.10:50051" type App struct { ctx context.Context mu sync.Mutex client *grpcclient.Client currentFrequency string connectionStatus string } func NewApp() *App { return &App{ currentFrequency: "145", connectionStatus: "модуль не подключен", } } func (a *App) startup(ctx context.Context) { a.ctx = ctx go func() { client, status := connectClient(ctx) a.mu.Lock() defer a.mu.Unlock() a.client = client a.connectionStatus = status }() } func (a *App) Quit() { a.mu.Lock() client := a.client a.client = nil a.mu.Unlock() if client != nil { _ = client.Close() } runtime.Quit(a.ctx) } type DeviceResponse struct { Status string `json:"status"` Frequency string `json:"frequency"` } func (a *App) CheckConnection() (DeviceResponse, error) { log.Println("CheckConnection called") start := time.Now() log.Println("[TIMING] CheckConnection START") resp := DeviceResponse{ Status: "модуль не подключен", Frequency: a.getFrequency(), } //клиента вызываю в другом месте теперь //client, err := a.getClientOrConnect() //if err != nil { // return resp, err //} //avMsg, err := client.Send(&pb.ClientMessage{ // RequestId: requestID(), // Payload: &pb.ClientMessage_Av{Av: &pb.AvCommand{}}, //}) t1 := time.Now() avMsg, err := a.sendWithReconnect(func() *pb.ClientMessage { return &pb.ClientMessage{ RequestId: requestID(), Payload: &pb.ClientMessage_Av{Av: &pb.AvCommand{}}, } }) log.Printf("[TIMING] AV request took: %v", time.Since(t1)) if err != nil { return resp, err } if errMsg := avMsg.GetError(); errMsg != nil { return resp, errors.New(errMsg.GetMessage()) } if av := avMsg.GetAv(); av != nil { resp.Status = fmt.Sprintf("HW: %s FW: %s", av.GetHw(), av.GetSw()) } //freqMsg, err := client.Send(&pb.ClientMessage{ // RequestId: requestID(), // Payload: &pb.ClientMessage_GetFrequency{GetFrequency: &pb.GetFrequencyCommand{}}, //}) t2 := time.Now() freqMsg, err := a.sendWithReconnect(func() *pb.ClientMessage { return &pb.ClientMessage{ RequestId: requestID(), Payload: &pb.ClientMessage_GetFrequency{GetFrequency: &pb.GetFrequencyCommand{}}, } }) log.Printf("[TIMING] GetFreq request took: %v", time.Since(t2)) if err != nil { return resp, err } if errMsg := freqMsg.GetError(); errMsg != nil { return resp, errors.New(errMsg.GetMessage()) } if freq := freqMsg.GetGetFrequency(); freq != nil { resp.Frequency = strconv.FormatUint(uint64(freq.GetFrequency()), 10) a.setFrequency(resp.Frequency) } a.mu.Lock() a.connectionStatus = resp.Status a.mu.Unlock() log.Printf("[TIMING] CheckConnection TOTAL: %v", time.Since(start)) return resp, nil } func (a *App) SetFrequency(freq string) (string, error) { val, err := strconv.Atoi(freq) if err != nil || val < 70 || val > 8545 { //return "", errors.New("вводимое значение должно быть целым положительным числом") return "", errors.New(` - значение может быть только числом - диапазон от 70 до 8545 `) } //клиента я вызываю теперь в другом месте //client, err := a.getClientOrConnect() //if err != nil { // return "", err //} //resp, err := client.Send(&pb.ClientMessage{ // RequestId: requestID(), // Payload: &pb.ClientMessage_SetFrequency{ // SetFrequency: &pb.SetFrequencyCommand{Frequency: uint32(val)}, // }, //}) resp, err := a.sendWithReconnect(func() *pb.ClientMessage { return &pb.ClientMessage{ RequestId: requestID(), Payload: &pb.ClientMessage_SetFrequency{ SetFrequency: &pb.SetFrequencyCommand{Frequency: uint32(val)}, }, } }) if err != nil { return "", err } if errMsg := resp.GetError(); errMsg != nil { return "", errors.New(errMsg.GetMessage()) } if ack := resp.GetSetFrequency(); ack != nil && !ack.GetOk() { return "", errors.New("не удалось установить частоту") } freqStr := strconv.Itoa(val) a.setFrequency(freqStr) return freqStr, nil } func (a *App) getClient() *grpcclient.Client { a.mu.Lock() defer a.mu.Unlock() return a.client } func (a *App) getClientOrConnect() (*grpcclient.Client, error) { client := a.getClient() if client != nil { return client, nil } ctx := a.ctx if ctx == nil { ctx = context.Background() } client, status := connectClient(ctx) if client == nil { return nil, errors.New(status) } a.mu.Lock() if a.client == nil { a.client = client a.connectionStatus = status a.mu.Unlock() return client, nil } existing := a.client a.mu.Unlock() _ = client.Close() return existing, nil } func (a *App) dropClient(client *grpcclient.Client) { a.mu.Lock() if a.client == client { a.client = nil a.connectionStatus = "модуль не подключен" } a.mu.Unlock() if client != nil { _ = client.Close() } } func (a *App) sendWithReconnect(build func() *pb.ClientMessage) (*pb.ServerMessage, error) { client, err := a.getClientOrConnect() if err != nil { return nil, err } resp, err := client.Send(build()) if err == nil { return resp, nil } // stream умер (часто после рестарта демона) — сбрасываем и пробуем 1 раз заново a.dropClient(client) client, err = a.getClientOrConnect() if err != nil { return nil, err } return client.Send(build()) } func (a *App) getFrequency() string { a.mu.Lock() defer a.mu.Unlock() return a.currentFrequency } func (a *App) setFrequency(freq string) { a.mu.Lock() a.currentFrequency = freq a.mu.Unlock() } func connectClient(ctx context.Context) (*grpcclient.Client, string) { cfg := config.Default() addr := cfg.GRPCAdvertiseAddr discoveryCtx, cancel := context.WithTimeout(ctx, 2*time.Second) if discovered, err := discovery.Discover(discoveryCtx, cfg.DiscoveryPort); err == nil && discovered != "" { addr = discovered } cancel() dialCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() //client, err := grpcclient.Dial(dialCtx, addr) client, err := grpcclient.Dial(dialCtx, HardAddr) if err != nil { log.Printf("gRPC connect failed: %v", err) return nil, "gRPC-клиент недоступен" } log.Printf("connected to gRPC at %s", addr) return client, "модуль подключен" } func requestID() string { return strconv.FormatInt(time.Now().UnixNano(), 10) }