From bf55b2f3083cbfdeb83611b2fa2dd552bf5b0775 Mon Sep 17 00:00:00 2001
From: LAPTOP-SNT8I5JK\Boounion <Chenluhua@qq.com>
Date: 星期一, 03 三月 2025 16:20:00 +0800
Subject: [PATCH] 1.开始处理Step属性值; 2.增加左侧master面板;

---
 SourceCode/Bond/Servo/Servo.vcxproj         |   10 
 SourceCode/Bond/Servo/resource.h            |    0 
 SourceCode/Bond/Servo/HorizontalLine.h      |   87 ++
 SourceCode/Bond/Servo/Servo.cpp             |    3 
 SourceCode/Bond/Servo/CPanelMaster.cpp      |  176 ++++
 SourceCode/Bond/Servo/CEqModeStep.h         |    1 
 SourceCode/Bond/Servo/CMaster.h             |    1 
 SourceCode/Bond/Servo/VerticalLine.h        |   95 ++
 SourceCode/Bond/Servo/CEqAlarmStep.h        |    1 
 SourceCode/Bond/Servo/CStep.h               |    6 
 SourceCode/Bond/Servo/ServoGraph.cpp        |   18 
 SourceCode/Bond/Servo/AlarmDlg.cpp          |  415 ++++++++++
 SourceCode/Bond/Servo/CEquipment.cpp        |    5 
 SourceCode/Bond/Servo/Servo.rc              |    0 
 SourceCode/Bond/Servo/CMaster.cpp           |    9 
 SourceCode/Bond/Servo/ApredTreeCtrl2.h      |   53 +
 SourceCode/Bond/Servo/CStep.cpp             |   27 
 SourceCode/Bond/Servo/ServoDlg.h            |    5 
 SourceCode/Bond/Servo/AlarmDlg.h            |   65 +
 SourceCode/Bond/Servo/CEqStatusStep.h       |    1 
 SourceCode/Bond/Servo/HorizontalLine.cpp    |  209 +++++
 SourceCode/Bond/Servo/CPanelMaster.h        |   42 +
 SourceCode/Bond/Servo/CEqAlarmStep.cpp      |   20 
 SourceCode/Bond/Servo/ServoDlg.cpp          |   56 +
 SourceCode/Bond/Servo/VerticalLine.cpp      |  316 ++++++++
 SourceCode/Bond/Servo/CEqProcessStep.cpp    |  134 +++
 SourceCode/Bond/Servo/CAttribute.cpp        |   21 
 SourceCode/Bond/Servo/CAttributeVector.h    |   24 
 SourceCode/Bond/Servo/ApredTreeCtrl2.cpp    |  379 +++++++++
 SourceCode/Bond/Servo/CAttribute.h          |   18 
 SourceCode/Bond/Servo/CEqModeStep.cpp       |   11 
 SourceCode/Bond/Servo/Servo.vcxproj.filters |   10 
 SourceCode/Bond/Servo/CEqStatusStep.cpp     |   18 
 SourceCode/Bond/Servo/CAttributeVector.cpp  |   44 +
 SourceCode/Bond/Servo/ServoGraph.h          |    4 
 SourceCode/Bond/Servo/CEqProcessStep.h      |   36 
 SourceCode/Bond/Servo/CEquipment.h          |    1 
 SourceCode/Bond/Servo/Common.h              |   11 
 38 files changed, 2,327 insertions(+), 5 deletions(-)

diff --git a/SourceCode/Bond/Servo/AlarmDlg.cpp b/SourceCode/Bond/Servo/AlarmDlg.cpp
new file mode 100644
index 0000000..05ccd39
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmDlg.cpp
@@ -0,0 +1,415 @@
+锘�// AlarmDlg.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "afxdialogex.h"
+#include "AlarmDlg.h"
+#include "AlarmManager.h"
+#include "Common.h"
+
+#define PAGE_SIZE						10
+#define PAGE_BACKGROUND_COLOR			RGB(252, 252, 255)
+
+// CAlarmDlg 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CAlarmDlg, CDialogEx)
+
+CAlarmDlg::CAlarmDlg(CWnd* pParent /*=nullptr*/)
+	: CDialogEx(IDD_DIALOG_ALARM, pParent)
+{
+	m_crBkgnd = PAGE_BACKGROUND_COLOR;
+	m_hbrBkgnd = nullptr;
+	m_pObserver = nullptr;
+
+	m_strEqName = "";
+	m_strKeyword = "";
+	m_nCurPage = 0;
+	m_nTotalPages = 0;
+	m_nDateTimeFlag = 0;
+
+	memset(m_szTimeStart, 0, sizeof(m_szTimeStart));
+	memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd));
+	m_szTimeStart[0] = '\0';
+	m_szTimeEnd[0] = '\0';
+}
+
+CAlarmDlg::~CAlarmDlg()
+{
+}
+
+void CAlarmDlg::InitRxWindow()
+{
+	/* code */
+	// 璁㈤槄鏁版嵁
+	IRxWindows* pRxWindows = RX_GetRxWindows();
+	pRxWindows->enableLog(5);
+	if (m_pObserver == NULL) {
+		m_pObserver = pRxWindows->allocObserver([&](IAny* pAny) -> void {
+			// onNext
+			pAny->addRef();
+			int code = pAny->getCode();
+
+			if (RX_CODE_STEP_EVENT_READDATA == code) {
+				LOGI("<CAlarmDlg> Accept RX_CODE_STEP_EVENT_READDATA successfully!");
+				// 閫氱煡璁惧鐘舵��
+				SERVO::CEqAlarmStep* pStep = nullptr;
+				if (pAny->getPtrValue("ptr", (void*&)pStep)) {
+					if (pStep != nullptr) {
+						// 鑾峰彇 AlarmManager 鍗曚緥
+						AlarmManager& alarmManager = AlarmManager::getInstance();
+
+						// 浠� pStep 鑾峰彇闇�瑕佺殑鍙傛暟锛屽亣璁捐繖浜涘�兼槸浠� pStep 涓幏鍙栫殑
+						std::string id = std::to_string(pStep->getAlarmId());
+						std::string deviceName = std::to_string(pStep->getUnitId());
+						std::string description = "pStep->getText()";
+						std::string startTime = "2025-02-25 10:00";
+						std::string endTime = "2025-02-25 12:00";
+
+						// 鎻掑叆鍒楄〃鎺т欢
+						CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_ALARM);
+						if (pListCtrl != nullptr) {
+							InsertAlarmData(pListCtrl, id.c_str(), deviceName.c_str(), description.c_str(), startTime.c_str(), endTime.c_str());
+						}
+					}
+				}
+			}
+
+
+		pAny->release();
+		}, [&]() -> void {
+			// onComplete
+		}, [&](IThrowable* pThrowable) -> void {
+			// onErrorm
+			pThrowable->printf();
+		});
+
+		theApp.m_model.getObservable()->observeOn(pRxWindows->mainThread())
+			->subscribe(m_pObserver);
+	}
+}
+
+void CAlarmDlg::Resize()
+{
+	CWnd* pItem;
+	CRect rcClient;
+	GetClientRect(&rcClient);
+
+	pItem = GetDlgItem(IDC_LIST_ALARM);
+	pItem->MoveWindow(12, 52, rcClient.Width() - 24, rcClient.Height() - 64);
+}
+
+void CAlarmDlg::LoadAlarms()
+{
+	// 鍒锋柊鍘嗗彶鎶ヨ鏁版嵁
+	m_nCurPage = 1;
+	UpdatePageData();
+}
+
+void CAlarmDlg::UpdatePageData()
+{
+	// 鏍规嵁杩囨护鏉′欢鍔犺浇鏁版嵁锛屾彁渚涙弿杩板拰鏃堕棿鑼冨洿鏌ヨ
+	auto vecData = AlarmManager::getInstance().getFilteredAlarms("", m_strEqName, m_strKeyword, m_szTimeStart, m_szTimeEnd, m_nCurPage, PAGE_SIZE);
+
+	// 濉厖鏁版嵁鍒版帶浠�
+	CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_ALARM);
+	FillDataToListCtrl(pListCtrl, vecData);
+
+	// 鏇存柊鍒嗛〉鎺т欢锛堥〉鏁板拰鎸夐挳锛�
+	UpdatePageControls();
+}
+
+void CAlarmDlg::UpdatePageControls()
+{
+	// 鏇存柊鍒嗛〉淇℃伅
+	CString strPage;
+	strPage.Format(_T("绗� %d 椤�"), m_nCurPage);
+	SetDlgItemText(IDC_LABEL_PAGE_NUMBER, strPage);
+
+	// 鍚敤/绂佺敤缈婚〉鎸夐挳
+	GetDlgItem(IDC_BUTTON_PREV_PAGE)->EnableWindow(m_nCurPage > 1);
+	GetDlgItem(IDC_BUTTON_NEXT_PAGE)->EnableWindow(m_nCurPage < m_nTotalPages);
+}
+
+void CAlarmDlg::FillDataToListCtrl(CListCtrl* pListCtrl, const std::vector<std::vector<std::string>>& vecData)
+{
+	// 娓呯┖褰撳墠CListCtrl涓殑鎵�鏈夐」
+	pListCtrl->DeleteAllItems();
+
+	// 閬嶅巻鏁版嵁骞舵彃鍏ュ埌CListCtrl涓�
+	for (const auto& item : vecData) {
+		int nItem = pListCtrl->InsertItem(pListCtrl->GetItemCount(), _T(""));	// 鎻掑叆鏂拌
+		pListCtrl->SetItemText(nItem, 1, item[0].c_str());						// 璁剧疆绗竴鍒楃殑鏂囨湰锛圛D锛�
+		pListCtrl->SetItemText(nItem, 2, item[1].c_str());						// 璁剧疆绗簩鍒楃殑鏂囨湰锛堣澶囧悕绉帮級
+		pListCtrl->SetItemText(nItem, 3, item[2].c_str());						// 璁剧疆绗笁鍒楃殑鏂囨湰锛堟弿杩帮級
+		pListCtrl->SetItemText(nItem, 4, item[3].c_str());						// 璁剧疆绗洓鍒楃殑鏂囨湰锛堝紑濮嬫椂闂达級
+		pListCtrl->SetItemText(nItem, 5, item[4].c_str());						// 璁剧疆绗洓鍒楃殑鏂囨湰锛堣В闄ゆ椂闂达級
+	}
+}
+
+void CAlarmDlg::InsertAlarmData(CListCtrl* pListCtrl, const CString& alarmId, const CString& deviceName, const CString& description, const CString& startTime, const CString& endTime)
+{
+	int nRowCount = pListCtrl->GetItemCount();
+	if (nRowCount >= PAGE_SIZE) {
+		pListCtrl->DeleteItem(nRowCount - 1);
+	}
+
+	int nNewItem = pListCtrl->InsertItem(0, _T(""));
+	pListCtrl->SetItemText(nNewItem, 1, alarmId);       // 璀﹀憡ID
+	pListCtrl->SetItemText(nNewItem, 2, deviceName);    // 璁惧鍚嶇О
+	pListCtrl->SetItemText(nNewItem, 3, description);   // 鎻忚堪
+	pListCtrl->SetItemText(nNewItem, 4, startTime);     // 鍙戠敓鏃堕棿
+	pListCtrl->SetItemText(nNewItem, 5, endTime);       // 瑙i櫎鏃堕棿
+}
+
+void CAlarmDlg::DoDataExchange(CDataExchange* pDX)
+{
+	DDX_Control(pDX, IDC_DATETIMEPICKER_START, m_dateTimeStart);
+	DDX_Control(pDX, IDC_DATETIMEPICKER_END, m_dateTimeEnd);
+	CDialogEx::DoDataExchange(pDX);
+}
+
+
+BEGIN_MESSAGE_MAP(CAlarmDlg, CDialogEx)
+	ON_WM_CTLCOLOR()
+	ON_WM_DESTROY()
+	ON_WM_SIZE()
+	ON_CBN_SELCHANGE(IDC_COMBO_DATETIME, &CAlarmDlg::OnCbnSelchangeComboDatetime)
+	ON_BN_CLICKED(IDC_BUTTON_SEARCH, &CAlarmDlg::OnBnClickedButtonSearch)
+	ON_BN_CLICKED(IDC_BUTTON_EXPORT, &CAlarmDlg::OnBnClickedButtonExport)
+	ON_BN_CLICKED(IDC_BUTTON_PREV_PAGE, &CAlarmDlg::OnBnClickedButtonPrevPage)
+	ON_BN_CLICKED(IDC_BUTTON_NEXT_PAGE, &CAlarmDlg::OnBnClickedButtonNextPage)
+END_MESSAGE_MAP()
+
+
+// CAlarmDlg 娑堟伅澶勭悊绋嬪簭
+BOOL CAlarmDlg::OnInitDialog()
+{
+	CDialogEx::OnInitDialog();
+	InitRxWindow();
+
+	// 涓嬫媺妗嗘帶浠�
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	pComboBox->AddString(_T("涓嶉檺"));
+	pComboBox->AddString(_T("浠婂ぉ"));
+	pComboBox->AddString(_T("涓冨ぉ鍐�"));
+	pComboBox->AddString(_T("鏈湀"));
+	pComboBox->AddString(_T("浠婂勾"));
+	pComboBox->AddString(_T("鑷畾涔�"));
+	pComboBox->SetCurSel(0);
+
+	// 鏃ユ湡鎺т欢
+	m_dateTimeStart.EnableWindow(FALSE);
+	m_dateTimeEnd.EnableWindow(FALSE);
+
+	// 鎶ヨ〃鎺т欢
+	CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_ALARM);
+	DWORD dwStyle = pListCtrl->GetExtendedStyle();
+	dwStyle |= LVS_EX_FULLROWSELECT;
+	dwStyle |= LVS_EX_GRIDLINES;
+	pListCtrl->SetExtendedStyle(dwStyle);
+
+	HIMAGELIST imageList = ImageList_Create(24, 24, ILC_COLOR24, 1, 1);
+	ListView_SetImageList(pListCtrl->GetSafeHwnd(), imageList, LVSIL_SMALL);
+	pListCtrl->InsertColumn(0, _T(""), LVCFMT_RIGHT, 0);
+	pListCtrl->InsertColumn(1, _T("璀﹀憡ID"), LVCFMT_LEFT, 50);
+	pListCtrl->InsertColumn(2, _T("璁惧鍚嶇О"), LVCFMT_LEFT, 120);
+	pListCtrl->InsertColumn(3, _T("鎻忚堪"), LVCFMT_LEFT, 280);
+	pListCtrl->InsertColumn(4, _T("鍙戠敓鏃堕棿"), LVCFMT_LEFT, 180);
+	pListCtrl->InsertColumn(5, _T("瑙i櫎鏃堕棿"), LVCFMT_LEFT, 180);
+
+	// 璁$畻鎬婚〉鏁�
+	int totalRecords = AlarmManager::getInstance().getTotalAlarmCount("", m_strEqName, m_strKeyword, m_szTimeStart, m_szTimeEnd);
+	m_nTotalPages = (totalRecords + PAGE_SIZE - 1) / PAGE_SIZE;
+	m_nCurPage = 1;
+
+	Resize();
+	LoadAlarms();
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+	// 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
+
+BOOL CAlarmDlg::DestroyWindow()
+{
+	return CDialogEx::DestroyWindow();
+}
+
+HBRUSH CAlarmDlg::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+	if (nCtlColor == CTLCOLOR_STATIC) {
+		pDC->SetBkColor(m_crBkgnd);
+	}
+
+	if (m_hbrBkgnd == nullptr) {
+		m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
+	}
+
+	return m_hbrBkgnd;
+}
+
+void CAlarmDlg::OnDestroy()
+{
+	CDialogEx::OnDestroy();
+
+	if (m_hbrBkgnd != nullptr) {
+		::DeleteObject(m_hbrBkgnd);
+	}
+
+	if (m_pObserver != NULL) {
+		m_pObserver->unsubscribe();
+		m_pObserver = NULL;
+	}
+}
+
+void CAlarmDlg::OnSize(UINT nType, int cx, int cy)
+{
+	CDialogEx::OnSize(nType, cx, cy);
+	if (GetDlgItem(IDC_LIST_ALARM) == nullptr) return;
+	Resize();
+}
+
+void CAlarmDlg::OnCbnSelchangeComboDatetime()
+{
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	int nIndex = pComboBox->GetCurSel();
+	int nCount = pComboBox->GetCount();
+	m_dateTimeStart.EnableWindow(nIndex == nCount - 1);
+	m_dateTimeEnd.EnableWindow(nIndex == nCount - 1);
+}
+
+void CAlarmDlg::OnBnClickedButtonSearch()
+{
+	// 鑾峰彇鍏抽敭瀛�
+	CString cstrKeyword;
+	GetDlgItemText(IDC_EDIT_KEYWORD, cstrKeyword);
+	m_strKeyword = CT2A(cstrKeyword);
+
+	// 鑾峰彇鏃ユ湡
+	CComboBox* pComboBox = (CComboBox*)GetDlgItem(IDC_COMBO_DATETIME);
+	m_nDateTimeFlag = pComboBox->GetCurSel();
+	if (m_nDateTimeFlag == 0) {
+		memset(m_szTimeStart, 0, sizeof(m_szTimeStart));
+		memset(m_szTimeEnd, 0, sizeof(m_szTimeEnd));
+		m_szTimeStart[0] = '\0';
+		m_szTimeEnd[0] = '\0';
+	}
+	else {
+		CTime time = CTime::GetCurrentTime();
+		if (m_nDateTimeFlag == 1) {
+			// 浠婂ぉ
+			sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time.GetYear(), time.GetMonth(), time.GetDay());
+			sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+		}
+		else if (m_nDateTimeFlag == 2) {
+			// 7澶╁唴
+			CTime time2 = time - CTimeSpan(7, 0, 0, 0);
+			sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d 00:00:00", time2.GetYear(), time2.GetMonth(), time2.GetDay());
+			sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+		}
+		else if (m_nDateTimeFlag == 3) {
+			// 鏈湀
+			sprintf_s(m_szTimeStart, 64, "%d-%02d-01 00:00:00", time.GetYear(), time.GetMonth());
+			sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d 23:59:59", time.GetYear(), time.GetMonth(), time.GetDay());
+		}
+		else if (m_nDateTimeFlag == 4) {
+			// 浠婂勾
+			sprintf_s(m_szTimeStart, 64, "%d-01-01 00:00:00", time.GetYear());
+			sprintf_s(m_szTimeEnd, 64, "%d-12-31 23:59:59", time.GetYear());
+		}
+		else if (m_nDateTimeFlag == 5) {
+			// 鑷畾涔�
+			SYSTEMTIME t1, t2;
+			m_dateTimeStart.GetTime(&t1);
+			m_dateTimeEnd.GetTime(&t2);
+
+			sprintf_s(m_szTimeStart, 64, "%d-%02d-%02d %02d:%02d:%02d",
+				t1.wYear, t1.wMonth, t1.wDay, t1.wHour, t1.wMinute, t1.wSecond);
+			sprintf_s(m_szTimeEnd, 64, "%d-%02d-%02d %02d:%02d:%02d",
+				t2.wYear, t2.wMonth, t2.wDay, t2.wHour, t2.wMinute, t2.wSecond);
+		}
+	}
+
+	// 璁$畻鎬婚〉鏁�
+	int totalRecords = AlarmManager::getInstance().getTotalAlarmCount("", m_strEqName, m_strKeyword, m_szTimeStart, m_szTimeEnd);
+	m_nTotalPages = (totalRecords + PAGE_SIZE - 1) / PAGE_SIZE;
+	m_nCurPage = 1;
+
+	UpdatePageData();  // 璋冪敤鍒嗛〉鏇存柊鍑芥暟
+}
+
+void CAlarmDlg::OnBnClickedButtonExport()
+{
+	CFileDialog fileDialog(FALSE, "csv", "", OFN_HIDEREADONLY, "csv.files(*.csv)|*.csv||");
+	if (fileDialog.DoModal() != IDOK) {
+		return;
+	}
+
+	CStdioFile file;
+	if (!file.Open(fileDialog.GetPathName(), CFile::modeCreate | CFile::modeWrite)) {
+		AfxMessageBox("鍒涘缓鏂囦欢澶辫触锛�");
+	}
+
+	int nSubItemCount = 0;
+	CString strSubItem, strHeader, strRow;
+	char szItem[256];
+	HDITEM hdItem[35];
+	for (int i = 0; i < 35; i++) {
+		hdItem[i].pszText = szItem;
+		hdItem[i].cchTextMax = 256;
+		hdItem[i].mask = HDI_TEXT | HDI_WIDTH;
+	}
+
+	// 鍏堣琛ㄥご
+	CListCtrl* pListCtrl = (CListCtrl*)GetDlgItem(IDC_LIST_ALARM);
+	CHeaderCtrl* pHeader = pListCtrl->GetHeaderCtrl();
+	nSubItemCount = pHeader->GetItemCount();
+	ASSERT(nSubItemCount <= 35);
+	for (int i = 0; i < pHeader->GetItemCount(); i++) {
+		pHeader->GetItem(i, &hdItem[i]);
+		if (hdItem[i].cxy > 0) {
+			if (!strHeader.IsEmpty()) {
+				strHeader.Append(",");
+			}
+			strHeader.Append(CString(hdItem[i].pszText));
+		}
+	}
+	strHeader.Append("\n");
+	file.WriteString(strHeader);
+
+	// 鍐欒〃鏍煎唴瀹�
+	int count = pListCtrl->GetItemCount();
+	for (int i = 0; i < count; i++) {
+		strRow.Empty();
+		for (int j = 0; j < nSubItemCount; j++) {
+			if (hdItem[j].cxy > 0) {
+				if (!strRow.IsEmpty()) {
+					strRow.Append(",");
+				}
+				CString strTemp = pListCtrl->GetItemText(i, j);
+				strTemp.Replace("* ", "");
+				strRow.Append(strTemp);
+			}
+		}
+		strRow.Append("\n");
+		file.WriteString(strRow);
+	}
+
+	file.Close();
+}
+
+void CAlarmDlg::OnBnClickedButtonPrevPage()
+{
+	// 鐐瑰嚮涓婁竴椤�
+	m_nCurPage--;
+	UpdatePageData();  // 璋冪敤鍒嗛〉鏇存柊鍑芥暟
+}
+
+void CAlarmDlg::OnBnClickedButtonNextPage()
+{
+	// 鐐瑰嚮涓嬩竴椤�
+	m_nCurPage++;
+	UpdatePageData();  // 璋冪敤鍒嗛〉鏇存柊鍑芥暟
+}
diff --git a/SourceCode/Bond/Servo/AlarmDlg.h b/SourceCode/Bond/Servo/AlarmDlg.h
new file mode 100644
index 0000000..9ce2938
--- /dev/null
+++ b/SourceCode/Bond/Servo/AlarmDlg.h
@@ -0,0 +1,65 @@
+锘�#pragma once
+#include "afxdialogex.h"
+#include <vector>
+#include <string>
+
+// CAlarmDlg 瀵硅瘽妗�
+
+class CAlarmDlg : public CDialogEx
+{
+	DECLARE_DYNAMIC(CAlarmDlg)
+
+public:
+	CAlarmDlg(CWnd* pParent = nullptr);   // 鏍囧噯鏋勯�犲嚱鏁�
+	virtual ~CAlarmDlg();
+
+private:
+	void InitRxWindow();
+	void Resize();
+	void LoadAlarms();
+	void UpdatePageData();
+	void UpdatePageControls();
+	void FillDataToListCtrl(CListCtrl* pListCtrl, const std::vector<std::vector<std::string>>& vecData);
+	void InsertAlarmData(CListCtrl* pListCtrl, const CString& alarmId, const CString& deviceName, const CString& description, const CString& startTime, const CString& endTime);
+
+private:
+	COLORREF m_crBkgnd;
+	HBRUSH m_hbrBkgnd;
+	IObserver* m_pObserver;
+
+	// 鍏抽敭瀛�
+	std::string m_strEqName;
+	std::string m_strKeyword;
+
+	// 椤电爜
+	int m_nCurPage;
+	int m_nTotalPages;
+
+	// 鏃ユ湡
+	int m_nDateTimeFlag;
+	char m_szTimeStart[64];
+	char m_szTimeEnd[64];
+
+	// 鎺т欢
+	CDateTimeCtrl m_dateTimeStart;
+	CDateTimeCtrl m_dateTimeEnd;
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+	enum { IDD = IDD_DIALOG_ALARM };
+#endif
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
+	virtual BOOL OnInitDialog();
+	virtual BOOL DestroyWindow();
+	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+	afx_msg void OnDestroy();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnCbnSelchangeComboDatetime();
+	afx_msg void OnBnClickedButtonSearch();
+	afx_msg void OnBnClickedButtonExport();
+	afx_msg void OnBnClickedButtonPrevPage();
+	afx_msg void OnBnClickedButtonNextPage();
+	DECLARE_MESSAGE_MAP()
+};
diff --git a/SourceCode/Bond/Servo/ApredTreeCtrl2.cpp b/SourceCode/Bond/Servo/ApredTreeCtrl2.cpp
new file mode 100644
index 0000000..92956c6
--- /dev/null
+++ b/SourceCode/Bond/Servo/ApredTreeCtrl2.cpp
@@ -0,0 +1,379 @@
+#include "stdafx.h"
+#include "ApredTreeCtrl2.h"
+
+
+#define ROFFSET 7
+#define WIDE	10
+#define WIDE2	5
+#define EXPANDED_WIDE	8
+
+#define BADGE_HIDE                  0
+#define BADGE_DOT                   1
+#define BADGE_NUMBER                2
+#define BADGE_DOT_WIDTH             12
+#define BADGE_NUMBER_WIDTH          20
+
+
+IMPLEMENT_DYNAMIC(CApredTreeCtrl2, CTreeCtrl)
+
+CApredTreeCtrl2::CApredTreeCtrl2()
+{
+	m_hBrushItem[0] = NULL;
+	m_hBrushItem[1] = NULL;
+	m_hBrushItem[2] = NULL;
+	m_crItemBk[0] = GetSysColor(COLOR_WINDOW);
+	m_crItemBk[1] = GetSysColor(COLOR_HIGHLIGHT);
+	m_crItemBk[2] = RGB(204, 206, 219);
+	m_crItemBtn[0] = RGB(30, 30, 30);
+	m_crItemBtn[1] = RGB(255, 255, 255);
+	m_crItemBtn[2] = RGB(255, 255, 255);
+}
+
+CApredTreeCtrl2::~CApredTreeCtrl2()
+{
+	if (m_hBrushItem[0] != NULL) {
+		::DeleteObject(m_hBrushItem[0]);
+	}
+	if (m_hBrushItem[1] != NULL) {
+		::DeleteObject(m_hBrushItem[1]);
+	}
+	if (m_hBrushItem[2] != NULL) {
+		::DeleteObject(m_hBrushItem[2]);
+	}
+}
+
+BEGIN_MESSAGE_MAP(CApredTreeCtrl2, CTreeCtrl)
+	ON_NOTIFY_REFLECT(NM_CUSTOMDRAW, &CApredTreeCtrl2::OnNMCustomdraw)
+END_MESSAGE_MAP()
+
+
+void CApredTreeCtrl2::OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	LPNMCUSTOMDRAW pNMCD = reinterpret_cast<LPNMCUSTOMDRAW>(pNMHDR);
+    LPNMTVCUSTOMDRAW lpnmcd = (LPNMTVCUSTOMDRAW)pNMCD;
+    HTREEITEM hItem;
+    UINT unt;
+    *pResult = 0;
+
+
+    switch (lpnmcd->nmcd.dwDrawStage)
+    {
+    case CDDS_PREPAINT:
+        *pResult = CDRF_NOTIFYITEMDRAW;
+        m_items.clear();
+        break;
+
+    case CDDS_ITEMPREPAINT:
+        m_items[(HTREEITEM)lpnmcd->nmcd.dwItemSpec] = lpnmcd->iLevel;
+        hItem = (HTREEITEM)lpnmcd->nmcd.dwItemSpec;
+        if (hItem != NULL) {
+            if ( (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED)== TVIS_SELECTED) {
+                if (GetFocus() != this) {
+                    lpnmcd->clrTextBk = RGB(204, 206, 219);
+                }
+            }
+        }
+        *pResult = CDRF_NEWFONT | CDRF_NOTIFYPOSTPAINT;   // 订阅绘制结束通知
+
+        break;
+
+
+    case CDDS_ITEMPOSTPAINT: // 项绘制结束通知
+		unt = GetIndent();
+		hItem = (HTREEITEM)lpnmcd->nmcd.dwItemSpec;
+		auto iter = m_items.find(hItem);
+		if (iter != m_items.end()) {
+			CRect button_rect = pNMCD->rc;
+			button_rect.right = button_rect.left + unt + 5;
+			int off = unt * iter->second;
+			button_rect.OffsetRect(off, 0);
+			DrawItemButton(hItem, pNMCD->hdc, &button_rect);
+
+			// 是否有小圆点
+			CRect rcItem = pNMCD->rc;
+			Gdiplus::Graphics graphics(pNMCD->hdc);
+			graphics.SetSmoothingMode(Gdiplus::SmoothingModeAntiAlias);
+			if (m_badges.find(hItem) != m_badges.end()) {
+				BADGE& badge = m_badges[hItem];
+				if (badge.type == BADGE_DOT) {
+					Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground)));
+					int x = rcItem.right - 18 - BADGE_DOT_WIDTH;
+					int y = rcItem.top + (rcItem.Height() - BADGE_DOT_WIDTH) / 2;
+					graphics.FillEllipse(&brush, x, y, BADGE_DOT_WIDTH, BADGE_DOT_WIDTH);
+				}
+				else if (badge.type == BADGE_NUMBER) {
+					Gdiplus::SolidBrush brush(Gdiplus::Color(GetRValue(badge.badgeBackground), GetGValue(badge.badgeBackground), GetBValue(badge.badgeBackground)));
+					int x = rcItem.right - 18 - BADGE_NUMBER_WIDTH;
+					int y = rcItem.top + (rcItem.Height() - BADGE_NUMBER_WIDTH) / 2;
+					graphics.FillEllipse(&brush, x, y, BADGE_NUMBER_WIDTH, BADGE_NUMBER_WIDTH);
+					RECT rcBadge;
+					rcBadge.left = x;
+					rcBadge.right = rcBadge.left + BADGE_NUMBER_WIDTH;
+					rcBadge.top = y;
+					rcBadge.bottom = rcBadge.top + BADGE_NUMBER_WIDTH;
+					::SetTextColor(pNMCD->hdc, badge.badgeForeground);
+					char szBuffer[32];
+					sprintf_s(szBuffer, 32, "%d%s", min(badge.number, 9), badge.number > 9 ? "+" : "");
+					DrawText(pNMCD->hdc, szBuffer, (int)strlen(szBuffer), &rcBadge, DT_CENTER | DT_VCENTER | DT_SINGLELINE);
+				}
+			}
+
+			*pResult = CDRF_SKIPDEFAULT;
+		}
+
+        break;
+    }
+}
+
+void CApredTreeCtrl2::DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect)
+{
+	if (!ItemHasChildren(hItem)) {
+		return;
+	}
+
+
+	// 按钮要刷一下
+	::FillRect(hDC, pRect, GetItemBkBrush(hItem));
+	if ((GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED) == TVIS_EXPANDED) {
+		int nBottomOffset = (pRect->Height() - EXPANDED_WIDE) / 2;
+		POINT pt[3];
+		pt[0].x = pRect->right - ROFFSET - EXPANDED_WIDE;
+		pt[0].y = pRect->bottom - nBottomOffset;
+		pt[1].x = pRect->right - ROFFSET;
+		pt[1].y = pRect->bottom - nBottomOffset;
+		pt[2].x = pRect->right - ROFFSET;
+		pt[2].y = pRect->bottom - nBottomOffset - EXPANDED_WIDE;
+
+		HBRUSH hBrush = GetItemBtnBrush(hItem);
+		HPEN hPen = GetItemBtnPen(hItem);
+		HBRUSH hOldBrush = (HBRUSH)::SelectObject(hDC, hBrush);
+		HPEN hOldPen = (HPEN)::SelectObject(hDC, hPen);
+		::Polygon(hDC, pt, 3);
+		::SelectObject(hDC, hOldBrush);
+		::SelectObject(hDC, hOldPen);
+		::DeleteObject(hBrush);
+		::DeleteObject(hPen);
+	}
+	else {
+		int nBottomOffset = (pRect->Height() - WIDE) / 2;
+		POINT pt[3];
+		pt[0].x = pRect->right - ROFFSET - WIDE2;
+		pt[0].y = pRect->bottom - nBottomOffset - WIDE;
+		pt[1].x = pRect->right - ROFFSET - WIDE2;
+		pt[1].y = pRect->bottom - nBottomOffset;
+		pt[2].x = pRect->right - ROFFSET;
+		pt[2].y = pRect->bottom - nBottomOffset - WIDE2;
+
+		HBRUSH hBrush = GetItemBtnBrush(hItem);
+		HPEN hPen = GetItemBtnPen(hItem);
+		HBRUSH hOldBrush = (HBRUSH)::SelectObject(hDC, hBrush);
+		HPEN hOldPen = (HPEN)::SelectObject(hDC, hPen);
+		::Polygon(hDC, pt, 3);
+		::SelectObject(hDC, hOldBrush);
+		::SelectObject(hDC, hOldPen);
+		::DeleteObject(hBrush);
+		::DeleteObject(hPen);
+	}
+}
+
+COLORREF CApredTreeCtrl2::SetBkColor(COLORREF crColor)
+{
+	if (m_hBrushItem[0] != NULL) {
+		::DeleteObject(m_hBrushItem[0]);
+	}
+	m_hBrushItem[0] = CreateSolidBrush(crColor);
+
+	return CTreeCtrl::SetBkColor(crColor);
+}
+
+int CApredTreeCtrl2::OnCreate(LPCREATESTRUCT lpCreateStruct)
+{
+	if (CTreeCtrl::OnCreate(lpCreateStruct) == -1)
+		return -1;
+
+	m_crItemBk[0] = GetBkColor();
+	m_crItemBk[1] = GetSysColor(COLOR_HIGHLIGHT);
+	m_hBrushItem[0] = CreateSolidBrush(m_crItemBk[0]);
+	m_hBrushItem[1] = CreateSolidBrush(m_crItemBk[1]);
+	m_hBrushItem[2] = CreateSolidBrush(m_crItemBk[2]);
+
+	return 0;
+}
+
+HBRUSH CApredTreeCtrl2::GetItemBkBrush(HTREEITEM hItem)
+{
+	BOOL bSelected = (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED) == TVIS_SELECTED;
+	BOOL bHilited = (GetItemState(hItem, TVIS_DROPHILITED) & TVIS_DROPHILITED) == TVIS_DROPHILITED;;
+	HTREEITEM hClickedItem = GetDropHilightItem();
+
+
+	if (bHilited) {
+		return m_hBrushItem[1];
+	}
+
+	if (!bSelected) {
+		return m_hBrushItem[0];
+	}
+
+	if (GetFocus() == this) {
+		if (hClickedItem == NULL) {
+			return m_hBrushItem[1];
+		}
+		else {
+			return m_hBrushItem[0];
+		}
+	}
+
+
+	return m_hBrushItem[2];
+}
+
+HBRUSH CApredTreeCtrl2::GetItemBtnBrush(HTREEITEM hItem)
+{
+	BOOL bExpanded = (GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED) == TVIS_EXPANDED;
+	BOOL bSelected = (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED) == TVIS_SELECTED;
+	BOOL bHilited = (GetItemState(hItem, TVIS_DROPHILITED) & TVIS_DROPHILITED) == TVIS_DROPHILITED;;
+	HTREEITEM hClickedItem = GetDropHilightItem();
+
+
+	// 展开时实心三角
+	if (bExpanded) {
+		if (bHilited) {
+			return CreateSolidBrush(m_crItemBtn[1]);
+		}
+
+		if (!bSelected) {
+			return CreateSolidBrush(m_crItemBtn[0]);
+		}
+
+		if (GetFocus() == this && hClickedItem == NULL) {
+			return CreateSolidBrush(m_crItemBtn[1]);
+		}
+
+		return CreateSolidBrush(m_crItemBtn[0]);
+	}
+	else {
+		// 收起时空心三角
+		if (bHilited) {
+			return CreateSolidBrush(m_crItemBk[1]);
+		}
+
+		if (!bSelected) {
+			return CreateSolidBrush(m_crItemBk[0]);
+		}
+
+		if (GetFocus() == this && hClickedItem == NULL) {
+			return CreateSolidBrush(m_crItemBk[1]);
+		}
+
+		return CreateSolidBrush(m_crItemBk[2]);
+	}
+}
+
+HPEN CApredTreeCtrl2::GetItemBtnPen(HTREEITEM hItem)
+{
+	BOOL bExpanded = (GetItemState(hItem, TVIS_EXPANDED) & TVIS_EXPANDED) == TVIS_EXPANDED;
+	BOOL bSelected = (GetItemState(hItem, TVIS_SELECTED) & TVIS_SELECTED) == TVIS_SELECTED;
+	BOOL bHilited = (GetItemState(hItem, TVIS_DROPHILITED) & TVIS_DROPHILITED) == TVIS_DROPHILITED;;
+	HTREEITEM hClickedItem = GetDropHilightItem();
+
+
+	// 展开时实心三角
+	if (bExpanded) {
+		if (bHilited) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[1]);
+		}
+
+		if (!bSelected) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[0]);
+		}
+
+		if (GetFocus() == this && hClickedItem == NULL) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[1]);
+		}
+
+		return CreatePen(PS_SOLID, 1, m_crItemBtn[0]);
+	}
+	else {
+		// 收起时空心三角
+		if (bHilited) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[1]);
+		}
+
+		if (!bSelected) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[0]);
+		}
+
+		if (GetFocus() == this && hClickedItem == NULL) {
+			return CreatePen(PS_SOLID, 1, m_crItemBtn[1]);
+		}
+
+		return CreatePen(PS_SOLID, 1, m_crItemBtn[0]);
+	}
+}
+
+void CApredTreeCtrl2::PreSubclassWindow()
+{
+	m_crItemBk[0] = GetBkColor();
+	m_crItemBk[1] = GetSysColor(COLOR_HIGHLIGHT);
+	m_hBrushItem[0] = CreateSolidBrush(m_crItemBk[0]);
+	m_hBrushItem[1] = CreateSolidBrush(m_crItemBk[1]);
+	m_hBrushItem[2] = CreateSolidBrush(m_crItemBk[2]);
+
+	CTreeCtrl::PreSubclassWindow();
+}
+
+void CApredTreeCtrl2::SetItemBadge(HTREEITEM hItem, COLORREF badgeBackground, COLORREF badgeForeground)
+{
+	if (m_badges.find(hItem) == m_badges.end()) {
+		BADGE badge;
+		badge.badgeBackground = badgeBackground;
+		badge.badgeForeground = badgeForeground;
+		badge.type = BADGE_HIDE;
+		m_badges[hItem] = badge;
+	}
+	else {
+		BADGE& badge = m_badges[hItem];
+		badge.badgeBackground = badgeBackground;
+		badge.badgeForeground = badgeForeground;
+		badge.type = BADGE_HIDE;
+	}
+}
+
+void CApredTreeCtrl2::ShowItemBadgeNumber(HTREEITEM hItem, int number)
+{
+	if (m_badges.find(hItem) == m_badges.end()) {
+		return;
+	}
+
+	BADGE& badge = m_badges[hItem];
+	badge.type = BADGE_NUMBER;
+	badge.number = number;
+	InvalidateRect(NULL, TRUE);
+}
+
+void CApredTreeCtrl2::ShowItemBadgeDotMode(HTREEITEM hItem)
+{
+	if (m_badges.find(hItem) == m_badges.end()) {
+		return;
+	}
+
+	BADGE& badge = m_badges[hItem];
+	if (badge.type != BADGE_DOT) {
+		badge.type = BADGE_DOT;
+		InvalidateRect(NULL, TRUE);
+	}
+}
+
+void CApredTreeCtrl2::HideItemBadge(HTREEITEM hItem)
+{
+	if (m_badges.find(hItem) == m_badges.end()) {
+		return;
+	}
+
+	BADGE& badge = m_badges[hItem];
+	if (badge.type != BADGE_HIDE) {
+		badge.type = BADGE_HIDE;
+		InvalidateRect(NULL, TRUE);
+	}
+}
diff --git a/SourceCode/Bond/Servo/ApredTreeCtrl2.h b/SourceCode/Bond/Servo/ApredTreeCtrl2.h
new file mode 100644
index 0000000..68f5310
--- /dev/null
+++ b/SourceCode/Bond/Servo/ApredTreeCtrl2.h
@@ -0,0 +1,53 @@
+#pragma once
+#include <afxcmn.h>
+#include <map>
+
+class CApredTreeCtrl2 :
+    public CTreeCtrl
+{
+public:
+    typedef struct tagBADGE
+    {
+        HTREEITEM hTreeItem;
+        COLORREF badgeBackground;
+        COLORREF badgeForeground;
+        int type;                       /* 0: 无,不显示*/
+        int number;
+    } BADGE;
+
+
+    DECLARE_DYNAMIC(CApredTreeCtrl2)
+
+public:
+    CApredTreeCtrl2();
+    virtual ~CApredTreeCtrl2();
+
+
+public:
+    virtual COLORREF SetBkColor(COLORREF crColor);
+    void SetItemBadge(HTREEITEM hItem, COLORREF badgeBackground, COLORREF badgeForeground);
+    void ShowItemBadgeNumber(HTREEITEM hItem, int number);
+    void ShowItemBadgeDotMode(HTREEITEM hItem);
+    void HideItemBadge(HTREEITEM hItem);
+
+private:
+    HBRUSH GetItemBkBrush(HTREEITEM hItem);
+    HBRUSH GetItemBtnBrush(HTREEITEM hItem);
+    HPEN GetItemBtnPen(HTREEITEM hItem);
+    void DrawItemButton(HTREEITEM hItem, HDC hDC, CRect* pRect);
+
+
+private:
+    std::map<HTREEITEM, BADGE> m_badges;
+    std::map<HTREEITEM, int> m_items;
+    HBRUSH m_hBrushItem[3];
+    COLORREF m_crItemBk[3];
+    COLORREF m_crItemBtn[3];
+
+public:
+    DECLARE_MESSAGE_MAP()
+    afx_msg void OnNMCustomdraw(NMHDR* pNMHDR, LRESULT* pResult);
+    afx_msg int OnCreate(LPCREATESTRUCT lpCreateStruct);
+    virtual void PreSubclassWindow();
+};
+
diff --git a/SourceCode/Bond/Servo/CAttribute.cpp b/SourceCode/Bond/Servo/CAttribute.cpp
new file mode 100644
index 0000000..fbba186
--- /dev/null
+++ b/SourceCode/Bond/Servo/CAttribute.cpp
@@ -0,0 +1,21 @@
+#include "stdafx.h"
+#include "CAttribute.h"
+
+namespace SERVO {
+	CAttribute::CAttribute()
+	{
+
+	}
+
+	CAttribute::CAttribute(const char* pszName, const char* pszValue, const char* pszDescription)
+	{
+		m_strName = pszName;
+		m_strValue = pszValue;
+		m_strDescription = pszDescription;
+	}
+
+	CAttribute::~CAttribute()
+	{
+
+	}
+}
diff --git a/SourceCode/Bond/Servo/CAttribute.h b/SourceCode/Bond/Servo/CAttribute.h
new file mode 100644
index 0000000..b87c662
--- /dev/null
+++ b/SourceCode/Bond/Servo/CAttribute.h
@@ -0,0 +1,18 @@
+#pragma once
+
+
+namespace SERVO {
+	class CAttribute
+	{
+	public:
+		CAttribute();
+		CAttribute(const char* pszName, const char* pszValue, const char* pszDescription);
+		~CAttribute();
+
+	private:
+		std::string m_strName;
+		std::string m_strValue;
+		std::string m_strDescription;
+	};
+}
+
diff --git a/SourceCode/Bond/Servo/CAttributeVector.cpp b/SourceCode/Bond/Servo/CAttributeVector.cpp
new file mode 100644
index 0000000..bb673f7
--- /dev/null
+++ b/SourceCode/Bond/Servo/CAttributeVector.cpp
@@ -0,0 +1,44 @@
+#include "stdafx.h"
+#include "CAttributeVector.h"
+
+
+namespace SERVO {
+	CAttributeVector::CAttributeVector()
+	{
+
+	}
+
+	CAttributeVector::~CAttributeVector()
+	{
+
+	}
+
+	void CAttributeVector::addAttribute(CAttribute* pAttribute)
+	{
+		m_attributes.push_back(pAttribute);
+	}
+
+	unsigned int CAttributeVector::size()
+	{
+		return m_attributes.size();
+	}
+
+	void CAttributeVector::clear()
+	{
+		for (auto item : m_attributes) {
+			delete item;
+		}
+		m_attributes.clear();
+	}
+
+	bool CAttributeVector::empty()
+	{
+		return m_attributes.empty();
+	}
+
+	CAttribute* CAttributeVector::getAttribute(unsigned int index)
+	{
+		ASSERT(index < m_attributes.size());
+		return m_attributes[index];
+	}
+}
diff --git a/SourceCode/Bond/Servo/CAttributeVector.h b/SourceCode/Bond/Servo/CAttributeVector.h
new file mode 100644
index 0000000..c3b9b34
--- /dev/null
+++ b/SourceCode/Bond/Servo/CAttributeVector.h
@@ -0,0 +1,24 @@
+#pragma once
+#include <vector>
+#include "CAttribute.h"
+
+
+namespace SERVO {
+	class CAttributeVector
+	{
+	public:
+		CAttributeVector();
+		~CAttributeVector();
+
+	public:
+		void addAttribute(CAttribute* pAttribute);
+		void clear();
+		unsigned int size();
+		bool empty();
+		CAttribute* getAttribute(unsigned int index);
+
+	private:
+		std::vector<CAttribute*> m_attributes;
+	};
+}
+
diff --git a/SourceCode/Bond/Servo/CEqAlarmStep.cpp b/SourceCode/Bond/Servo/CEqAlarmStep.cpp
index 9f4bdc9..1aa6dac 100644
--- a/SourceCode/Bond/Servo/CEqAlarmStep.cpp
+++ b/SourceCode/Bond/Servo/CEqAlarmStep.cpp
@@ -20,6 +20,26 @@
 
 	}
 
+	void CEqAlarmStep::getAttributeVector(CAttributeVector& attrubutes)
+	{
+		CStep::getAttributeVector(attrubutes);
+
+		attrubutes.addAttribute(new CAttribute("Alarm State",
+			std::to_string(m_nAlarmState).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Unit ID",
+			std::to_string(m_nUnitId).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Alarm Level",
+			std::to_string(m_nAlarmLevel).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Alarm Code",
+			std::to_string(m_nAlarmCode).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Alarm ID",
+			std::to_string(m_nAlarmId).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Text",
+			m_strText.c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Description",
+			m_strDescription.c_str(), ""));
+	}
+
 	int CEqAlarmStep::onReadData()
 	{
 		CStep::onReadData();
diff --git a/SourceCode/Bond/Servo/CEqAlarmStep.h b/SourceCode/Bond/Servo/CEqAlarmStep.h
index fd574fe..5157215 100644
--- a/SourceCode/Bond/Servo/CEqAlarmStep.h
+++ b/SourceCode/Bond/Servo/CEqAlarmStep.h
@@ -10,6 +10,7 @@
 		~CEqAlarmStep();
 
 	public:
+		virtual void getAttributeVector(CAttributeVector& attrubutes);
 		virtual int onReadData();
 		virtual int onComplete();
 		virtual int onTimeout();
diff --git a/SourceCode/Bond/Servo/CEqModeStep.cpp b/SourceCode/Bond/Servo/CEqModeStep.cpp
index c7883b2..70787df 100644
--- a/SourceCode/Bond/Servo/CEqModeStep.cpp
+++ b/SourceCode/Bond/Servo/CEqModeStep.cpp
@@ -15,6 +15,17 @@
 
 	}
 
+	void CEqModeStep::getAttributeVector(CAttributeVector& attrubutes)
+	{
+		CStep::getAttributeVector(attrubutes);
+
+		std::string strTemp;
+		attrubutes.addAttribute(new CAttribute("Mode",
+			std::to_string(m_nMode).c_str(), getModeDescription(strTemp).c_str()));
+		attrubutes.addAttribute(new CAttribute("Mode Dev",
+			std::to_string(m_nModeDev).c_str(), ""));
+	}
+
 	int CEqModeStep::onReadData()
 	{
 		CStep::onReadData();
diff --git a/SourceCode/Bond/Servo/CEqModeStep.h b/SourceCode/Bond/Servo/CEqModeStep.h
index 6193736..ff68005 100644
--- a/SourceCode/Bond/Servo/CEqModeStep.h
+++ b/SourceCode/Bond/Servo/CEqModeStep.h
@@ -10,6 +10,7 @@
 		~CEqModeStep();
 
 	public:
+		virtual void getAttributeVector(CAttributeVector& attrubutes);
 		virtual int onReadData();
 		virtual int onComplete();
 		virtual int onTimeout();
diff --git a/SourceCode/Bond/Servo/CEqProcessStep.cpp b/SourceCode/Bond/Servo/CEqProcessStep.cpp
new file mode 100644
index 0000000..868554d
--- /dev/null
+++ b/SourceCode/Bond/Servo/CEqProcessStep.cpp
@@ -0,0 +1,134 @@
+#include "stdafx.h"
+#include "Common.h"
+#include "CEqProcessStep.h"
+#include "Log.h"
+#include "ToolUnits.h"
+
+
+namespace SERVO {
+	CEqProcessStep::CEqProcessStep()
+	{
+		m_nProcessDev = 0;
+		m_nTotalParameter = 0;
+	}
+
+	CEqProcessStep::~CEqProcessStep()
+	{
+
+	}
+
+#define PROGRESS_BUF_SIZE		(1024 + 64)
+	int CEqProcessStep::onReadData()
+	{
+		CStep::onReadData();
+
+		// W1864 ~ W1A74, 529个word, 1058 bytes
+		char szBuffer[PROGRESS_BUF_SIZE];
+		int nRet = m_pCclink->ReadData2(m_station, DeviceType::W,
+			m_nProcessDev, PROGRESS_BUF_SIZE, szBuffer);
+		if (0 != nRet) {
+			return -1;
+		}
+
+		// 解释数据
+		// Glass ID(1864~186D)
+		int index = 0;
+		convertString(&szBuffer[index], (0x186d - 0x1864 + 1) * 2, m_strStartTime);
+		index += (0x186d - 0x1864 + 1) * 2;
+
+		// Process Start Time(186e~1875)
+		convertString(&szBuffer[index], (0x1875 - 0x186e + 1) * 2, m_strStartTime);
+		index += (0x1875 - 0x186e + 1) * 2;
+
+		// Process End Time(1876~187d)
+		convertString(&szBuffer[index], (0x187d - 0x1876 + 1) * 2, m_strEndTime);
+		index += (0x187d - 0x1876 + 1) * 2;
+
+		// parameter count
+		m_nTotalParameter = (unsigned int)CToolUnits::toInt16(&szBuffer[index]);
+		index += 2;
+
+		// total group
+		m_nTotalGroup = (unsigned int)CToolUnits::toInt16(&szBuffer[index]);
+		index += 2;
+
+		// current group
+		m_nCurrentGroup = (unsigned int)CToolUnits::toInt16(&szBuffer[index]);
+		index += 2;
+
+		// param list(0x1881~0x1a74), 共1000 bytes, 20个字符为一个参数, 50组
+		// 最后一group可能不满足50, 以m_nTotalParameter为依据
+		int size = (m_nCurrentGroup == m_nTotalGroup) ? m_nTotalParameter % 50 : 50;
+		for (int i = 0; i < size; i++) {
+			std::string strParam;
+			convertString(&szBuffer[index], 20, strParam);
+			if (!strParam.empty()) {
+				m_params.push_back(strParam);
+			}
+			index += 20;
+		}
+
+		if (m_nCurrentGroup == m_nTotalGroup && m_listener.onEvent != nullptr) {
+			m_listener.onEvent(this, STEP_EVENT_PROCESS_DATA, nullptr);
+		}
+
+
+		LOGI("<CEqProcessStep> Process Data<GlassId:%s>\n",
+			m_strGlassId.c_str());
+
+		return 0;
+	}
+
+	int CEqProcessStep::onComplete()
+	{
+		CStep::onComplete();
+		LOGI("<CEqProcessStep> onComplete.");
+
+		return 0;
+	}
+
+	int CEqProcessStep::onTimeout()
+	{
+		CStep::onTimeout();
+		LOGI("<CEqProcessStep> onTimeout.");
+
+		return 0;
+	}
+
+	void CEqProcessStep::setProcessDev(int nDev)
+	{
+		m_nProcessDev = nDev;
+	}
+
+	std::string& CEqProcessStep::getGlassId()
+	{
+		return m_strGlassId;
+	}
+
+	std::string& CEqProcessStep::getStartTime()
+	{
+		return m_strStartTime;
+	}
+
+	std::string& CEqProcessStep::getEndTime()
+	{
+		return m_strEndTime;
+	}
+
+	unsigned int CEqProcessStep::getTotalParameter()
+	{
+		return m_nTotalParameter;
+	}
+
+	const std::list<std::string> CEqProcessStep::getParameters()
+	{
+		return m_params;
+	}
+
+	void CEqProcessStep::getParameters(std::list<std::string>& list)
+	{
+		Lock();
+		std::copy(m_params.begin(), m_params.end(), std::back_inserter(list));
+		Unlock();
+	}
+}
diff --git a/SourceCode/Bond/Servo/CEqProcessStep.h b/SourceCode/Bond/Servo/CEqProcessStep.h
new file mode 100644
index 0000000..399d7f8
--- /dev/null
+++ b/SourceCode/Bond/Servo/CEqProcessStep.h
@@ -0,0 +1,36 @@
+#pragma once
+#include "CStep.h"
+#include <list>
+
+
+namespace SERVO {
+	class CEqProcessStep : public CStep
+	{
+	public:
+		CEqProcessStep();
+		~CEqProcessStep();
+
+	public:
+		virtual int onReadData();
+		virtual int onComplete();
+		virtual int onTimeout();
+		void setProcessDev(int nDev);
+		std::string& getGlassId();
+		std::string& getStartTime();
+		std::string& getEndTime();
+		unsigned int getTotalParameter();
+		const std::list<std::string> getParameters();
+		void getParameters(std::list<std::string>& list);
+
+	private:
+		int m_nProcessDev;
+		std::string m_strGlassId;
+		std::string m_strStartTime;
+		std::string m_strEndTime;
+		unsigned int m_nTotalParameter;
+		unsigned int m_nTotalGroup;
+		unsigned int m_nCurrentGroup;
+		std::list<std::string> m_params;
+	};
+}
+
diff --git a/SourceCode/Bond/Servo/CEqStatusStep.cpp b/SourceCode/Bond/Servo/CEqStatusStep.cpp
index 56216a8..697e384 100644
--- a/SourceCode/Bond/Servo/CEqStatusStep.cpp
+++ b/SourceCode/Bond/Servo/CEqStatusStep.cpp
@@ -21,6 +21,24 @@
 
 	}
 
+	void CEqStatusStep::getAttributeVector(CAttributeVector& attrubutes)
+	{
+		CStep::getAttributeVector(attrubutes);
+
+		char szName[256];
+		for (int i = 0; i < STATUS_MAX; i++) {
+			sprintf_s(szName, 256, "Status %d", i + 1);
+			attrubutes.addAttribute(new CAttribute(szName,
+				std::to_string(m_nStatus[i]).c_str(), ""));
+			sprintf_s(szName, 256, "Reason Code %d", i + 1);
+			attrubutes.addAttribute(new CAttribute(szName,
+				std::to_string(m_nReasonCode[i]).c_str(), ""));
+		}
+
+		attrubutes.addAttribute(new CAttribute("Status Dev",
+			std::to_string(m_nStatusDev).c_str(), ""));
+	}
+
 	int CEqStatusStep::getStatus(unsigned int uint)
 	{
 		if (uint < STATUS_MAX) {
diff --git a/SourceCode/Bond/Servo/CEqStatusStep.h b/SourceCode/Bond/Servo/CEqStatusStep.h
index a4f2ca3..dd4e47f 100644
--- a/SourceCode/Bond/Servo/CEqStatusStep.h
+++ b/SourceCode/Bond/Servo/CEqStatusStep.h
@@ -13,6 +13,7 @@
 		~CEqStatusStep();
 
 	public:
+		virtual void getAttributeVector(CAttributeVector& attrubutes);
 		virtual int onReadData();
 		virtual int onComplete();
 		virtual int onTimeout();
diff --git a/SourceCode/Bond/Servo/CEquipment.cpp b/SourceCode/Bond/Servo/CEquipment.cpp
index 756d946..afd0c0e 100644
--- a/SourceCode/Bond/Servo/CEquipment.cpp
+++ b/SourceCode/Bond/Servo/CEquipment.cpp
@@ -60,6 +60,11 @@
 		container.push_back(std::make_pair("Version", "1.0"));
 	}
 
+	std::map<unsigned int, CStep*>& CEquipment::getSteps()
+	{
+		return m_mapStep;
+	}
+
 	CStep* CEquipment::getStep(unsigned int addr)
 	{
 		auto iter = m_mapStep.find(addr);
diff --git a/SourceCode/Bond/Servo/CEquipment.h b/SourceCode/Bond/Servo/CEquipment.h
index 8b6d8bb..978dbd2 100644
--- a/SourceCode/Bond/Servo/CEquipment.h
+++ b/SourceCode/Bond/Servo/CEquipment.h
@@ -64,6 +64,7 @@
 		void getProperties(std::vector<std::pair<std::string, std::string>>& container);
 		int addStep(unsigned int addr, CStep* pStep);
 		CStep* getStep(unsigned int addr);
+		std::map<unsigned int, CStep*>& getSteps();
 		virtual void init();
 		virtual void term();
 		virtual void onTimer(UINT nTimerid);
diff --git a/SourceCode/Bond/Servo/CMaster.cpp b/SourceCode/Bond/Servo/CMaster.cpp
index 04d7574..d0bd080 100644
--- a/SourceCode/Bond/Servo/CMaster.cpp
+++ b/SourceCode/Bond/Servo/CMaster.cpp
@@ -74,7 +74,7 @@
 		// 初始化添加各子设备
 		addEFEM(listener);
 
-		/*
+		
 		{
 			CBonder* pBonder = new CBonder();
 			pBonder->setName("Bonder 1");
@@ -84,7 +84,7 @@
 			addEquipment(pBonder);
 			LOGE("已添加“Bonder 1”.");
 		}
-		*/
+		
 
 		// 定时器
 		g_pMaster = this;
@@ -127,6 +127,11 @@
 		return 0;
 	}
 
+	std::list<CEquipment*>& CMaster::getEquipmentList()
+	{
+		return m_listEquipment;
+	}
+
 	CEquipment* CMaster::getEquipment(int id)
 	{
 		for (auto item : m_listEquipment) {
diff --git a/SourceCode/Bond/Servo/CMaster.h b/SourceCode/Bond/Servo/CMaster.h
index 73ae99b..0fefda3 100644
--- a/SourceCode/Bond/Servo/CMaster.h
+++ b/SourceCode/Bond/Servo/CMaster.h
@@ -28,6 +28,7 @@
         int init();
         int term();
         void onTimer(UINT nTimerid);
+        std::list<CEquipment*>& getEquipmentList();
         CEquipment* getEquipment(int id);
 
     private:
diff --git a/SourceCode/Bond/Servo/CPanelMaster.cpp b/SourceCode/Bond/Servo/CPanelMaster.cpp
new file mode 100644
index 0000000..7991ae7
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPanelMaster.cpp
@@ -0,0 +1,176 @@
+锘�// CPanelMaster.cpp: 瀹炵幇鏂囦欢
+//
+
+#include "stdafx.h"
+#include "Servo.h"
+#include "CPanelMaster.h"
+#include "afxdialogex.h"
+#include "Common.h"
+#include "VerticalLine.h"
+
+
+// CPanelMaster 瀵硅瘽妗�
+
+IMPLEMENT_DYNAMIC(CPanelMaster, CDialogEx)
+
+CPanelMaster::CPanelMaster(CWnd* pParent /*=nullptr*/)
+	: CDialogEx(IDD_PANEL_MASTER, pParent)
+{
+	m_crBkgnd = PANEL_MASTER_BACKGROUND_COLOR;
+	m_hbrBkgnd = nullptr;
+	m_nPanelWidth = 388;
+}
+
+CPanelMaster::~CPanelMaster()
+{
+}
+
+void CPanelMaster::DoDataExchange(CDataExchange* pDX)
+{
+	CDialogEx::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_TREE1, m_treeCtrl);
+}
+
+
+BEGIN_MESSAGE_MAP(CPanelMaster, CDialogEx)
+	ON_WM_CTLCOLOR()
+	ON_WM_DESTROY()
+	ON_WM_SIZE()
+	ON_NOTIFY(BYVERTICALLINE_MOVEX, IDC_LINE1, &CPanelMaster::OnVLineMoveX)
+	ON_WM_TIMER()
+END_MESSAGE_MAP()
+
+
+// CPanelMaster 娑堟伅澶勭悊绋嬪簭
+
+
+int CPanelMaster::getPanelWidth()
+{
+	return m_nPanelWidth;
+}
+
+BOOL CPanelMaster::OnInitDialog()
+{
+	CDialogEx::OnInitDialog();
+
+
+	CVerticalLine* pLine1 = CVerticalLine::Hook(GetDlgItem(IDC_LINE1)->GetSafeHwnd());
+	pLine1->SetBkgndColor(RGB(225, 225, 225));
+	pLine1->SetLineColor(RGB(198, 198, 198));
+	pLine1->EnableResize();
+
+
+	// 璇诲彇闈㈡澘瀹�
+	CString strIniFile;
+	strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
+	m_nPanelWidth = GetPrivateProfileInt(_T("App"), _T("MasterPanelWidth"),
+		int((double)GetSystemMetrics(SM_CXSCREEN) * 0.25), (LPTSTR)(LPCTSTR)strIniFile);
+
+
+	// treectrl
+	m_treeCtrl.SetBkColor(PANEL_MASTER_BACKGROUND_COLOR);
+	m_treeCtrl.SetItemHeight(28);
+	SetTimer(1, 2000, nullptr);
+
+	return TRUE;  // return TRUE unless you set the focus to a control
+				  // 寮傚父: OCX 灞炴�ч〉搴旇繑鍥� FALSE
+}
+
+HBRUSH CPanelMaster::OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor)
+{
+	HBRUSH hbr = CDialogEx::OnCtlColor(pDC, pWnd, nCtlColor);
+
+	if (nCtlColor == CTLCOLOR_STATIC) {
+		pDC->SetBkColor(m_crBkgnd);
+		pDC->SetTextColor(RGB(0, 0, 0));
+	}
+
+	if (m_hbrBkgnd == nullptr) {
+		m_hbrBkgnd = CreateSolidBrush(m_crBkgnd);
+	}
+
+	return m_hbrBkgnd;
+}
+
+void CPanelMaster::OnDestroy()
+{
+	CDialogEx::OnDestroy();
+
+	if (m_hbrBkgnd != nullptr) {
+		::DeleteObject(m_hbrBkgnd);
+	}
+}
+
+void CPanelMaster::OnSize(UINT nType, int cx, int cy)
+{
+	CDialogEx::OnSize(nType, cx, cy);
+	if (GetDlgItem(IDC_LINE1) == nullptr) return;
+
+	CWnd* pItem;
+	CRect rcClient;
+
+	GetClientRect(&rcClient);
+	pItem = GetDlgItem(IDC_LINE1);
+	pItem->MoveWindow(rcClient.right - 3, 0, 3, rcClient.Height());
+
+	m_treeCtrl.MoveWindow(5, 5, rcClient.Width() - 13, rcClient.Height() - 10);
+}
+
+
+#define MASTER_PANEL_MIN_WIDTH		88
+#define MASTER_PANEL_MAX_WIDTH		588
+void CPanelMaster::OnVLineMoveX(NMHDR* nmhdr, LRESULT* result)
+{
+	BYVERTICALLINE_NMHDR* pNmhdrex = (BYVERTICALLINE_NMHDR*)nmhdr;
+	int x = pNmhdrex->dwData;
+	m_nPanelWidth += x;
+	m_nPanelWidth = max(m_nPanelWidth, MASTER_PANEL_MIN_WIDTH);
+	m_nPanelWidth = min(m_nPanelWidth, MASTER_PANEL_MAX_WIDTH);
+	GetParent()->SendMessage(ID_MSG_PANEL_RESIZE, m_nPanelWidth, 0);
+
+	CString strIniFile, strValue;
+	strIniFile.Format(_T("%s\\%s.ini"), (LPTSTR)(LPCTSTR)theApp.m_strAppDir, (LPTSTR)(LPCTSTR)theApp.m_strAppFile);
+	strValue.Format(_T("%d"), m_nPanelWidth);
+	WritePrivateProfileString(_T("App"), _T("MasterPanelWidth"),
+		(LPTSTR)(LPCTSTR)strValue, (LPTSTR)(LPCTSTR)strIniFile);
+	OnSize(0, 0, 0);
+	
+	* result = 0;
+}
+
+void CPanelMaster::OnTimer(UINT_PTR nIDEvent)
+{
+	if (1 == nIDEvent) {
+		KillTimer(1);
+		loadEquipmentList();
+	}
+
+	CDialogEx::OnTimer(nIDEvent);
+}
+
+void CPanelMaster::loadEquipmentList()
+{
+	HTREEITEM hItemMaster = m_treeCtrl.InsertItem("Master");
+
+	std::list<SERVO::CEquipment*>& eqs = theApp.m_model.m_master.getEquipmentList();
+	for (auto item : eqs) {
+		HTREEITEM hItemEq = m_treeCtrl.InsertItem(item->getName().c_str(), hItemMaster);
+		m_treeCtrl.SetItemData(hItemEq, (DWORD_PTR)item);
+		loadSteps(item, hItemEq);
+		m_treeCtrl.Expand(hItemEq, TVE_EXPAND);
+	}
+
+
+	m_treeCtrl.Expand(hItemMaster, TVE_EXPAND);
+}
+
+void CPanelMaster::loadSteps(SERVO::CEquipment* pEquipment, HTREEITEM hItemEq)
+{
+	std::map<unsigned int, SERVO::CStep*>& steps = pEquipment->getSteps();
+
+	for (auto item : steps) {
+		HTREEITEM hStep = m_treeCtrl.InsertItem(item.second->getName().c_str(), hItemEq);
+		m_treeCtrl.SetItemData(hStep, (DWORD_PTR)item.second);
+	}
+}
+
diff --git a/SourceCode/Bond/Servo/CPanelMaster.h b/SourceCode/Bond/Servo/CPanelMaster.h
new file mode 100644
index 0000000..50f44e3
--- /dev/null
+++ b/SourceCode/Bond/Servo/CPanelMaster.h
@@ -0,0 +1,42 @@
+锘�#pragma once
+#include "ApredTreeCtrl2.h"
+
+
+// CPanelMaster 瀵硅瘽妗�
+
+class CPanelMaster : public CDialogEx
+{
+	DECLARE_DYNAMIC(CPanelMaster)
+
+public:
+	CPanelMaster(CWnd* pParent = nullptr);   // 鏍囧噯鏋勯�犲嚱鏁�
+	virtual ~CPanelMaster();
+	int getPanelWidth();
+	void loadEquipmentList();
+	void loadSteps(SERVO::CEquipment* pEquipment, HTREEITEM hItemEq);
+
+
+private:
+	COLORREF m_crBkgnd;
+	HBRUSH m_hbrBkgnd;
+	CApredTreeCtrl2 m_treeCtrl;
+	int m_nPanelWidth;
+
+
+// 瀵硅瘽妗嗘暟鎹�
+#ifdef AFX_DESIGN_TIME
+	enum { IDD = IDD_PANEL_MASTER };
+#endif
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV 鏀寔
+
+	DECLARE_MESSAGE_MAP()
+public:
+	virtual BOOL OnInitDialog();
+	afx_msg HBRUSH OnCtlColor(CDC* pDC, CWnd* pWnd, UINT nCtlColor);
+	afx_msg void OnDestroy();
+	afx_msg void OnSize(UINT nType, int cx, int cy);
+	afx_msg void OnVLineMoveX(NMHDR* nmhdr, LRESULT* result);
+	afx_msg void OnTimer(UINT_PTR nIDEvent);
+};
diff --git a/SourceCode/Bond/Servo/CStep.cpp b/SourceCode/Bond/Servo/CStep.cpp
index 2e43de3..6690b0f 100644
--- a/SourceCode/Bond/Servo/CStep.cpp
+++ b/SourceCode/Bond/Servo/CStep.cpp
@@ -1,5 +1,6 @@
 #include "stdafx.h"
 #include "CStep.h"
+#include "Common.h"
 
 
 namespace SERVO {
@@ -66,6 +67,19 @@
 	std::string& CStep::getName()
 	{
 		return m_strName;
+	}
+
+	void CStep::getAttributeVector(CAttributeVector& attrubutes)
+	{
+		attrubutes.clear();
+		attrubutes.addAttribute(new CAttribute("Network", 
+			std::to_string(m_station.nNetNo).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Station",
+			std::to_string(m_station.nStNo).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Current Step",
+			std::to_string(m_nCurStep).c_str(), ""));
+		attrubutes.addAttribute(new CAttribute("Signal Dev",
+			std::to_string(m_nWriteSignalDev).c_str(), ""));
 	}
 
 	void CStep::setWriteSignalDev(int dev)
@@ -205,5 +219,18 @@
 		m_nCurStep++;
 		Unlock();
 	}
+
+	void CStep::convertString(const char* pszBuffer, int size, std::string& strOut)
+	{
+		strOut.clear();
+		int nLength = 0;
+		for (int i = 0; i < size; i++) {
+			if (pszBuffer[i] == '\0') break;
+			nLength++;
+		}
+		if (nLength > 0) {
+			strOut = std::string(pszBuffer, nLength);
+		}
+	}
 }
 
diff --git a/SourceCode/Bond/Servo/CStep.h b/SourceCode/Bond/Servo/CStep.h
index 628f116..d1ece05 100644
--- a/SourceCode/Bond/Servo/CStep.h
+++ b/SourceCode/Bond/Servo/CStep.h
@@ -1,10 +1,9 @@
 #pragma once
 #include "CCLinkIEControl.h"
+#include "CAttributeVector.h"
 
 
 namespace SERVO {
-#define STEP_EVENT_READDATA			0x01
-#define STEP_EVENT_COMPLETE			0x02
 
 	typedef std::function<void(void* pStep, int code, void* pData)> ONSTEPEVENT;
 	typedef struct _StepListener
@@ -27,6 +26,7 @@
 		CEquipment* getEquipment();
 		void setName(const char* pszName);
 		std::string& getName();
+		virtual void getAttributeVector(CAttributeVector& attrubutes);
 		virtual void setWriteSignalDev(int dev);
 		virtual void init();
 		virtual void CStep::term();
@@ -40,6 +40,7 @@
 		inline void Unlock() { LeaveCriticalSection(&m_criticalSection); }
 		inline void nextStep();
 		inline void resetStep();
+		void convertString(const char* pszBuffer, int size, std::string& strOut);
 
 	protected:
 		StepListener m_listener;
@@ -48,7 +49,6 @@
 		CEquipment* m_pEquipment;
 		CCCLinkIEControl* m_pCclink;
 		CRITICAL_SECTION m_criticalSection;
-		std::string strName;
 		HANDLE m_hWorkThreadHandle;
 		unsigned m_nWordThreadAddr;
 		HANDLE m_hWorkStop;
diff --git a/SourceCode/Bond/Servo/Common.h b/SourceCode/Bond/Servo/Common.h
index b3f1260..39897aa 100644
--- a/SourceCode/Bond/Servo/Common.h
+++ b/SourceCode/Bond/Servo/Common.h
@@ -17,6 +17,7 @@
 /* 颜色 */
 #define APPDLG_BACKGROUND_COLOR			RGB(255, 255, 255)
 #define LOGDLG_BACKGROUND_COLOR			RGB(255, 255, 255)
+#define PANEL_MASTER_BACKGROUND_COLOR	RGB(255, 255, 255)
 
 
 /* LOG BTN */
@@ -56,3 +57,13 @@
 #define BASE_ALARM_EFEM		10000
 #define BASE_ALARM_BONDER1	20000
 #define BASE_ALARM_BONDER2	30000
+
+
+/* step event */
+#define STEP_EVENT_READDATA			0x01
+#define STEP_EVENT_COMPLETE			0x02
+#define STEP_EVENT_PROCESS_DATA		0x1001
+
+
+/* 自定义消息 */
+#define ID_MSG_PANEL_RESIZE			WM_USER + 1998
diff --git a/SourceCode/Bond/Servo/HorizontalLine.cpp b/SourceCode/Bond/Servo/HorizontalLine.cpp
new file mode 100644
index 0000000..fc48b76
--- /dev/null
+++ b/SourceCode/Bond/Servo/HorizontalLine.cpp
@@ -0,0 +1,209 @@
+// HorizontalLine.cpp: implementation of the CHorizontalLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "HorizontalLine.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CHorizontalLine::CHorizontalLine()
+{
+	m_hWnd = NULL;
+	m_crBkgnd = RGB(255, 255, 255);
+	m_crLineColor = RGB(222, 222, 222);
+}
+
+CHorizontalLine::~CHorizontalLine()
+{
+}
+
+BOOL CHorizontalLine::RegisterWndClass()
+{
+	WNDCLASS wc;
+	wc.lpszClassName = BYHORIZONTALLINE_CLASS;
+	wc.hInstance = AfxGetInstanceHandle();
+	wc.lpfnWndProc = WindowProc;
+	wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+	wc.hIcon = 0;
+	wc.lpszMenuName = NULL;
+	wc.hbrBackground = NULL;
+	wc.style = CS_GLOBALCLASS|CS_DBLCLKS;
+	wc.cbClsExtra = 0;
+	wc.cbWndExtra = 0;
+	
+	// 注册自定义类
+	return (::RegisterClass(&wc) != 0);
+}
+
+CHorizontalLine* CHorizontalLine::Hook(HWND hWnd)
+{
+	CHorizontalLine* pHorizontalLine = (CHorizontalLine*)GetProp(hWnd, BYSTAG_HORIZONTALLINE);
+	if(pHorizontalLine == NULL)
+	{
+		pHorizontalLine = new CHorizontalLine;
+		pHorizontalLine->m_hWnd = hWnd;
+
+		SetProp(hWnd, BYSTAG_HORIZONTALLINE, (HANDLE)pHorizontalLine);
+	}
+	
+	return pHorizontalLine;
+}
+
+void CHorizontalLine::Release()
+{
+	// delete
+	delete this;
+}
+
+void CHorizontalLine::Notify(int nCode, int dwData, int dwData1/* = 0*/, int dwData2/* = 0*/)
+{
+	HWND hParent;
+	hParent = GetParent(m_hWnd);
+	if(hParent != NULL) {
+		BYHORIZONTALLINE_NMHDR iii_nmhdr;
+		iii_nmhdr.nmhdr.hwndFrom = m_hWnd;
+		iii_nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
+		iii_nmhdr.nmhdr.code = nCode;
+		iii_nmhdr.dwData = dwData;
+		iii_nmhdr.dwData1 = dwData1;
+		iii_nmhdr.dwData2 = dwData2;
+		SendMessage(hParent, WM_NOTIFY, (WPARAM)iii_nmhdr.nmhdr.idFrom, (LPARAM)&iii_nmhdr);
+	}
+}
+
+////////////////////////////////
+// 拦截窗口消息函数
+LRESULT CALLBACK CHorizontalLine::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)   
+{
+	CHorizontalLine* pHorizontalLine = (CHorizontalLine *)GetProp(hWnd, BYSTAG_HORIZONTALLINE);
+	if(pHorizontalLine == NULL && uMsg != WM_NCCREATE)
+	{
+		return ::DefWindowProc(hWnd, uMsg, wParam, lParam);   
+	}
+
+	
+	// 如果Hook则响应消息
+	ASSERT(hWnd);
+	switch(uMsg)   
+	{
+	case WM_NCCREATE:
+		return OnNcCreate(hWnd, wParam, lParam);
+
+	case WM_DESTROY:
+		return pHorizontalLine->OnDestroy(wParam, lParam);
+
+	case WM_PAINT:
+		return pHorizontalLine->OnPaint(wParam, lParam);
+
+	case WM_TIMER:
+		return pHorizontalLine->OnTimer(wParam, lParam);
+
+	case WM_GETDLGCODE:
+		return DLGC_WANTALLKEYS;
+
+	default:
+		break;
+	}
+	
+	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);   
+}  
+
+///////////////////////////////
+// WM_NCCREATE
+// 窗口创建前的初始化工作
+LRESULT CHorizontalLine::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+	CHorizontalLine* pHorizontalLine = (CHorizontalLine *)GetProp(hWnd, BYSTAG_HORIZONTALLINE);
+	ASSERT(pHorizontalLine == NULL);
+
+	Hook(hWnd);
+	return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_DESTROY
+LRESULT CHorizontalLine::OnDestroy(WPARAM wParam, LPARAM lParam)
+{
+	Release();
+	return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_TIMER
+LRESULT CHorizontalLine::OnTimer(WPARAM wParam, LPARAM lParam)
+{
+	return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
+}
+
+
+///////////////////////////////
+// WM_PAINT
+LRESULT CHorizontalLine::OnPaint(WPARAM wParam, LPARAM lParam)
+{
+	HDC hDC, hMemDC;
+	HBITMAP hBitmap;
+	RECT rcClient;
+	CString strText;
+	HFONT hFont;
+	HBRUSH hBrushBK;
+
+
+	// BeginPaint
+	PAINTSTRUCT ps;
+	hDC = BeginPaint(m_hWnd, &ps);
+	GetClientRect(m_hWnd, &rcClient);
+
+	hMemDC = ::CreateCompatibleDC(hDC);
+	hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right-rcClient.left,
+		rcClient.bottom-rcClient.top);
+	::SelectObject(hMemDC, hBitmap);
+
+	hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+	::SelectObject(hMemDC, hFont);
+
+	
+	// 背景颜色
+	hBrushBK = CreateSolidBrush( m_crBkgnd );
+	::FillRect(hMemDC, &rcClient, hBrushBK);
+	DeleteObject(hBrushBK);
+
+
+	// 画线
+	HPEN hPen = CreatePen(PS_SOLID, 1, m_crLineColor);
+	HPEN hOldPen = (HPEN)::SelectObject(hMemDC, hPen);
+	::MoveToEx(hMemDC, 0, 0, NULL);
+	LineTo(hMemDC, rcClient.right, 0);
+	::SelectObject(hMemDC, hOldPen);
+
+
+	// EndPaint
+	::BitBlt(hDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
+		hMemDC, 0, 0, SRCCOPY);
+	EndPaint(m_hWnd, &ps);
+	::DeleteObject(hBitmap);
+	::DeleteDC(hMemDC);
+
+	
+	return 1;
+}
+
+void CHorizontalLine::SetBkgndColor(COLORREF cr)
+{
+	m_crBkgnd = cr;
+}
+
+void CHorizontalLine::SetLineColor(COLORREF cr)
+{
+	m_crLineColor = cr;
+}
+
+
diff --git a/SourceCode/Bond/Servo/HorizontalLine.h b/SourceCode/Bond/Servo/HorizontalLine.h
new file mode 100644
index 0000000..f5b3647
--- /dev/null
+++ b/SourceCode/Bond/Servo/HorizontalLine.h
@@ -0,0 +1,87 @@
+// HorizontalLine.h: interface for the CHorizontalLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_HORIZONTALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
+#define AFX_HORIZONTALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_
+
+
+#pragma comment(lib, "Msimg32.lib")			// TransparentBlt
+
+
+
+//====== HorizontalLine =====================================================
+
+#ifndef NOHORIZONTALLINE
+
+#ifdef _WIN32
+
+#define BYHORIZONTALLINE_CLASSA       "BYHorizontalLine"
+#define BYHORIZONTALLINE_CLASSW       L"BYHorizontalLine"
+
+#ifdef UNICODE
+#define  BYHORIZONTALLINE_CLASS       BYHORIZONTALLINE_CLASSW
+#else
+#define  BYHORIZONTALLINE_CLASS       BYHORIZONTALLINE_CLASSA
+#endif
+
+#else
+#define BYHORIZONTALLINE_CLASS        "BYHorizontalLine"
+#endif
+
+
+#define BYSTAG_HORIZONTALLINE		 _T("ISHORIZONTALLINE")
+
+
+//====== WM_NOTIFY codes (NMHDR.code values) ==================================
+#define BYHORIZONTALLINE_FIRST				 (0U-590U)       //
+#define BYHORIZONTALLINE_LAST				 (0U-550U)
+#define BYHORIZONTALLINE_		 			 (BYHORIZONTALLINE_FIRST - 1)
+
+
+typedef struct tagBYHORIZONTALLINE_NMHDR
+{
+	NMHDR		nmhdr;
+	DWORD		dwData;
+	DWORD		dwData1;
+	DWORD		dwData2;
+} BYHORIZONTALLINE_NMHDR;
+
+
+
+#endif
+
+
+
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+class CHorizontalLine
+{
+public:
+	CHorizontalLine();
+	virtual ~CHorizontalLine();
+
+public:
+	static BOOL RegisterWndClass();
+	static CHorizontalLine* Hook(HWND hWnd);
+	void Notify(int nCode, int dwData, int dwData1 = 0, int dwData2 = 0);
+	void Release();
+	void SetBkgndColor(COLORREF cr);
+	void SetLineColor(COLORREF cr);
+	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+	static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
+	LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
+	LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
+	LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
+
+private:
+	HWND		m_hWnd;
+	COLORREF m_crBkgnd;
+	COLORREF m_crLineColor;
+};
+
+#endif // !defined(AFX_HORIZONTALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
diff --git a/SourceCode/Bond/Servo/Servo.cpp b/SourceCode/Bond/Servo/Servo.cpp
index 6c31df8..d88737c 100644
--- a/SourceCode/Bond/Servo/Servo.cpp
+++ b/SourceCode/Bond/Servo/Servo.cpp
@@ -8,6 +8,8 @@
 #include "ServoGraph.h"
 #include "AlarmManager.h"
 #include "SECSRuntimeManager.h"
+#include "VerticalLine.h"
+
 
 // 声明全局变量,用于管理 GDI+ 初始化
 ULONG_PTR g_diplusToken;
@@ -92,6 +94,7 @@
 
 	// 注册控件
 	CServoGraph::RegisterWndClass();
+	CVerticalLine::RegisterWndClass();
 
 
 	// 初始化Rx库
diff --git a/SourceCode/Bond/Servo/Servo.rc b/SourceCode/Bond/Servo/Servo.rc
index 228ffdd..36b313b 100644
--- a/SourceCode/Bond/Servo/Servo.rc
+++ b/SourceCode/Bond/Servo/Servo.rc
Binary files differ
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj b/SourceCode/Bond/Servo/Servo.vcxproj
index 871eaad..74d6e70 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj
+++ b/SourceCode/Bond/Servo/Servo.vcxproj
@@ -197,7 +197,10 @@
   <ItemGroup>
     <ClInclude Include="AlarmDlg.h" />
     <ClInclude Include="AlarmManager.h" />
+    <ClInclude Include="ApredTreeCtrl2.h" />
     <ClInclude Include="BlButton.h" />
+    <ClInclude Include="CAttribute.h" />
+    <ClInclude Include="CAttributeVector.h" />
     <ClInclude Include="CBonder.h" />
     <ClInclude Include="CCLinkPerformance\CCLinkIEControl.h" />
     <ClInclude Include="CCLinkPerformance\PerformanceMelsec.h" />
@@ -205,6 +208,7 @@
     <ClInclude Include="CEqModeStep.h" />
     <ClInclude Include="CEqProcessStep.h" />
     <ClInclude Include="CEqStatusStep.h" />
+    <ClInclude Include="CPanelMaster.h" />
     <ClInclude Include="CStep.h" />
     <ClInclude Include="DevicePropertyDlg.h" />
     <ClInclude Include="CEFEM.h" />
@@ -229,11 +233,15 @@
     <ClInclude Include="targetver.h" />
     <ClInclude Include="TerminalDisplayDlg.h" />
     <ClInclude Include="ToolUnits.h" />
+    <ClInclude Include="VerticalLine.h" />
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="AlarmDlg.cpp" />
     <ClCompile Include="AlarmManager.cpp" />
+    <ClCompile Include="ApredTreeCtrl2.cpp" />
     <ClCompile Include="BlButton.cpp" />
+    <ClCompile Include="CAttribute.cpp" />
+    <ClCompile Include="CAttributeVector.cpp" />
     <ClCompile Include="CBonder.cpp" />
     <ClCompile Include="CCLinkPerformance\CCLinkIEControl.cpp" />
     <ClCompile Include="CCLinkPerformance\PerformanceMelsec.cpp" />
@@ -241,6 +249,7 @@
     <ClCompile Include="CEqModeStep.cpp" />
     <ClCompile Include="CEqProcessStep.cpp" />
     <ClCompile Include="CEqStatusStep.cpp" />
+    <ClCompile Include="CPanelMaster.cpp" />
     <ClCompile Include="CStep.cpp" />
     <ClCompile Include="DevicePropertyDlg.cpp" />
     <ClCompile Include="CEFEM.cpp" />
@@ -267,6 +276,7 @@
     </ClCompile>
     <ClCompile Include="TerminalDisplayDlg.cpp" />
     <ClCompile Include="ToolUnits.cpp" />
+    <ClCompile Include="VerticalLine.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Servo.rc" />
diff --git a/SourceCode/Bond/Servo/Servo.vcxproj.filters b/SourceCode/Bond/Servo/Servo.vcxproj.filters
index 5bd101b..5988a2a 100644
--- a/SourceCode/Bond/Servo/Servo.vcxproj.filters
+++ b/SourceCode/Bond/Servo/Servo.vcxproj.filters
@@ -42,6 +42,11 @@
     <ClCompile Include="CEqAlarmStep.cpp" />
     <ClCompile Include="AlarmDlg.cpp" />
     <ClCompile Include="CEqProcessStep.cpp" />
+    <ClCompile Include="CAttribute.cpp" />
+    <ClCompile Include="CAttributeVector.cpp" />
+    <ClCompile Include="CPanelMaster.cpp" />
+    <ClCompile Include="VerticalLine.cpp" />
+    <ClCompile Include="ApredTreeCtrl2.cpp" />
   </ItemGroup>
   <ItemGroup>
     <ClInclude Include="AlarmManager.h" />
@@ -82,6 +87,11 @@
     <ClInclude Include="CEqAlarmStep.h" />
     <ClInclude Include="AlarmDlg.h" />
     <ClInclude Include="CEqProcessStep.h" />
+    <ClInclude Include="CAttribute.h" />
+    <ClInclude Include="CAttributeVector.h" />
+    <ClInclude Include="CPanelMaster.h" />
+    <ClInclude Include="VerticalLine.h" />
+    <ClInclude Include="ApredTreeCtrl2.h" />
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="Servo.rc" />
diff --git a/SourceCode/Bond/Servo/ServoDlg.cpp b/SourceCode/Bond/Servo/ServoDlg.cpp
index f6b355c..4217090 100644
--- a/SourceCode/Bond/Servo/ServoDlg.cpp
+++ b/SourceCode/Bond/Servo/ServoDlg.cpp
@@ -90,6 +90,7 @@
 	m_pLogDlg = nullptr;
 	m_pTerminalDisplayDlg = nullptr;
 	m_pObserver = nullptr;
+	m_pPanelMaster = nullptr;
 }
 
 void CServoDlg::DoDataExchange(CDataExchange* pDX)
@@ -126,6 +127,9 @@
 	ON_WM_TIMER()
 	ON_WM_ERASEBKGND()
 	ON_BN_CLICKED(IDC_BUTTON_ALARM, &CServoDlg::OnBnClickedButtonAlarm)
+	ON_BN_CLICKED(IDC_BUTTON_ALARM, &CServoDlg::OnBnClickedButtonAlarm)
+	ON_NOTIFY(BYSERVOGRAPH_ITEM_CLICKED, IDC_SERVO_GRAPH1, &CServoDlg::OnGraphItemClicked)
+	ON_MESSAGE(ID_MSG_PANEL_RESIZE, OnPanelResize)
 END_MESSAGE_MAP()
 
 
@@ -277,6 +281,8 @@
 	m_pGraph->SetBoxText(INDICATE_ROBOT_ARM2, "6", "Robot");
 
 
+
+
 	// Vacuum bake
 	m_pGraph->AddIndicateBox(INDICATE_VACUUM_BAKE, 396, 516, 48, RGB(22, 22, 22),
 		RGB(255, 127, 39), RGB(0, 176, 80));
@@ -293,6 +299,12 @@
 	m_pGraph->AddIndicateBox(INDICATE_MEASUREMENT, 736, 516, 48, RGB(22, 22, 22),
 		RGB(255, 127, 39), RGB(0, 176, 80));
 	m_pGraph->SetBoxText(INDICATE_MEASUREMENT, "13", "Measurement");
+
+
+
+	m_pPanelMaster = new CPanelMaster();
+	m_pPanelMaster->Create(IDD_PANEL_MASTER, this);
+	m_pPanelMaster->ShowWindow(SW_SHOW);
 
 
 	// 调整初始窗口位置
@@ -317,6 +329,13 @@
 
 	// 相当于延时调用master的初始化
 	theApp.m_model.m_master.init();
+
+
+	// 绑定数据
+	{
+		SERVO::CEquipment* pEquipment = theApp.m_model.m_master.getEquipment(EQ_ID_EFEM);
+		m_pGraph->SetIndicateBoxData(INDICATE_ROBOT_ARM1, pEquipment);
+	}
 
 
 	return TRUE;  // 除非将焦点设置到控件,否则返回 TRUE
@@ -533,6 +552,12 @@
 		m_pTerminalDisplayDlg = nullptr;
 	}
 
+	if (m_pPanelMaster != nullptr) {
+		m_pPanelMaster->DestroyWindow();
+		delete m_pPanelMaster;
+		m_pPanelMaster = nullptr;
+	}
+
 	if (m_hbrBkgnd != nullptr) {
 		::DeleteObject(m_hbrBkgnd);
 	}
@@ -731,6 +756,14 @@
 
 	x = 0;
 	y = 0;
+	int nPanelWidth = 0;
+	if (m_pPanelMaster != nullptr) {
+		nPanelWidth = m_pPanelMaster->getPanelWidth();
+		m_pPanelMaster->MoveWindow(x, y, nPanelWidth, rcClient.Height());
+		x += nPanelWidth;
+	}
+
+
 	pItem = GetDlgItem(IDC_SERVO_GRAPH1);
 	pItem->GetClientRect(&rcItem);
 	pItem->MoveWindow(x, y, rcItem.Width(), rcItem.Height());
@@ -828,3 +861,26 @@
 	CAlarmDlg dlg;
 	dlg.DoModal();
 }
+
+void CServoDlg::OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult)
+{
+	BYSERVOGRAPH_NMHDR* pGraphNmhdr = reinterpret_cast<BYSERVOGRAPH_NMHDR*>(pNMHDR);
+	CString s; s.Format(_T("OnGraphItemClicked %d"), pGraphNmhdr->dwData);
+	SERVO::CEquipment* pEquipment = (SERVO::CEquipment*)m_pGraph->GetIndicateBoxData(pGraphNmhdr->dwData);
+	if (pEquipment != nullptr) {
+		AfxMessageBox(pEquipment->getName().c_str());
+	}
+	
+	
+	*pResult = 0;
+}
+
+LRESULT CServoDlg::OnPanelResize(WPARAM wParam, LPARAM lParam)
+{
+	int width = wParam;
+	// m_pPanel->SetPanelWidth(width);
+	Resize();
+
+	return 0;
+}
+
diff --git a/SourceCode/Bond/Servo/ServoDlg.h b/SourceCode/Bond/Servo/ServoDlg.h
index ab71ed2..b3f4cca 100644
--- a/SourceCode/Bond/Servo/ServoDlg.h
+++ b/SourceCode/Bond/Servo/ServoDlg.h
@@ -7,6 +7,8 @@
 #include "BlButton.h"
 #include "LogDlg.h"
 #include "TerminalDisplayDlg.h"
+#include "CPanelMaster.h"
+
 
 enum DeviceStatus {
 	ONLINE,       // 在线
@@ -62,6 +64,7 @@
 	HBRUSH m_hbrBkgnd;
 	CBlButton m_btnLog;
 	CBlButton m_btnAlarm;
+	CPanelMaster* m_pPanelMaster;
 
 
 	// 生成的消息映射函数
@@ -94,4 +97,6 @@
 	afx_msg void OnTimer(UINT_PTR nIDEvent);
 	afx_msg BOOL OnEraseBkgnd(CDC* pDC);
 	afx_msg void OnBnClickedButtonAlarm();
+	afx_msg void OnGraphItemClicked(NMHDR* pNMHDR, LRESULT* pResult);
+	afx_msg LRESULT OnPanelResize(WPARAM wParam, LPARAM lParam);
 };
diff --git a/SourceCode/Bond/Servo/ServoGraph.cpp b/SourceCode/Bond/Servo/ServoGraph.cpp
index daf274a..63bfab7 100644
--- a/SourceCode/Bond/Servo/ServoGraph.cpp
+++ b/SourceCode/Bond/Servo/ServoGraph.cpp
@@ -693,4 +693,22 @@
 		graphics.DrawImage(&bitmap, item.x, item.y);
 		graphics.ResetTransform();
 	}
+}
+
+void CServoGraph::SetIndicateBoxData(int id, void* pData)
+{
+	INDICATEBOX* pib = GetIndicateBox(id);
+	if (pib != nullptr) {
+		pib->m_pData = pData;
+	}
+}
+
+void* CServoGraph::GetIndicateBoxData(int id)
+{
+	INDICATEBOX* pib = GetIndicateBox(id);
+	if (pib != nullptr) {
+		return pib->m_pData;
+	}
+
+	return nullptr;
 }
\ No newline at end of file
diff --git a/SourceCode/Bond/Servo/ServoGraph.h b/SourceCode/Bond/Servo/ServoGraph.h
index d409660..3dc0518 100644
--- a/SourceCode/Bond/Servo/ServoGraph.h
+++ b/SourceCode/Bond/Servo/ServoGraph.h
@@ -94,6 +94,7 @@
 			this->box2BackgroundColor = RGB(0, 255, 255);;
 			this->box2FrameColor = RGB(255, 255, 0);;
 			this->bBox2Visible = FALSE;
+			this->m_pData = nullptr;
 		};
 		~INDICATEBOX() {};
 
@@ -110,6 +111,7 @@
 		COLORREF box2FrameColor;
 		BOOL bBox2Visible;
 		std::vector<void*> m_contexts;
+		void* m_pData;
 	};
 
 	class INDICATEBKGND
@@ -154,6 +156,8 @@
 	BOOL RemoveIndicateBoxAllContext(int id);
 	const std::vector<void*>& GetIndicateBoxContexts(int id);
 	bool IsIndicateBoxContextsEmpty(int id);
+	void SetIndicateBoxData(int id, void* pData);
+	void* GetIndicateBoxData(int id);
 	void ShowIndicateBoxInterior(int id, COLORREF color);
 	void HideIndicateBoxInterior(int id);
 	CServoGraph::INDICATEBOX* GetIndicateBox(int id);
diff --git a/SourceCode/Bond/Servo/VerticalLine.cpp b/SourceCode/Bond/Servo/VerticalLine.cpp
new file mode 100644
index 0000000..319e508
--- /dev/null
+++ b/SourceCode/Bond/Servo/VerticalLine.cpp
@@ -0,0 +1,316 @@
+// VerticalLine.cpp: implementation of the CVerticalLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#include "stdafx.h"
+#include "VerticalLine.h"
+
+#ifdef _DEBUG
+#undef THIS_FILE
+static char THIS_FILE[]=__FILE__;
+#define new DEBUG_NEW
+#endif
+
+//////////////////////////////////////////////////////////////////////
+// Construction/Destruction
+//////////////////////////////////////////////////////////////////////
+
+CVerticalLine::CVerticalLine()
+{
+	m_hWnd = NULL;
+	m_crBkgnd = RGB(255, 255, 255);
+	m_crLineColor = RGB(222, 222, 222);
+	m_bEnableResize = FALSE;
+}
+
+CVerticalLine::~CVerticalLine()
+{
+}
+
+BOOL CVerticalLine::RegisterWndClass()
+{
+	WNDCLASS wc;
+	wc.lpszClassName = BYVERTICALLINE_CLASS;
+	wc.hInstance = AfxGetInstanceHandle();
+	wc.lpfnWndProc = WindowProc;
+	wc.hCursor = ::LoadCursor(NULL, IDC_ARROW);
+	wc.hIcon = 0;
+	wc.lpszMenuName = NULL;
+	wc.hbrBackground = NULL;
+	wc.style = CS_GLOBALCLASS|CS_DBLCLKS;
+	wc.cbClsExtra = 0;
+	wc.cbWndExtra = 0;
+	
+	// 注册自定义类
+	return (::RegisterClass(&wc) != 0);
+}
+
+CVerticalLine* CVerticalLine::Hook(HWND hWnd)
+{
+	CVerticalLine* pVerticalLine = (CVerticalLine*)GetProp(hWnd, BYSTAG_VERTICALLINE);
+	if(pVerticalLine == NULL)
+	{
+		pVerticalLine = new CVerticalLine;
+		pVerticalLine->m_hWnd = hWnd;
+
+		SetProp(hWnd, BYSTAG_VERTICALLINE, (HANDLE)pVerticalLine);
+	}
+	
+	return pVerticalLine;
+}
+
+void CVerticalLine::Release()
+{
+	// delete
+	delete this;
+}
+
+void CVerticalLine::Notify(int nCode, int dwData, int dwData1/* = 0*/, int dwData2/* = 0*/)
+{
+	HWND hParent;
+	hParent = GetParent(m_hWnd);
+	if(hParent != NULL) {
+		BYVERTICALLINE_NMHDR iii_nmhdr;
+		iii_nmhdr.nmhdr.hwndFrom = m_hWnd;
+		iii_nmhdr.nmhdr.idFrom = GetWindowLong(m_hWnd, GWL_ID);
+		iii_nmhdr.nmhdr.code = nCode;
+		iii_nmhdr.dwData = dwData;
+		iii_nmhdr.dwData1 = dwData1;
+		iii_nmhdr.dwData2 = dwData2;
+		SendMessage(hParent, WM_NOTIFY, (WPARAM)iii_nmhdr.nmhdr.idFrom, (LPARAM)&iii_nmhdr);
+	}
+}
+
+////////////////////////////////
+// 拦截窗口消息函数
+LRESULT CALLBACK CVerticalLine::WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)   
+{
+	CVerticalLine* pVerticalLine = (CVerticalLine *)GetProp(hWnd, BYSTAG_VERTICALLINE);
+	if(pVerticalLine == NULL && uMsg != WM_NCCREATE)
+	{
+		return ::DefWindowProc(hWnd, uMsg, wParam, lParam);   
+	}
+
+	
+	// 如果Hook则响应消息
+	ASSERT(hWnd);
+	switch(uMsg)   
+	{
+	case WM_NCCREATE:
+		return OnNcCreate(hWnd, wParam, lParam);
+
+	case WM_DESTROY:
+		return pVerticalLine->OnDestroy(wParam, lParam);
+
+	case WM_PAINT:
+		return pVerticalLine->OnPaint(wParam, lParam);
+
+	case WM_TIMER:
+		return pVerticalLine->OnTimer(wParam, lParam);
+
+	case WM_SETCURSOR:
+		return pVerticalLine->OnSetCursor(wParam, lParam);
+
+	case WM_LBUTTONDOWN:
+		return pVerticalLine->OnLButtonDown(wParam, lParam);
+
+	case WM_GETDLGCODE:
+		return DLGC_WANTALLKEYS;
+
+	default:
+		break;
+	}
+	
+	return ::DefWindowProc(hWnd, uMsg, wParam, lParam);   
+}  
+
+///////////////////////////////
+// WM_NCCREATE
+// 窗口创建前的初始化工作
+LRESULT CVerticalLine::OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+	CVerticalLine* pVerticalLine = (CVerticalLine *)GetProp(hWnd, BYSTAG_VERTICALLINE);
+	ASSERT(pVerticalLine == NULL);
+
+	Hook(hWnd);
+	return ::DefWindowProc(hWnd, WM_NCCREATE, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_DESTROY
+LRESULT CVerticalLine::OnDestroy(WPARAM wParam, LPARAM lParam)
+{
+	Release();
+	return ::DefWindowProc(m_hWnd, WM_DESTROY, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_TIMER
+LRESULT CVerticalLine::OnTimer(WPARAM wParam, LPARAM lParam)
+{
+	return ::DefWindowProc(m_hWnd, WM_TIMER, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_SETCURSOR
+LRESULT CVerticalLine::OnSetCursor(WPARAM wParam, LPARAM lParam)
+{
+	if(m_bEnableResize) {
+		SetCursor(::LoadCursor(NULL, IDC_SIZEWE));
+		return TRUE;
+	}
+
+	return ::DefWindowProc(m_hWnd, WM_SETCURSOR, wParam, lParam);
+}
+
+/*
+ * WM_LBUTTONDOWN
+ * 鼠标左键按下
+ */
+LRESULT CVerticalLine::OnLButtonDown(WPARAM wParam, LPARAM lParam)
+{
+	if (!m_bEnableResize) {
+		return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
+	}
+
+
+	POINT pt, ptNew;
+	pt.x = LOWORD(lParam);
+	pt.y = HIWORD(lParam);
+	int nMoveX = 0;
+
+
+	// 捕捉鼠标消息,检测是否拖动
+	RECT rcParent, rcWindows;
+	GetClientRect(m_hWnd, &rcWindows);
+	::ClientToScreen(m_hWnd, (LPPOINT)&rcWindows);
+	::ClientToScreen(m_hWnd, (LPPOINT)&rcWindows.right);
+	GetClientRect(GetParent(m_hWnd), &rcParent);
+	::ClientToScreen(GetParent(m_hWnd), (LPPOINT)&rcParent);
+	HDC hDC = GetDC(GetDesktopWindow());
+	::DrawFocusRect(hDC, &rcWindows);
+
+	if (::GetCapture() == NULL) {
+		SetCapture(m_hWnd);
+		ASSERT(m_hWnd == GetCapture());
+		AfxLockTempMaps();
+		for (;;) {
+			MSG msg;
+			VERIFY(::GetMessage(&msg, NULL, 0, 0));
+
+			if (GetCapture() != m_hWnd) break;
+
+			switch (msg.message)
+			{
+			case WM_MOUSEMOVE:
+				ptNew = msg.pt;
+				if (ptNew.x < rcParent.left) ptNew.x = rcParent.left;
+				::DrawFocusRect(hDC, &rcWindows);
+				rcWindows.left = ptNew.x - 3;
+				rcWindows.right = ptNew.x + 3;
+				::DrawFocusRect(hDC, &rcWindows);
+				::ScreenToClient(m_hWnd, &ptNew);
+				break;
+
+			case WM_LBUTTONUP:
+				ptNew = msg.pt;
+				::ScreenToClient(m_hWnd, &ptNew);
+				nMoveX = ptNew.x - pt.x;
+				goto ExitLoop;
+
+			case WM_KEYDOWN:
+				if (msg.wParam == VK_ESCAPE) {
+					goto ExitLoop;
+				}
+				break;
+
+			default:
+				DispatchMessage(&msg);
+				break;
+			}
+		}
+
+	ExitLoop:
+		::DrawFocusRect(hDC, &rcWindows);
+		ReleaseDC(GetDesktopWindow(), hDC);
+		ReleaseCapture();
+		::InvalidateRect(m_hWnd, NULL, TRUE);
+		Notify((int)BYVERTICALLINE_MOVEX, nMoveX);
+		AfxUnlockTempMaps(FALSE);
+	}
+
+
+	return ::DefWindowProc(m_hWnd, WM_LBUTTONDOWN, wParam, lParam);
+}
+
+///////////////////////////////
+// WM_PAINT
+LRESULT CVerticalLine::OnPaint(WPARAM wParam, LPARAM lParam)
+{
+	HDC hDC, hMemDC;
+	HBITMAP hBitmap;
+	RECT rcClient;
+	CString strText;
+	HFONT hFont;
+	HBRUSH hBrushBK;
+
+
+	// BeginPaint
+	PAINTSTRUCT ps;
+	hDC = BeginPaint(m_hWnd, &ps);
+	GetClientRect(m_hWnd, &rcClient);
+
+	hMemDC = ::CreateCompatibleDC(hDC);
+	hBitmap = ::CreateCompatibleBitmap(hDC, rcClient.right-rcClient.left,
+		rcClient.bottom-rcClient.top);
+	::SelectObject(hMemDC, hBitmap);
+
+	hFont = (HFONT)GetStockObject(DEFAULT_GUI_FONT);
+	::SelectObject(hMemDC, hFont);
+
+	
+	// 背景颜色
+	hBrushBK = CreateSolidBrush( m_crBkgnd );
+	::FillRect(hMemDC, &rcClient, hBrushBK);
+	DeleteObject(hBrushBK);
+
+
+	// 画线
+	HPEN hPen = CreatePen(PS_SOLID, 1, m_crLineColor);
+	HPEN hOldPen = (HPEN)::SelectObject(hMemDC, hPen);
+	::MoveToEx(hMemDC, rcClient.right-1, 0, NULL);
+	LineTo(hMemDC, rcClient.right - 1, rcClient.bottom);
+	::SelectObject(hMemDC, hOldPen);
+
+
+	// EndPaint
+	::BitBlt(hDC, 0, 0, rcClient.right-rcClient.left, rcClient.bottom-rcClient.top,
+		hMemDC, 0, 0, SRCCOPY);
+	EndPaint(m_hWnd, &ps);
+	::DeleteObject(hBitmap);
+	::DeleteDC(hMemDC);
+
+	
+	return 1;
+}
+
+void CVerticalLine::SetBkgndColor(COLORREF cr)
+{
+	m_crBkgnd = cr;
+}
+
+void CVerticalLine::SetLineColor(COLORREF cr)
+{
+	m_crLineColor = cr;
+}
+
+void CVerticalLine::EnableResize()
+{
+	m_bEnableResize = TRUE;
+}
+
+void CVerticalLine::DisableResize()
+{
+	m_bEnableResize = FALSE;
+}
+
diff --git a/SourceCode/Bond/Servo/VerticalLine.h b/SourceCode/Bond/Servo/VerticalLine.h
new file mode 100644
index 0000000..d27f7b6
--- /dev/null
+++ b/SourceCode/Bond/Servo/VerticalLine.h
@@ -0,0 +1,95 @@
+// VerticalLine.h: interface for the CVerticalLine class.
+//
+//////////////////////////////////////////////////////////////////////
+
+#if !defined(AFX_VERTICALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
+#define AFX_VERTICALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_
+
+
+#pragma comment(lib, "Msimg32.lib")			// TransparentBlt
+
+
+
+//====== VerticalLine =====================================================
+
+#ifndef NOVERTICALLINE
+
+#ifdef _WIN32
+
+#define BYVERTICALLINE_CLASSA       "BYVerticalLine"
+#define BYVERTICALLINE_CLASSW       L"BYVerticalLine"
+
+#ifdef UNICODE
+#define  BYVERTICALLINE_CLASS       BYVERTICALLINE_CLASSW
+#else
+#define  BYVERTICALLINE_CLASS       BYVERTICALLINE_CLASSA
+#endif
+
+#else
+#define BYVERTICALLINE_CLASS        "BYVerticalLine"
+#endif
+
+
+#define BYSTAG_VERTICALLINE		 _T("ISVERTICALLINE")
+
+
+//====== WM_NOTIFY codes (NMHDR.code values) ==================================
+#define BYVERTICALLINE_FIRST			 (0U-2330U)       //
+#define BYVERTICALLINE_LAST				 (0U-2320U)
+#define BYVERTICALLINE_MOVEX 			 (BYVERTICALLINE_FIRST - 1)
+
+
+typedef struct tagBYVERTICALLINE_NMHDR
+{
+	NMHDR		nmhdr;
+	DWORD		dwData;
+	DWORD		dwData1;
+	DWORD		dwData2;
+} BYVERTICALLINE_NMHDR;
+
+
+
+#endif
+
+
+
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+
+class CVerticalLine
+{
+public:
+	CVerticalLine();
+	virtual ~CVerticalLine();
+	
+
+public:
+	void EnableResize();
+	void DisableResize();
+	static BOOL RegisterWndClass();
+	static CVerticalLine* Hook(HWND hWnd);
+	void Notify(int nCode, int dwData, int dwData1 = 0, int dwData2 = 0);
+	void Release();
+	void SetBkgndColor(COLORREF cr);
+	void SetLineColor(COLORREF cr);
+	static LRESULT CALLBACK WindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
+	static LRESULT OnNcCreate(HWND hWnd, WPARAM wParam, LPARAM lParam);
+	LRESULT OnDestroy(WPARAM wParam, LPARAM lParam);
+	LRESULT OnTimer(WPARAM wParam, LPARAM lParam);
+	LRESULT OnSetCursor(WPARAM wParam, LPARAM lParam);
+	LRESULT OnPaint(WPARAM wParam, LPARAM lParam);
+	LRESULT OnLButtonDown(WPARAM wParam, LPARAM lParam);
+
+private:
+	HWND		m_hWnd;
+	COLORREF m_crBkgnd;
+	COLORREF m_crLineColor;
+
+private:
+	BOOL m_bEnableResize;
+};
+
+#endif // !defined(AFX_VERTICALLINE_H__FBB8916A_DE77_4EA3_9C21_E51E6B06194C__INCLUDED_)
diff --git a/SourceCode/Bond/Servo/resource.h b/SourceCode/Bond/Servo/resource.h
index 2afa563..c96ed4e 100644
--- a/SourceCode/Bond/Servo/resource.h
+++ b/SourceCode/Bond/Servo/resource.h
Binary files differ

--
Gitblit v1.9.3