package main import ( "context" "flag" "fmt" "git.entr0py.de/garionion/berkutschi/client" "git.entr0py.de/garionion/gstreamer-graphix/api" "github.com/ilyakaznacheev/cleanenv" jsoniter "github.com/json-iterator/go" "github.com/rs/zerolog/pkgerrors" "io/ioutil" "os" "strings" "time" "github.com/rs/zerolog" "github.com/rs/zerolog/log" "git.entr0py.de/garionion/berkutschi/berkutschi" ) type Config struct { GstServer string `yaml:"gstServer"` ImagePath string `yaml:"imagePath" env-default:"./images"` PositionsPath string `yaml:"positionsPath" env-default:"./positions.json"` Event int `yaml:"event"` LogFile string `yaml:"logFile"` } //TODO config struct thingy var Judges = [5]JugdeRaceInfo{} var PositionsConfig = Positions{} var cfg Config var json = jsoniter.ConfigCompatibleWithStandardLibrary var apiClient *client.APIClient func main() { zerolog.TimeFieldFormat = time.RFC1123Z consoleWriter := zerolog.ConsoleWriter{Out: os.Stdout, TimeFormat: time.RFC1123Z} log.Logger = log.Output(consoleWriter).With().Timestamp().Caller().Logger() if err := cleanenv.ReadConfig("config.toml", &cfg); err != nil { log.Fatal().Msgf("No configfile: ", err) } fset := flag.NewFlagSet("config", flag.ContinueOnError) fset.Usage = cleanenv.FUsage(fset.Output(), &cfg, nil, fset.Usage) fset.Parse(os.Args[1:]) if cfg.LogFile != "" { l, _ := os.OpenFile(cfg.LogFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0664) multi := zerolog.MultiLevelWriter(consoleWriter, l) log.Logger = zerolog.New(multi).With().Timestamp().Caller().Logger() } if log.Logger.GetLevel() == zerolog.DebugLevel { zerolog.ErrorStackMarshaler = pkgerrors.MarshalStack } //TODO: error handling jsonFile, err := os.Open(cfg.PositionsPath) byteValue, _ := ioutil.ReadAll(jsonFile) jsoniter.Unmarshal(byteValue, PositionsConfig) apiClient = client.Init(cfg.GstServer) pollData, err := berkutschi.Poll(cfg.Event) if err != nil { log.Panic().Err(fmt.Errorf("Error while polling: %s", err)).Send() } fillJudgeraceinfo(pollData) ctx, _ := context.WithCancel(context.Background()) b := berkutschi.Init(cfg.Event, ctx) go func() { jumperInfoMap := make(map[string]string) jumperScoreMap := make(map[string]string) jumperInfo := JumperInfo{} jumperScore := JumperScore{} for { message := <-b.RX var jumpMessage BerkutschiJumpUpdateMessages err := json.Unmarshal(message, &jumpMessage) //log.Debug().Msgf("Received message: %v", string(message)) if err == nil && jumpMessage[0].Data.Current.Lastname != "" { jumper, err := fillJumperInfo(jumpMessage) if err != nil { continue } if newJumperInfoData(jumperInfo, jumper) { log.Info().Msgf("New jumper info: %+v", jumperInfo) if jumper.Name == jumperInfo.Name { overlays, _ := jumper.convertToAPIOverlays(PositionsConfig) err = apiClient.UpdatePipeline(jumperInfoMap[jumper.Name], overlays) if err != nil { log.Error().Err(err).Msgf("Error while updating jumperinfo of %s pipeline", jumper.Name) } } else { id, err := jumper.createPipeline() if err != nil { log.Error().Err(err).Msgf("Error while creating jumperInfo pipeline for %s", jumper.Name) } log.Info().Msgf("Created jumperInfo pipeline for %s", jumper.Name) jumperInfoMap[jumper.Name] = id err = apiClient.DeletePipeline(jumperInfoMap[jumperInfo.Name]) if err != nil { log.Error().Err(err).Msgf("Error while deleting pipeline of %s", jumperInfo.Name) } delete(jumperInfoMap, jumperInfo.Name) } jumperInfo = jumper } score, _ := fillJumperScore(jumpMessage) if score.Rank != 0 && newJumperScoreData(jumperScore, score) { log.Info().Msgf("New jumper score: %+v", jumperScore) if jumperScore.Name == score.Name { overlays, _ := score.convertToAPIOverlays(PositionsConfig) err = apiClient.UpdatePipeline(jumperScoreMap[jumperScore.Name], overlays) if err != nil { log.Error().Err(err).Msgf("Error while updating jumperScore of %s pipeline", jumperScore.Name) } } else { id, err := score.createPipeline() if err != nil { log.Error().Err(err).Msgf("Error while creating jumperScore pipeline for %s", score.Name) } log.Info().Msgf("Created jumperScore pipeline for %s", score.Name) jumperScoreMap[score.Name] = id err = apiClient.DeletePipeline(jumperScoreMap[jumperScore.Name]) if err != nil { log.Error().Err(err).Msgf("Error while deleting pipeline of %s", jumperScore.Name) } delete(jumperScoreMap, jumperScore.Name) } jumperScore = score } } else if err != nil { log.Error().RawJSON("receivedMessage", message).Err(fmt.Errorf("%v", err)) } else { if jumpMessage[0].Data.Next.Lastname == "" { log.Debug().RawJSON("receivedMessage", message).Msg("Received message i cant decode") } } } }() <-ctx.Done() } func (d JumperInfo) createPipeline() (string, error) { overlays, err := d.convertToAPIOverlays(PositionsConfig) if err != nil { return "", err } pipeline := &api.Pipeline{ Name: "jumperinfo", Clip: "CoC_20_Jumper_Infos_Fast.raw", Output: "decklinkvideosink", Overlays: overlays, } return apiClient.CreatePipeline(pipeline) } func (d JumperScore) createPipeline() (string, error) { overlays, err := d.convertToAPIOverlays(PositionsConfig) if err != nil { return "", err } pipeline := &api.Pipeline{ Name: "jumperscore", Clip: "CoC_20_Jumper_Score_Fast.raw", Output: "decklinkvideosink", Overlays: overlays, } return apiClient.CreatePipeline(pipeline) } func fillJumperInfo(m BerkutschiJumpUpdateMessages) (JumperInfo, error) { var err error JumperInfo := JumperInfo{} if m[0].Data.Current.Firstname == "" || m[0].Data.Current.Lastname == "" { err = fmt.Errorf("Name is empty") return JumperInfo, err } JumperInfo.Name = fmt.Sprint(m[0].Data.Current.Firstname, " ", strings.ToUpper(m[0].Data.Current.Lastname)) JumperInfo.Bib = m[0].Data.Current.Bib JumperInfo.Nation = strings.ToUpper(m[0].Data.Current.Nat) JumperInfo.Image = m[0].Data.Current.Image2 return JumperInfo, err } func fillJumperScore(m BerkutschiJumpUpdateMessages) (JumperScore, error) { var err error JumperScore := JumperScore{} if m[0].Data.Current.Firstname == "" || m[0].Data.Current.Lastname == "" { err = fmt.Errorf("Name is empty") return JumperScore, err } JumperScore.Name = fmt.Sprint(m[0].Data.Current.Firstname, " ", strings.ToUpper(m[0].Data.Current.Lastname)) JumperScore.Bib = m[0].Data.Current.Bib JumperScore.Nation = strings.ToUpper(m[0].Data.Current.Nat) JumperScore.Points = m[0].Data.Current.Cumul.Points JumperScore.Rank = m[0].Data.Current.Cumul.Rank JumperScore.Wind = m[0].Data.Current.Wind.Wind JumperScore.Length = fmt.Sprintf("%vm", m[0].Data.Current.Length.Length) for i := range JumperScore.Judges { switch i { case 0: JumperScore.Judges[i].Score = m[0].Data.Current.Judge.One.Rate JumperScore.Judges[i].Nation = Judges[i].Nation JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.One.Discard case 1: JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Two.Rate JumperScore.Judges[i].Nation = Judges[i].Nation JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Two.Discard case 2: JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Three.Rate JumperScore.Judges[i].Nation = Judges[i].Nation JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Three.Discard case 3: JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Four.Rate JumperScore.Judges[i].Nation = Judges[i].Nation JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Four.Discard case 4: JumperScore.Judges[i].Score = m[0].Data.Current.Judge.Five.Rate JumperScore.Judges[i].Nation = Judges[i].Nation JumperScore.Judges[i].Discard = m[0].Data.Current.Judge.Five.Discard } } return JumperScore, err } func fillJudgeraceinfo(data berkutschi.PollData) { for i := range Judges { switch i { case 0: Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.One.Nation) case 1: Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Two.Nation) case 2: Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Three.Nation) case 3: Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Four.Nation) case 4: Judges[i].Nation = strings.ToUpper(data.Data.Raceinfo.Judges.Five.Nation) } } } func newJumperInfoData(oldData, newData JumperInfo) bool { if oldData.Name != newData.Name || oldData.Bib != newData.Bib || oldData.Nation != newData.Nation || oldData.Image != newData.Image { return true } return false } func newJumperScoreData(oldData, newData JumperScore) bool { if oldData.Name != newData.Name || oldData.Bib != newData.Bib || oldData.Nation != newData.Nation || oldData.Points != newData.Points || oldData.Rank != newData.Rank || oldData.Wind != newData.Wind || oldData.Length != newData.Length { return true } for i, oldJudge := range oldData.Judges { if oldJudge.Score != newData.Judges[i].Score || oldJudge.Nation != newData.Judges[i].Nation || oldJudge.Discard != newData.Judges[i].Discard { return true } } return false }