power-monitor.c 6.65 KB
Newer Older
Owen W. Taylor's avatar
Owen W. Taylor committed
1
2
3
4
5
6
7
/* -*- mode: C; c-file-style: "stroustrup"; indent-tabs-mode: nil; -*- */

#include <stdlib.h>

#include <gio/gio.h>

#include "power-monitor.h"
8
#include "power-supply.h"
Owen W. Taylor's avatar
Owen W. Taylor committed
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

/* Time between reading values out of proc (ms) */
#define UPDATE_FREQUENCY 250

struct _GbbPowerMonitor {
    GObject parent;
    GList *batteries;
    GList *adapters;
    GbbPowerState current_state;
    guint update_timeout;
};

struct _GbbPowerMonitorClass {
    GObjectClass parent_class;
};

enum {
    CHANGED,
    LAST_SIGNAL
};

static guint signals[LAST_SIGNAL];

G_DEFINE_TYPE(GbbPowerMonitor, gbb_power_monitor, G_TYPE_OBJECT)

void
gbb_power_state_free(GbbPowerState   *state)
{
    g_slice_free(GbbPowerState, state);
}
39
40

double
41
gbb_power_state_get_percent (const GbbPowerState *state)
42
{
43
    return 100 * state->energy_now / state->energy_full;
44
45
}

46
static gboolean
Owen W. Taylor's avatar
Owen W. Taylor committed
47
48
49
50
51
52
gbb_power_state_equal(GbbPowerState *a,
                      GbbPowerState *b)
{
    return (a->online == b->online &&
            a->energy_now == b->energy_now &&
            a->energy_full == b->energy_full &&
53
            a->energy_full_design == b->energy_full_design);
Owen W. Taylor's avatar
Owen W. Taylor committed
54
55
56
57
58
59
60
61
62
63
}

void
gbb_power_statistics_free (GbbPowerStatistics *statistics)
{
    g_slice_free(GbbPowerStatistics, statistics);
}


static gboolean
64
65
66
find_power_supplies(GbbPowerMonitor *monitor,
                    GCancellable *cancellable,
                    GError      **error)
Owen W. Taylor's avatar
Owen W. Taylor committed
67
68
{

69
70
    GList *supplies;
    GList *l;
Owen W. Taylor's avatar
Owen W. Taylor committed
71

72
    supplies = gbb_power_supply_discover();
Owen W. Taylor's avatar
Owen W. Taylor committed
73

74
75
76
77
78
79
80
81
82
    for (l = supplies; l != NULL; l = l->next) {
        if (GBB_IS_BATTERY(l->data)) {
            monitor->batteries = g_list_prepend(monitor->batteries, l->data);
        } else if (GBB_IS_MAINS(l->data)) {
            monitor->adapters = g_list_prepend(monitor->adapters, l->data);
        } else {
            g_assert_not_reached();
        }
    }
Owen W. Taylor's avatar
Owen W. Taylor committed
83

84
    g_list_free(supplies);
Owen W. Taylor's avatar
Owen W. Taylor committed
85

86
87
88
89
90
91
    if (monitor->batteries == NULL) {
        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED,
                            "No batteries found!");
    } else if (monitor->adapters == NULL) {
        g_set_error_literal(error, G_IO_ERROR, G_IO_ERROR_FAILED,
                            "No power adapter found!");
Owen W. Taylor's avatar
Owen W. Taylor committed
92
93
94
95
96
97
98
99
100
101
    }

    return *error == NULL;
}

static void
gbb_power_monitor_finalize(GObject *object)
{
    GbbPowerMonitor *monitor = GBB_POWER_MONITOR(object);

102
103
    g_list_foreach(monitor->batteries, (GFunc)g_object_unref, NULL);
    g_list_foreach(monitor->adapters, (GFunc)g_object_unref, NULL);
Owen W. Taylor's avatar
Owen W. Taylor committed
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

    G_OBJECT_CLASS(gbb_power_monitor_parent_class)->finalize(object);
}

static void
gbb_power_monitor_init(GbbPowerMonitor *monitor)
{
}

static void
gbb_power_monitor_class_init(GbbPowerMonitorClass *monitor_class)
{
    GObjectClass *gobject_class = G_OBJECT_CLASS(monitor_class);

    gobject_class->finalize = gbb_power_monitor_finalize;

    signals[CHANGED] =
        g_signal_new ("changed",
                      GBB_TYPE_POWER_MONITOR,
                      G_SIGNAL_RUN_LAST,
                      0,
                      NULL, NULL, NULL,
                      G_TYPE_NONE, 0);
}

129
static void
Owen W. Taylor's avatar
Owen W. Taylor committed
130
131
132
133
134
135
136
137
add_to (double *total, double  increment)
{
    if (*total >= 0)
        *total += increment;
    else
        *total = increment;
}

138
139
140
141
142
143
144
145
static void
gbb_power_state_init(GbbPowerState *state)
{
    state->time_us = 0;
    state->online = FALSE;
    state->energy_now = -1.0;
    state->energy_full = -1.0;
    state->energy_full_design = -1.0;
146
    state->voltage_now = -1.0;
147
148
149
150
151
152
153
154
155
156
}

GbbPowerState *
gbb_power_state_new(void)
{
    GbbPowerState *state = g_slice_new(GbbPowerState);
    gbb_power_state_init(state);
    return state;
}

157
158
159
160
161
162
GbbPowerState *
gbb_power_state_copy(const GbbPowerState *state)
{
    return g_slice_dup(GbbPowerState, state);
}

163
static GbbPowerState *
Owen W. Taylor's avatar
Owen W. Taylor committed
164
165
166
167
168
169
read_state(GbbPowerMonitor *monitor,
           GbbPowerState   *state)
{
    GList *l;
    int n_batteries = 0;

170
    gbb_power_state_init(state);
Owen W. Taylor's avatar
Owen W. Taylor committed
171
172
173
    state->time_us = g_get_monotonic_time();

    for (l = monitor->adapters; l; l = l->next) {
174
175
176
177
        GbbMains *mains = l->data;
        gboolean online = gbb_mains_poll(mains);

        if (online)
Owen W. Taylor's avatar
Owen W. Taylor committed
178
179
180
181
            state->online = TRUE;
    }

    for (l = monitor->batteries; l; l = l->next) {
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
        GbbBattery *battery = l->data;
        double energy_now = gbb_battery_poll(battery);
        double energy_full = -1.0;
        double energy_full_design = -1.0;

        g_object_get(battery,
                     "energy-full", &energy_full,
                     "energy-full-design", &energy_full_design,
                     NULL);

        add_to (&state->energy_now, energy_now);
        add_to (&state->energy_full, energy_full);

        if (energy_full_design >= 0) {
            if (n_batteries == 0 || state->energy_full_design >= 0)
                add_to (&state->energy_full_design, energy_full_design);
        } else {
            state->energy_full_design = -1;
Owen W. Taylor's avatar
Owen W. Taylor committed
200
201
        }

202
        state->voltage_now = -1.0;
Owen W. Taylor's avatar
Owen W. Taylor committed
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
        n_batteries += 1;
    }

    return state;
}

static gboolean
update_timeout(gpointer data)
{
    GbbPowerMonitor *monitor = data;
    GbbPowerState state;
    read_state(monitor, &state);

    if (!gbb_power_state_equal(&monitor->current_state, &state)) {
        monitor->current_state = state;
        g_signal_emit(monitor, signals[CHANGED], 0);
    }

    return G_SOURCE_CONTINUE;
}

GbbPowerMonitor *
gbb_power_monitor_new(void)
{
    GbbPowerMonitor *monitor = g_object_new(GBB_TYPE_POWER_MONITOR, NULL);
    GError *error = NULL;

    if (!find_power_supplies(monitor, NULL, &error))
        g_error("%s\n", error->message);

    read_state(monitor, &monitor->current_state);
    monitor->update_timeout = g_timeout_add(UPDATE_FREQUENCY, update_timeout, monitor);

    return monitor;
}

239
const GbbPowerState *
Owen W. Taylor's avatar
Owen W. Taylor committed
240
241
gbb_power_monitor_get_state (GbbPowerMonitor *monitor)
{
242
    return &monitor->current_state;
Owen W. Taylor's avatar
Owen W. Taylor committed
243
244
245
}

GbbPowerStatistics *
246
247
gbb_power_statistics_compute (const GbbPowerState   *base,
                              const GbbPowerState   *current)
Owen W. Taylor's avatar
Owen W. Taylor committed
248
249
250
251
252
253
254
255
256
{
    GbbPowerStatistics *statistics = g_slice_new(GbbPowerStatistics);
    statistics->power = -1;
    statistics->current = -1;
    statistics->battery_life = -1;
    statistics->battery_life_design = -1;

    double time_elapsed = (current->time_us - base->time_us) / 1000000.;

257
258
259
260
261
262
263
264
265
266
267
    if (time_elapsed < (UPDATE_FREQUENCY / 1000.)) {
        return statistics;
    }

    double energy_used = base->energy_now - current->energy_now;
    if (energy_used > 0) {
        statistics->power = 3600 * (energy_used) / time_elapsed;
        if (base->energy_full >= 0)
            statistics->battery_life = 3600 * base->energy_full / statistics->power;
        if (base->energy_full_design >= 0)
            statistics->battery_life_design = 3600 * base->energy_full_design / statistics->power;
Owen W. Taylor's avatar
Owen W. Taylor committed
268
269
270
271
    }

    return statistics;
}