001/*
002 * Copyright 2010-2015 Institut Pasteur.
003 * 
004 * This file is part of Icy.
005 * 
006 * Icy is free software: you can redistribute it and/or modify
007 * it under the terms of the GNU General Public License as published by
008 * the Free Software Foundation, either version 3 of the License, or
009 * (at your option) any later version.
010 * 
011 * Icy is distributed in the hope that it will be useful,
012 * but WITHOUT ANY WARRANTY; without even the implied warranty of
013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
014 * GNU General Public License for more details.
015 * 
016 * You should have received a copy of the GNU General Public License
017 * along with Icy. If not, see <http://www.gnu.org/licenses/>.
018 */
019package icy.gui.system;
020
021import java.awt.BasicStroke;
022import java.awt.Color;
023import java.awt.Dimension;
024import java.awt.Font;
025import java.awt.Graphics;
026import java.awt.Graphics2D;
027import java.awt.Image;
028import java.awt.RenderingHints;
029import java.awt.event.MouseEvent;
030import java.awt.event.MouseListener;
031import java.awt.image.BufferedImage;
032import java.util.Timer;
033import java.util.TimerTask;
034
035import javax.swing.JPanel;
036
037import icy.image.ImageUtil;
038import icy.image.cache.ImageCache;
039import icy.math.UnitUtil;
040import icy.network.NetworkUtil;
041import icy.resource.ResourceUtil;
042import icy.system.SystemUtil;
043import icy.system.thread.ThreadUtil;
044import icy.util.ColorUtil;
045import icy.util.GraphicsUtil;
046import vtk.vtkObjectBase;
047
048/**
049 * Memory monitor.
050 * 
051 * @author Fab & Stephane
052 */
053public class MemoryMonitorPanel extends JPanel implements MouseListener
054{
055    private static final long serialVersionUID = 5629509450385435829L;
056
057    private static int NBVAL = 94;
058
059    /**
060     * 0 est la valeur la plus ancienne.
061     */
062    private final double[][] valeur;
063    private final String[] infos;
064    private final Timer updateTimer;
065
066    private final Color cacheTextColor = ColorUtil.mix(Color.yellow, Color.white);
067    private final Color cpuColor = ColorUtil.mix(Color.blue, Color.white);
068    private final Color cpuTextColor = ColorUtil.mix(cpuColor, Color.white);
069    private final Color memColor = Color.green;
070    private final Color memTextColor = ColorUtil.mix(memColor, Color.white);
071    private final Color connectionColor = ColorUtil.mix(Color.red, Color.white);
072    private final BasicStroke cpuStroke = new BasicStroke(2);
073    private final BasicStroke memStroke = new BasicStroke(3);
074    private final Font textFont = new Font("Arial", Font.BOLD, 9);
075    private BufferedImage background = new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB);
076    private final Image networkImage = ImageUtil.getColorImageFromAlphaImage(ResourceUtil.ICON_NETWORK, Color.gray);
077    private final Image deleteImage = ImageUtil.getColorImageFromAlphaImage(ResourceUtil.ICON_DELETE, Color.red);
078
079    boolean displayHelpMessage = false;
080    int lastCacheUpdate;
081
082    public MemoryMonitorPanel()
083    {
084        super();
085
086        updateTimer = new Timer("Memory / CPU monitor");
087
088        // init tables
089        valeur = new double[NBVAL][2];
090        for (int i = 0; i < NBVAL; i++)
091        {
092            // mem
093            valeur[i][0] = 0;
094            // cpu load
095            valeur[i][1] = 0;
096        }
097        infos = new String[3];
098        for (int i = 0; i < 2; i++)
099            infos[i] = "";
100
101        lastCacheUpdate = 10;
102
103        setMinimumSize(new Dimension(120, 50));
104        setPreferredSize(new Dimension(140, 55));
105
106        addMouseListener(this);
107
108        updateTimer.scheduleAtFixedRate(new TimerTask()
109        {
110            @Override
111            public void run()
112            {
113                updateStats();
114            }
115        }, 100, 100);
116    }
117
118    @Override
119    protected void paintComponent(Graphics g)
120    {
121        final Graphics2D g2 = (Graphics2D) g.create();
122
123        final int w = getWidth();
124        final int h = getHeight();
125
126        // refresh BG
127        if ((background.getWidth() != w) || (background.getHeight() != h))
128        {
129            background = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
130            Graphics2D background_g2 = background.createGraphics();
131            GraphicsUtil.paintIcyBackGround(w, h, background_g2);
132        }
133
134        // draw cached BG
135        g2.drawImage(background, 0, 0, null);
136
137        // enabled AA
138        g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
139
140        // display graph
141        if (valeur != null)
142        {
143            float x;
144            double max;
145            double ymul;
146            final float step = w / 100f;
147
148            // draw used memory
149            g2.setStroke(memStroke);
150            g2.setColor(memColor);
151
152            max = SystemUtil.getJavaMaxMemory();
153            ymul = (h - 8) / max;
154            x = 6;
155            for (int i = 0; i < NBVAL - 1; i++)
156            {
157                final double v1 = Math.min(valeur[i][0], max);
158                final double v2 = Math.min(valeur[i + 1][0], max);
159                final int y1 = h - (int) (v1 * ymul);
160                final int y2 = h - (int) (v2 * ymul);
161                g2.drawLine((int) x, y1 - 4, (int) (x + step), y2 - 4);
162                x += step;
163            }
164
165            // draw CPU load
166            g2.setStroke(cpuStroke);
167            g2.setColor(cpuColor);
168
169            max = 100d;
170            ymul = (h - 8) / max;
171            x = 6;
172            for (int i = 0; i < NBVAL - 1; i++)
173            {
174                final double v1 = Math.min(valeur[i][1], max);
175                final double v2 = Math.min(valeur[i + 1][1], max);
176                final int y1 = h - (int) (v1 * ymul);
177                final int y2 = h - (int) (v2 * ymul);
178                g2.drawLine((int) x, y1 - 4, (int) (x + step), y2 - 4);
179                x += step;
180            }
181        }
182
183        // display text
184        g2.setFont(textFont);
185
186        // display Used & Max Memory
187        g2.setColor(Color.black);
188        GraphicsUtil.drawHCenteredString(g2, infos[0], (w / 2) + 1, 6 + 1, false);
189        g2.setColor(memTextColor);
190        GraphicsUtil.drawHCenteredString(g2, infos[0], w / 2, 6, false);
191        // display CPU Load
192        g2.setColor(Color.black);
193        GraphicsUtil.drawHCenteredString(g2, infos[1], (w / 2) + 1, 18 + 1, false);
194        g2.setColor(cpuTextColor);
195        GraphicsUtil.drawHCenteredString(g2, infos[1], w / 2, 18, false);
196        // display cache Load
197        g2.setColor(Color.black);
198        GraphicsUtil.drawHCenteredString(g2, infos[2], (w / 2) + 1, 30 + 1, false);
199        g2.setColor(cacheTextColor);
200        GraphicsUtil.drawHCenteredString(g2, infos[2], w / 2, 30, false);
201
202        String text;
203
204        // display internet connection
205        if (!NetworkUtil.hasInternetAccess())
206        {
207            g2.drawImage(networkImage, 10, 30, 16, 16, null);
208            g2.drawImage(deleteImage, 13, 35, 10, 10, null);
209
210            if (displayHelpMessage)
211            {
212                text = "Not connected to internet";
213
214                g2.setColor(Color.black);
215                GraphicsUtil.drawHCenteredString(g2, text, (w / 2) + 1, 30 + 1, false);
216                g2.setColor(connectionColor);
217                GraphicsUtil.drawHCenteredString(g2, text, w / 2, 30, false);
218            }
219        }
220
221        if (displayHelpMessage)
222        {
223            text = "click to force a garbage collector event";
224            g2.setColor(Color.black);
225            GraphicsUtil.drawHCenteredString(g2, text, (w / 2) + 1, 44 + 1, false);
226            g2.setColor(Color.white);
227            GraphicsUtil.drawHCenteredString(g2, text, w / 2, 44, false);
228        }
229
230        g2.dispose();
231    }
232
233    void updateStats()
234    {
235        final double usedMemory = SystemUtil.getJavaUsedMemory();
236        final int cpuLoad = SystemUtil.getCpuLoad();
237
238        // save used memory
239        newValue(0, usedMemory);
240        // save CPU load
241        newValue(1, cpuLoad);
242
243        setInfo(0, "Memory: " + UnitUtil.getBytesString(usedMemory) + " / "
244                + UnitUtil.getBytesString(SystemUtil.getJavaMaxMemory()));
245        setInfo(1, "CPU: " + cpuLoad + "%");
246        if (ImageCache.isEnabled())
247        {
248            // don't update cache stats (take sometime) at each frame
249            if (--lastCacheUpdate == 0)
250            {
251                try
252                {
253                        setInfo(2, "Cache - Memory: " + UnitUtil.getBytesString(ImageCache.usedMemory()) + "  Disk: "
254                        + UnitUtil.getBytesString(ImageCache.usedDisk()));
255                }
256                catch(Throwable t)
257                {
258                        // can happen when we exit Icy as the cache engine may be shutdown
259                        // we can ignore safely
260                }
261                
262                lastCacheUpdate = 10;
263            }
264        }
265        else
266            setInfo(2, "Cache disabled");
267
268        repaint();
269    }
270
271    /**
272     * Scroll les valeurs et en ajoute ( un seeker serait plus joli...)
273     */
274    public void newValue(int curve, double val)
275    {
276        for (int i = 0; i < NBVAL - 1; i++)
277            valeur[i][curve] = valeur[i + 1][curve];
278
279        valeur[NBVAL - 1][curve] = val;
280    }
281
282    public void setInfo(int infonb, String info)
283    {
284        infos[infonb] = info;
285    }
286
287    @Override
288    public void mouseClicked(MouseEvent event)
289    {
290        final MouseEvent e = event;
291
292        ThreadUtil.bgRun(new Runnable()
293        {
294            @Override
295            public void run()
296            {
297                final double freeBefore = SystemUtil.getJavaFreeMemory();
298
299                // force garbage collector
300                System.gc();
301
302                final double freeAfter = SystemUtil.getJavaFreeMemory();
303                final double released = freeAfter - freeBefore;
304                final double usedMemory = SystemUtil.getJavaUsedMemory();
305
306                System.out.println("Max / Used memory: " + UnitUtil.getBytesString(SystemUtil.getJavaMaxMemory())
307                        + " / " + UnitUtil.getBytesString((usedMemory > 0) ? usedMemory : 0) + " (released by GC: "
308                        + UnitUtil.getBytesString((released > 0) ? released : 0) + ")");
309            }
310        });
311
312        // double click --> force VTK garbage collection (need to be done in EDT or it crashes on OSX)
313        if (e.getClickCount() > 1)
314        {
315            vtkObjectBase.JAVA_OBJECT_MANAGER.gc(true);
316            System.out.println("VTK GC forced");
317        }
318    }
319
320    @Override
321    public void mouseEntered(MouseEvent arg0)
322    {
323        displayHelpMessage = true;
324    }
325
326    @Override
327    public void mouseExited(MouseEvent arg0)
328    {
329        displayHelpMessage = false;
330    }
331
332    @Override
333    public void mousePressed(MouseEvent arg0)
334    {
335
336    }
337
338    @Override
339    public void mouseReleased(MouseEvent arg0)
340    {
341
342    }
343}