Phi: nicer scroll animation for METAR widget
[fg:fgdata.git] / Nasal / performance_monitor / monitor.nas
1 # GUI displaying FG system performance statistics 
2 # performance_monitor.dialog.show() -- displays pilot list dialog
3
4 var PERFMON_RUNNING = 0;
5
6 var dialog = {
7     init: func(x = nil, y = nil) {
8         me.x = x;
9         me.y = y;
10         me.bg = [0, 0, 0, 0.3];    # background color
11         me.fg = [[0.9, 0.9, 0.2, 1], [1, 1, 1, 1]]; # alternative active
12
13         # "private"
14         var font = { name: "FIXED_8x13" };
15         me.header = ["  submodule", "cumulative/ms", "total/ms", "max/ms", "min/ms", "mean/ms", "stddev/ms", "iterations" ];
16         me.columns = [
17             { type: "text", property: "name",          format:   " %s", label: "------------------", halign: "fill", font: font },
18             { type: "text", property: "cumulative-ms", format: "%9.2f", label: "-------------",      halign: "fill", font: font },
19             { type: "text", property: "total-ms",      format: "%6.2f", label: "----------",         halign: "fill", font: font },
20             { type: "text", property: "max-ms",        format: "%5.2f", label: "---------",          halign: "fill", font: font },
21             { type: "text", property: "min-ms",        format: "%5.2f", label: "---------",          halign: "fill", font: font },
22             { type: "text", property: "mean-ms",       format: "%5.2f", label: "---------",          halign: "fill", font: font },
23             { type: "text", property: "stddev-ms",     format: "%5.2f", label: "---------",          halign: "fill", font: font },
24             { type: "text", property: "count",         format: "%3d",   label: "------",             halign: "fill", font: font },
25         ];
26         me.name = "performance-monitor";
27         me.dialog = nil;
28         me.loopid = 0;
29
30         me.listeners=[];
31         append(me.listeners, setlistener("/sim/startup/xsize", func me._redraw_()));
32         append(me.listeners, setlistener("/sim/startup/ysize", func me._redraw_()));
33         append(me.listeners, setlistener("/sim/signals/reinit-gui", func me._redraw_()));
34     },
35     create: func {
36         if (me.dialog != nil)
37             me.close();
38
39         setprop("/sim/performance-monitor/enabled",1);
40
41         me.dialog = gui.dialog[me.name] = gui.Widget.new();
42         me.dialog.set("name", me.name);
43         me.dialog.set("dialog-name", me.name);
44         if (me.x != nil)
45             me.dialog.set("x", me.x);
46         if (me.y != nil)
47             me.dialog.set("y", me.y);
48
49         me.dialog.set("layout", "vbox");
50         me.dialog.set("default-padding", 0);
51
52         me.dialog.setColor(me.bg[0], me.bg[1], me.bg[2], me.bg[3]);
53
54         var titlebar = me.dialog.addChild("group");
55         titlebar.set("layout", "hbox");
56
57         var w = titlebar.addChild("button");
58         w.node.setValues({ "pref-width": 32, "pref-height": 16, legend: "sort", default: 0 });
59         w.setBinding("nasal", "performance_monitor.dialog._redraw_()");
60
61         titlebar.addChild("empty").set("stretch", 1);
62         titlebar.addChild("text").set("label", "worst/average frame rate: ");
63         titlebar.addChild("text").node.setValues({ live: 1, property: "/sim/frame-rate-worst", label: "--" });
64         titlebar.addChild("text").set("label", "/");
65         titlebar.addChild("text").node.setValues({ live: 1, property: "/sim/frame-rate", label: "-- fps,", format: "%2d fps," });
66         titlebar.addChild("text").set("label", " worst frame delay: ");
67         titlebar.addChild("text").node.setValues({ live: 1, property: "/sim/frame-latency-max-ms", label: "----.-", format: "%4d ms" });
68         titlebar.addChild("empty").set("stretch", 1);
69
70         var w = titlebar.addChild("button");
71         w.node.setValues({ "pref-width": 16, "pref-height": 16, legend: "", default: 0 });
72         # "Esc" causes dialog-close
73         w.set("key", "Esc");
74         w.setBinding("nasal", "performance_monitor.dialog.del()");
75
76         me.dialog.addChild("hrule");
77
78         var content = me.dialog.addChild("group");
79         content.set("layout", "table");
80         content.set("default-padding", 0);
81
82         var modulelist = props.globals.getNode( "/sim/performance-monitor/subsystems", 1 ).getChildren();
83         var DataReady = size(modulelist) > 0;
84         if (!DataReady)
85         {
86             content.addChild("text").set("label", "wait...");
87         }
88         else
89         {
90             var row = 0;
91             var col = 0;
92             foreach (var h; me.header) {
93                 var w = content.addChild("text");
94                 var l = typeof(h) == "func" ? h() : h;
95                 w.node.setValues({ "label": l, "row": row, "col": col, halign: me.columns[col].halign });
96                 w = content.addChild("hrule");
97                 w.node.setValues({ "row": row + 1, "col": col });
98                 col += 1;
99             }
100             row += 2;
101             var odd = 1;
102             modulelist = sort (modulelist, func (a,b) a.getChild("cumulative-ms",0,1).getValue() < b.getChild("cumulative-ms",0,1).getValue());
103             foreach (var mp; modulelist) {
104                 var col = 0;
105                 var color = me.fg[odd = !odd];
106                 foreach (var column; me.columns) {
107                     var p = column.property;
108                     var w = nil;
109                     if (column.type == "text") {
110                         w = content.addChild("text");
111                         w.node.setValues(column);
112                     }
113                     w.setColor(color[0], color[1], color[2], color[3]);
114                     w.node.setValues({ row: row, col: col, live: 1, property: mp.getPath() ~ "/" ~ p });
115                     col += 1;
116                 }
117                 row += 1;
118             }
119         }
120         if (me.x != nil)
121             me.dialog.set("x", me.x);
122         if (me.y != nil)
123             me.dialog.set("y", me.y);
124
125         fgcommand("dialog-new", me.dialog.prop());
126         fgcommand("dialog-show", me.dialog.prop());
127         if (!DataReady)
128             settimer(func me.update(me.loopid+=1), 1, 1);
129     },
130     update: func(id) {
131         id == me.loopid or return;
132         if (!PERFMON_RUNNING)
133             return;
134         me._redraw_();
135     },
136     _redraw_: func {
137         if (me.dialog != nil) {
138             me.close();
139             me.create();
140         }
141     },
142     close: func {
143         if (me.dialog != nil) {
144             me.x = me.dialog.prop().getNode("x").getValue();
145             me.y = me.dialog.prop().getNode("y").getValue();
146         }
147         fgcommand("dialog-close", me.dialog.prop());
148         setprop("/sim/performance-monitor/enabled",0);
149     },
150     del: func {
151         PERFMON_RUNNING = 0;
152         me.close();
153         foreach (var l; me.listeners)
154             removelistener(l);
155         delete(gui.dialog, me.name);
156     },
157     show: func {
158         if (!PERFMON_RUNNING) {
159             PERFMON_RUNNING = 1;
160             me.init(2, 20);
161             me.create();
162         }
163     },
164     toggle: func {
165         if (!PERFMON_RUNNING)
166             me.show();
167         else
168             me.del();
169     },
170 };
171