// // 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" "strings" "time" ) type ServerHealth bool const ( ServerDead ServerHealth = false ServerAlive ServerHealth = true ServerChannelReq = "channel?" ) func (s ServerHealth) String() string { switch s { case ServerDead: return "dead" case ServerAlive: return "alive" } return "unknown" } func (s ServerHealth) MarshalText() (data []byte, err error) { data = []byte(s.String()) return } type ServerState struct { Name string Health ServerHealth Channel string Updated time.Time } type PlayoutServer struct { name string device *SerialPort hbtimeout time.Duration hbtimer *time.Timer hbthreshold uint hbcnt uint state ServerState StateChanges chan ServerState Commands chan Command UpdateRequest chan bool SwitchUpdates chan SwitchUpdate } func (srv *PlayoutServer) handleHeartbeat() { srv.hbtimer.Reset(srv.hbtimeout) if (srv.hbcnt + 1) < srv.hbthreshold { srv.hbcnt++ return } old := srv.state.Health srv.state.Health = ServerAlive srv.state.Updated = time.Now() if old != srv.state.Health { srv.StateChanges <- srv.state rhl.Printf("Server(%s): is back from the dead!", srv.name) if srv.state.Channel == "" { srv.device.tx <- ServerChannelReq } } } func (srv *PlayoutServer) handleMessage(data string) { if data == "hb" { // rhdl.Printf("Server(%s): got heartbeat message", srv.name) srv.handleHeartbeat() return } rhdl.Printf("Server(%s): got message: %q", srv.name, data) if strings.HasPrefix(data, "channel ") { if len(data) <= 8 { rhl.Printf("Server(%s) sent empty channel name", srv.name) return } srv.state.Channel = data[8:] srv.state.Updated = time.Now() srv.StateChanges <- srv.state return } if strings.HasPrefix(data, "handover ") { if len(data) <= 9 { rhl.Printf("Server(%s) sent empty handover", srv.name) return } srv.Commands <- Command{Type: CmdServer, Args: []string{data[9:]}, Response: nil} return } rhl.Printf("Server(%s): ignoring unknown message: %q", srv.name, data) } func (srv *PlayoutServer) handleHBTimeout() { rhl.Printf("Server(%s): heartbeat timed-out", srv.name) srv.hbcnt = 0 srv.state.Health = ServerDead srv.state.Updated = time.Now() srv.StateChanges <- srv.state } func (srv *PlayoutServer) Run() { stop := make(chan bool) srv.device.Run(stop) srv.hbtimer = time.NewTimer(srv.hbtimeout) srv.hbcnt = 0 rhdl.Printf("Server(%s): handler running...", srv.name) for { select { case <-stop: return case data := <-srv.device.rx: srv.handleMessage(data) case <-srv.hbtimer.C: srv.handleHBTimeout() case <-srv.UpdateRequest: if srv.state.Health == ServerDead { srv.StateChanges <- srv.state } else { srv.device.tx <- ServerChannelReq } case update := <-srv.SwitchUpdates: srv.device.tx <- update.Data } } } func ServerInit(name string, conf *Config) (srv *PlayoutServer, err error) { srv = &PlayoutServer{} srv.name = name srv.hbtimeout = 3 * time.Second if conf.Servers[name].HeartbeatTimeout.Duration > time.Duration(0) { srv.hbtimeout = conf.Servers[name].HeartbeatTimeout.Duration } srv.hbthreshold = 10 if conf.Servers[name].HeartbeatThreshold > 0 { srv.hbthreshold = conf.Servers[name].HeartbeatThreshold } srv.state.Name = srv.name srv.state.Health = ServerDead srv.state.Channel = "" srv.StateChanges = make(chan ServerState, 16) srv.Commands = make(chan Command, 8) srv.UpdateRequest = make(chan bool, 8) srv.SwitchUpdates = make(chan SwitchUpdate, 32) if srv.device, err = SerialOpen(conf.Servers[name].Device, conf.Servers[name].Baudrate, "\n"); err != nil { err = fmt.Errorf("Server(%s): error opening serial port: %s", srv.name, err) return } return }