|
// SGMeasurementDlg.cpp: 实现文件
|
//
|
|
#include "pch.h"
|
#include "framework.h"
|
#include "SGMeasurement.h"
|
#include "SGMeasurementDlg.h"
|
#include "afxdialogex.h"
|
#include "SGIF.h"
|
#include "Logger.h"
|
|
#ifdef _DEBUG
|
#define new DEBUG_NEW
|
#endif
|
|
#define DeviceID 0
|
|
// 托盘图标 ID 与消息宏
|
#define ID_TRAY_RESTORE 2001 // 恢复窗口
|
#define ID_TRAY_EXIT 2002 // 退出程序
|
#define WM_TRAY_ICON_NOTIFY (WM_USER + 1000) // 托盘图标回调消息 ID
|
|
// 托盘提示文本宏
|
#define TRAY_ICON_TOOLTIP_TEXT _T("SGMeasurement")
|
|
// 日志颜色宏定义
|
#define LOG_COLOR_NORMAL RGB(0, 0, 0) // 普通:黑色
|
#define LOG_COLOR_SUCCESS RGB(0, 128, 0) // 成功:绿色
|
#define LOG_COLOR_ERROR RGB(255, 0, 0) // 错误:红色
|
#define LOG_COLOR_WARNING RGB(255, 165, 0) // 警告:橙色
|
#define LOG_COLOR_TIME RGB(0, 0, 255) // 时间戳:蓝色
|
|
// 定时器相关宏定义
|
#define TIMER_INTERVAL_MS 500
|
#define TIMER_ID_OUTPUT_UPDATE 1
|
|
// 计时宏定义
|
#define MEASURE_FUNC_START() \
|
clock_t __startClock = clock();
|
|
#define MEASURE_FUNC_END() \
|
do { \
|
clock_t __endClock = clock(); \
|
double __elapsedMs = 1000.0 * (__endClock - __startClock) / CLOCKS_PER_SEC; \
|
CString __strElapsed; \
|
__strElapsed.Format(_T("%s 执行耗时:%.1f ms"), _T(__FUNCTION__), __elapsedMs); \
|
AppendLogLineRichStyled(__strElapsed, LOG_COLOR_SUCCESS); \
|
} while (0)
|
|
class CAboutDlg : public CDialogEx
|
{
|
public:
|
CAboutDlg();
|
|
// 对话框数据
|
#ifdef AFX_DESIGN_TIME
|
enum { IDD = IDD_ABOUTBOX };
|
#endif
|
|
protected:
|
virtual void DoDataExchange(CDataExchange* pDX); // DDX/DDV 支持
|
|
// 实现
|
protected:
|
DECLARE_MESSAGE_MAP()
|
};
|
|
CAboutDlg::CAboutDlg() : CDialogEx(IDD_ABOUTBOX)
|
{
|
}
|
|
void CAboutDlg::DoDataExchange(CDataExchange* pDX)
|
{
|
CDialogEx::DoDataExchange(pDX);
|
}
|
|
BEGIN_MESSAGE_MAP(CAboutDlg, CDialogEx)
|
END_MESSAGE_MAP()
|
|
CSGMeasurementDlg::CSGMeasurementDlg(CWnd* pParent /*=nullptr*/)
|
: CDialogEx(IDD_SGMEASUREMENT_DIALOG, pParent)
|
, m_bConnected(false)
|
, m_bSaving(false)
|
, m_dOutValues{ 0.0, 0.0, 0.0, 0.0 }
|
, m_nUseTrigger(0)
|
, m_nSavePointCount(100000)
|
, m_fJumpThreshold(0.2f)
|
, m_nJumpWindow(3)
|
, m_nValleyMargin(0)
|
, m_nMinGlass1Count(10)
|
, m_nFixedCount(5)
|
, m_fMaxDelta(0.05f)
|
, m_nTrayIconID(0)
|
, m_bTrayIconCreated(FALSE)
|
, m_bExitingFromTray(FALSE)
|
{
|
m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);
|
}
|
|
void CSGMeasurementDlg::DoDataExchange(CDataExchange* pDX)
|
{
|
CDialogEx::DoDataExchange(pDX);
|
DDX_Text(pDX, IDC_EDIT_OUT1, m_dOutValues[0]);
|
DDX_Text(pDX, IDC_EDIT_OUT2, m_dOutValues[1]);
|
DDX_Text(pDX, IDC_EDIT_OUT3, m_dOutValues[2]);
|
DDX_Text(pDX, IDC_EDIT_OUT4, m_dOutValues[3]);
|
DDX_Control(pDX, IDC_RICHEDIT_LOG, m_editLog);
|
DDX_Check(pDX, IDC_CHECK_USE_TRIGGER, m_nUseTrigger);
|
DDX_Control(pDX, IDC_COMBO_OUTPUT_PORT, m_comboOutputPort);
|
DDX_Text(pDX, IDC_EDIT_STORE_COUNT, m_nSavePointCount);
|
DDX_Text(pDX, IDC_EDIT_JUMP_WINDOW, m_nJumpWindow);
|
DDX_Text(pDX, IDC_EDIT_VALLEY_MARGIN, m_nValleyMargin);
|
DDX_Text(pDX, IDC_EDIT_MIN_GLASS_COUNT, m_nMinGlass1Count);
|
DDX_Text(pDX, IDC_EDIT_FIXED_COUNT, m_nFixedCount);
|
DDX_Text(pDX, IDC_EDIT_JUMP_THRESHOLD, m_fJumpThreshold);
|
DDX_Text(pDX, IDC_EDIT_MAX_DELTA, m_fMaxDelta);
|
}
|
|
void CSGMeasurementDlg::ExitApplication()
|
{
|
// 断开设备连接(如已连接)
|
if (m_bConnected) {
|
DisconnectFromDevice();
|
}
|
|
// 删除托盘图标(如已添加)
|
if (m_bTrayIconCreated) {
|
Shell_NotifyIcon(NIM_DELETE, &m_trayIconData);
|
m_bTrayIconCreated = FALSE;
|
}
|
|
m_plcListener.Stop();
|
|
DestroyWindow();
|
CDialogEx::OnClose();
|
}
|
|
void CSGMeasurementDlg::UpdateControlStatus(BOOL bConnected, BOOL bStoring/* = FALSE*/)
|
{
|
// 连接按钮和断开按钮状态互斥
|
GetDlgItem(IDC_BUTTON_CONNECT)->EnableWindow(!bConnected);
|
GetDlgItem(IDC_BUTTON_DISCONNECT)->EnableWindow(bConnected);
|
|
// IP地址输入只允许在未连接时修改
|
GetDlgItem(IDC_IPADDRESS)->EnableWindow(!bConnected);
|
|
// 存储配置控件仅在已连接且未开始存储时允许操作
|
BOOL bCanConfigure = bConnected && !bStoring;
|
/*GetDlgItem(IDC_CHECK_USE_TRIGGER)->EnableWindow(bCanConfigure);*/
|
GetDlgItem(IDC_COMBO_OUTPUT_PORT)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_STORE_COUNT)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_JUMP_THRESHOLD)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_JUMP_WINDOW)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_VALLEY_MARGIN)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_MIN_GLASS_COUNT)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_FIXED_COUNT)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_EDIT_MAX_DELTA)->EnableWindow(bCanConfigure);
|
|
// 存储相关按钮
|
GetDlgItem(IDC_BUTTON_RECEIVE_FROM_CTRL)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_BUTTON_SEND_TO_CTRL)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_BUTTON_CLEAR_STORE)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_BUTTON_START_STORE)->EnableWindow(bCanConfigure);
|
GetDlgItem(IDC_BUTTON_STOP_STORE)->EnableWindow(bStoring);
|
}
|
|
void CSGMeasurementDlg::AppendLogLineBatchBegin()
|
{
|
m_editLog.SetRedraw(FALSE);
|
m_editLog.SetEventMask(0); // 防止触发不必要的通知
|
}
|
|
void CSGMeasurementDlg::AppendLogLineBatchEnd()
|
{
|
m_editLog.SetRedraw(TRUE);
|
m_editLog.Invalidate(); // 强制重绘
|
m_editLog.SetEventMask(ENM_CHANGE | ENM_SELCHANGE);
|
}
|
|
void CSGMeasurementDlg::TrimRichEditLineLimit(int nMaxLines)
|
{
|
int nLineCount = m_editLog.GetLineCount();
|
if (nLineCount < nMaxLines) {
|
return;
|
}
|
|
// 获取多余行的字符数范围
|
int charIndex = m_editLog.LineIndex(nMaxLines);
|
m_editLog.SetSel(0, charIndex); // 选中多余内容
|
m_editLog.ReplaceSel(_T("")); // 删除
|
}
|
|
void CSGMeasurementDlg::AppendLogLineRichStyled(const CString& strContent, COLORREF color /*= RGB(0, 0, 0)*/)
|
{
|
if (!::IsWindow(GetSafeHwnd()) || !::IsWindow(m_editLog.GetSafeHwnd())) {
|
return;
|
}
|
|
// 时间戳
|
CString strTimestamp;
|
CTime now = CTime::GetCurrentTime();
|
strTimestamp.Format(_T("[%02d:%02d:%02d] "), now.GetHour(), now.GetMinute(), now.GetSecond());
|
|
// 插入点移到最后(也可以设为 0 表示顶部)
|
m_editLog.SetSel(-1, -1);
|
|
// 插入时间(蓝色)
|
CHARFORMAT2 cfTime = {};
|
cfTime.cbSize = sizeof(cfTime);
|
cfTime.dwMask = CFM_COLOR;
|
cfTime.crTextColor = LOG_COLOR_TIME;
|
m_editLog.SetSelectionCharFormat(cfTime);
|
m_editLog.ReplaceSel(strTimestamp);
|
|
// 生成日志级别标签
|
CString strLevel;
|
if (color == LOG_COLOR_WARNING) {
|
strLevel = _T("[警告]");
|
}
|
else if (color == LOG_COLOR_ERROR) {
|
strLevel = _T("[错误]");
|
}
|
else if (color == LOG_COLOR_NORMAL) {
|
strLevel = _T("[信息]");
|
}
|
else if (color == LOG_COLOR_SUCCESS) {
|
strLevel = _T("[成功]");
|
}
|
else {
|
strLevel = _T("[未知]");
|
}
|
|
// 插入日志正文(传入颜色)
|
CHARFORMAT2 cfMsg = {};
|
cfMsg.cbSize = sizeof(cfMsg);
|
cfMsg.dwMask = CFM_COLOR;
|
cfMsg.crTextColor = color;
|
m_editLog.SetSelectionCharFormat(cfMsg);
|
m_editLog.ReplaceSel(strLevel + strContent + _T("\r\n"));
|
|
// 限制最大行数
|
TrimRichEditLineLimit(100);
|
|
// 拼接完整日志行
|
CString strFullLogLine;
|
strFullLogLine.Format(_T("%s %s"), strLevel, strContent);
|
|
// 写入日志文件
|
// LOG_LINE(strFullLogLine);
|
}
|
|
void CSGMeasurementDlg::HighlightAllMatches(const CString& strSearch, COLORREF clrHighlight/* = RGB(255, 165, 0)*/)
|
{
|
if (strSearch.IsEmpty()) {
|
return;
|
}
|
|
long nStart = 0;
|
long nEnd = m_editLog.GetTextLength();
|
FINDTEXTEX ft = { 0 };
|
ft.chrg.cpMin = 0;
|
ft.chrg.cpMax = nEnd;
|
ft.lpstrText = strSearch.GetString();
|
|
// 高亮前不清除全文颜色,避免历史多色混淆
|
while (m_editLog.FindText(FR_DOWN, &ft) != -1) {
|
m_editLog.SetSel(ft.chrgText.cpMin, ft.chrgText.cpMax);
|
|
CHARFORMAT2 cf = {};
|
cf.cbSize = sizeof(cf);
|
cf.dwMask = CFM_COLOR;
|
cf.crTextColor = clrHighlight;
|
m_editLog.SetSelectionCharFormat(cf);
|
|
// 下次搜索从后面开始
|
ft.chrg.cpMin = ft.chrgText.cpMax;
|
}
|
m_editLog.SetSel(-1, 0);
|
}
|
|
void CSGMeasurementDlg::PrintSampleData(int nOutNo, const std::vector<float>& vecBuffer)
|
{
|
int nReceived = static_cast<int>(vecBuffer.size());
|
if (vecBuffer.empty() || nReceived < 0) {
|
CString strError;
|
strError.Format(_T("OUT%d: 接收数据为空或无效,无法打印。"), nOutNo);
|
AppendLogLineRichStyled(strError, LOG_COLOR_WARNING);
|
return;
|
}
|
|
CString strLine;
|
for (int i = 0; i < nReceived; ++i) {
|
CString strOne;
|
strOne.Format(_T("%10.3f "), vecBuffer[i]);
|
strLine += strOne;
|
|
// 每 7 个值输出一行
|
if ((i + 1) % 7 == 0 || i == nReceived - 1) {
|
AppendLogLineRichStyled(strLine, LOG_COLOR_NORMAL);
|
strLine.Empty();
|
}
|
}
|
}
|
|
bool CSGMeasurementDlg::ConnectToDevice()
|
{
|
if (m_bConnected) {
|
AppendLogLineRichStyled(_T("设备已连接,请先断开连接。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
SGIF_OPENPARAM_ETHERNET stIpAddress = { 0 };
|
|
CIPAddressCtrl* pIPCtrl = (CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS);
|
if (!::IsWindow(pIPCtrl->GetSafeHwnd())) {
|
AppendLogLineRichStyled(_T("IP 控件无效,连接失败!"), LOG_COLOR_ERROR);
|
return false;
|
}
|
|
pIPCtrl->GetAddress(
|
stIpAddress.IPAddress.S_un.S_un_b.s_b1,
|
stIpAddress.IPAddress.S_un.S_un_b.s_b2,
|
stIpAddress.IPAddress.S_un.S_un_b.s_b3,
|
stIpAddress.IPAddress.S_un.S_un_b.s_b4
|
);
|
|
RC nRet = SGIF_OpenDeviceETHER(DeviceID, &stIpAddress);
|
if (nRet == RC_OK) {
|
AppendLogLineRichStyled(_T("打开连接成功!"), LOG_COLOR_SUCCESS);
|
|
m_bConnected = TRUE;
|
UpdateControlStatus(TRUE, FALSE);
|
SetTimer(TIMER_ID_OUTPUT_UPDATE, TIMER_INTERVAL_MS, nullptr);
|
return true;
|
}
|
else {
|
CString strError;
|
strError.Format(_T("打开连接失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
|
m_bConnected = FALSE;
|
UpdateControlStatus(FALSE, FALSE);
|
return false;
|
}
|
}
|
|
bool CSGMeasurementDlg::DisconnectFromDevice()
|
{
|
if (!m_bConnected) {
|
AppendLogLineRichStyled(_T("设备未连接,无需断开。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
// 停止定时器,避免在断开后仍尝试更新输出数据
|
KillTimer(TIMER_ID_OUTPUT_UPDATE);
|
|
// 先停止数据存储(如果正在进行)
|
RC nRet;
|
if (m_bConnected && m_bSaving) {
|
nRet = SGIF_DataStorageStop(DeviceID);
|
if (nRet == RC_OK) {
|
m_bSaving = FALSE;
|
AppendLogLineRichStyled(_T("数据存储已停止。"), LOG_COLOR_SUCCESS);
|
}
|
else {
|
CString strError;
|
strError.Format(_T("停止数据存储失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
}
|
}
|
|
// 断开设备连接
|
nRet = SGIF_CloseDevice(DeviceID);
|
if (nRet == RC_OK) {
|
AppendLogLineRichStyled(_T("断开连接成功!"), LOG_COLOR_SUCCESS);
|
|
m_bConnected = FALSE;
|
UpdateControlStatus(FALSE, FALSE);
|
return true;
|
}
|
else {
|
CString strError;
|
strError.Format(_T("断开连接失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
AppendLogLineRichStyled(_T("保持当前状态,断开失败。"), LOG_COLOR_ERROR);
|
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
return false;
|
}
|
}
|
|
void CSGMeasurementDlg::CleanInvalidValuesInPlace(int nOutNo, std::vector<float>& vecData, float fInvalid/* = -999.0f*/)
|
{
|
MEASURE_FUNC_START();
|
|
// 找到第一个有效值
|
auto itStart = std::find_if(vecData.begin(), vecData.end(), [=](float v) { return v > fInvalid; });
|
|
// 找到最后一个有效值
|
auto itEnd = std::find_if(vecData.rbegin(), vecData.rend(), [=](float v) { return v > fInvalid; }).base();
|
|
// 检查是否有有效数据
|
if (itStart < itEnd) {
|
// 先裁边
|
std::vector<float> vecTrimmed(itStart, itEnd);
|
|
// 再移除中间的无效值
|
auto itNewEnd = std::remove_if(vecTrimmed.begin(), vecTrimmed.end(), [=](float v) { return v == fInvalid; });
|
vecTrimmed.erase(itNewEnd, vecTrimmed.end());
|
|
vecData = std::move(vecTrimmed);
|
|
CString strLog;
|
strLog.Format(_T("OUT%d: 已裁剪无效边界数据,剩余有效数据 %d 个。"), nOutNo, static_cast<int>(vecData.size()));
|
AppendLogLineRichStyled(strLog, LOG_COLOR_NORMAL);
|
|
if (vecData.size() < 20) {
|
AppendLogLineRichStyled(_T("警告:有效数据量过少,可能影响后续处理。"), LOG_COLOR_WARNING);
|
}
|
else {
|
CString strInfo;
|
strInfo.Format(_T("OUT%d: 有效数据范围 [%f, %f]"), nOutNo, *std::min_element(vecData.begin(), vecData.end()), *std::max_element(vecData.begin(), vecData.end()));
|
AppendLogLineRichStyled(strInfo, LOG_COLOR_NORMAL);
|
}
|
|
PrintSampleData(nOutNo, vecData);
|
}
|
else {
|
vecData.clear();
|
}
|
|
MEASURE_FUNC_END();
|
}
|
|
bool CSGMeasurementDlg::SplitGlassSegments(int nOutNo, const std::vector<float>& vecData, std::vector<float>& vecGlass1, std::vector<float>& vecGlass2, float fJumpThreshold /*= 0.2f*/, int nWindow /*= 3*/, int nValleyMargin /*= 0*/, int nMinGlassCount /*= 10*/)
|
{
|
MEASURE_FUNC_START();
|
|
CString strLog;
|
const int nTotal = static_cast<int>(vecData.size());
|
|
if (nTotal < 2 * nWindow + 1 + nMinGlassCount) {
|
strLog.Format(_T("OUT%d: 数据量不足,至少需要 %d 个点才能切割。"), nOutNo, 2 * nWindow + 1 + nMinGlassCount);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
|
return false;
|
}
|
|
// 从中间向两边查找 valley 候选点
|
int nMid = nTotal / 2;
|
int nValleyLeft = -1, nValleyRight = -1;
|
|
// 向左查找 valley
|
for (int i = nMid; i >= nWindow; --i) {
|
float fDelta = std::fabs(vecData[i] - vecData[i - nWindow]);
|
if (fDelta > fJumpThreshold &&
|
vecData[i] < vecData[i - 1] &&
|
vecData[i] < vecData[i + 1]) {
|
nValleyLeft = i;
|
break;
|
}
|
}
|
|
// 向右查找 valley
|
for (int i = nMid; i <= nTotal - nWindow - 2; ++i) {
|
float fDelta = std::fabs(vecData[i + nWindow] - vecData[i]);
|
if (fDelta > fJumpThreshold &&
|
vecData[i] < vecData[i - 1] &&
|
vecData[i] < vecData[i + 1]) {
|
nValleyRight = i;
|
break;
|
}
|
}
|
|
// 选定 valley 点
|
int nValleyIdx = -1;
|
if (nValleyLeft > 0 && nValleyRight > 0) {
|
nValleyIdx = (vecData[nValleyLeft] < vecData[nValleyRight]) ? nValleyLeft : nValleyRight;
|
}
|
else if (nValleyLeft > 0) {
|
nValleyIdx = nValleyLeft;
|
}
|
else if (nValleyRight > 0) {
|
nValleyIdx = nValleyRight;
|
}
|
|
// fallback: valley 未找到,使用中间切割法
|
if (nValleyIdx < 0) {
|
AppendLogLineRichStyled(_T("未找到 valley 跳变点,使用中间位置切割。"), LOG_COLOR_WARNING);
|
nValleyIdx = nMid;
|
}
|
|
// 应用切割位置,限制边界
|
int nCutPos = max(1, min(nTotal - 1, nValleyIdx + nValleyMargin));
|
|
vecGlass1.assign(vecData.begin(), vecData.begin() + nCutPos);
|
vecGlass2.assign(vecData.begin() + nCutPos, vecData.end());
|
|
int nGlass1Count = static_cast<int>(vecGlass1.size());
|
int nGlass2Count = static_cast<int>(vecGlass2.size());
|
|
// 日志输出
|
strLog.Format(_T("OUT%d: 切割成功,第一片玻璃 %d 点,第二片玻璃 %d 点。"), nOutNo, nGlass1Count, nGlass2Count);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
|
|
if (nGlass1Count < nMinGlassCount) {
|
strLog.Format(_T("OUT%d: 第一片玻璃数据量少于 %d 点,可能影响计算。"), nOutNo, nMinGlassCount);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
|
}
|
|
if (nGlass2Count < nMinGlassCount) {
|
strLog.Format(_T("OUT%d: 第二片玻璃数据量少于 %d 点,可能影响计算。"), nOutNo, nMinGlassCount);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_WARNING);
|
}
|
|
AppendLogLineRichStyled(_T("第一片玻璃数据:"), LOG_COLOR_NORMAL);
|
PrintSampleData(nOutNo, vecGlass1);
|
|
AppendLogLineRichStyled(_T("第二片玻璃数据:"), LOG_COLOR_NORMAL);
|
PrintSampleData(nOutNo, vecGlass2);
|
|
MEASURE_FUNC_END();
|
|
return true;
|
}
|
|
bool CSGMeasurementDlg::FilterDominantGroup(int nOutNo, const std::vector<float>& vecInput, std::vector<float>& vecOutput)
|
{
|
MEASURE_FUNC_START();
|
|
if (vecInput.empty()) {
|
AppendLogLineRichStyled(_T("输入数据为空,无法进行分组筛选。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
// 分组:map<int整数部分, vector<float>>
|
std::map<int, std::vector<float>> mapGroup;
|
for (float fVal : vecInput) {
|
int nKey = static_cast<int>(fVal);
|
mapGroup[nKey].push_back(fVal);
|
}
|
|
// 找出数量最多的那组
|
size_t nMaxCount = 0;
|
auto itMaxGroup = mapGroup.begin();
|
for (auto it = mapGroup.begin(); it != mapGroup.end(); ++it) {
|
if (it->second.size() > nMaxCount) {
|
nMaxCount = it->second.size();
|
itMaxGroup = it;
|
}
|
}
|
|
if (nMaxCount == 0) {
|
AppendLogLineRichStyled(_T("所有数据都被过滤或为空,无法筛选出主要分组。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
vecOutput = itMaxGroup->second;
|
|
CString strLog;
|
strLog.Format(_T("成功从 %d 个组中筛选出主要分组:值约为 %d,共 %zu 个点。"), static_cast<int>(mapGroup.size()), itMaxGroup->first, nMaxCount);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
|
|
// 打印筛选后的数据
|
AppendLogLineRichStyled(_T("筛选后的主要分组数据:"), LOG_COLOR_NORMAL);
|
PrintSampleData(nOutNo, vecOutput);
|
|
MEASURE_FUNC_END();
|
|
return true;
|
}
|
|
bool CSGMeasurementDlg::ExtractStableRegionFixed(int nOutNo, const std::vector<float>& vecIn, std::vector<float>& vecOut, int nFixedCount/* = 5*/, float fMaxDelta/* = 0.04f*/)
|
{
|
MEASURE_FUNC_START();
|
|
const int n = static_cast<int>(vecIn.size());
|
if (n < nFixedCount) {
|
CString strError;
|
strError.Format(_T("OUT%d: 数据量不足,至少需要 %d 个点才能提取稳定区。"), nOutNo, nFixedCount);
|
return false;
|
}
|
|
int nBestStart = -1;
|
float fBestRange = FLT_MAX;
|
|
for (int i = 0; i <= n - nFixedCount; ++i) {
|
auto first = vecIn.begin() + i;
|
auto last = first + nFixedCount;
|
|
float fMinVal = *std::min_element(first, last);
|
float fMaxVal = *std::max_element(first, last);
|
float fRange = fMaxVal - fMinVal;
|
|
if (fRange <= fMaxDelta && fRange < fBestRange) {
|
fBestRange = fRange;
|
nBestStart = i;
|
}
|
}
|
|
if (nBestStart < 0) {
|
return false;
|
}
|
|
vecOut.assign(vecIn.begin() + nBestStart, vecIn.begin() + nBestStart + nFixedCount);
|
|
CString strLog;
|
strLog.Format(_T("OUT%d: 提取稳定区成功,起始点索引 %d,范围 %.3f。"), nOutNo, nBestStart, fBestRange);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
|
|
if (vecOut.size() < nFixedCount) {
|
CString strWarning;
|
strWarning.Format(_T("OUT%d: 提取的稳定区数据量不足,可能影响后续处理。"), nOutNo);
|
AppendLogLineRichStyled(strWarning, LOG_COLOR_WARNING);
|
}
|
|
// 打印提取的稳定区数据
|
AppendLogLineRichStyled(_T("提取的稳定区数据:"), LOG_COLOR_NORMAL);
|
PrintSampleData(nOutNo, vecOut);
|
|
MEASURE_FUNC_END();
|
|
return true;
|
}
|
|
bool CSGMeasurementDlg::CalcGlassOffset(const std::vector<float>& vecGlass1, const std::vector<float>& vecGlass2, float& fAvg1, float& fAvg2, float& fOffset)
|
{
|
MEASURE_FUNC_START();
|
|
if (vecGlass1.empty() || vecGlass2.empty()) {
|
AppendLogLineRichStyled(_T("稳定区数据为空,无法计算平均值和偏移。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
auto CalcAverage = [](const std::vector<float>& data) -> float {
|
float sum = std::accumulate(data.begin(), data.end(), 0.0f);
|
return sum / static_cast<float>(data.size());
|
};
|
|
fAvg1 = CalcAverage(vecGlass1);
|
fAvg2 = CalcAverage(vecGlass2);
|
fOffset = std::fabs(fAvg2 - fAvg1); // 第二片 - 第一片
|
|
CString strLog;
|
strLog.Format(_T("第一片玻璃平均值: %.3f,第二片玻璃平均值: %.3f,偏移量: %.3f"), fAvg1, fAvg2, fOffset);
|
AppendLogLineRichStyled(strLog, LOG_COLOR_SUCCESS);
|
|
MEASURE_FUNC_END();
|
|
return true;
|
}
|
|
bool CSGMeasurementDlg::InitDataStorage()
|
{
|
if (!m_bConnected) {
|
AppendLogLineRichStyled(_T("设备未连接,请先连接设备。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
if (m_bSaving) {
|
AppendLogLineRichStyled(_T("数据存储正在进行中,请先停止存储。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
RC nRet = SGIF_DataStorageInit(DeviceID);
|
if (nRet == RC_OK) {
|
AppendLogLineRichStyled(_T("数据存储已清除。"), LOG_COLOR_SUCCESS);
|
return true;
|
}
|
|
CString strError;
|
strError.Format(_T("清除数据存储失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
return false;
|
}
|
|
bool CSGMeasurementDlg::StartDataStorage()
|
{
|
if (m_bSaving) {
|
AppendLogLineRichStyled(_T("数据存储已在进行中,请先停止存储。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
RC nRet = SGIF_DataStorageStart(DeviceID);
|
if (nRet == RC_OK) {
|
m_bSaving = TRUE;
|
AppendLogLineRichStyled(_T("数据存储已开始。"), LOG_COLOR_SUCCESS);
|
return true;
|
}
|
|
CString strError;
|
strError.Format(_T("开始数据存储失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
return false;
|
}
|
|
bool CSGMeasurementDlg::StopDataStorage()
|
{
|
if (!m_bSaving) {
|
AppendLogLineRichStyled(_T("数据存储未在进行中,请先开始存储。"), LOG_COLOR_WARNING);
|
return false;
|
}
|
|
RC nRet = SGIF_DataStorageStop(DeviceID);
|
if (nRet == RC_OK) {
|
m_bSaving = FALSE;
|
AppendLogLineRichStyled(_T("数据存储已停止。"), LOG_COLOR_SUCCESS);
|
return true;
|
}
|
|
CString strError;
|
strError.Format(_T("停止数据存储失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
return false;
|
}
|
|
float CSGMeasurementDlg::AnalyzeStoredData(int nOutNo)
|
{
|
MEASURE_FUNC_START();
|
|
UpdateData(TRUE);
|
|
if (m_nUseTrigger) {
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
AfxMessageBox(_T("当前是硬触发模式,请检查触发器状态。"), MB_ICONINFORMATION);
|
return -1.0f;
|
}
|
|
if (!m_bConnected) {
|
AppendLogLineRichStyled(_T("设备未连接,请先连接设备。"), LOG_COLOR_WARNING);
|
return -1.0f;
|
}
|
|
if (m_bSaving) {
|
AppendLogLineRichStyled(_T("数据存储正在进行中,请先停止存储。"), LOG_COLOR_WARNING);
|
return -1.0f;
|
}
|
|
if (nOutNo < 1 || nOutNo > 4) {
|
AppendLogLineRichStyled(_T("输出端口编号无效,必须在 1 到 4 之间。"), LOG_COLOR_ERROR);
|
return -1.0f;
|
}
|
|
if (m_nSavePointCount < 0) {
|
AppendLogLineRichStyled(_T("数据点数必须大于 0。"), LOG_COLOR_ERROR);
|
return -1.0f;
|
}
|
|
std::vector<float> vecBuffer(m_nSavePointCount, 0.0f);
|
int nReceived = 0;
|
|
RC nRet = SGIF_DataStorageGetData(DeviceID, nOutNo, m_nSavePointCount, vecBuffer.data(), &nReceived);
|
if (nRet != RC_OK) {
|
CString strError;
|
strError.Format(_T("读取 OUT%d 数据失败,错误码:%#X"), nOutNo, nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
return -1.0f;
|
}
|
|
vecBuffer.resize(nReceived);
|
CleanInvalidValuesInPlace(nOutNo, vecBuffer);
|
|
std::vector<float> vecGlass1, vecGlass2;
|
if (!SplitGlassSegments(nOutNo, vecBuffer, vecGlass1, vecGlass2, m_fJumpThreshold, m_nJumpWindow, m_nValleyMargin, m_nMinGlass1Count)) {
|
AppendLogLineRichStyled(_T("未能识别出两片玻璃的数据。"), LOG_COLOR_WARNING);
|
return -1.0f;
|
}
|
|
std::vector<float> vecGlass1Filtered, vecGlass2Filtered;
|
bool bGlass1Filtered = FilterDominantGroup(nOutNo, vecGlass1, vecGlass1Filtered);
|
bool bGlass2Filtered = FilterDominantGroup(nOutNo, vecGlass2, vecGlass2Filtered);
|
|
if (!bGlass1Filtered) {
|
AppendLogLineRichStyled(_T("Glass1 分段中未能识别出主数据组,使用原始数据。"), LOG_COLOR_WARNING);
|
vecGlass1Filtered = vecGlass1;
|
}
|
else {
|
AppendLogLineRichStyled(_T("Glass1 主数据组已提取。"), LOG_COLOR_SUCCESS);
|
}
|
|
if (!bGlass2Filtered) {
|
AppendLogLineRichStyled(_T("Glass2 分段中未能识别出主数据组,使用原始数据。"), LOG_COLOR_WARNING);
|
vecGlass2Filtered = vecGlass2;
|
}
|
else {
|
AppendLogLineRichStyled(_T("Glass2 主数据组已提取。"), LOG_COLOR_SUCCESS);
|
}
|
|
std::vector<float> vecStable1, vecStable2;
|
bool bStable1 = ExtractStableRegionFixed(nOutNo, vecGlass1Filtered, vecStable1, m_nFixedCount, m_fMaxDelta);
|
bool bStable2 = ExtractStableRegionFixed(nOutNo, vecGlass2Filtered, vecStable2, m_nFixedCount, m_fMaxDelta);
|
|
float fAvg1 = 0.0f, fAvg2 = 0.0f, fOffset = 0.0f;
|
if (bStable1 && bStable2) {
|
AppendLogLineRichStyled(_T("成功提取到两片玻璃的稳定区数据。"), LOG_COLOR_SUCCESS);
|
CalcGlassOffset(vecStable1, vecStable2, fAvg1, fAvg2, fOffset);
|
}
|
else {
|
AppendLogLineRichStyled(_T("未能提取到稳定区数据,尝试使用原始分段数据计算偏移。"), LOG_COLOR_WARNING);
|
CalcGlassOffset(vecGlass1, vecGlass2, fAvg1, fAvg2, fOffset);
|
}
|
|
MEASURE_FUNC_END();
|
|
return fOffset;
|
}
|
|
BEGIN_MESSAGE_MAP(CSGMeasurementDlg, CDialogEx)
|
ON_WM_SYSCOMMAND()
|
ON_WM_PAINT()
|
ON_WM_QUERYDRAGICON()
|
ON_WM_TIMER()
|
ON_WM_MEASUREITEM()
|
ON_WM_DRAWITEM()
|
ON_WM_CLOSE()
|
ON_MESSAGE(WM_TRAY_ICON_NOTIFY, &CSGMeasurementDlg::OnTrayIconClick)
|
ON_COMMAND(ID_TRAY_RESTORE, &CSGMeasurementDlg::OnTrayRestore)
|
ON_COMMAND(ID_TRAY_EXIT, &CSGMeasurementDlg::OnTrayExit)
|
ON_BN_CLICKED(IDC_BUTTON_CONNECT, &CSGMeasurementDlg::OnBnClickedButtonConnect)
|
ON_BN_CLICKED(IDC_BUTTON_DISCONNECT, &CSGMeasurementDlg::OnBnClickedButtonDisconnect)
|
ON_BN_CLICKED(IDC_BUTTON_RECEIVE_FROM_CTRL, &CSGMeasurementDlg::OnBnClickedButtonReceiveFromCtrl)
|
ON_BN_CLICKED(IDC_BUTTON_SEND_TO_CTRL, &CSGMeasurementDlg::OnBnClickedButtonSendToCtrl)
|
ON_BN_CLICKED(IDC_BUTTON_CLEAR_STORE, &CSGMeasurementDlg::OnBnClickedButtonClearStore)
|
ON_BN_CLICKED(IDC_BUTTON_START_STORE, &CSGMeasurementDlg::OnBnClickedButtonStartStore)
|
ON_BN_CLICKED(IDC_BUTTON_STOP_STORE, &CSGMeasurementDlg::OnBnClickedButtonStopStore)
|
ON_BN_CLICKED(IDC_BUTTON_CLEAR_LOG, &CSGMeasurementDlg::OnBnClickedButtonClearLog)
|
END_MESSAGE_MAP()
|
|
BOOL CSGMeasurementDlg::OnInitDialog()
|
{
|
CDialogEx::OnInitDialog();
|
|
// 将“关于...”菜单项添加到系统菜单中。
|
|
// IDM_ABOUTBOX 必须在系统命令范围内。
|
ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
|
ASSERT(IDM_ABOUTBOX < 0xF000);
|
|
CMenu* pSysMenu = GetSystemMenu(FALSE);
|
if (pSysMenu != nullptr)
|
{
|
BOOL bNameValid;
|
CString strAboutMenu;
|
bNameValid = strAboutMenu.LoadString(IDS_ABOUTBOX);
|
ASSERT(bNameValid);
|
if (!strAboutMenu.IsEmpty())
|
{
|
pSysMenu->AppendMenu(MF_SEPARATOR);
|
pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
|
}
|
}
|
|
// 设置此对话框的图标。 当应用程序主窗口不是对话框时,框架将自动
|
// 执行此操作
|
SetIcon(m_hIcon, TRUE); // 设置大图标
|
SetIcon(m_hIcon, FALSE); // 设置小图标
|
|
// TODO: 在此添加额外的初始化代码
|
UpdateData(FALSE);
|
UpdateControlStatus(FALSE, FALSE);
|
|
// 托盘图标初始化
|
m_trayIconData.cbSize = sizeof(NOTIFYICONDATA); // 设置托盘图标数据结构的大小
|
m_trayIconData.hWnd = m_hWnd; // 设置窗口句柄
|
m_trayIconData.uID = m_nTrayIconID; // 设置托盘图标 ID
|
m_trayIconData.uFlags = NIF_ICON | NIF_MESSAGE | NIF_TIP; // 设置托盘图标的标志(图标、消息、提示文本)
|
m_trayIconData.uCallbackMessage = WM_TRAY_ICON_NOTIFY; // 设置回调消息 WM_TRAY_ICON_NOTIFY
|
m_trayIconData.hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME); // 加载托盘图标
|
lstrcpy(m_trayIconData.szTip, TRAY_ICON_TOOLTIP_TEXT); // 设置托盘提示文本
|
|
// 添加托盘图标
|
Shell_NotifyIcon(NIM_ADD, &m_trayIconData);
|
m_bTrayIconCreated = TRUE;
|
|
for (int i = 0; i < 4; i++) {
|
CString str;
|
str.Format(_T("OUT%d"), i + 1);
|
m_comboOutputPort.AddString(str);
|
}
|
m_comboOutputPort.SetCurSel(0);
|
|
// 初始化IP地址控件,设置为默认IP地址
|
((CIPAddressCtrl*)GetDlgItem(IDC_IPADDRESS))->SetAddress(192, 168, 0, 10);
|
|
// 设置 PLC 监听器的日志回调函数
|
m_plcListener.SetLogCallback([this](const CString& msg, int type) {
|
if (type == -1) {
|
AppendLogLineRichStyled(msg, LOG_COLOR_ERROR);
|
}
|
if (type == 0) {
|
AppendLogLineRichStyled(msg, LOG_COLOR_SUCCESS);
|
}
|
else if (type == 1) {
|
AppendLogLineRichStyled(msg, LOG_COLOR_WARNING);
|
}
|
else {
|
AppendLogLineRichStyled(msg, LOG_COLOR_NORMAL);
|
}
|
});
|
|
// 初始化 PLC 监听器
|
m_plcListener.Initialize(StationIdentifier(0, 255), 200);
|
|
// 设置 PLC 监听器的开始采集回调函数
|
m_plcListener.SetStartCallback([this]() {
|
InitDataStorage();
|
StartDataStorage();
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
});
|
|
// 设置 PLC 监听器的停止采集回调函数
|
m_plcListener.SetStopCallback([this]() {
|
StopDataStorage();
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
});
|
|
// 设置 PLC 监听器的分析回调函数
|
m_plcListener.SetAnalyzeCallback([this]() {
|
std::array<double, 4> result;
|
for (int i = 0; i < 4; ++i) {
|
result[i] = AnalyzeStoredData(i + 1); // OUT1 ~ OUT4
|
}
|
|
if (std::any_of(result.begin(), result.end(), [](double v) { return v < 0; })) {
|
AppendLogLineRichStyled(_T("分析失败,某些输出端口数据无效。"), LOG_COLOR_ERROR);
|
return std::array<double, 4>{ -1.0, -1.0, -1.0, -1.0 };
|
}
|
|
CString strLog;
|
strLog.Format(_T("分析结果:OUT1: %.3f, OUT2: %.3f, OUT3: %.3f, OUT4: %.3f"), result[0], result[1], result[2], result[3]);
|
return result;
|
});
|
m_plcListener.Start();
|
|
// 初始化日志框
|
AppendLogLineRichStyled(_T("准备就绪..."), LOG_COLOR_SUCCESS);
|
|
return TRUE; // 除非将焦点设置到控件,否则返回 TRUE
|
}
|
|
void CSGMeasurementDlg::OnSysCommand(UINT nID, LPARAM lParam)
|
{
|
if ((nID & 0xFFF0) == IDM_ABOUTBOX)
|
{
|
CAboutDlg dlgAbout;
|
dlgAbout.DoModal();
|
}
|
else
|
{
|
CDialogEx::OnSysCommand(nID, lParam);
|
}
|
}
|
|
void CSGMeasurementDlg::OnPaint()
|
{
|
if (IsIconic()) {
|
CPaintDC dc(this);
|
|
SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);
|
|
// 使图标在工作区矩形中居中
|
int cxIcon = GetSystemMetrics(SM_CXICON);
|
int cyIcon = GetSystemMetrics(SM_CYICON);
|
CRect rect;
|
GetClientRect(&rect);
|
int x = (rect.Width() - cxIcon + 1) / 2;
|
int y = (rect.Height() - cyIcon + 1) / 2;
|
|
// 绘制图标
|
dc.DrawIcon(x, y, m_hIcon);
|
}
|
else {
|
CDialogEx::OnPaint();
|
}
|
}
|
|
//当用户拖动最小化窗口时系统调用此函数取得光标显示。
|
HCURSOR CSGMeasurementDlg::OnQueryDragIcon()
|
{
|
return static_cast<HCURSOR>(m_hIcon);
|
}
|
|
void CSGMeasurementDlg::OnTimer(UINT_PTR nIDEvent)
|
{
|
// TODO: 在此添加消息处理程序代码和/或调用默认值
|
if (nIDEvent == TIMER_ID_OUTPUT_UPDATE) {
|
SGIF_FLOATVALUE_OUT value[4] = { 0 };
|
RC nRet = SGIF_GetCalcDataALL(DeviceID, value);
|
if (nRet == RC_OK) {
|
for (int i = 0; i < 4; ++i) {
|
m_dOutValues[i] = value[i].Value;
|
}
|
|
// 更新绑定控件
|
UpdateData(FALSE);
|
}
|
else {
|
CString strError;
|
strError.Format(_T("获取测量值失败,错误码:%#X"), nRet);
|
AppendLogLineRichStyled(strError, LOG_COLOR_ERROR);
|
|
// 断开连接
|
if (m_bConnected) {
|
DisconnectFromDevice();
|
}
|
}
|
}
|
|
CDialogEx::OnTimer(nIDEvent);
|
}
|
|
void CSGMeasurementDlg::OnMeasureItem(int nIDCtl, LPMEASUREITEMSTRUCT lpMeasureItemStruct)
|
{
|
if (lpMeasureItemStruct->CtlType == ODT_MENU) {
|
lpMeasureItemStruct->itemHeight = 24;
|
lpMeasureItemStruct->itemWidth = 140;
|
}
|
}
|
|
void CSGMeasurementDlg::OnDrawItem(int nIDCtl, LPDRAWITEMSTRUCT lpDrawItemStruct)
|
{
|
if (lpDrawItemStruct->CtlType != ODT_MENU) {
|
return;
|
}
|
|
CDC* pDC = CDC::FromHandle(lpDrawItemStruct->hDC);
|
CRect rc = lpDrawItemStruct->rcItem;
|
UINT id = lpDrawItemStruct->itemID;
|
|
// 背景
|
COLORREF bgColor = (lpDrawItemStruct->itemState & ODS_SELECTED) ? RGB(200, 220, 255) : RGB(255, 255, 255);
|
pDC->FillSolidRect(rc, bgColor);
|
|
// 图标
|
HICON hIcon = nullptr;
|
if (id == ID_TRAY_RESTORE) {
|
hIcon = AfxGetApp()->LoadIcon(IDI_ICON_RESTORE);
|
}
|
|
if (id == ID_TRAY_EXIT) {
|
hIcon = AfxGetApp()->LoadIcon(IDI_ICON_EXIT);
|
}
|
|
if (hIcon) {
|
DrawIconEx(pDC->GetSafeHdc(), rc.left + 4, rc.top + 4, hIcon, 16, 16, 0, NULL, DI_NORMAL);
|
}
|
|
// 文本
|
CString str;
|
if (id == ID_TRAY_RESTORE) {
|
str = _T("恢复界面");
|
}
|
|
if (id == ID_TRAY_EXIT) {
|
str = _T("退出程序");
|
}
|
|
pDC->SetBkMode(TRANSPARENT);
|
pDC->SetTextColor(RGB(0, 0, 0));
|
pDC->DrawText(str, CRect(rc.left + 28, rc.top, rc.right, rc.bottom), DT_SINGLELINE | DT_VCENTER | DT_LEFT);
|
}
|
|
void CSGMeasurementDlg::OnClose()
|
{
|
// TODO: 在此添加消息处理程序代码和/或调用默认值
|
if (m_bExitingFromTray) {
|
// 从托盘退出流程
|
ExitApplication();
|
}
|
else {
|
// 正常关闭按钮
|
int nResult = AfxMessageBox(_T("是否最小化到托盘?"), MB_YESNO | MB_ICONQUESTION);
|
if (nResult == IDYES) {
|
ShowWindow(SW_HIDE);
|
}
|
else {
|
ExitApplication();
|
}
|
}
|
}
|
|
LRESULT CSGMeasurementDlg::OnTrayIconClick(WPARAM wParam, LPARAM lParam) {
|
if (wParam == m_nTrayIconID) {
|
if (LOWORD(lParam) == WM_LBUTTONUP) {
|
// 左键点击恢复窗口
|
ShowWindow(SW_SHOW);
|
SetForegroundWindow();
|
}
|
else if (LOWORD(lParam) == WM_RBUTTONUP) {
|
// 右键点击弹出菜单
|
CMenu menu;
|
menu.CreatePopupMenu();
|
menu.AppendMenu(MF_OWNERDRAW, ID_TRAY_RESTORE, (LPCTSTR)ID_TRAY_RESTORE);
|
menu.AppendMenu(MF_OWNERDRAW, ID_TRAY_EXIT, (LPCTSTR)ID_TRAY_EXIT);
|
|
// 加载图标
|
HICON hIconRestore = (HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON_RESTORE), IMAGE_ICON, 16, 16, LR_SHARED);
|
HICON hIconExit = (HICON)::LoadImage(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_ICON_EXIT), IMAGE_ICON, 16, 16, LR_SHARED);
|
|
// 设置图标到菜单项
|
MENUITEMINFO mii = { sizeof(MENUITEMINFO) };
|
mii.fMask = MIIM_BITMAP;
|
|
// 恢复菜单项图标
|
mii.hbmpItem = HBMMENU_CALLBACK;
|
menu.SetMenuItemInfo(ID_TRAY_RESTORE, &mii);
|
|
// 退出菜单项图标
|
mii.hbmpItem = HBMMENU_CALLBACK;
|
menu.SetMenuItemInfo(ID_TRAY_EXIT, &mii);
|
|
// 获取鼠标当前位置,并显示菜单
|
POINT pt;
|
GetCursorPos(&pt);
|
SetForegroundWindow();
|
menu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RIGHTBUTTON, pt.x, pt.y, this);
|
}
|
}
|
return 0;
|
}
|
|
void CSGMeasurementDlg::OnTrayRestore()
|
{
|
ShowWindow(SW_SHOW); // 恢复窗口
|
SetForegroundWindow(); // 将窗口置于前端
|
}
|
|
void CSGMeasurementDlg::OnTrayExit()
|
{
|
// 从托盘图标菜单选择“退出程序”
|
if (AfxMessageBox(_T("确定要退出程序吗?"), MB_YESNO | MB_ICONQUESTION) == IDYES) {
|
m_bExitingFromTray = TRUE;
|
PostMessage(WM_CLOSE);
|
}
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonConnect()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
ConnectToDevice();
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonDisconnect()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
DisconnectFromDevice();
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonReceiveFromCtrl()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
if (!m_bConnected) {
|
AppendLogLineRichStyled(_T("设备未连接,请先连接设备。"), LOG_COLOR_WARNING);
|
return;
|
}
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonSendToCtrl()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
if (!m_bConnected) {
|
AppendLogLineRichStyled(_T("设备未连接,请先连接设备。"), LOG_COLOR_WARNING);
|
return;
|
}
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonClearStore()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
InitDataStorage();
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonStartStore()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
StartDataStorage();
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonStopStore()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
StopDataStorage();
|
UpdateControlStatus(m_bConnected, m_bSaving);
|
|
int nSel = m_comboOutputPort.GetCurSel();
|
if (CB_ERR == nSel) {
|
AppendLogLineRichStyled(_T("请选择一个有效的输出端口。"), LOG_COLOR_WARNING);
|
return;
|
}
|
|
int nOutNo = nSel + 1;
|
AnalyzeStoredData(nOutNo);
|
}
|
|
void CSGMeasurementDlg::OnBnClickedButtonClearLog()
|
{
|
// TODO: 在此添加控件通知处理程序代码
|
if (::IsWindow(m_editLog.GetSafeHwnd())) {
|
m_editLog.SetWindowText(_T(""));
|
}
|
}
|