package service import ( "context" "fmt" "log" "regexp" "strconv" "sync" pb "spektr/internal/grpc/pb" "spektr/internal/uart" ) type Service struct { mu sync.Mutex uart *uart.UART lastFreq uint32 } var ( versionRe = regexp.MustCompile(`(?i)HW[:=]\s*([^\s]+).*?(?:FW|SW)[:=]\s*([^\s]+)`) freqRe = regexp.MustCompile(`(?i)\bf\d{7,9}\b`) ) func New(u *uart.UART) *Service { return &Service{uart: u} } func (s *Service) Handle(ctx context.Context, msg *pb.ClientMessage) *pb.ServerMessage { _ = ctx switch payload := msg.GetPayload().(type) { case *pb.ClientMessage_Av: return s.handleAV(msg.GetRequestId()) case *pb.ClientMessage_SetFrequency: return s.handleSetFrequency(msg.GetRequestId(), payload.SetFrequency.GetFrequency()) case *pb.ClientMessage_GetFrequency: return s.handleGetFrequency(msg.GetRequestId()) default: return errorMessage(msg.GetRequestId(), "unknown command") } } func (s *Service) handleAV(requestID string) *pb.ServerMessage { s.mu.Lock() defer s.mu.Unlock() line, err := s.uart.Exec("av") if err != nil { log.Printf("uart av error: %v", err) return errorMessage(requestID, err.Error()) } log.Printf("uart av raw: %q", line) hw, sw := parseVersion(line) if hw == "" && sw == "" { hw = line } return &pb.ServerMessage{ RequestId: requestID, Payload: &pb.ServerMessage_Av{ Av: &pb.AvResponse{Ok: true, Hw: hw, Sw: sw}, }, } } func (s *Service) handleSetFrequency(requestID string, frequency uint32) *pb.ServerMessage { s.mu.Lock() defer s.mu.Unlock() cmd := encodeFrequencyCommand(frequency) if err := s.uart.Write(cmd); err != nil { log.Printf("uart af write error: %v", err) return errorMessage(requestID, err.Error()) } log.Printf("uart af sent: %q", cmd) s.lastFreq = frequency return &pb.ServerMessage{ RequestId: requestID, Payload: &pb.ServerMessage_SetFrequency{ SetFrequency: &pb.SetFrequencyResponse{Ok: true}, }, } } func (s *Service) handleGetFrequency(requestID string) *pb.ServerMessage { s.mu.Lock() defer s.mu.Unlock() line, err := s.uart.Exec("ar") if err != nil { log.Printf("uart ar error: %v", err) return errorMessage(requestID, err.Error()) } log.Printf("uart ar raw: %q", line) frequency, err := decodeFrequencyResponse(line) if err != nil { return errorMessage(requestID, err.Error()) } s.lastFreq = frequency return &pb.ServerMessage{ RequestId: requestID, Payload: &pb.ServerMessage_GetFrequency{ GetFrequency: &pb.GetFrequencyResponse{Frequency: frequency}, }, } } func errorMessage(requestID, message string) *pb.ServerMessage { return &pb.ServerMessage{ RequestId: requestID, Payload: &pb.ServerMessage_Error{ Error: &pb.Error{Message: message}, }, } } func parseVersion(line string) (string, string) { match := versionRe.FindStringSubmatch(line) if len(match) == 3 { return match[1], match[2] } return "", "" } func encodeFrequencyCommand(frequency uint32) string { return fmt.Sprintf("af%07d", frequency*100) } func decodeFrequencyResponse(line string) (uint32, error) { match := freqRe.FindString(line) if match == "" { return 0, fmt.Errorf("invalid frequency response %q", line) } value, err := strconv.ParseUint(match[1:], 10, 32) if err != nil { return 0, fmt.Errorf("invalid frequency response %q: %w", line, err) } return uint32(value / 100), nil }