Newer
Older
powermon_manager_sw / gui / scope_display.h
@Razvan Turiac Razvan Turiac on 8 Jul 9 KB Initial import
/* Copyright (C) 2020 - 2024, Thornwave Labs Inc
 * Written by Razvan Turiac <razvan.turiac@thornwave.com>
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated 
 * documentation files (the “Software”), to deal in the Software without restriction, including without limitation 
 * the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, 
 * and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
 * Attribution shall be given to Thornwave Labs Inc. and shall be made visible to the final user. 
 * 
 * THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 
 * TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL 
 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 
 * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef _SCOPE_DISPLAY_H
#define _SCOPE_DISPLAY_H


#include <wx/wx.h>
#include <stdint.h>

#include <vector>
#include <list>

#include <wx/tglbtn.h>



typedef std::vector<float> DataVector;


class ScopeDisplay: public wxWindow
{
public:
	class Listener
	{
	public:
		virtual void getChartData(uint64_t start_time, uint64_t last_time, size_t point_count, std::vector<DataVector> &data) = 0;
	};


	ScopeDisplay(wxFrame* parent, wxWindowID id, const wxPoint &pos = wxDefaultPosition, const wxSize &size = wxDefaultSize, long style = 0);
	~ScopeDisplay();

	void setListener(Listener* listener)
	{
		mListener = listener;
	}
	
	void addWaveform(const wxString &name, const wxString &scale_name, const wxString &unit, float resolution);
	void clearWaveforms(void);


	void UpdateScope(void);

	wxBitmap* SaveImage(void);

	void ChangeVerticalScale(bool up);
	void ChangeTimeScale(bool up);
	
	void ChangeVerticalPos(bool up, uint32_t increment);
	void HomeVerticalPos(void);

	void SetTimeScales(const std::vector<uint64_t> &scales, size_t default_scale_index);

	void ChangeTimePos(bool up, uint32_t increment);		//0 means a full division while, >0 means number of pixels on screen
	void HomeTimePos(void);
	void EndTimePos(void);

	void AdvanceTime(uint64_t time);
	
	uint64_t GetTime(void) const
	{
		return mMaxTime;
	}

	void setMinTime(uint64_t time)
	{
		mMinTime = time;
	}

	void setMaxTime(uint64_t time)
	{
		mMaxTime = time;
	}

	void UpdateWaveforms(void)
	{
		EndTimePos();
		Refresh();
		Update();
	}

	void CursorsEnable(bool enable)
	{
		mCursorsEnable = enable;

		if (mCursorsEnable)
		{
			mCursor1Pos.x = (mGraphLeft * 2 + mGraphRight) / 3;
			mCursor2Pos.x = (mGraphLeft + mGraphRight * 2) / 3;
		}

		Refresh();
		Update();
	}

	wxWindowID SaveDataButtonID(void) const
	{
		return ID_BUTTON_SAVE_DATA;
	}

	wxWindowID LoadDataButtonID(void) const
	{
		return ID_BUTTON_LOAD_DATA;
	}


	void OnKeyPressed(wxKeyEvent &event);
	void OnKeyReleased(wxKeyEvent &event);

	DECLARE_EVENT_TABLE()

private:
	enum
	{
		ID_BUTTON_VERT_SCALE_UP = wxID_HIGHEST + 1,
		ID_BUTTON_VERT_SCALE_DOWN,
		ID_BUTTON_VERT_POS_UP,
		ID_BUTTON_VERT_POS_DOWN,
		ID_BUTTON_VERT_POS_HOME,

		ID_BUTTON_TIME_SCALE_UP,
		ID_BUTTON_TIME_SCALE_DOWN,
		ID_BUTTON_TIME_POS_END,
		ID_BUTTON_TIME_POS_UP,
		ID_BUTTON_TIME_POS_DOWN,
		ID_BUTTON_TIME_POS_HOME,

		ID_BUTTON_CLEAR_SCOPE,
		ID_BUTTON_SAVE_IMAGE,
		ID_BUTTON_CURSORS,
		ID_BUTTON_SAVE_DATA,
		ID_BUTTON_LOAD_DATA
	};


	class VerticalScale
	{
	public:
		VerticalScale(const wxString &init_name, const wxString &init_unit, float init_resolution): name(init_name), unit(init_unit), resolution(init_resolution)
		{
		}

		bool operator==(const VerticalScale &rhs) const
		{
			return name == rhs.name;
		}

		wxString name;
		wxString unit;	
		float resolution;		//vertical resolution (units / division)
	};

	class WaveformDescriptor
	{
	public:
		WaveformDescriptor(VerticalScale &init_scale): scale(init_scale), button(nullptr), enabled(true), id(wxID_ANY)
		{
		}

		VerticalScale &scale;
		wxString name;

		wxColor color;

		wxButton* button;
		bool enabled;
		int32_t id;
	};


	class WaveColor
	{
	public:
		WaveColor(wxColour c): color(c), in_use(false)
		{
		}
		
		wxColour color;
		bool in_use;
	};
	

	Listener* mListener;

	std::vector<VerticalScale*> mVerticalScaleList;
	std::vector<WaveformDescriptor> mWaveformDescriptorList;
	
	std::vector<DataVector> mDataVectors;
	bool mWaveRefresh;

	int32_t mGraphTop;
	int32_t mGraphBottom;
	int32_t mGraphLeft;
	int32_t mGraphRight;
	
	wxColor mGridColor;
	wxColor mScalesColor;
	wxColor mCursor1Color;
	wxColor mCursor2Color;

	std::vector<WaveColor> mWaveColors;

	wxFont mFontBig;
	wxFont mFontMed;		
	wxFont mFontSmall;
	wxFont mScalesFont;

	int32_t mVGridDivisions;
	int32_t mHGridDivisions;

	//time scale
	size_t mTimeScalesIndex;
	std::vector<uint64_t> mTimeScales;
	uint64_t mTimeResolution;		//micro-seconds per division - horizontal resolution

	uint64_t mTimePosition;			//in us
	uint64_t mMinTime;				//in us
	uint64_t mMaxTime;				//in us

	int32_t mVertPosition;			//in divisions

	//this gets multiplied with the resolution
	int32_t mVerticalScale;
	int32_t mVerticalScaleExponent;

	uint64_t mCurrentTimeSpan;		//in divisons on screen
	uint32_t mCurrentVertSpan;		//in divisions
	
	float mSamplesOnScreen[4096];

	bool mCursorsEnable;
	wxPoint mCursor1Pos;
	wxPoint mCursor2Pos;
	bool mCursor1Dragging;
	bool mCursor2Dragging;

	
	uint32_t mWaveStreamSamplePeriod;		//in us
	uint32_t mWaveStreamVerticalOffset;


	wxPanel* mTopPanel;
	wxPanel* mBottomPanel;
	wxPanel* mLeftPanel;
	wxPanel* mScopePanel;

	wxBoxSizer* mTopSizer;

	wxBitmapButton* mTimeScaleUpButton;
	wxBitmapButton* mTimeScaleDownButton;

	wxBitmapButton* mTimePosUpButton;
	wxBitmapButton* mTimePosDownButton;
	wxBitmapButton* mTimePosEndButton;
	wxBitmapButton* mTimePosHomeButton;

	wxBitmapButton* mVertScaleUpButton;
	wxBitmapButton* mVertScaleDownButton;

	wxBitmapButton* mVertPosUpButton;
	wxBitmapButton* mVertPosDownButton;
	wxBitmapButton* mVertPosHomeButton;

	wxButton* mClearScopeButton;
	wxButton* mSaveImageButton;
	wxToggleButton* mCursorsButton;

	wxButton* mSaveDataButton;
	wxButton* mLoadDataButton;


	inline wxString PrintTimeResolution(uint64_t us)
	{
		uint64_t scale = 1;
		
		if (us < (scale * 1000))
		{
			return wxString::Format(wxT("%lluus / div"), us / scale);
		}
		
		scale *= 1000;
		if (us < (scale * 1000))
		{
			return wxString::Format(wxT("%llums / div"), us / scale);
		}

		scale *= 1000;
		if (us < (scale * 60))
		{
			return wxString::Format(wxT("%llus / div"), us / scale);
		}

		scale *= 60;
		if (us < (scale * 60))
		{
			return wxString::Format(wxT("%llum / div"), us / scale);
		}

		scale *= 60;
		if (us < (scale * 24))
		{
			return wxString::Format(wxT("%lluh / div"), us / scale);
		}
		
		scale *= 24;
		return wxString::Format(wxT("%llud / div"), us / scale);
	}


	inline wxString PrintTime(int64_t us)
	{
		wxDateTime dt;
		dt.Set((time_t)us / 1000000);
		dt.MakeUTC();

		return dt.Format(wxT("%I:%M:%S %p\n")) + dt.FormatDate();
	}
	
	
	inline wxString PrintDataSize(uint64_t size)
	{
		if ((size >= (1 << 30)) || (size == 0))
			return wxString::Format(wxT("%.2fGB"), (double)size / (double)(1 << 30));
		else if (size >= (1 << 20))
			return wxString::Format(wxT("%.2fMB"), (double)size / (double)(1 << 20));
		else if (size >= (1 << 10))
			return wxString::Format(wxT("%.2fkB"), (double)size / (double)(1 << 10));
		else
			return wxString::Format(wxT("%llubytes"), size);
	}
	
	
	inline wxString PrintValue(float val, const wxString &unit)
	{
		if (!isnan(val))
		{
			const float val_abs = fabs(val);
			if (val_abs == 0)
				return wxT("0");
			else if (val_abs < 0.001)		//use micro
				return wxString::Format(wxT("%.0fu%s"), val * 1000000, unit.c_str());
			else if (val_abs < 1.0)		//use milli
				return wxString::Format(wxT("%.0fm%s "), val * 1000, unit.c_str());
			else if (val_abs >= 1000)		//use kilo
				return wxString::Format(wxT("%.3fk%s"), val / 1000, unit.c_str());
			else
				return wxString::Format(wxT("%.3f%s"), val, unit.c_str());
		}
		else
		{
			return wxT("N/A");
		}
	}

	void DrawArrow(wxDC &dc, const wxPoint &pos, uint8_t dir, int32_t height, int32_t base);

	void DrawAnalogWaveform(wxDC &dc, const std::vector<float> &wave, const WaveformDescriptor &wd, int32_t x0, int32_t y0, int32_t x1, int32_t y1);
	
	void render(wxDC &dc);
	

	void OnButtonEnableWaveformClicked(wxCommandEvent &event);


	void OnButtonVertScaleUpClicked(wxCommandEvent &event);
	void OnButtonVertScaleDownClicked(wxCommandEvent &event);

	void OnButtonTimeScaleUpClicked(wxCommandEvent &event);
	void OnButtonTimeScaleDownClicked(wxCommandEvent &event);
	
	
	void OnButtonVertPosUpClicked(wxCommandEvent &event);
	void OnButtonVertPosDownClicked(wxCommandEvent &event);
	void OnButtonVertPosHomeClicked(wxCommandEvent &event);

	void OnButtonTimePosUpClicked(wxCommandEvent &event);
	void OnButtonTimePosDownClicked(wxCommandEvent &event);
	
	void OnButtonTimePosEndClicked(wxCommandEvent &event);
	void OnButtonTimePosHomeClicked(wxCommandEvent &event);
	
	void OnButtonClearScopeClicked(wxCommandEvent &event);
	void OnButtonSaveImageClicked(wxCommandEvent &event);
	
	void OnButtonCursorsClicked(wxCommandEvent &event);
	
	void OnPaint(wxPaintEvent &event);
	void OnSize(wxSizeEvent &event);

	void OnMouseLeftDown(wxMouseEvent &event);
	void OnMouseLeftUp(wxMouseEvent &event);
	void OnMouseRightDown(wxMouseEvent &event);
	void OnMouseRightUp(wxMouseEvent &event);
	void OnMouseLeaveWindow(wxMouseEvent &event);

	void OnMouseWheel(wxMouseEvent &event);
	void OnMouseMotion(wxMouseEvent &event);
};




#endif