summaryrefslogtreecommitdiff
path: root/src/rhctl/playout_server.go
blob: ea9926811eea0ba86b95a5f5341b4145448ff581 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//
//  rhctl
//
//  Copyright (C) 2009-2016 Christian Pointner <equinox@helsinki.at>
//
//  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 <http://www.gnu.org/licenses/>.
//

package main

import (
	"fmt"
	"strings"
	"time"
)

type ServerHealth bool

const (
	ServerDead  ServerHealth = false
	ServerAlive ServerHealth = true
)

func (s ServerHealth) String() string {
	switch s {
	case ServerDead:
		return "dead"
	case ServerAlive:
		return "alive"
	}
	return "unknown"
}

type ServerStatus struct {
	Health  ServerHealth
	Channel string
}

type PlayoutServer struct {
	name        string
	control     *SerialPort
	heartbeat   *SerialPort
	hbtimeout   time.Duration
	hbtimer     *time.Timer
	hbthreshold uint
	hbcnt       uint
	status      ServerStatus
	Updates     chan ServerStatus
}

func (srv *PlayoutServer) handleControl(data string) {
	rhdl.Printf("Server(%s): got control 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.status.Channel = data[8:]
		srv.Updates <- srv.status
		return
	}
	rhl.Printf("Warning: ignoring unknown control message from '%s': %q", srv.name, data)
}

func (srv *PlayoutServer) handleHeartbeat(data string) {
	rhdl.Printf("Server(%s): got heartbeat message: %q", srv.name, data)
	srv.hbtimer.Reset(srv.hbtimeout)
	srv.hbcnt++
	if srv.hbcnt < srv.hbthreshold {
		return
	}

	old := srv.status.Health
	srv.status.Health = ServerAlive
	if old != srv.status.Health {
		srv.Updates <- srv.status
	}
}

func (srv *PlayoutServer) handleHBTimeout() {
	rhl.Printf("Server(%s): heartbeat timed-out", srv.name)
	srv.hbcnt = 0
	srv.status.Health = ServerDead
	srv.Updates <- srv.status
}

func (srv *PlayoutServer) Run() {
	stop := make(chan bool)
	srv.control.Run(stop)
	srv.heartbeat.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.control.rx:
			srv.handleControl(data)
		case data := <-srv.heartbeat.rx:
			srv.handleHeartbeat(data)
		case <-srv.hbtimer.C:
			srv.handleHBTimeout()
		}
	}
}

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.status.Health = ServerDead
	srv.status.Channel = ""
	srv.Updates = make(chan ServerStatus, 8)

	if srv.control, err = SerialOpen(conf.Servers[name].ControlDevice, conf.Servers[name].ControlBaudrate, "\r\n"); err != nil {
		err = fmt.Errorf("Error opening control port(%s): %s", srv.name, err)
		return
	}
	if srv.heartbeat, err = SerialOpen(conf.Servers[name].HeartbeatDevice, conf.Servers[name].HeartbeatBaudrate, "\r\n"); err != nil {
		err = fmt.Errorf("Error opening control port(%s): %s", srv.name, err)
		return
	}

	return
}