// // rhctl // // Copyright (C) 2009-2016 Christian Pointner // // This file is part of rhctl. // // rhctl is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhctl is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with rhctl. If not, see . // package main import ( "fmt" "time" "github.com/btittelbach/pubsub" ) type Mood uint const ( MoodAwakening Mood = iota MoodSad MoodNervous MoodHappy ) func (m Mood) String() string { switch m { case MoodAwakening: return "awakening" case MoodSad: return "sad" case MoodNervous: return "nervous" case MoodHappy: return "happy" } return "unknown" } type State struct { Mood Mood Switch SwitchState ActiveServer string Server map[string]ServerState } type CommandType uint8 const ( CmdState CommandType = iota CmdServer CmdSwitch ) func (c CommandType) String() string { switch c { case CmdState: return "state" case CmdServer: return "server" case CmdSwitch: return "switch" } return "unknown" } type Command struct { Type CommandType Args []string Response chan<- interface{} } type SwitchControl struct { sw *AudioSwitch servers []*PlayoutServer state State startup time.Time Updates *pubsub.PubSub Commands chan *Command } func (ctrl *SwitchControl) handleCommand(cmd *Command) { switch cmd.Type { case CmdState: cmd.Response <- ctrl.state case CmdServer: if len(cmd.Args) == 0 { cmd.Response <- fmt.Errorf("no server specified") return } cmd.Response <- ctrl.reconcile(cmd.Args[0]) case CmdSwitch: if len(cmd.Args) == 0 { cmd.Response <- fmt.Errorf("no command specified") return } c, err := NewSwitchCommandFromStrings(cmd.Args[0], cmd.Args[1:]...) if err != nil { cmd.Response <- fmt.Errorf("switch command syntax error: %s", err.Error()) return } c.Response = cmd.Response ctrl.sw.Commands <- c } } func handleServer(in <-chan ServerState, out chan<- ServerState) { for { update := <-in out <- update } } func (ctrl *SwitchControl) reconcile(requestedServer string) (result bool) { rhdl.Printf("SwitchCTRL: reconciling state... (requested server: '%s')", requestedServer) // send out commands to switch and/or server to get missing infos if time.Since(ctrl.state.Switch.Changed) > (2 * time.Hour) { // TODO: hardcoded value ctrl.sw.Commands <- &SwitchCommand{SwitchCmdStateAudio, nil, nil} ctrl.sw.Commands <- &SwitchCommand{SwitchCmdStateGPIAll, nil, nil} ctrl.sw.Commands <- &SwitchCommand{SwitchCmdStateOC, nil, nil} ctrl.sw.Commands <- &SwitchCommand{SwitchCmdStateRelay, nil, nil} ctrl.sw.Commands <- &SwitchCommand{SwitchCmdStateSilence, nil, nil} } for _, server := range ctrl.servers { s, exists := ctrl.state.Server[server.name] if !exists || time.Since(s.Changed) > (2*time.Hour) { // TODO: hardcoded value server.UpdateRequest <- true } } if ctrl.state.Mood == MoodAwakening && time.Since(ctrl.startup) < (time.Minute) { // TODO: hardcoded value // it's to early to take actions - let's learn i little bit more about current state return } // TODO: change active server if it fails or on request // return true if requested server got selected // TODO: change switch output mappings if servers change channels or fail // TODO: set mood dependent on overall-state return } func (ctrl *SwitchControl) Run() { rhdl.Printf("SwitchCTRL: handler running...") ctrl.startup = time.Now() serverStateChanges := make(chan ServerState, 8) for _, srv := range ctrl.servers { go handleServer(srv.StateChanges, serverStateChanges) } ctrl.reconcile("") for { select { case update := <-ctrl.sw.Updates: rhdl.Printf("got update from switch: %+v", update) for _, srv := range ctrl.servers { srv.SwitchUpdates <- update } ctrl.Updates.Pub(update, "switch:"+update.Type.String()) case state := <-ctrl.sw.StateChanges: // rhdl.Printf("got switch state update: %+v", state) ctrl.Updates.Pub(state, "switch:state") ctrl.state.Switch = state ctrl.reconcile("") ctrl.Updates.Pub(ctrl.state, "state") case state := <-serverStateChanges: rhdl.Printf("got server state update: %+v", state) ctrl.Updates.Pub(state, "server:state") ctrl.state.Server[state.Name] = state ctrl.reconcile("") ctrl.Updates.Pub(ctrl.state, "state") case cmd := <-ctrl.Commands: ctrl.handleCommand(cmd) } } } func SwitchControlInit(conf *Config, sw *AudioSwitch, servers []*PlayoutServer) (ctrl *SwitchControl) { ctrl = &SwitchControl{} ctrl.sw = sw ctrl.servers = servers ctrl.state.Mood = MoodAwakening ctrl.state.Server = make(map[string]ServerState) ctrl.Updates = pubsub.NewNonBlocking(32) ctrl.Commands = make(chan *Command, 8) return }