Rind 5 éve
commit
0d448de75b
8 módosított fájl, 1811 hozzáadás és 0 törlés
  1. 14 0
      .gitignore
  2. 0 0
      README.md
  3. 92 0
      gfaqt.pro
  4. 26 0
      src/defines.h
  5. 359 0
      src/qappctrl.cpp
  6. 336 0
      src/qappctrl.h
  7. 472 0
      src/qappinfo.cpp
  8. 512 0
      src/qsysinfo.cpp

+ 14 - 0
.gitignore

@@ -0,0 +1,14 @@
+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~
+
+Debug/
+Profile/
+Release/
+install/
+*.pro.user
+*.bak
+buildall.sh

+ 0 - 0
README.md


+ 92 - 0
gfaqt.pro

@@ -0,0 +1,92 @@
+TEMPLATE = lib
+VERSION = 1.0
+QT += qml quick core
+CONFIG -= app_bundle
+CONFIG += c++11 shared thread
+
+####################################################################################
+
+SOURCES += \
+    src/qappctrl.cpp \
+    src/qappinfo.cpp \
+    src/qsysinfo.cpp
+
+HEADERS += \
+    src/qappctrl.h
+
+####################################################################################
+
+GFA_LIB_PATH = /usr/lib/gfa
+GFA_INC_PATH = /usr/include/gfa
+QMAKE_RPATHLINKDIR += $$GFA_LIB_PATH
+QMAKE_RPATHDIR += $$GFA_LIB_PATH
+QMAKE_DEL_DIR = rmdir --ignore-fail-on-non-empty
+QMAKE_LN_SHLIB = :
+
+####################################################################################
+
+CONFIG(debug, debug|release) {
+    QMAKE_CXXFLAGS -= -Os
+    QMAKE_CFLAGS -= -Os
+    QMAKE_CXXFLAGS += -D_DEBUG
+    QMAKE_CFLAGS += -D_DEBUG
+	TARGET = $$join(TARGET,,,d)
+}
+
+####################################################################################
+
+target.path = $$GFA_LIB_PATH
+INSTALLS += target
+
+####################################################################################
+
+linux-g++ {
+	includes.path = $$GFA_INC_PATH
+	includes.extra += -$(INSTALL_FILE) $$PWD/src/qappctrl.h $(INSTALL_ROOT)$$includes.path
+	includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/qappctrl.h
+	INSTALLS += includes
+}
+
+####################################################################################
+
+linux-buildroot-g++ {
+	exists($$[QT_SYSROOT]) {
+		library.path = $$[QT_SYSROOT]$$target.path
+		library.extra += -$(INSTALL_PROGRAM) $(TARGET) $(INSTALL_ROOT)$$library.path/$(TARGET)
+		library.extra += $$escape_expand(\\n\\t)-$(SYMLINK) $(TARGET) $(INSTALL_ROOT)$$library.path/$(TARGET0)
+		library.extra += $$escape_expand(\\n\\t)-$(SYMLINK) $(TARGET) $(INSTALL_ROOT)$$library.path/$(TARGET1)
+		library.extra += $$escape_expand(\\n\\t)-$(SYMLINK) $(TARGET) $(INSTALL_ROOT)$$library.path/$(TARGET2)
+		library.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$library.path/$(TARGET)
+		library.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$library.path/$(TARGET0)
+		library.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$library.path/$(TARGET1)
+		library.uninstall += $$escape_expand(\\n\\t)-$(DEL_FILE) $(INSTALL_ROOT)$$library.path/$(TARGET2)
+		INSTALLS += library
+
+		includes.path = $$[QT_SYSROOT]$$GFA_INC_PATH
+		includes.extra += -$(INSTALL_FILE) $$PWD/src/qappctrl.h $(INSTALL_ROOT)$$includes.path
+		includes.uninstall += -$(DEL_FILE) $(INSTALL_ROOT)$$includes.path/qappctrl.h
+		INSTALLS += includes
+
+		itoolchain.target = install_toolchain
+		itoolchain.depends = install_library install_includes
+		QMAKE_EXTRA_TARGETS += itoolchain
+
+		utoolchain.target = uninstall_toolchain
+		utoolchain.depends = uninstall_library uninstall_includes
+		QMAKE_EXTRA_TARGETS += utoolchain
+	}
+    QMAKE_CXXFLAGS += -D_TARGET_BUILD
+    QMAKE_CFLAGS += -D_TARGET_BUILD
+}
+
+####################################################################################
+
+deploylib.target = deploylib
+deploylib.commands = @echo BASENAME="$(QMAKE_TARGET)" > deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET="$(TARGET)" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET0="$(TARGET0)" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET1="$(TARGET1)" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo TARGET2="$(TARGET2)" >> deploytargets
+deploylib.commands += $$escape_expand(\\n\\t)@echo HEADERS="\\\"qappctrl.h\\\"" >> deploytargets
+PRE_TARGETDEPS += deploylib
+QMAKE_EXTRA_TARGETS += deploylib

+ 26 - 0
src/defines.h

@@ -0,0 +1,26 @@
+// defines.h :
+//
+
+#if !defined(AGD_DEFINES_H__CBCFEAE4_7504_435D_BD29_909BA80D72CD__INCLUDED_)
+#define AGD_DEFINES_H__CBCFEAE4_7504_435D_BD29_909BA80D72CD__INCLUDED_
+
+#ifdef __cplusplus
+extern "C" {
+#endif	//	__cplusplus
+
+/////////////////////////////////////////////////////////////////////////////
+// defines.h - Declarations:
+
+#ifdef _DEBUG
+#define TRACE(...)							fprintf(stdout, __VA_ARGS__), fflush(stdout)
+#else	//	_DEBUG
+#define TRACE(...)
+#endif	//	_DEBUG
+
+#define UNUSED(v)							(void)v
+
+/////////////////////////////////////////////////////////////////////////////
+#ifdef __cplusplus
+}
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_DEFINES_H__CBCFEAE4_7504_435D_BD29_909BA80D72CD__INCLUDED_)

+ 359 - 0
src/qappctrl.cpp

@@ -0,0 +1,359 @@
+#include <QTimerEvent>
+#include "qappctrl.h"
+#include "defines.h"
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _INVALID_SLOT_INDEX								-1
+#define _APP_CTRL_MAX_SLOTS								((int)(sizeof(appid_t) * 8))
+#define _APP_INDEX_FROM_APP_ID(aid)						(ffsll(aid) - 1)
+#define _IS_POWER_OF_2(x)								(!!(x) && !((x) & ((x) - 1)))
+#define _IS_VALID_APP_ID(i)								_IS_POWER_OF_2(i)
+#define _TIMESPEC_2_US(ts)								(((clock64_t)(ts).tv_sec) * 1000000LL + ((clock64_t)(ts).tv_nsec) / 1000LL)
+#define _TIMESPEC_DIFF_US(ts1, ts2)						(_TIMESPEC_2_US(ts1) - _TIMESPEC_2_US(ts2))
+#define _MIN_TIMER_INT									20
+
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QGfaAppCtrl::QGfaAppCtrl(QObject *pParent) :	QObject(pParent),
+												m_bSysInfoRunning(false),
+												m_hAC(NULL),
+												m_nAppID(0),
+												m_nTimerID(0),
+												m_sysInfo(this),
+												m_curPass(0),
+												m_minPass(LLONG_MAX),
+												m_maxPass(LLONG_MIN),
+												m_avgPass(0),
+												m_nEvtSrcs(0),
+												m_nHeavyLoadUpdateIntervalUs(2500000)
+{
+	for(int i = 0; i < _APP_CTRL_MAX_SLOTS; ++i)
+	{
+		QGfaAppInfo *pai = new QGfaAppInfo(i, this);
+		connect(pai, SIGNAL(sendControlMessage(appid_t, ctrlmsg_t)), SLOT(onSendControlMessage(appid_t, ctrlmsg_t)));
+		m_appInfo.append(pai);
+	}
+
+	memset(&m_tsLastHeavyLoadUpdate, 0, sizeof(m_tsLastHeavyLoadUpdate));
+    setObjectName("QGfaAppCtrl");
+}
+
+QGfaAppCtrl::~QGfaAppCtrl(void)
+{
+	Release();
+	for(int i = 0; i < _APP_CTRL_MAX_SLOTS; ++i)
+	{
+		QGfaAppInfo *pai = m_appInfo.at(i);
+		delete pai;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool QGfaAppCtrl::Create(appid_t nAppID, const char *pszDisplayName, int nTimerIntMs, clock64_t nHeavyLoadUpdateIntervalMs)
+{
+	if(!m_hAC)
+	{
+		if(nTimerIntMs < _MIN_TIMER_INT)
+			nTimerIntMs = _MIN_TIMER_INT;
+		if(nHeavyLoadUpdateIntervalMs < (nTimerIntMs * 50))
+			nHeavyLoadUpdateIntervalMs = (nTimerIntMs * 50);
+
+		if((m_hAC = ::GfaIpcAppCtrlAcquire(nAppID, pszDisplayName, 0, (clock64_t)(nTimerIntMs * 1000 * 2))))
+		{
+			m_nAppID = nAppID;
+			m_nHeavyLoadUpdateIntervalUs = nHeavyLoadUpdateIntervalMs * 1000;
+			::GfaIpcAppCtrlSubscribeSysEvents(m_hAC, GFA_APPCTRL_SYSEVENT_ALL_STG_DEV);
+			m_nTimerID = startTimer(nTimerIntMs, Qt::CoarseTimer);
+			
+			bool bBootFromEmmc;
+			if(::GfaIpcAppCtrlGetBootFromEmmc(m_hAC, &bBootFromEmmc))
+			{
+				m_sysInfo.setBootFromEmmc(bBootFromEmmc);
+			}
+		}
+	}
+
+	return !!m_hAC;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QGfaAppCtrl::Release(void)
+{
+	if(m_hAC)
+	{
+		killTimer(m_nTimerID);
+		::GfaIpcAppCtrlRelease(m_hAC);
+		m_hAC = NULL;
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QGfaAppCtrl::RegisterQmlTypes(QQmlEngine &rEng, int nVerMajor, int nVerMinor)
+{
+	qmlRegisterUncreatableType<QGfaStgDevList>("com.gfa.ipc.appctrl", nVerMajor, nVerMinor, "QGfaStgDevList", QStringLiteral("class not creatable in QML"));
+	qmlRegisterUncreatableType<QGfaSysInfo>("com.gfa.ipc.appctrl", nVerMajor, nVerMinor, "QGfaSysInfo", QStringLiteral("class not creatable in QML"));
+	qmlRegisterUncreatableType<QGfaAppInfo>("com.gfa.ipc.appctrl", nVerMajor, nVerMinor, "QGfaAppInfo", QStringLiteral("class not creatable in QML"));
+	qmlRegisterUncreatableType<QGfaAppCtrl>("com.gfa.ipc.appctrl", nVerMajor, nVerMinor, "QGfaAppCtrl", QStringLiteral("class not creatable in QML"));
+	rEng.rootContext()->setContextProperty(QStringLiteral("qGfaAppCtrl"), this);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool QGfaAppCtrl::PresetDisplayName(appid_t nAppID, const char *pszName)
+{
+	if(m_hAC)
+		return ::GfaIpcAppCtrlPresetDisplayName(m_hAC, nAppID, pszName);
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool QGfaAppCtrl::SubscribeStateEvents(appid_t nAppMask)
+{
+	if(m_hAC)
+	{
+		nAppMask &= ~m_nAppID;
+		m_nEvtSrcs |= nAppMask;
+		return ::GfaIpcAppCtrlSubscribeStateEvents(m_hAC, nAppMask);
+	}
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QGfaAppCtrl::timerEvent(QTimerEvent *event)
+{
+	if((event->timerId() == m_nTimerID) && m_hAC)
+	{
+		static clock64_t nCurPass = 0;
+		struct timespec	tsEnterUpdate, tsLeaveUpdate;
+		::clock_gettime(CLOCK_MONOTONIC, &tsEnterUpdate);
+		GFA_APPCTRL_SYSMEM sm;
+		HAPPINFO hAI;
+		bool bDoHeavyLoadUpdate = (_TIMESPEC_DIFF_US(tsEnterUpdate, m_tsLastHeavyLoadUpdate) >= m_nHeavyLoadUpdateIntervalUs);
+
+		if((hAI = ::GfaIpcAppCtrlInfoUpdate(m_hAC, nCurPass)))
+		{
+			int b;
+		    appid_t app, nAppIdSrc;
+		    sysevt_t nSysEvt;
+			GFA_APPCTRL_APPTIMES at;
+			GFA_APPCTRL_APPMEM am;
+			GFA_SYSINFO_DISK disk;
+			GFA_SYSINFO_PARTITION part;
+			char szDispName[128];
+
+			while((nAppIdSrc = ::GfaIpcAppCtrlGetNextStateEvtSrc(hAI)))
+			{
+				GfaIpcAppStates state = ::GfaIpcAppCtrlGetState(m_hAC, nAppIdSrc);
+				int nIndex = appIndexFromAppID(nAppIdSrc);
+				QGfaAppInfo &ai = *m_appInfo[nIndex];
+				bool bStateChanged = ai.setState(state);
+
+				if(bStateChanged)
+				{
+					if(state == GIAS_Running)
+						memset(&m_tsLastHeavyLoadUpdate, 0, sizeof(m_tsLastHeavyLoadUpdate));
+					else if(state != GIAS_Paused)
+					{
+						ai.setAppTimes(NULL, state);
+						ai.setAppMemInfo(NULL, state);
+					}
+					
+					if(nAppIdSrc == GFA_APPCTRL_APPID_SYSINFO)
+					{
+						m_bSysInfoRunning = (state == GIAS_Running);
+						m_sysInfo.setSysInfoRunning(m_bSysInfoRunning);
+					}
+				}
+
+				if((state >= GIAS_StateNotRunning) && (state <= GIAS_Paused))
+				{
+					if(::GfaIpcAppCtrlGetDisplayName(m_hAC, nAppIdSrc, szDispName, sizeof(szDispName)))
+					{
+						ai.setName(szDispName);
+					}
+				}
+			}
+			
+			if(m_bSysInfoRunning)
+			{
+				while((nSysEvt = ::GfaIpcAppCtrlGetNextSysEvt(hAI)))
+				{
+					int nIndex;
+					
+					switch(nSysEvt)
+					{
+					case GFA_APPCTRL_SYSEVENT_DISK_EVT:
+						while((nIndex = ::GfaIpcAppCtrlGetNextDiskRemoved(m_hAC, &disk)) >= 0)
+						{
+							m_sysInfo.diskRemoved(nIndex, disk);
+						}
+						while((nIndex = ::GfaIpcAppCtrlGetNextDiskAdded(m_hAC, &disk)) >= 0)
+						{
+							m_sysInfo.diskAdded(nIndex, disk);
+						}
+						break;
+					case GFA_APPCTRL_SYSEVENT_PART_EVT:
+						while((nIndex = ::GfaIpcAppCtrlGetNextPartitionRemoved(m_hAC, &part)) >= 0)
+						{
+							m_sysInfo.partitionRemoved(nIndex, part);
+						}
+						while((nIndex = ::GfaIpcAppCtrlGetNextPartitionAdded(m_hAC, &part)) >= 0)
+						{
+							m_sysInfo.partitionAdded(nIndex, part);
+						}
+						break;
+					case GFA_APPCTRL_SYSEVENT_MOUNT_EVT:
+						while((nIndex = ::GfaIpcAppCtrlGetNextMountRemoved(m_hAC, &part)) >= 0)
+						{
+							m_sysInfo.mountRemoved(nIndex, part);
+						}
+						while((nIndex = ::GfaIpcAppCtrlGetNextMountAdded(m_hAC, &part)) >= 0)
+						{
+							m_sysInfo.mountAdded(nIndex, part);
+						}
+						break;
+					}
+				}
+			}
+
+			nAppIdSrc = m_nEvtSrcs;
+			bool bDontSaveLast = false;
+
+			while(nAppIdSrc)
+			{
+				b = ffsll(nAppIdSrc) - 1;
+				app = ((appid_t)0x1 << b);
+				nAppIdSrc &= ~app;
+				QGfaAppInfo &ai = *m_appInfo[b];
+				GfaIpcAppStates state = ::GfaIpcAppCtrlGetState(m_hAC, app);
+				
+				if((state < GIAS_Running) || (state > GIAS_Hanging))
+					continue;
+
+				if(::GfaIpcAppCtrlGetAppMem(m_hAC, app, &am))
+				{
+					ai.setAppMemInfo(&am, state, bDoHeavyLoadUpdate);
+				}
+
+				if(::GfaIpcAppCtrlGetAppTimes(m_hAC, app, &at) >= 0)
+				{
+					if(!at.nCycleLastUs)
+					{
+						bDontSaveLast = true;
+						continue;
+					}
+
+					ai.setAppTimes(&at, state, bDoHeavyLoadUpdate);
+				}
+
+				size_t nSize = ::GfaIpcAppCtrlGetAppSize(m_hAC, app);
+				ai.setAppSize((quint32)nSize, state);
+			}
+
+			if(bDoHeavyLoadUpdate && !bDontSaveLast)
+				m_tsLastHeavyLoadUpdate = tsEnterUpdate;
+		}
+			
+		if(::GfaIpcAppCtrlGetSysMem(m_hAC, &sm))
+		{
+			m_sysInfo.setSysMemInfo(&sm, bDoHeavyLoadUpdate);
+		}
+
+		::clock_gettime(CLOCK_MONOTONIC, &tsLeaveUpdate);
+		m_curPass = nCurPass = _TIMESPEC_DIFF_US(tsLeaveUpdate, tsEnterUpdate);
+
+		if(m_minPass > m_curPass)
+		{
+			m_minPass = m_curPass;
+			emit minPassChanged(m_minPass);
+		}
+
+		if(m_maxPass < m_curPass)
+		{
+			m_maxPass = m_curPass;
+			emit maxPassChanged(m_maxPass);
+		}
+		
+		if(!m_avgPass)
+		{
+			m_avgPass = m_curPass;
+			emit avgPassChanged(m_avgPass);
+		}
+		else
+		{
+			static uint64_t nPasses = 0;
+			++nPasses;
+			clock64_t avgPass = (m_avgPass + m_curPass) / 2;
+			if(m_avgPass != avgPass)
+			{
+				m_avgPass = avgPass;
+				if(!(nPasses % 50))
+					emit avgPassChanged(m_avgPass);
+			}
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QGfaAppCtrl::onSendControlMessage(appid_t nAppID, ctrlmsg_t msg)
+{
+	if(m_hAC)
+		::GfaIpcAppCtrlSendCtrlMsg(m_hAC, nAppID, msg);
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int QGfaAppCtrl::SetState(int nState)
+{
+	if(m_hAC)
+		::GfaIpcAppCtrlSetState(m_hAC, (GfaIpcAppStates)nState);
+	return GIAS_Invalid;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+quint64 QGfaAppCtrl::minPass(void) const
+{
+	return m_minPass;
+}
+
+quint64 QGfaAppCtrl::maxPass(void) const
+{
+	return m_maxPass;
+}
+
+quint64 QGfaAppCtrl::avgPass(void) const
+{
+	return m_avgPass;
+}
+
+QGfaSysInfo* QGfaAppCtrl::sysInfo(void)
+{
+	return &m_sysInfo;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int QGfaAppCtrl::appIndexFromAppID(quint64 nAppID)
+{
+	if(_IS_VALID_APP_ID(nAppID))
+		return _APP_INDEX_FROM_APP_ID(nAppID);
+	return _INVALID_SLOT_INDEX;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+QQmlListProperty<QGfaAppInfo> QGfaAppCtrl::appInfo(void)
+{
+	return QQmlListProperty<QGfaAppInfo>(this, m_appInfo);
+}

+ 336 - 0
src/qappctrl.h

@@ -0,0 +1,336 @@
+// qappctrl.h :
+//
+
+#if !defined(AGD_QAPPCTRL_H__ADD46467_F628_4A07_8844_613598E359DE__INCLUDED_)
+#define AGD_QAPPCTRL_H__ADD46467_F628_4A07_8844_613598E359DE__INCLUDED_
+
+#include <QObject>
+#include <QQmlEngine>
+#include <QQmlContext>
+#include <QAbstractTableModel>
+#include <vector>
+#include <gfa/gfaipc.h>
+#include <gfa/ipcpriv.h>
+
+#ifdef __cplusplus
+
+/////////////////////////////////////////////////////////////////////////////
+// qappctrl.h - Declarations:
+
+typedef struct _STG_DEV_DISK_PART
+{
+	_STG_DEV_DISK_PART(int d, int p) {
+		nDiskIdx = d;
+		nPartIdx = p;
+	}
+	int nDiskIdx;
+	int nPartIdx;
+}STG_DEV_DISK_PART, *LPSTG_DEV_DISK_PART;
+typedef const STG_DEV_DISK_PART *LPCSTG_DEV_DISK_PART;
+
+/////////////////////////////////////////////////////////////////////////////
+
+class QGfaStgDevList : public QAbstractTableModel
+{
+    Q_OBJECT
+
+public:
+	explicit QGfaStgDevList(QObject *pParent = NULL);
+	virtual ~QGfaStgDevList(void);
+
+	int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
+	int columnCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
+	QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
+	QHash<int, QByteArray> roleNames(void) const Q_DECL_OVERRIDE;
+	
+	void diskAdded(int nIndex, const GFA_SYSINFO_DISK &disk);
+	void diskRemoved(int nIndex, const GFA_SYSINFO_DISK &disk);
+	void partitionAdded(int nIndex, const GFA_SYSINFO_PARTITION &part);
+	void partitionRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part);
+	void mountAdded(int nIndex, const GFA_SYSINFO_PARTITION &part);
+	void mountRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part);
+	void clearAll(void);
+
+private:
+	void insertPartition(int nDiskIdx, int nPartIdx);
+	void removePartition(int nDiskIdx, int nPartIdx);
+	int FindPartVecEntry(int nDiskIdx, int nPartIdx);
+
+private:
+	GFA_SYSINFO_STORAGE_DEVICE_MAP m_stgDevShadowCpy;
+	std::vector<STG_DEV_DISK_PART> m_partVec;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class QGfaSysInfo : public QObject
+{
+    Q_OBJECT
+	Q_PROPERTY(quint64 memTotal READ memTotal NOTIFY memTotalChanged)
+	Q_PROPERTY(quint64 memUsed READ memUsed NOTIFY memUsedChanged)
+	Q_PROPERTY(quint64 memFree READ memFree NOTIFY memFreeChanged)
+	Q_PROPERTY(quint64 memAvailable READ memAvailable NOTIFY memAvailableChanged)
+	Q_PROPERTY(quint64 memBuffers READ memBuffers NOTIFY memBuffersChanged)
+	Q_PROPERTY(quint64 memCached READ memCached NOTIFY memCachedChanged)
+    Q_PROPERTY(QGfaStgDevList *stgDev READ stgDev CONSTANT)
+    Q_PROPERTY(bool bootFromEmmc READ bootFromEmmc NOTIFY bootFromEmmcChanged)
+
+public:
+	explicit QGfaSysInfo(QObject *pParent = NULL);
+	virtual ~QGfaSysInfo(void);
+
+signals:
+	void memTotalChanged(quint64 val);
+	void memUsedChanged(quint64 val);
+	void memFreeChanged(quint64 val);
+	void memAvailableChanged(quint64 val);
+	void memBuffersChanged(quint64 val);
+	void memCachedChanged(quint64 val);
+	void bootFromEmmcChanged(bool val);
+
+private:
+	quint64 memTotal(void) const;
+	quint64 memUsed(void) const;
+	quint64 memFree(void) const;
+	quint64 memAvailable(void) const;
+	quint64 memBuffers(void) const;
+	quint64 memCached(void) const;
+	QGfaStgDevList* stgDev(void);
+	bool bootFromEmmc(void) const;
+
+public:
+	void setSysMemInfo(LPCGFA_APPCTRL_SYSMEM psm, bool bDoHeavyLoadUpdate = false);
+	void setSysInfoRunning(bool bRunning);
+	void setBootFromEmmc(bool bootFromEmmc);
+
+	inline void diskAdded(int nIndex, const GFA_SYSINFO_DISK &disk) {
+		return m_stgDevList.diskAdded(nIndex, disk);}
+
+	inline void diskRemoved(int nIndex, const GFA_SYSINFO_DISK &disk) {
+		return m_stgDevList.diskRemoved(nIndex, disk);}
+
+	inline void partitionAdded(int nIndex, const GFA_SYSINFO_PARTITION &part) {
+		return m_stgDevList.partitionAdded(nIndex, part);}
+		
+	inline void partitionRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part) {
+		return m_stgDevList.partitionRemoved(nIndex, part);}
+
+	inline void mountAdded(int nIndex, const GFA_SYSINFO_PARTITION &part) {
+		return m_stgDevList.mountAdded(nIndex, part);}
+
+	inline void mountRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part) {
+		return m_stgDevList.mountRemoved(nIndex, part);}
+
+private:
+	bool m_bSysInfoRunning;
+	quint64 m_nMemTotal;
+	quint64 m_nMemUsed;
+	quint64 m_nMemFree;
+	quint64 m_nMemAvailable;
+	quint64 m_nMemBuffers;
+	quint64 m_nMemCached;
+	QGfaStgDevList m_stgDevList;
+	bool m_bootFromEmmc;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class QGfaAppInfo : public QObject
+{
+    Q_OBJECT
+	Q_PROPERTY(int state READ state NOTIFY stateChanged)
+	Q_PROPERTY(QString stateText READ stateText NOTIFY stateTextChanged)
+	Q_PROPERTY(QString name READ name NOTIFY nameChanged)
+	Q_PROPERTY(quint64 cycInt READ cycInt NOTIFY cycIntChanged)
+	Q_PROPERTY(quint64 cycCur READ cycCur NOTIFY cycCurChanged)
+	Q_PROPERTY(quint64 cycMin READ cycMin NOTIFY cycMinChanged)
+	Q_PROPERTY(quint64 cycMax READ cycMax NOTIFY cycMaxChanged)
+	Q_PROPERTY(quint64 wktCur READ wktCur NOTIFY wktCurChanged)
+	Q_PROPERTY(quint64 wktMin READ wktMin NOTIFY wktMinChanged)
+	Q_PROPERTY(quint64 wktMax READ wktMax NOTIFY wktMaxChanged)
+	Q_PROPERTY(quint64 upTime READ upTime NOTIFY upTimeChanged)
+	Q_PROPERTY(double cpuTime READ cpuTime NOTIFY cpuTimeChanged)
+	Q_PROPERTY(double cpuCur READ cpuCur NOTIFY cpuCurChanged)
+	Q_PROPERTY(double cpuAvg READ cpuAvg NOTIFY cpuAvgChanged)
+	Q_PROPERTY(quint32 vmPeak READ vmPeak NOTIFY vmPeakChanged)
+	Q_PROPERTY(quint32 vmSize READ vmSize NOTIFY vmSizeChanged)
+	Q_PROPERTY(quint32 vmHWM READ vmHWM NOTIFY vmHWMChanged)
+	Q_PROPERTY(quint32 vmRSS READ vmRSS NOTIFY vmRSSChanged)
+	Q_PROPERTY(quint32 appSize READ appSize NOTIFY appSizeChanged)
+
+public:
+	Q_INVOKABLE bool pause(void);
+	Q_INVOKABLE bool resume(void);
+	Q_INVOKABLE bool stop(void);
+
+signals:
+	void stateChanged(int val);
+	void stateTextChanged(const QString &val);
+	void nameChanged(const QString &val);
+	void sendControlMessage(appid_t nAppID, ctrlmsg_t msg);
+	void cycIntChanged(quint64 val);
+	void cycCurChanged(quint64 val);
+	void cycMinChanged(quint64 val);
+	void cycMaxChanged(quint64 val);
+	void wktCurChanged(quint64 val);
+	void wktMinChanged(quint64 val);
+	void wktMaxChanged(quint64 val);
+	void upTimeChanged(quint64 val);
+	void cpuTimeChanged(double val);
+	void cpuCurChanged(double val);
+	void cpuAvgChanged(double val);
+	void vmPeakChanged(quint32 val);
+	void vmSizeChanged(quint32 val);
+	void vmHWMChanged(quint32 val);
+	void vmRSSChanged(quint32 val);
+	void appSizeChanged(quint32 val);
+
+public:
+	explicit QGfaAppInfo(int nIndex, QObject *pParent = NULL);
+	virtual ~QGfaAppInfo(void);
+	
+	appid_t appId(void) const;
+
+	int state(void) const;
+	bool setState(int val);
+
+	QString stateText(void) const;
+	void setStateText(int val);
+
+	QString name(void) const;
+	void setName(const QString &val);
+	
+	quint64 cycInt(void) const;
+	quint64 cycCur(void) const;
+	quint64 cycMin(void) const;
+	quint64 cycMax(void) const;
+	quint64 wktCur(void) const;
+	quint64 wktMin(void) const;
+	quint64 wktMax(void) const;
+	quint64 upTime(void) const;
+	double cpuTime(void) const;
+	double cpuCur(void) const;
+	double cpuAvg(void) const;
+	quint32 vmPeak(void) const;
+	quint32 vmSize(void) const;
+	quint32 vmHWM(void) const;
+	quint32 vmRSS(void) const;
+
+	quint32 appSize(void) const;
+	void setAppSize(quint32 size, GfaIpcAppStates state);
+
+	void setAppTimes(LPCGFA_APPCTRL_APPTIMES pat, GfaIpcAppStates state, bool bDoHeavyLoadUpdate = false);
+	void setAppMemInfo(LPCGFA_APPCTRL_APPMEM pam, GfaIpcAppStates state, bool bDoHeavyLoadUpdate = false);
+
+private:
+
+private:
+	int m_state;
+	QString m_stateText;
+	QString m_name;
+	int m_nIndex;
+	appid_t m_nAppID;
+	quint64 m_cycInt;
+	quint64 m_cycCur;
+	quint64 m_cycMin;
+	quint64 m_cycMax;
+	quint64 m_wktCur;
+	quint64 m_wktMin;
+	quint64 m_wktMax;
+	quint64 m_upTime;
+	double m_cpuTime;
+	double m_cpuPercCur;
+	double m_cpuPercAvg;
+	quint32 m_vmPeak;
+	quint32 m_vmSize;
+	quint32 m_vmHWM;
+	quint32 m_vmRSS;
+	quint32 m_appSize;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+class QGfaAppCtrl : public QObject
+{
+    Q_OBJECT
+    Q_PROPERTY(QQmlListProperty<QGfaAppInfo> appInfo READ appInfo CONSTANT)
+	Q_PROPERTY(quint64 minPass READ minPass NOTIFY minPassChanged)
+	Q_PROPERTY(quint64 maxPass READ maxPass NOTIFY maxPassChanged)
+	Q_PROPERTY(quint64 avgPass READ avgPass NOTIFY avgPassChanged)
+    Q_PROPERTY(QGfaSysInfo *sysInfo READ sysInfo CONSTANT)
+
+public:
+	enum AppStates
+	{
+		STATE_NOT_RUNNING	= GIAS_StateNotRunning,
+		STATE_INITIALIZING,
+		STATE_RUNNING,
+		STATE_PAUSED,
+		STATE_HANGING,
+		STATE_TERMINATING,
+		STATE_ZOMBIE,
+		STATE_INVALID
+	};
+	Q_ENUM(AppStates)
+	static_assert((STATE_INVALID - STATE_NOT_RUNNING) == (GIAS_Invalid - GIAS_StateNotRunning), "enum AppStates does not match enum GfaIpcAppStates!");
+	
+	enum AppIndex
+	{
+		APP_INDEX_REMANENT		= GAI_Remanent,
+		APP_INDEX_DATALOGGER	= GAI_Datalogger,
+		APP_INDEX_SUMMARIST		= GAI_Summarist,
+		APP_INDEX_REST			= GAI_Rest,
+		APP_INDEX_MQTTCL		= GAI_Mqttcl,
+		APP_INDEX_SYSINFO		= GAI_SysInfo
+	};
+	Q_ENUM(AppIndex)
+
+signals:
+	void minPassChanged(quint64 val);
+	void maxPassChanged(quint64 val);
+	void avgPassChanged(quint64 val);
+
+public:
+	explicit QGfaAppCtrl(QObject *pParent = NULL);
+	virtual ~QGfaAppCtrl(void);
+
+	bool Create(appid_t nAppID, const char *pszDisplayName, int nTimerIntMs, clock64_t nHeavyLoadUpdateIntervalMs);
+	void Release(void);
+	void RegisterQmlTypes(QQmlEngine &rEng, int nVerMajor = 1, int nVerMinor = 0);
+	bool PresetDisplayName(appid_t nAppID, const char *pszName);
+	bool SubscribeStateEvents(appid_t nAppMask);
+	int SetState(int nState);
+
+public:
+	Q_INVOKABLE int appIndexFromAppID(quint64 nAppID);
+
+public:
+	QQmlListProperty<QGfaAppInfo> appInfo(void);
+	quint64 minPass(void) const;
+	quint64 maxPass(void) const;
+	quint64 avgPass(void) const;
+	QGfaSysInfo* sysInfo(void);
+
+private slots:
+	void onSendControlMessage(appid_t nAppID, ctrlmsg_t msg);
+	void timerEvent(QTimerEvent *event) override;
+
+private:
+	bool m_bSysInfoRunning;
+	HAPPCTRL m_hAC;
+	appid_t m_nAppID;
+	int m_nTimerID;
+	QGfaSysInfo m_sysInfo;
+	QList<QGfaAppInfo*> m_appInfo;
+	clock64_t m_curPass;
+	clock64_t m_minPass;
+	clock64_t m_maxPass;
+	clock64_t m_avgPass;
+	appid_t m_nEvtSrcs;
+	clock64_t m_nHeavyLoadUpdateIntervalUs;
+	struct timespec m_tsLastHeavyLoadUpdate;
+};
+
+/////////////////////////////////////////////////////////////////////////////
+#endif	//	__cplusplus
+#endif	//	!defined(AGD_QAPPCTRL_H__ADD46467_F628_4A07_8844_613598E359DE__INCLUDED_)

+ 472 - 0
src/qappinfo.cpp

@@ -0,0 +1,472 @@
+#include "qappctrl.h"
+#include "defines.h"
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QGfaAppInfo::QGfaAppInfo(int nIndex, QObject *pParent) :	QObject(pParent),
+															m_state(GIAS_StateNotRunning),
+															m_nIndex(nIndex),
+															m_cycInt(0),
+															m_cycCur(0),
+															m_cycMin(0),
+															m_cycMax(0),
+															m_wktCur(0),
+															m_wktMin(0),
+															m_wktMax(0),
+															m_upTime(0),
+															m_cpuTime(0.0),
+															m_cpuPercCur(0.0),
+															m_cpuPercAvg(0.0),
+															m_vmPeak(0),
+															m_vmSize(0),
+															m_vmHWM(0),
+															m_vmRSS(0),
+															m_appSize(0)
+{
+	m_nAppID = 1ull << m_nIndex;
+	setStateText(m_state);
+    setObjectName("QGfaAppInfo");
+}
+
+QGfaAppInfo::~QGfaAppInfo(void)
+{
+	this->disconnect();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+appid_t QGfaAppInfo::appId(void) const
+{
+	return m_nAppID;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+int QGfaAppInfo::state(void) const
+{
+	return m_state;
+}
+
+bool QGfaAppInfo::setState(int val)
+{
+	if(m_state != val)
+	{
+		m_state = val;
+		emit stateChanged(val);
+		setStateText(val);
+		return true;
+	}
+
+	return false;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+QString QGfaAppInfo::stateText(void) const
+{
+	return m_stateText;
+}
+
+void QGfaAppInfo::setStateText(int val)
+{
+	QString sval = ::GfaIpcAppCtrlGetStateText((GfaIpcAppStates)val);
+	if(m_stateText != sval)
+	{
+		m_stateText = sval;
+		emit stateTextChanged(sval);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+QString QGfaAppInfo::name(void) const
+{
+	return m_name;
+}
+
+void QGfaAppInfo::setName(const QString &val)
+{
+	if(m_name != val)
+	{
+		m_name = val;
+		emit nameChanged(val);
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+quint64 QGfaAppInfo::cycInt(void) const
+{
+	return m_cycInt;
+}
+
+quint64 QGfaAppInfo::cycCur(void) const
+{
+	return m_cycCur;
+}
+
+quint64 QGfaAppInfo::cycMin(void) const
+{
+	return m_cycMin;
+}
+
+quint64 QGfaAppInfo::cycMax(void) const
+{
+	return m_cycMax;
+}
+
+
+quint64 QGfaAppInfo::wktCur(void) const
+{
+	return m_wktCur;
+}
+
+quint64 QGfaAppInfo::wktMin(void) const
+{
+	return m_wktMin;
+}
+
+quint64 QGfaAppInfo::wktMax(void) const
+{
+	return m_wktMax;
+}
+
+
+quint64 QGfaAppInfo::upTime(void) const
+{
+	return m_upTime;
+}
+
+double QGfaAppInfo::cpuTime(void) const
+{
+	return m_cpuTime;
+}
+
+double QGfaAppInfo::cpuCur(void) const
+{
+	return m_cpuPercCur;
+}
+
+double QGfaAppInfo::cpuAvg(void) const
+{
+	return m_cpuPercAvg;
+}
+
+quint32 QGfaAppInfo::vmPeak(void) const
+{
+	return m_vmPeak;
+}
+
+quint32 QGfaAppInfo::vmSize(void) const
+{
+	return m_vmSize;
+}
+
+quint32 QGfaAppInfo::vmHWM(void) const
+{
+	return m_vmHWM;
+}
+
+quint32 QGfaAppInfo::vmRSS(void) const
+{
+	return m_vmRSS;
+}
+
+quint32 QGfaAppInfo::appSize(void) const
+{
+	return m_appSize;
+}
+
+void QGfaAppInfo::setAppSize(quint32 size, GfaIpcAppStates state)
+{
+	if((state > GIAS_StateNotRunning) && (state < GIAS_Invalid))
+	{
+		if(m_appSize != size)
+		{
+			m_appSize = size;
+			emit appSizeChanged(m_appSize);
+		}
+	}
+	else
+	{
+		if(m_appSize != 0)
+		{
+			m_appSize = 0;
+			emit appSizeChanged(m_appSize);
+		}
+	}
+}
+
+void QGfaAppInfo::setAppMemInfo(LPCGFA_APPCTRL_APPMEM pam, GfaIpcAppStates state, bool bDoHeavyLoadUpdate)
+{
+	UNUSED(state);
+
+	if(pam && (state != GIAS_Zombie))
+	{
+		if(bDoHeavyLoadUpdate)
+		{
+			if(m_vmPeak != pam->vmPeak)
+			{
+				m_vmPeak = pam->vmPeak;
+				emit vmPeakChanged(m_vmPeak);
+			}
+
+			if(m_vmSize != pam->vmSize)
+			{
+				m_vmSize = pam->vmSize;
+				emit vmSizeChanged(m_vmSize);
+			}
+
+			if(m_vmHWM != pam->vmHWM)
+			{
+				m_vmHWM = pam->vmHWM;
+				emit vmHWMChanged(m_vmHWM);
+			}
+
+			if(m_vmRSS != pam->vmRSS)
+			{
+				m_vmRSS = pam->vmRSS;
+				emit vmRSSChanged(m_vmRSS);
+			}
+		}
+	}
+	else if(state != GIAS_Hanging)
+	{
+		if(m_vmPeak != 0)
+		{
+			m_vmPeak = 0;
+			emit vmPeakChanged(m_vmPeak);
+		}
+
+		if(m_vmSize != 0)
+		{
+			m_vmSize = 0;
+			emit vmSizeChanged(m_vmSize);
+		}
+
+		if(m_vmHWM != 0)
+		{
+			m_vmHWM = 0;
+			emit vmHWMChanged(m_vmHWM);
+		}
+
+		if(m_vmRSS != 0)
+		{
+			m_vmRSS = 0;
+			emit vmRSSChanged(m_vmRSS);
+		}
+	}
+}
+
+void QGfaAppInfo::setAppTimes(LPCGFA_APPCTRL_APPTIMES pat, GfaIpcAppStates state, bool bDoHeavyLoadUpdate)
+{
+	if(pat && (state != GIAS_Zombie))
+	{
+		if(m_cycInt != (quint64)pat->nCyclePresetUs)
+		{
+			m_cycInt = (quint64)pat->nCyclePresetUs;
+			emit cycIntChanged(m_cycInt);
+		}
+
+		if(state == GIAS_Running)
+		{
+			if(m_cycMin != (quint64)pat->nCycleMinUs)
+			{
+				m_cycMin = (quint64)pat->nCycleMinUs;
+				emit cycMinChanged(m_cycMin);
+			}
+
+			if(m_cycMax != (quint64)pat->nCycleMaxUs)
+			{
+				m_cycMax = (quint64)pat->nCycleMaxUs;
+				emit cycMaxChanged(m_cycMax);
+			}
+
+			if(m_wktMin != (quint64)pat->nWorkMinUs)
+			{
+				m_wktMin = (quint64)pat->nWorkMinUs;
+				emit wktMinChanged(m_wktMin);
+			}
+
+			if(m_wktMax != (quint64)pat->nWorkMaxUs)
+			{
+				m_wktMax = (quint64)pat->nWorkMaxUs;
+				emit wktMaxChanged(m_wktMax);
+			}
+		}
+		else
+		{
+			if(m_cycCur != 0)
+			{
+				m_cycCur = 0;
+				emit cycCurChanged(m_cycCur);
+			}
+
+			if(m_wktCur != 0)
+			{
+				m_wktCur = 0;
+				emit wktCurChanged(m_wktCur);
+			}
+		}
+
+		if(bDoHeavyLoadUpdate)
+		{
+			quint64 upTime = time(NULL) - pat->nTsStart;
+
+			if(m_upTime != upTime)
+			{
+				m_upTime = upTime;
+				emit upTimeChanged(m_upTime);
+			}
+
+			if(state == GIAS_Running)
+			{
+				if(m_cycCur != (quint64)pat->nCycleLastUs)
+				{
+					m_cycCur = (quint64)pat->nCycleLastUs;
+					emit cycCurChanged(m_cycCur);
+				}
+
+				if(m_wktCur != (quint64)pat->nWorkLastUs)
+				{
+					m_wktCur = (quint64)pat->nWorkLastUs;
+					emit wktCurChanged(m_wktCur);
+				}
+			}
+
+			if(state != GIAS_Hanging)
+			{
+				if(m_cpuTime != pat->fCpuTime)
+				{
+					m_cpuTime = pat->fCpuTime;
+					emit cpuTimeChanged(m_cpuTime);
+				}
+
+				if(m_cpuPercCur != pat->fCpuCur)
+				{
+					m_cpuPercCur = pat->fCpuCur;
+					emit cpuCurChanged(m_cpuPercCur);
+				}
+
+				if(m_cpuPercAvg != pat->fCpuAvg)
+				{
+					m_cpuPercAvg = pat->fCpuAvg;
+					emit cpuAvgChanged(m_cpuPercAvg);
+				}
+			}
+		}
+	}
+	else
+	{
+		if(m_cycInt != 0)
+		{
+			m_cycInt = 0;
+			emit cycIntChanged(m_cycInt);
+		}
+
+		if(m_cycCur != 0)
+		{
+			m_cycCur = 0;
+			emit cycCurChanged(m_cycCur);
+		}
+
+		if(m_wktCur != 0)
+		{
+			m_wktCur = 0;
+			emit wktCurChanged(m_wktCur);
+		}
+
+		if(state == GIAS_Hanging)
+		{
+			if(m_cpuTime != -1)
+			{
+				m_cpuTime = -1;
+				emit cpuTimeChanged(m_cpuTime);
+			}
+
+			if(m_cpuPercCur != -1)
+			{
+				m_cpuPercCur = -1;
+				emit cpuCurChanged(m_cpuPercCur);
+			}
+
+			if(m_cpuPercAvg != -1)
+			{
+				m_cpuPercAvg = -1;
+				emit cpuAvgChanged(m_cpuPercAvg);
+			}
+		}
+		else
+		{
+			if(m_cycMin != 0)
+			{
+				m_cycMin = 0;
+				emit cycMinChanged(m_cycMin);
+			}
+
+			if(m_cycMax != 0)
+			{
+				m_cycMax = 0;
+				emit cycMaxChanged(m_cycMax);
+			}
+
+			if(m_wktMin != 0)
+			{
+				m_wktMin = 0;
+				emit wktMinChanged(m_wktMin);
+			}
+
+			if(m_wktMax != 0)
+			{
+				m_wktMax = 0;
+				emit wktMaxChanged(m_wktMax);
+			}
+
+			if(m_upTime != 0)
+			{
+				m_upTime = 0;
+				emit upTimeChanged(m_upTime);
+			}
+
+			if(m_cpuTime != 0)
+			{
+				m_cpuTime = 0;
+				emit cpuTimeChanged(m_cpuTime);
+			}
+
+			if(m_cpuPercCur != 0)
+			{
+				m_cpuPercCur = 0;
+				emit cpuCurChanged(m_cpuPercCur);
+			}
+
+			if(m_cpuPercAvg != 0)
+			{
+				m_cpuPercAvg = 0;
+				emit cpuAvgChanged(m_cpuPercAvg);
+			}
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+bool QGfaAppInfo::pause(void)
+{
+	emit sendControlMessage(m_nAppID, GFA_APPCTRL_CTRLMSG_PAUSE);
+	return true;
+}
+
+bool QGfaAppInfo::resume(void)
+{
+	emit sendControlMessage(m_nAppID, GFA_APPCTRL_CTRLMSG_RESUME);
+	return true;
+}
+
+bool QGfaAppInfo::stop(void)
+{
+	emit sendControlMessage(m_nAppID, GFA_APPCTRL_CTRLMSG_STOP);
+	return true;
+}

+ 512 - 0
src/qsysinfo.cpp

@@ -0,0 +1,512 @@
+#include "qappctrl.h"
+#include "defines.h"
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+#define _countof(a)							(sizeof(a) / sizeof(*a))
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _OPT_STRING(s, o)					(*s ? s : o)
+#define _NA_STRING(s)						_OPT_STRING(s, "n/a")
+
+/////////////////////////////////////////////////////////////////////////////
+
+#define _BYTE_SIZE_KIB						(1ULL << 10) // Kibibyte
+#define _BYTE_SIZE_MIB						(1ULL << 20) // Mebibyte
+#define _BYTE_SIZE_GIB						(1ULL << 30) // Gibibyte
+#define _BYTE_SIZE_TIB						(1ULL << 40) // Tebibyte
+#define _BYTE_SIZE_PIB						(1ULL << 50) // Pebibyte
+#define _BYTE_SIZE_EIB						(1ULL << 60) // Exbibyte
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const char *_FormatByteSize(unsigned long long nCb, char *pszBuf, size_t nCChBuf, bool bShortUnits = true, int nPrec = 1);
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const char *_FormatByteSize(unsigned long long nCb, char *pszBuf, size_t nCChBuf, bool bShortUnits, int nPrec)
+{
+	if(pszBuf && nCChBuf)
+	{
+		double val = nCb;
+		
+		if(nCb < _BYTE_SIZE_KIB)
+		{
+			snprintf(pszBuf, nCChBuf, "%llu %s", nCb, bShortUnits ? "B" : "Byte"); // Byte
+		}
+		else if(nCb < _BYTE_SIZE_MIB)
+		{
+			val /= _BYTE_SIZE_KIB;
+			snprintf(pszBuf, nCChBuf, "%.*f %s", nPrec, val, bShortUnits ? "K" : "KiB"); // KiB
+		}
+		else if(nCb < _BYTE_SIZE_GIB)
+		{
+			val /= _BYTE_SIZE_MIB;
+			snprintf(pszBuf, nCChBuf, "%.*f %s", nPrec, val, bShortUnits ? "M" : "MiB"); // MiB
+		}
+		else if(nCb < _BYTE_SIZE_TIB)
+		{
+			val /= _BYTE_SIZE_GIB;
+			snprintf(pszBuf, nCChBuf, "%.*f %s", nPrec, val, bShortUnits ? "G" : "GiB"); // GiB
+		}
+		else if(nCb < _BYTE_SIZE_PIB)
+		{
+			val /= _BYTE_SIZE_TIB;
+			snprintf(pszBuf, nCChBuf, "%.*f %s", nPrec, val, bShortUnits ? "T" : "TiB"); // TiB
+		}
+		else if(nCb < _BYTE_SIZE_EIB)
+		{
+			val /= _BYTE_SIZE_PIB;
+			snprintf(pszBuf, nCChBuf, "%.*f %s", nPrec, val, bShortUnits ? "P" : "PiB"); // PiB
+		}
+		
+		return pszBuf;
+	}
+	
+	return NULL;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+typedef enum
+{
+    SDR_DiskName = Qt::UserRole,
+    SDR_DiskDevNode,
+    SDR_DiskBusType,
+    SDR_DiskVendorName,
+    SDR_DiskVendorID,
+	SDR_DiskProductID,
+	SDR_PartFsLabel,
+	SDR_PartFsType,
+	SDR_PartFsVersion,
+	SDR_PartDevNode,
+	SDR_PartMountPoint,
+	SDR_PartSize,
+	SDR_PartFsSize,
+	SDR_PartFsFree,
+	SDR_PartFsUsed
+}StgDevRoles;
+
+typedef struct _STG_DEV_ROLES
+{
+	int nRole;
+	const char *pszRoleName;
+}STG_DEV_ROLES, *LPSTG_DEV_ROLES;
+typedef const STG_DEV_ROLES *LPCSTG_DEV_ROLES;
+
+/////////////////////////////////////////////////////////////////////////////
+
+static const STG_DEV_ROLES g_roles[] =
+{
+	{SDR_DiskName,			"DiskName"},
+	{SDR_DiskDevNode,		"DiskDevNode"},
+	{SDR_DiskBusType,		"DiskDevType"},
+	{SDR_DiskVendorName,	"DiskVendorName"},
+	{SDR_DiskVendorID,		"DiskVendorID"},
+	{SDR_DiskProductID,		"DiskProductID"},
+	{SDR_PartFsLabel,		"PartFsLabel"},
+	{SDR_PartFsType,		"PartFsType"},
+	{SDR_PartFsVersion,		"PartFsVersion"},
+	{SDR_PartDevNode,		"PartDevNode"},
+	{SDR_PartMountPoint,	"PartMountPoint"},
+	{SDR_PartSize,			"PartSize"},
+	{SDR_PartFsSize,		"PartFsSize"},
+	{SDR_PartFsFree,		"PartFsFree"},
+	{SDR_PartFsUsed,		"PartFsUsed"}
+};
+
+/////////////////////////////////////////////////////////////////////////////
+
+QGfaStgDevList::QGfaStgDevList(QObject *pParent) : 	QAbstractTableModel(pParent)
+{
+	memset(&m_stgDevShadowCpy, 0, sizeof(m_stgDevShadowCpy));
+    setObjectName("QGfaStgDevList");
+}
+
+QGfaStgDevList::~QGfaStgDevList(void)
+{
+}
+
+int QGfaStgDevList::rowCount(const QModelIndex &parent) const
+{
+	Q_UNUSED(parent);
+	return (int)m_partVec.size();
+}
+
+int QGfaStgDevList::columnCount(const QModelIndex &parent) const
+{
+	Q_UNUSED(parent);
+	return (int)_countof(g_roles);
+}
+
+QVariant QGfaStgDevList::data(const QModelIndex &index, int role) const
+{
+	if(!index.isValid())
+		return QVariant();
+
+	QVariant val;
+	char szBuf[64];
+	int nIndex = index.row();
+	const STG_DEV_DISK_PART &dp = m_partVec[nIndex];
+	const GFA_SYSINFO_DISK &disk = m_stgDevShadowCpy.disks[dp.nDiskIdx];
+	const GFA_SYSINFO_PARTITION &part = m_stgDevShadowCpy.parts[dp.nPartIdx];
+	
+	if(disk.valid && part.valid)
+	{
+		switch(role)
+		{
+		case SDR_DiskName:
+			val = _NA_STRING(disk.szName);
+			break;
+		case SDR_DiskDevNode:
+			val = _NA_STRING(disk.szDevNode);
+			break;
+		case SDR_DiskBusType:
+			val = _OPT_STRING(disk.szBus, "mmc");
+			break;
+		case SDR_DiskVendorName:
+			val = _NA_STRING(disk.szVendor);
+			break;
+		case SDR_DiskVendorID:
+			val = (int)disk.nVendorID;
+			break;
+		case SDR_DiskProductID:
+			val = (int)disk.nProductID;
+			break;
+		case SDR_PartFsLabel:
+			val = _NA_STRING(part.szFsLabel);
+			break;
+		case SDR_PartFsType:
+			val = _NA_STRING(part.szFsType);
+			break;
+		case SDR_PartFsVersion:
+			val = _NA_STRING(part.szFsVersion);
+			break;
+		case SDR_PartDevNode:
+			val = _NA_STRING(part.szDevNode);
+			break;
+		case SDR_PartMountPoint:
+			val = _NA_STRING(part.szMntPoint);
+			break;
+		case SDR_PartSize:
+			val = _FormatByteSize(part.nKiBPartSize * _BYTE_SIZE_KIB, szBuf, _countof(szBuf));
+			break;
+		case SDR_PartFsSize:
+			val = _FormatByteSize(part.nKiBSize * _BYTE_SIZE_KIB, szBuf, _countof(szBuf));
+			break;
+		case SDR_PartFsFree:
+			val = _FormatByteSize(part.nKiBFree * _BYTE_SIZE_KIB, szBuf, _countof(szBuf));
+			break;
+		case SDR_PartFsUsed:
+			val = _FormatByteSize(part.nKiBUsed * _BYTE_SIZE_KIB, szBuf, _countof(szBuf));
+			break;
+		default:
+			break;
+		}
+	}
+
+	return val;
+}
+
+QHash<int, QByteArray> QGfaStgDevList::roleNames(void) const
+{
+	QHash<int, QByteArray> roles;
+	
+	for(size_t i = 0; i < _countof(g_roles); ++i)
+	{
+		roles[g_roles[i].nRole] = g_roles[i].pszRoleName;
+	}
+
+	return roles;
+}
+
+int QGfaStgDevList::FindPartVecEntry(int nDiskIdx, int nPartIdx)
+{
+	for(auto it = m_partVec.begin(); it != m_partVec.end(); ++it)
+	{
+		const STG_DEV_DISK_PART &dp = *it;
+		if((dp.nDiskIdx == nDiskIdx) && (dp.nPartIdx == nPartIdx))
+			return it - m_partVec.begin();
+	}
+	return -1;
+}
+
+void QGfaStgDevList::insertPartition(int nDiskIdx, int nPartIdx)
+{
+	QModelIndex parent = QModelIndex();
+	int nIndex = (int)m_partVec.size();
+	emit beginInsertRows(parent, nIndex, nIndex);
+	m_partVec.emplace_back(nDiskIdx, nPartIdx);
+	emit endInsertRows();
+}
+
+void QGfaStgDevList::removePartition(int nDiskIdx, int nPartIdx)
+{
+	int nIndex;
+	if((nIndex = FindPartVecEntry(nDiskIdx, nPartIdx)) >= 0)
+	{
+		QModelIndex parent = QModelIndex();
+		emit beginRemoveRows(parent, nIndex, nIndex);
+		m_partVec.erase(m_partVec.begin() + nIndex);
+		emit endRemoveRows();
+	}
+}
+
+void QGfaStgDevList::diskAdded(int nIndex, const GFA_SYSINFO_DISK &disk)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.disks)) && !disk.internal)
+	{
+		TRACE("Disk [ID=%d] added:   %s [%s]\n", nIndex, disk.szDevNode, *disk.szName ? disk.szName : "Unnamed");
+		memcpy(&m_stgDevShadowCpy.disks[nIndex], &disk, sizeof(GFA_SYSINFO_DISK));
+	}
+}
+
+void QGfaStgDevList::diskRemoved(int nIndex, const GFA_SYSINFO_DISK &disk)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.disks)) && !disk.internal)
+	{
+		TRACE("Disk [ID=%d] removed: %s [%s]\n", nIndex, disk.szDevNode, *disk.szName ? disk.szName : "Unnamed");
+		m_stgDevShadowCpy.disks[nIndex].valid = false;
+	}
+}
+
+void QGfaStgDevList::partitionAdded(int nIndex, const GFA_SYSINFO_PARTITION &part)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.parts)) && !part.internal)
+	{
+		TRACE("Partition [ID=%d:%d] added:   %s [%s]\n", part.nDiskIdx, nIndex, part.szDevNode, *part.szFsLabel ? part.szFsLabel : "Unnamed");
+		memcpy(&m_stgDevShadowCpy.parts[nIndex], &part, sizeof(GFA_SYSINFO_PARTITION));
+		insertPartition(part.nDiskIdx, nIndex);
+	}
+}
+
+void QGfaStgDevList::partitionRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.parts)) && !part.internal)
+	{
+		TRACE("Partition [ID=%d:%d] removed: %s [%s]\n", part.nDiskIdx, nIndex, part.szDevNode, *part.szFsLabel ? part.szFsLabel : "Unnamed");
+		m_stgDevShadowCpy.parts[nIndex].valid = false;
+		removePartition(part.nDiskIdx, nIndex);
+	}
+}
+
+void QGfaStgDevList::mountAdded(int nIndex, const GFA_SYSINFO_PARTITION &part)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.parts)) && !part.internal)
+	{
+		TRACE("Partition [ID=%d:%d] mounted:   %s -> %s\n", part.nDiskIdx, nIndex, part.szDevNode, part.szMntPoint);
+		GFA_SYSINFO_PARTITION &p = m_stgDevShadowCpy.parts[nIndex];
+		memcpy(&p.szMntPoint, &part.szMntPoint, sizeof(GFA_SYSINFO_PARTITION::szMntPoint));
+		if((nIndex = FindPartVecEntry(part.nDiskIdx, nIndex)) >= 0)
+		{
+			p.nKiBSize = part.nKiBSize;
+			p.nKiBFree = part.nKiBFree;
+			p.nKiBUsed = part.nKiBUsed;
+			QModelIndex i = createIndex(nIndex, 0);
+			emit dataChanged(i, i, {SDR_PartMountPoint, SDR_PartFsSize, SDR_PartFsFree, SDR_PartFsUsed});
+		}
+	}
+}
+
+void QGfaStgDevList::mountRemoved(int nIndex, const GFA_SYSINFO_PARTITION &part)
+{
+	if((nIndex >= 0) && (nIndex < (int)_countof(m_stgDevShadowCpy.parts)) && !part.internal)
+	{
+		TRACE("Partition [ID=%d:%d] unmounted: %s\n", part.nDiskIdx, nIndex, part.szDevNode);
+		GFA_SYSINFO_PARTITION &p = m_stgDevShadowCpy.parts[nIndex];
+		memset(&p.szMntPoint, 0, sizeof(GFA_SYSINFO_PARTITION::szMntPoint));
+		if((nIndex = FindPartVecEntry(part.nDiskIdx, nIndex)) >= 0)
+		{
+			p.nKiBSize = 0;
+			p.nKiBFree = 0;
+			p.nKiBUsed = 0;
+			QModelIndex i = createIndex(nIndex, 0);
+			emit dataChanged(i, i, {SDR_PartMountPoint, SDR_PartFsSize, SDR_PartFsFree, SDR_PartFsUsed});
+		}
+	}
+}
+
+void QGfaStgDevList::clearAll(void)
+{
+	int nLast = m_partVec.size() - 1;
+	if(nLast >= 0)
+	{
+		QModelIndex parent = QModelIndex();
+		emit beginRemoveRows(parent, 0, nLast);
+		m_partVec.clear();
+		memset(&m_stgDevShadowCpy, 0, sizeof(m_stgDevShadowCpy));
+		emit endRemoveRows();
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+/////////////////////////////////////////////////////////////////////////////
+
+QGfaSysInfo::QGfaSysInfo(QObject *pParent) : 	QObject(pParent),
+												m_bSysInfoRunning(false),
+												m_nMemTotal(0),
+												m_nMemUsed(0),
+												m_nMemFree(0),
+												m_nMemAvailable(0),
+												m_nMemBuffers(0),
+												m_nMemCached(0),
+												m_bootFromEmmc(false)
+{
+}
+
+QGfaSysInfo::~QGfaSysInfo(void)
+{
+    setObjectName("QGfaSysInfo");
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+void QGfaSysInfo::setSysInfoRunning(bool bRunning)
+{
+	if(m_bSysInfoRunning != bRunning)
+	{
+		if(!(m_bSysInfoRunning = bRunning))
+		{
+			setSysMemInfo(NULL);
+			m_stgDevList.clearAll();
+		}
+	}
+}
+
+void QGfaSysInfo::setSysMemInfo(LPCGFA_APPCTRL_SYSMEM psm, bool bDoHeavyLoadUpdate)
+{
+	if(psm && m_bSysInfoRunning)
+	{
+		if(bDoHeavyLoadUpdate)
+		{
+			if(m_nMemTotal != psm->nMemTotal)
+			{
+				m_nMemTotal = psm->nMemTotal;
+				emit memTotalChanged(m_nMemTotal);
+			}
+
+			if(m_nMemUsed != psm->nMemUsed)
+			{
+				m_nMemUsed = psm->nMemUsed;
+				emit memUsedChanged(m_nMemUsed);
+			}
+			
+			if(m_nMemFree != psm->nMemFree)
+			{
+				m_nMemFree = psm->nMemFree;
+				emit memFreeChanged(m_nMemFree);
+			}
+
+			if(m_nMemAvailable != psm->nMemAvailable)
+			{
+				m_nMemAvailable = psm->nMemAvailable;
+				emit memAvailableChanged(m_nMemAvailable);
+			}
+			
+			if(m_nMemBuffers != psm->nMemBuffers)
+			{
+				m_nMemBuffers = psm->nMemBuffers;
+				emit memBuffersChanged(m_nMemBuffers);
+			}
+			
+			if(m_nMemCached != psm->nMemCached)
+			{
+				m_nMemCached = psm->nMemCached;
+				emit memCachedChanged(m_nMemCached);
+			}
+		}
+	}
+	else
+	{
+		if(m_nMemTotal != 0)
+		{
+			m_nMemTotal = 0;
+			emit memTotalChanged(m_nMemTotal);
+		}
+
+		if(m_nMemUsed != 0)
+		{
+			m_nMemUsed = 0;
+			emit memUsedChanged(m_nMemUsed);
+		}
+		
+		if(m_nMemFree != 0)
+		{
+			m_nMemFree = 0;
+			emit memFreeChanged(m_nMemFree);
+		}
+
+		if(m_nMemAvailable != 0)
+		{
+			m_nMemAvailable = 0;
+			emit memAvailableChanged(m_nMemAvailable);
+		}
+		
+		if(m_nMemBuffers != 0)
+		{
+			m_nMemBuffers = 0;
+			emit memBuffersChanged(m_nMemBuffers);
+		}
+		
+		if(m_nMemCached != 0)
+		{
+			m_nMemCached = 0;
+			emit memCachedChanged(m_nMemCached);
+		}
+	}
+}
+
+/////////////////////////////////////////////////////////////////////////////
+
+quint64 QGfaSysInfo::memTotal(void) const
+{
+	return m_nMemTotal;
+}
+
+quint64 QGfaSysInfo::memUsed(void) const
+{
+	return m_nMemUsed;
+}
+
+quint64 QGfaSysInfo::memFree(void) const
+{
+	return m_nMemFree;
+}
+
+quint64 QGfaSysInfo::memAvailable(void) const
+{
+	return m_nMemAvailable;
+}
+
+quint64 QGfaSysInfo::memBuffers(void) const
+{
+	return m_nMemBuffers;
+}
+
+quint64 QGfaSysInfo::memCached(void) const
+{
+	return m_nMemCached;
+}
+
+QGfaStgDevList* QGfaSysInfo::stgDev(void)
+{
+	return &m_stgDevList;
+}
+
+bool QGfaSysInfo::bootFromEmmc(void) const
+{
+	return m_bootFromEmmc;
+}
+
+void QGfaSysInfo::setBootFromEmmc(bool bootFromEmmc)
+{
+	if(m_bootFromEmmc != bootFromEmmc)
+	{
+		m_bootFromEmmc = bootFromEmmc;
+		emit bootFromEmmcChanged(m_bootFromEmmc);
+	}
+}