-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
186 lines (171 loc) · 4.68 KB
/
main.go
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
// Main application package.
package main
import (
"flag"
"fmt"
"log"
"os"
"os/signal"
"path/filepath"
"sync"
"syscall"
"time"
"github.com/csutorasa/icon-metrics/client"
"github.com/csutorasa/icon-metrics/config"
"github.com/csutorasa/icon-metrics/metrics"
)
// Main logger instance
var logger *log.Logger = log.Default()
func main() {
configPath := parseArgs()
c, err := readConfig(configPath)
if err != nil {
logger.Panicf("Failed to load configuration caused by %s", err.Error())
}
reporter := metrics.NewPrometheusReporter()
reporter.Uptime()
logger.Printf("Starting http server on port %d", c.Port)
start := metrics.NewTimer()
p := metrics.NewPrometheusPublisher(c.Port)
err = p.Start()
if err != nil {
logger.Panicf("Failed to start http server on port %d caused by %s", c.Port, err.Error())
}
defer func() {
start := metrics.NewTimer()
logger.Printf("Stopping http server on port %d", c.Port)
p.Close()
logger.Printf("Successfully stopped http server on port %d under %s", c.Port, start.End().String())
}()
logger.Printf("Successfully started http server on port %d under %s", c.Port, start.End().String())
var wg sync.WaitGroup
channels := make([]chan int, 0)
for _, device := range c.Devices {
reportConfig := device.Report
session := metrics.NewSession(device.SysId, reportConfig, reporter)
client, err := client.NewIconClient(device.Url, device.SysId, device.Password, session)
if err != nil {
logger.Printf("Failed to create client for device %s @ %s", device.SysId, device.Url)
continue
}
delay := time.Duration(device.Delay) * time.Second
ch := make(chan int)
channels = append(channels, ch)
wg.Add(1)
go func() {
defer wg.Done()
defer func() {
start := metrics.NewTimer()
logger.Printf("Disonnecting from %s", client.SysId())
err := client.Close()
reporter.RemoveDevice(client.SysId())
if err != nil {
logger.Printf("Failed to disonnect from %s caused by %s", client.SysId(), err.Error())
} else {
logger.Printf("Successfully disconnected from %s under %s", client.SysId(), start.End().String())
}
}()
reportValues(client, ch, delay, session)
}()
}
if len(channels) != 0 {
interruptHandler(channels)
wg.Wait()
}
}
// Parses configuration file path from command line options
func parseArgs() string {
configPath := flag.String("config", "", "Configuration file url")
flag.Parse()
if *configPath == "" {
dir := filepath.Dir(os.Args[0])
return filepath.Join(dir, "config.yml")
}
return *configPath
}
// Returns configuration from file.
func readConfig(configPath string) (*config.Configuration, error) {
logger.Printf("Loading configuration from %s", configPath)
c, err := config.ReadConfig(configPath)
if err != nil {
return nil, fmt.Errorf("failed to read config: %w", err)
}
logger.Printf("Configuration is loaded from %s", configPath)
return c, nil
}
// Handles OS signals for shutdown.
func interruptHandler(channels []chan int) {
c := make(chan os.Signal, 1)
signal.Notify(c, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT)
closing := false
go func() {
for {
switch <-c {
case syscall.SIGINT:
if !closing {
closing = true
logger.Printf("SIGINT received, graceful shutdown initiated")
for _, c := range channels {
ch := c
go func() {
ch <- 1
}()
}
} else {
logger.Printf("SIGINT received again, force shutdown initiated")
os.Exit(0)
}
case syscall.SIGTERM:
logger.Printf("SIGTERM received, force shutdown initiated")
os.Exit(0)
case syscall.SIGABRT:
logger.Printf("SIGABRT received, force shutdown initiated")
os.Exit(0)
}
}
}()
}
// Main loop for handling a single iCON device.
func reportValues(c client.IconClient, trigger chan int, d time.Duration, session metrics.MetricsSession) {
session.Connected(false)
for {
if !c.IsLoggedIn() {
logger.Printf("Connecting to %s", c.SysId())
err := c.Login()
if err != nil {
logger.Printf("Failed to connect to %s caused by %s", c.SysId(), err.Error())
session.Reset()
value := sleep(trigger, d)
if value > 0 {
break
}
continue
}
logger.Printf("Connected to %s", c.SysId())
session.Connected(true)
}
values, err := c.ReadValues()
if err != nil {
logger.Printf("Failed to read values from %s caused by %s", c.SysId(), err.Error())
session.Reset()
value := sleep(trigger, d)
if value > 0 {
break
}
continue
}
session.Report(values)
value := sleep(trigger, d)
if value > 0 {
break
}
}
}
// Sleeps for the duration, which can be interrupted.
func sleep(trigger chan int, d time.Duration) int {
go func() {
time.Sleep(d)
trigger <- 0
}()
return <-trigger
}