440 lines
16 KiB
C
440 lines
16 KiB
C
#include <ctype.h>
|
|
#include <ncurses.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
#include <time.h>
|
|
|
|
const int digit_bitmaps[10][5][3] = {
|
|
{{1,1,1},{1,0,1},{1,0,1},{1,0,1},{1,1,1}}, // 0
|
|
{{0,1,0},{1,1,0},{0,1,0},{0,1,0},{1,1,1}}, // 1
|
|
{{1,1,1},{0,0,1},{1,1,1},{1,0,0},{1,1,1}}, // 2
|
|
{{1,1,1},{0,0,1},{1,1,1},{0,0,1},{1,1,1}}, // 3
|
|
{{1,0,1},{1,0,1},{1,1,1},{0,0,1},{0,0,1}}, // 4
|
|
{{1,1,1},{1,0,0},{1,1,1},{0,0,1},{1,1,1}}, // 5
|
|
{{1,1,1},{1,0,0},{1,1,1},{1,0,1},{1,1,1}}, // 6
|
|
{{1,1,1},{0,0,1},{0,1,0},{0,1,0},{0,1,0}}, // 7
|
|
{{1,1,1},{1,0,1},{1,1,1},{1,0,1},{1,1,1}}, // 8
|
|
{{1,1,1},{1,0,1},{1,1,1},{0,0,1},{1,1,1}} // 9
|
|
};
|
|
|
|
typedef struct{
|
|
int data;
|
|
int ldata;
|
|
int hdata;
|
|
int digits;
|
|
WINDOW *lwin;
|
|
} StInt;
|
|
typedef struct{
|
|
float data;
|
|
float ldata;
|
|
float hdata;
|
|
int digits;
|
|
WINDOW *lwin;
|
|
} StFlt;
|
|
typedef struct{
|
|
WINDOW *lwin;
|
|
} StStr;
|
|
|
|
typedef struct {
|
|
StInt speed;
|
|
StInt rpm;
|
|
StFlt tq;
|
|
StInt power;
|
|
StFlt eff;
|
|
StInt bat;
|
|
StFlt bat_temp;
|
|
StFlt var_temp;
|
|
StFlt mot_temp;
|
|
StStr message;
|
|
} Tel;
|
|
|
|
void get_fake_data(Tel *t) {
|
|
t->speed.data = t->speed.ldata + rand() % (t->speed.hdata - t->speed.ldata + 1);
|
|
t->power.data = t->power.ldata + rand() % (t->power.hdata - t->power.ldata + 1);
|
|
t->bat.data = t->bat.ldata + rand() % (t->bat.hdata - t->bat.ldata + 1);
|
|
t->tq.data = t->tq.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->tq.hdata - t->tq.ldata);
|
|
t->rpm.data = t->rpm.ldata + rand() % (t->rpm.hdata - t->rpm.ldata + 1);
|
|
t->eff.data = t->eff.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->eff.hdata - t->eff.ldata);
|
|
t->bat_temp.data = t->bat_temp.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->bat_temp.hdata - t->bat_temp.ldata);
|
|
t->var_temp.data = t->var_temp.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->var_temp.hdata - t->var_temp.ldata);
|
|
t->mot_temp.data = t->mot_temp.ldata + ((float)rand() / (float)RAND_MAX) * (float)(t->mot_temp.hdata - t->mot_temp.ldata);
|
|
}
|
|
|
|
long now_ms(void) {
|
|
struct timespec ts;
|
|
clock_gettime(CLOCK_MONOTONIC, &ts);
|
|
return ts.tv_sec * 1000 + ts.tv_nsec / 1000000;
|
|
}
|
|
|
|
void win_clear(WINDOW *lwin) {
|
|
int y, x;
|
|
getmaxyx(lwin, y, x);
|
|
for (int i = 1; i < y - 1; i++) {
|
|
mvwhline(lwin, i, 1, ' ', x - 2);
|
|
}
|
|
}
|
|
|
|
int smaller_of(int a, int b) {
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
void win_int(WINDOW *lwin, int data, int digits, int use_color, int color) {
|
|
int lwiny, lwinx, len;
|
|
char buf[16];
|
|
getmaxyx(lwin, lwiny, lwinx);
|
|
snprintf(buf , sizeof(buf), "%d", data);
|
|
len = strlen(buf);
|
|
|
|
int bh = 5; // bitmap height
|
|
int bw = 3; // bitmap width
|
|
|
|
int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (digits - 1)) / (bw * digits));
|
|
|
|
int total_width = digits * (bw * size) + (digits - 1);
|
|
int startx = (lwinx - total_width) / 2;
|
|
int starty = (lwiny - bh * size) / 2;
|
|
int offset = (bw * size + 1) * (digits - len);
|
|
|
|
win_clear(lwin);
|
|
|
|
if (use_color) wattron(lwin, COLOR_PAIR(color));
|
|
|
|
for (int d = 0; d < len; d++) {
|
|
int digit = buf[d] - '0';
|
|
int dx = startx + d * (bw * size + 1) + offset;
|
|
|
|
for (int y = 0; y < bh; y++) {
|
|
for (int x = 0; x < bw; x++) {
|
|
if (digit_bitmaps[digit][y][x]) {
|
|
for (int yy = 0; yy < size; yy++)
|
|
for (int xx = 0; xx < size; xx++)
|
|
mvwaddch(lwin,
|
|
starty + y * size + yy,
|
|
dx + x * size + xx,
|
|
ACS_CKBOARD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (use_color) wattroff(lwin, COLOR_PAIR(color));
|
|
|
|
wrefresh(lwin);
|
|
}
|
|
|
|
void win_float(WINDOW *lwin, float data, int digits, int use_color, int color) {
|
|
int lwiny, lwinx, len;
|
|
char buf[16];
|
|
|
|
getmaxyx(lwin, lwiny, lwinx);
|
|
|
|
snprintf(buf , sizeof(buf), "%.1f", data);
|
|
len = strlen(buf);
|
|
|
|
int bh = 5; // bitmap height
|
|
int bw = 3; // bitmap width
|
|
|
|
int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (digits - 1)) / (bw * digits));
|
|
|
|
int total_width = digits * (bw * size) + (digits - 1);
|
|
int startx = (lwinx - total_width) / 2;
|
|
int starty = (lwiny - bh * size) / 2;
|
|
int offset = (bw * size + 1) * (digits - len);
|
|
|
|
win_clear(lwin);
|
|
|
|
if (use_color) wattron(lwin, COLOR_PAIR(color));
|
|
|
|
for (int d = 0; d < len; d++) {
|
|
int dx = startx + d * (bw * size + 1) + offset;
|
|
|
|
if (buf[d] == '.') {
|
|
for (int yy = 0; yy < size; yy++)
|
|
for (int xx = 0; xx < size; xx++)
|
|
mvwaddch(
|
|
lwin,
|
|
starty + (bh - 1) * size + yy,
|
|
dx + xx,
|
|
ACS_CKBOARD
|
|
);
|
|
continue;
|
|
}
|
|
|
|
int digit = buf[d] - '0';
|
|
|
|
for (int y = 0; y < bh; y++) {
|
|
for (int x = 0; x < bw; x++) {
|
|
if (digit_bitmaps[digit][y][x]) {
|
|
for (int yy = 0; yy < size; yy++)
|
|
for (int xx = 0; xx < size; xx++)
|
|
mvwaddch(lwin,
|
|
starty + y * size + yy,
|
|
dx + x * size + xx,
|
|
ACS_CKBOARD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (use_color) wattroff(lwin, COLOR_PAIR(color));
|
|
|
|
wrefresh(lwin);
|
|
}
|
|
|
|
void win_bar(WINDOW *lwin, int data, int data_max, int use_color, int digits) {
|
|
int lwiny, lwinx;
|
|
getmaxyx(lwin, lwiny, lwinx);
|
|
|
|
win_clear(lwin);
|
|
|
|
int bar_width = lwinx - 4;
|
|
int bar_height = lwiny - 2;
|
|
int filled = (data * bar_width) / data_max;
|
|
int yellow = (75 * bar_width) / 100;
|
|
int red = (90 * bar_width) / 100;
|
|
|
|
for (int y = 1; y <= bar_height; y++) {
|
|
for (int x = 0; x < filled; x++) {
|
|
if (use_color) {
|
|
if (x < yellow)
|
|
wattron(lwin, COLOR_PAIR(3));
|
|
else if (x < red)
|
|
wattron(lwin, COLOR_PAIR(2));
|
|
else
|
|
wattron(lwin, COLOR_PAIR(1));
|
|
}
|
|
mvwaddch(lwin, y, 2 + x, ACS_CKBOARD);
|
|
}
|
|
}
|
|
|
|
if (use_color) {
|
|
wattroff(lwin, COLOR_PAIR(3));
|
|
wattroff(lwin, COLOR_PAIR(2));
|
|
wattroff(lwin, COLOR_PAIR(1));
|
|
}
|
|
|
|
if (use_color) {
|
|
wattroff(lwin, COLOR_PAIR(0));
|
|
}
|
|
|
|
char buf[16];
|
|
int len;
|
|
snprintf(buf , sizeof(buf), "%d", data);
|
|
len = strlen(buf);
|
|
|
|
int bh = 5; // bitmap height
|
|
int bw = 3; // bitmap width
|
|
|
|
int size = smaller_of((lwiny - 2) / bh, (lwinx - 2 - (digits - 1)) / (bw * digits));
|
|
|
|
int total_width = digits * (bw * size) + (digits - 1);
|
|
int startx = (lwinx - total_width) / 3;
|
|
int starty = (lwiny - bh * size) / 2;
|
|
int offset = (bw * size + 1) * (digits - len);
|
|
|
|
for (int d = 0; d < len; d++) {
|
|
int digit = buf[d] - '0';
|
|
int dx = startx + d * (bw * size + 1) + offset;
|
|
|
|
for (int y = 0; y < bh; y++) {
|
|
for (int x = 0; x < bw; x++) {
|
|
if (digit_bitmaps[digit][y][x]) {
|
|
for (int yy = 0; yy < size; yy++)
|
|
for (int xx = 0; xx < size; xx++)
|
|
mvwaddch(lwin,
|
|
starty + y * size + yy,
|
|
dx + x * size + xx,
|
|
ACS_CKBOARD);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
wrefresh(lwin);
|
|
|
|
if (use_color) {
|
|
wattroff(lwin, COLOR_PAIR(0));
|
|
}
|
|
}
|
|
|
|
void bar_mark(WINDOW *lwin) {
|
|
int lwiny, lwinx;
|
|
int first_mark, last_mark;
|
|
getmaxyx(lwin, lwiny, lwinx);
|
|
first_mark = (75 * (lwinx - 4)) / 100;
|
|
last_mark = (90 * (lwinx - 4)) / 100;
|
|
mvwaddch(lwin, 0, first_mark + 1, '|');
|
|
mvwaddch(lwin, lwiny - 1, first_mark + 1, '|');
|
|
mvwaddch(lwin, 0, last_mark + 1, '|');
|
|
mvwaddch(lwin, lwiny - 1, last_mark + 1, '|');
|
|
}
|
|
|
|
int color_high(float data, float ldata, float hdata) {
|
|
int percent = (int)((100.0f * (data - ldata)) / (hdata - ldata) + 0.5f);
|
|
if (percent >= 90) return 1;
|
|
else if (percent >= 75) return 2;
|
|
else return 3;
|
|
}
|
|
|
|
short color_low(float data, float ldata, float hdata) {
|
|
int percent = 100 - (int)((100.0f * (data - ldata)) / (hdata - ldata) + 0.5f);
|
|
if (percent >= 90) return 1;
|
|
else if (percent >= 75) return 2;
|
|
else return 3;
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
int option;
|
|
int delay = 1;
|
|
int fake_data = 0;
|
|
|
|
while ((option = getopt(argc, argv, "d:hf")) !=-1) {
|
|
switch (option) {
|
|
case 'd' :
|
|
delay = atoi(optarg);
|
|
if (delay < 0) {
|
|
delay = 0;
|
|
}
|
|
break;
|
|
case 'f' :
|
|
fake_data = 1;
|
|
break;
|
|
case 'h' :
|
|
printf("Usage: ./dashboard [-d int DELAY by how much to slow down] [-h HELP] [-f generate fake data]\n");
|
|
return(0);
|
|
}
|
|
}
|
|
|
|
initscr();
|
|
noecho();
|
|
cbreak();
|
|
curs_set(0);
|
|
nodelay(stdscr, TRUE);
|
|
refresh();
|
|
|
|
int y,x;
|
|
getmaxyx(stdscr, y, x);
|
|
if (y < 42 || x < 100) {
|
|
endwin();
|
|
perror("terminal window too small");
|
|
return 1;
|
|
}
|
|
|
|
int use_color = 0;
|
|
if (has_colors()) {
|
|
use_color = 1;
|
|
start_color();
|
|
use_default_colors();
|
|
init_pair(1, COLOR_RED, -1);
|
|
init_pair(2, COLOR_YELLOW, -1);
|
|
init_pair(3, COLOR_GREEN, -1);
|
|
}
|
|
|
|
WINDOW *win[10];
|
|
|
|
Tel tel;
|
|
tel.speed = (StInt){0, 0, 200, 3, NULL};
|
|
tel.rpm = (StInt){0, 0, 6000, 4, NULL};
|
|
tel.tq = (StFlt){0.0f, 1.8f, 3.5f, 3, NULL};
|
|
tel.power = (StInt){0, 0, 1300, 4, NULL};
|
|
tel.eff = (StFlt){0.0f, 0.0f, 300.0f, 5, NULL};
|
|
tel.bat = (StInt){0, 0, 100, 3, NULL};
|
|
tel.bat_temp = (StFlt){0.0f, 0.0f, 150.0f, 5, NULL};
|
|
tel.var_temp = (StFlt){0.0f, 0.0f, 150.0f, 5, NULL};
|
|
tel.mot_temp = (StFlt){0.0f, 0.0f, 150.0f, 5, NULL};
|
|
tel.message = (StStr){NULL};
|
|
|
|
int x3 = x / 3;
|
|
int x3r = x - 2 * x3;
|
|
|
|
int y2 = y / 2;
|
|
int y2r = y - y2;
|
|
int y4 = y2 / 2;
|
|
int y4r = y2 - y4;
|
|
int y6 = y2r / 3;
|
|
int y6r = y2r - 2 * y6;
|
|
|
|
typedef struct {
|
|
int height, width, starty, startx;
|
|
char *title;
|
|
} WinInfo;
|
|
|
|
WinInfo win_infos[10] = {
|
|
{y2, x3, 0, 0, "speed (km/h)"}, // 0
|
|
{y2, x3, 0, x3, "rpm (tr/min)"}, // 1
|
|
{y4, x3r, 0, x - x3r, "batteries (%)"}, // 2
|
|
{y4r, x3r, y4, x - x3r, "torque (N/m)"}, // 3
|
|
{y6, 2 * x3, y2, 0, "power (W)"}, // 4
|
|
{y6, x3r, y2, 2 * x3, "efficiency (Wh/Km)"}, // 5
|
|
{y6, x3, y2 + y6, 0, "batteries temperature (deg C)"}, // 6
|
|
{y6, x3, y2 + y6, x3, "variator temperature (deg C)"}, // 7
|
|
{y6, x3r, y2 + y6, x - x3r, "motor temperature (deg C)"}, // 8
|
|
{y6r, x, y - y6r, 0, "warnings"} // 9
|
|
};
|
|
|
|
for (int i = 0; i < 10; i++) {
|
|
win[i] = newwin(win_infos[i].height, win_infos[i].width, win_infos[i].starty, win_infos[i].startx);
|
|
box(win[i], 0, 0);
|
|
mvwprintw(win[i], 0, 2, "%s", win_infos[i].title);
|
|
wrefresh(win[i]);
|
|
}
|
|
tel.speed.lwin = win[0];
|
|
tel.rpm.lwin = win[1];
|
|
tel.tq.lwin = win[3];
|
|
tel.power.lwin = win[4];
|
|
tel.eff.lwin = win[5];
|
|
tel.bat.lwin = win[2];
|
|
tel.bat_temp.lwin = win[6];
|
|
tel.var_temp.lwin = win[7];
|
|
tel.mot_temp.lwin = win[8];
|
|
tel.message.lwin = win[9];
|
|
|
|
|
|
bar_mark(tel.power.lwin);
|
|
|
|
long t100 = 0, t1000 = 0;
|
|
int ch = ERR;
|
|
|
|
while(1) {
|
|
ch = tolower(getch());
|
|
switch (ch) {
|
|
case 'q' :
|
|
goto end;
|
|
case ERR :
|
|
default :
|
|
if (fake_data) {
|
|
get_fake_data(&tel);
|
|
} else {
|
|
//real_data()
|
|
}
|
|
break;
|
|
}
|
|
long now = now_ms();
|
|
if (now - t100 >= 100 * delay) {
|
|
win_int(tel.speed.lwin, tel.speed.data, 3, use_color, color_high(tel.speed.data, tel.speed.ldata, tel.speed.hdata));
|
|
win_int(tel.rpm.lwin, tel.rpm.data, 4, use_color, color_high(tel.rpm.data, tel.rpm.ldata, tel.rpm.hdata));
|
|
win_float(tel.tq.lwin, tel.tq.data, 3, use_color, color_high(tel.tq.data, tel.tq.ldata, tel.tq.hdata));
|
|
win_bar(tel.power.lwin, tel.power.data, tel.power.hdata, use_color, tel.power.digits);
|
|
win_float(tel.eff.lwin, tel.eff.data, 5, use_color, color_high(tel.eff.data, tel.eff.ldata, tel.eff.hdata));
|
|
t100 = now;
|
|
}
|
|
if (now - t1000 >= 1000 * delay) {
|
|
win_int(tel.bat.lwin, tel.bat.data, 3, use_color, color_low(tel.bat.data, tel.bat.ldata, tel.bat.hdata));
|
|
win_float(tel.bat_temp.lwin, tel.bat_temp.data, 5, use_color, color_high(tel.bat_temp.data, tel.bat_temp.ldata, tel.bat_temp.hdata));
|
|
win_float(tel.var_temp.lwin, tel.var_temp.data, 5, use_color, color_high(tel.var_temp.data, tel.var_temp.ldata, tel.var_temp.hdata));
|
|
win_float(tel.mot_temp.lwin, tel.mot_temp.data, 5, use_color, color_high(tel.mot_temp.data, tel.mot_temp.ldata, tel.mot_temp.hdata));
|
|
t1000 = now;
|
|
}
|
|
//win[9];
|
|
napms(10);
|
|
};
|
|
end:
|
|
for (int i = 0; i < 10; i++) {
|
|
delwin(win[i]);
|
|
}
|
|
endwin();
|
|
return 0;
|
|
}
|