This commit is contained in:
coco
2026-07-03 16:05:30 +08:00
commit df489d5640
1101 changed files with 779140 additions and 0 deletions
+69
View File
@@ -0,0 +1,69 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# 忽略所有的本地配置文件
local_config.h
# 忽略所有的二进制可执行文件
**/bin/*
**/Debug/*
**/build/*
# 忽略所有的日志文件
**/logs/*
# 忽略所有的IDE配置目录
**/.vs/*
**/.vs/*
**/.idea/*
**/.svn/*
# 忽略所有的依赖管理文件和目录
**/deps/*
**/vcpkg/*
# 忽略所有的文档和示例文件
doc/
examples/
# 忽略所有的外部库文件和文档
**/external/*
# 忽略所有的测试结果文件
test_results/
# 忽略所有的版本控制文件
.git/
# 忽略所有的系统文件和可执行文件
.DS_Store
# Executables
*.exe
*.out
*.app
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
+41
View File
@@ -0,0 +1,41 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.31729.503
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "GeomativeSDK", "GeomativeSDK\GeomativeSDK.vcxproj", "{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Examples", "Examples\Examples.vcxproj", "{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Debug|x64.ActiveCfg = Debug|x64
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Debug|x64.Build.0 = Debug|x64
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Debug|x86.ActiveCfg = Debug|Win32
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Debug|x86.Build.0 = Debug|Win32
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Release|x64.ActiveCfg = Release|x64
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Release|x64.Build.0 = Release|x64
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Release|x86.ActiveCfg = Release|Win32
{80D50A7B-9C5A-4EA5-B9D8-0C224A9B3BBC}.Release|x86.Build.0 = Release|Win32
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Debug|x64.ActiveCfg = Debug|x64
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Debug|x64.Build.0 = Debug|x64
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Debug|x86.ActiveCfg = Debug|Win32
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Debug|x86.Build.0 = Debug|Win32
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Release|x64.ActiveCfg = Release|x64
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Release|x64.Build.0 = Release|x64
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Release|x86.ActiveCfg = Release|Win32
{ED8FC65E-83ED-4344-9FF0-551A025BEAFD}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A8477F66-70A5-4F9C-B8AB-C6FC6262B43A}
EndGlobalSection
EndGlobal
@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>16.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{80d50a7b-9c5a-4ea5-b9d8-0c224a9b3bbc}</ProjectGuid>
<RootNamespace>GeomativeSDK</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
<ProjectName>GeomativeSDK</ProjectName>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v142</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<LinkIncremental>true</LinkIncremental>
<IncludePath>$(SolutionDir);$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)\libs;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
<IncludePath>$(SolutionDir);$(IncludePath)</IncludePath>
<LibraryPath>$(SolutionDir)\libs;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<LinkIncremental>true</LinkIncremental>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<LinkIncremental>false</LinkIncremental>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;GEOMATIVESDK_EXPORTS;_WINDOWS;_USRDLL;_HAS_STD_BYTE=0;_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;GEOMATIVESDK_EXPORTS;_WINDOWS;_USRDLL;_HAS_STD_BYTE=0;_SILENCE_CXX17_STRSTREAM_DEPRECATION_WARNING;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>false</ConformanceMode>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
<LanguageStandard>stdcpp17</LanguageStandard>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;GEOMATIVESDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;GEOMATIVESDK_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
<PrecompiledHeader>Use</PrecompiledHeader>
<PrecompiledHeaderFile>pch.h</PrecompiledHeaderFile>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableUAC>false</EnableUAC>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="app_config.h" />
<ClInclude Include="framework.h" />
<ClInclude Include="ini_doc.h" />
<ClInclude Include="nanolog.h" />
<ClInclude Include="pch.h" />
<ClInclude Include="geo_meta_helper.h" />
<ClInclude Include="post_helper.h" />
<ClInclude Include="project.h" />
<ClInclude Include="util_helper.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\json\json_reader.cpp" />
<ClCompile Include="..\json\json_value.cpp" />
<ClCompile Include="..\json\json_writer.cpp" />
<ClCompile Include="app_config.cpp" />
<ClCompile Include="dllmain.cpp" />
<ClCompile Include="geo_meta_helper.cpp" />
<ClCompile Include="ini_doc.cpp" />
<ClCompile Include="nanolog.cpp" />
<ClCompile Include="pch.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|x64'">Create</PrecompiledHeader>
</ClCompile>
<ClCompile Include="post_helper.cpp">
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">NotUsing</PrecompiledHeader>
</ClCompile>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>
@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="源文件">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="头文件">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="资源文件">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClInclude Include="framework.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="pch.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="geo_meta_helper.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="post_helper.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="project.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="nanolog.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="util_helper.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="app_config.h">
<Filter>头文件</Filter>
</ClInclude>
<ClInclude Include="ini_doc.h">
<Filter>头文件</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="dllmain.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="pch.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="post_helper.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="nanolog.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="app_config.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="ini_doc.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="geo_meta_helper.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\json\json_reader.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\json\json_value.cpp">
<Filter>源文件</Filter>
</ClCompile>
<ClCompile Include="..\json\json_writer.cpp">
<Filter>源文件</Filter>
</ClCompile>
</ItemGroup>
</Project>
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>
+67
View File
@@ -0,0 +1,67 @@
#include <iostream>
#include "app_config.h"
#include "ini_doc.h"
#include "util_helper.h"
using namespace std;
AppConfig::AppConfig() {
LoadConfigIni();
Init();
}
AppConfig* AppConfig::Instance() {
static AppConfig* instance = new AppConfig();
return instance;
}
/*
×Ô¶¯²éÕÒ²»´æÔÚµÄkey
*/
void AppConfig::AutomaticSaveProjectPath(string path) {
for (int i = 1; i < 100; i++) {
string key = UtilHelper::StringFormat("PATH%02d", i);
if (m_doc.IsKeyExist(PROJECT_SEC_NAME, key))
continue;
SetProfile(PROJECT_SEC_NAME, key, path);
break;
}
}
void AppConfig::SetProfile(string secName, string key, string word) {
m_doc.setString(PROJECT_SEC_NAME, key, word);
m_doc.save();
}
void AppConfig::GetProfile(string secName, string key, string &word) {
if(m_doc.IsKeyExist(secName,key))
word = m_doc.getString(PROJECT_SEC_NAME, key, word);
}
void AppConfig::DelProfile(string secName, string word) {
IniDoc::KeyMap map = m_doc.getSection(secName);
string key;
for (auto it = map.begin(); it != map.end(); it++) {
if (it->second.compare(word) == 0) {
key = it->first;
break;
}
}
if (key.size() > 0) {
m_doc.delKey(secName, key);
m_doc.save();
}
}
void AppConfig::LoadConfigIni(void) {
string dir = UtilHelper::LaunchPath();
bool ret = m_doc.load(dir.append("\\GeomativeSDKConfig.ini"));
if (!ret) {
cout << "can't find GeomativeSDKConfig.ini,please check" << endl;
}
}
+91
View File
@@ -0,0 +1,91 @@
#pragma once
#include "ini_doc.h"
#include <sstream>
using namespace std;
class AppConfig
{
private:
AppConfig();
public:
const std::string PROJECT_SEC_NAME = "Project";
const std::string NETSETTING_SEC_NAME = "NetworkSetting";
const std::string DEVNETSETTING_SEC_NAME = "DevNetworkSetting";
const std::string LOGIN_SEC_NAME = "Login";
const std::string SYSTEM_SEC_NAME = "SystemConfig";
public:
static AppConfig* Instance();
void SetProfile(string secName,string key,string word);
void GetProfile(string secName, string key, string &word);
void DelProfile(string secName, string word);
void AutomaticSaveProjectPath(string path);
private:
void LoadConfigIni(void);
IniDoc m_doc;
private:
string DataServerIp;
string DataServerPort;
string WebServerIP;
string WebServerPort;
public:
string DataServerUrl;
string WebServerUrl;
private:
bool isNum(string str)
{
stringstream sin(str);
double d;
char c;
if (!(sin >> d))
{
return false;
}
if (sin >> c)
{
return false;
}
return true;
}
public:
void Init()
{
long sel = 0;
sel = m_doc.getInteger("EnvSelect", "ChooseEnv", sel);
string sectionName = (sel == 1) ? DEVNETSETTING_SEC_NAME:NETSETTING_SEC_NAME;
DataServerIp.clear();
DataServerIp = m_doc.getString(sectionName, "DataServerIp", DataServerIp);
DataServerPort.clear();
DataServerPort = m_doc.getString(sectionName, "DataServerPort", DataServerPort);
WebServerIP.clear();
WebServerIP = m_doc.getString(sectionName, "WebServerIP", WebServerIP);
WebServerPort.clear();
WebServerPort = m_doc.getString(sectionName, "WebServerPort", WebServerPort);
if (isNum(DataServerPort)){
DataServerUrl = DataServerIp + ":" + DataServerPort;
}
else{
DataServerUrl = DataServerIp + "/" + DataServerPort;
}
if (isNum(WebServerPort)) {
WebServerUrl = WebServerIP + ":" + WebServerPort;
}
else {
WebServerUrl = WebServerIP + "/" + WebServerPort;
}
}
};
+19
View File
@@ -0,0 +1,19 @@
// dllmain.cpp : 定义 DLL 应用程序的入口点。
#include "pch.h"
BOOL APIENTRY DllMain( HMODULE hModule,
DWORD ul_reason_for_call,
LPVOID lpReserved
)
{
switch (ul_reason_for_call)
{
case DLL_PROCESS_ATTACH:
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
case DLL_PROCESS_DETACH:
break;
}
return TRUE;
}
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#define WIN32_LEAN_AND_MEAN // 从 Windows 头文件中排除极少使用的内容
// Windows 头文件
#include <windows.h>
@@ -0,0 +1,216 @@
#include "geo_meta_helper.h"
#include "json/json.h"
#include "util_helper.h"
#include "app_config.h"
#include "post_helper.h"
static PostHelper post_helper;
bool GeoMetaHelper::Login(const std::string& name, const std::string& password) {
if (!post_helper.PostLogin(name, password)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = GeoMetaCode::success;
return IsOK();
}
bool GeoMetaHelper::RefreshToken() {
if (!post_helper.RefreshToken()) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = GeoMetaCode::success;
return IsOK();
}
bool GeoMetaHelper::GetProjectList(GeoMetaSceneId scene_id, std::vector<ProjectInfo>& project_list) {
Json::Value res;
if (!post_helper.Get(AppConfig::Instance()->DataServerUrl + "/business/project/page?sceneId=" + std::to_string(static_cast<std::int32_t>(scene_id)), res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
const auto& list = res["data"]["list"];
for (std::uint32_t j = 0; j < list.size(); j++) {
project_list.push_back(ProjectInfo(UtilHelper::U2G(list[j]["projectName"].asString().c_str()), std::atoll(list[j]["projectId"].asString().c_str())));
}
return true;
}
bool GeoMetaHelper::GetGsList(ProjectId id, std::vector<GsInfo>& gs_list) {
Json::Value res;
if (!post_helper.Get(AppConfig::Instance()->DataServerUrl + "/business/project/" + std::to_string(id) + "/gsObject", res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
const auto& list = res["data"];
for (std::uint32_t i = 0; i < list.size(); i++) {
gs_list.emplace_back(UtilHelper::U2G(list[i]["name"].asString().c_str()), list[i]["id"].asInt64());
}
return true;
}
bool GeoMetaHelper::GetTmList(GsId gs_id, ComponentId component_id, std::vector<TmInfo>& tm_list) {
Json::Value res;
if (!post_helper.Get(AppConfig::Instance()->DataServerUrl + "/business/project/gs-object/tm-list?gsObjectId=" + std::to_string(gs_id) + "&componentId=" + std::to_string(component_id), res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
const auto& list = res["data"]["paramConfigRows"];
for (std::uint32_t i = 0; i < list.size(); i++)
tm_list.emplace_back(UtilHelper::U2G(list[i]["name"].asString().c_str()), std::atoll(list[i]["tmObjectId"].asString().c_str()));
return true;
}
bool GeoMetaHelper::CreateProject(const GeoMetaSceneId scene_id, const std::string& project_name, Properties& properties, ProjectId& project_id) {
Json::Value args, res;
const auto& property_names = { "" };
for (const auto& name : property_names)
args[name] = properties[name];
args["sceneId"] = static_cast<std::uint64_t>(scene_id);
args["projectName"] = project_name;
if (!post_helper.Post(AppConfig::Instance()->DataServerUrl + "/business/project/create", args, res)) {
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
project_id = res["data"]["projectId"].asUInt64();
return true;
}
bool GeoMetaHelper::CreateGs(const ProjectId project_id, const std::string& gs_name, Properties& properties, GsId& gs_id) {
Json::Value args, res;
const auto& property_names = { ""};
for (const auto& name : property_names)
args[name] = properties[name];
args["name"] = gs_name;
args["projectId"] = project_id;
if (!post_helper.Post(AppConfig::Instance()->DataServerUrl + "/business/project/gsObject/base/add", args, res)) {
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
gs_id = res["data"]["gsObjectId"].asUInt64();
return true;
}
bool GeoMetaHelper::CreateTm(const GsId gs_id, const ComponentId component_id, Properties& properties, TmId& tm_id) {
Json::Value config_data, args, res;
const auto& property_names = { "name", "remark", "weather", "operator" };
for (const auto& name : property_names)
config_data[name] = properties[name];
args["componentId"] = component_id;
args["gsObjectId"] = gs_id;
args["configData"] = config_data;
if (!post_helper.Post(AppConfig::Instance()->DataServerUrl + "/business/project/gs-object/tm-add", args, res)) {
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
tm_id = res["data"]["tmObjectId"].asUInt64();
return true;
}
bool GeoMetaHelper::UploadFile(const std::string& file_path, const GeoMetaSceneId& scene_id, const GsId gs_id, const TmId& tm_id) {
Json::Value args, res, item;
const auto& file_name = std::filesystem::path(file_path).filename().string();
args["path"] = file_name;
if (!post_helper.Post(AppConfig::Instance()->DataServerUrl + "/common/file/upload", args, file_path, res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
res.clear();
args.clear();
args["gsObjectId"] = gs_id;
args["tmObjectId"] = tm_id;
args["datasetTypeId"] = 24;
item["fileName"] = std::filesystem::path(file_path).filename().string();
item["fileUrl"] = res["data"]["url"];
args["dataList"].append(item);
Json::StreamWriterBuilder builder;
const std::string strSavePost = Json::writeString(builder, args);
if (!post_helper.Post(AppConfig::Instance()->DataServerUrl + "/business/tool/node_earthquake", args, res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
return IsOK();
}
bool GeoMetaHelper::DownloadFile(const TmId& tm_id, const std::string& dest_path) {
Json::Value res;
if (!post_helper.Get(AppConfig::Instance()->DataServerUrl + "/business/node/earthquake/listAllNeFile/" + std::to_string(tm_id), res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
if (res["data"].size() == 0)
return true;
for (unsigned int i = 0; i < res["data"].size(); i++) {
if (!post_helper.Get(res["data"][i]["fileUrl"].asString(), std::filesystem::path(dest_path) / res["data"][i]["fileName"].asString())) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
}
return true;
}
bool GeoMetaHelper::GetData(const GsId gs_id, const ComponentId component_id, const DatasetId dataset_id, std::string& data) {
Json::Value res;
if (post_helper.Get(AppConfig::Instance()->DataServerUrl + "/business/project/gs-object/dataset-list?gsObjectId=" + std::to_string(gs_id) + "&componentId=" + std::to_string(component_id), res)) {
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
if (!IsOK())
return false;
for (unsigned int i = 0; i < res["datasetTree"].size(); i++) {
if (res["datasetTree"][i]["id"].asInt64() == dataset_id) {
data = res["datasetTree"][i]["data"].asString();
return true;
}
}
}
error_code_ = GeoMetaCode::resource_not_found;
return false;
}
bool GeoMetaHelper::SetData(const GsId gs_id, const TmId tm_id, const ComponentId component_id, const DatasetId dataset_id, const std::string& data) {
Json::Value args, res;
args["gsObjectId"] = gs_id;
args["tmObjectId"] = tm_id;
args["tmDatasetTypeId"] = 0;
args["tmDatasetId"] = dataset_id;
args["data"] = data;
if (post_helper.Post(AppConfig::Instance()->DataServerUrl + "/business/project/tm/dataset/save", args, res)) {
error_code_ = GeoMetaCode::system_failure;
return false;
}
error_code_ = static_cast<GeoMetaCode>(res["code"].asInt());
return IsOK();
}
@@ -0,0 +1,98 @@
#pragma once
#include <string>
#include <vector>
#include <map>
#include <set>
typedef std::int64_t ProjectId;
typedef std::int64_t GsId;
typedef std::int64_t TmId;
typedef std::int64_t ComponentId;
typedef std::int64_t DatasetId;
typedef std::map<std::string, std::string> Properties;
struct TmInfo
{
public:
TmInfo(const std::string name, const TmId id = 0) :id_(id), name_(name) {}
public:
TmId id_;
std::string name_;
};
struct GsInfo
{
public:
GsInfo(const std::string name, const GsId id = 0) :id_(id), name_(name) {}
public:
GsId id_;
std::string name_;
std::vector<TmInfo> tm_list_;
};
struct ProjectInfo
{
public:
ProjectInfo(const std::string name, const ProjectId id = 0) :id_(id), name_(name) {}
public:
ProjectId id_;
std::string name_;
std::vector<GsInfo> gs_list_;
};
#ifdef GEOMATIVESDK_EXPORTS
class __declspec(dllexport) GeoMetaHelper {
#else
class __declspec(dllimport) GeoMetaHelper {
#endif
public:
GeoMetaHelper() :error_code_(GeoMetaCode::success) {};
~GeoMetaHelper() = default;
public:
enum class GeoMetaCode {
success = 200,
system_failure = -1,
resource_not_found = -2
};
enum class GeoMetaSceneId {
scene_id_seis = 30
};
enum class GeoMetaComponentId {
component_id_ert = 18,
component_id_seis = 19
};
public:
GeoMetaCode ErrorCode() {
return error_code_;
}
bool Login(const std::string& name, const std::string& password);
bool RefreshToken();
bool GetProjectList(const GeoMetaSceneId scene_id, std::vector<ProjectInfo>& project_list);
bool GetGsList(const ProjectId project_id, std::vector<GsInfo>& gs_list);
bool GetTmList(const GsId gs_id, const ComponentId component_id, std::vector<TmInfo>& tm_list);
bool CreateProject(const GeoMetaSceneId scene_id, const std::string& project_name, Properties& properties, ProjectId& project_id);
bool CreateGs(const ProjectId project_id, const std::string& gs_name, Properties& properties, GsId& gs_id);
bool CreateTm(const GsId gs_id, const ComponentId component_id, Properties& properties, TmId& tm_id);
bool UploadFile(const std::string& file_path, const GeoMetaSceneId& scene_id, const GsId gs_id, const TmId& tm_id);
bool DownloadFile(const TmId& tm_id, const std::string& dest_path);
bool GetData(const GsId gs_id, const ComponentId component_id, const DatasetId dataset_id, std::string& data);
bool SetData(const GsId gs_id, const TmId tm_id, const ComponentId component_id, const DatasetId dataset_id, const std::string& data);
protected:
bool IsOK() {
return error_code_ == GeoMetaCode::code_200;
}
private:
GeoMetaCode error_code_;
};
+425
View File
@@ -0,0 +1,425 @@
#include <fstream>
#include <strstream>
#include "ini_doc.h"
using namespace std;
const char IniDoc::left_tag = '[';
const char IniDoc::right_tag = ']';
const char IniDoc::equal = '=';
const char IniDoc::cr = '\r';
const char IniDoc::new_line = '\n';
const char* IniDoc::empty_str = "";
const int IniDoc::BUFFER_LEN = 255;
const IniDoc::KeyMap IniDoc::ms_emptySection;
IniDoc::IniDoc() : m_modified(false)
{
}
IniDoc::IniDoc(const std::string& file_name) : m_modified(false)
{
load(file_name);
}
IniDoc::~IniDoc()
{
if (m_modified)
save();
}
void IniDoc::saveBeforeLoad()
{
if (m_modified)
save();
m_file_name.resize(0);
m_map.clear();
m_modified = false;
}
const char* IniDoc::key_value(const std::string& section, const std::string& key)
{
SectionIterator itSection = m_map.find(section);
if (m_map.end() != itSection)
{
KeyIterator itKey = itSection->second.find(key);
if (itKey != itSection->second.end())
return itKey->second.c_str();
}
return 0;
}
bool IniDoc::load(const std::string& file_name)
{
saveBeforeLoad();
ifstream file(file_name);
if (!file)
return false;
file.seekg(0, ios::end);
long len = file.tellg();
if (len < 0)
return false;
char* buffer = new char[len + 1];
if (0 == buffer)
return false;
file.seekg(0, ios::beg);
file.read(buffer, len);
buffer[len = file.gcount()] = 0;
loadString(buffer);
m_file_name = file_name;
delete[] buffer;
return true;
}
bool IniDoc::loadString(const std::string& str)
{
saveBeforeLoad();
unsigned long length = str.size();
if (str.size() == 0)
return false;
enum project_status
{
after_left_tag,
after_section_name,
after_section_name_ws,
after_key_name,
after_key_name_ws,
after_equal,
start
};
string section; // 当前 section.
string key; // 当前 key.
project_status sta = start; // 解析状态.
const char* p = str.c_str(); // 当前解析字符串的位置.
const char* beg = p; // 当前元素的开始.
const char* last_ws = p; // 最后一个空格字符.
for (; length; ++p, --length)
{
if (new_line == *p)
{
if (after_equal == sta)
{
if (cr == *(p - 1))
--p;
m_map[section][key] = string(beg, p - beg);
if (cr == *p)
++p;
}
sta = start;
}
else
{
switch (sta)
{
case after_left_tag:
if (right_tag == *p)
{
sta = start;
section = empty_str; // empty section name.
}
else if (!isspace((unsigned char)*p))
{
sta = after_section_name;
beg = p;
}
break;
case after_section_name:
if (right_tag == *p)
{
sta = start;
section = string(beg, p - beg);
}
else if (isspace((unsigned char)*p))
{
sta = after_section_name_ws;
last_ws = p;
}
break;
case after_section_name_ws:
if (right_tag == *p)
{
sta = start;
section = string(beg, last_ws - beg);
}
else if (!isspace((unsigned char)*p))
{
sta = after_section_name;
}
break;
case after_key_name:
if (equal == *p)
{
sta = after_equal;
key = string(beg, p - beg);
beg = p + 1;
}
else if (isspace((unsigned char)*p))
{
sta = after_key_name_ws;
last_ws = p;
}
break;
case after_key_name_ws:
if (equal == *p)
{
sta = after_equal;
key = string(beg, last_ws - beg);
beg = p + 1;
}
else if (!isspace((unsigned char)*p))
{
sta = after_key_name;
}
break;
case start:
if (left_tag == *p)
{
sta = after_left_tag;
}
else if (equal == *p)
{
key = empty_str; // an empty key.
sta = after_equal;
beg = p + 1;
}
else if (!isspace((unsigned char)*p))
{
sta = after_key_name;
beg = p;
}
break;
}
}
}
if (after_equal == sta)
m_map[section][key] = string(beg, p - beg);
return true;
}
bool IniDoc::save()
{
if (0 == m_file_name.c_str() || 0 == m_file_name[0])
return false; // file name invalid
ofstream file(m_file_name.c_str());
if (!file)
return false;
for (SectionMap::iterator itApp = m_map.begin(); itApp != m_map.end(); ++itApp)
{
file << left_tag << itApp->first << right_tag << endl;
for (KeyMap::iterator itKey = itApp->second.begin(); itKey != itApp->second.end(); ++itKey)
file << itKey->first << equal << itKey->second << endl;
file << endl;
}
m_modified = false;
return true;
}
bool IniDoc::saveAs(const std::string& file_name)
{
string old_file_name = m_file_name;
m_file_name = file_name;
if (save())
return true;
m_file_name = old_file_name;
return false;
}
long IniDoc::getInteger(const std::string& section, const std::string& key, long def_val)
{
istrstream(key_value(section, key)) >> def_val;
return def_val;
}
float IniDoc::getFloat(const std::string& section, const std::string& key, float def_val)
{
istrstream(key_value(section, key)) >> def_val;
return def_val;
}
long IniDoc::getStruct(const std::string& section, const std::string& key_, void* buffer, long size)
{
std::string key = key_value(section, key_);
if (key.size() == 0)
return 0;
const char* p = key.c_str();
char* dst = (char*)buffer;
long read_len = 0;
char value;
while (*p && read_len < size)
{
switch (*p)
{
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
value = *p - '0';
break;
case 'a': case 'b': case 'c': case 'd': case 'e': case 'f':
value = *p - 'a' + 10;
break;
case 'A': case 'B': case 'C': case 'D': case 'E': case 'F':
value = *p - 'A' + 10;
break;
default:
return read_len;
}
if (0 == (p - key.c_str()) % 2)
*(dst + read_len) = value << 4;
else
*(dst + read_len) = (*(dst + read_len) & 0xf0) + value;
if (0 == (++p - key.c_str()) % 2)
++read_len;
}
return read_len;
}
long IniDoc::getString(const std::string& section, const std::string& key_, const std::string& def_val, std::string& dst_str)
{
std::string key = key_value(section, key_);
dst_str = key.length() ? key : def_val;
return dst_str.length();
}
bool IniDoc::IsKeyExist(const std::string& section,string key)
{
SectionIterator itSection = m_map.find(section);
if (m_map.end() != itSection)
{
KeyIterator itKey = itSection->second.find(key);
if (itKey != itSection->second.end())
return true;
}
return false;
}
const std::string IniDoc::getString(const std::string& section, const std::string& key_, const std::string& def_val)
{
const char* pKey = key_value(section, key_);
std::string key = !pKey ? "" : std::string(pKey);
if (key.length() == 0)
key = def_val;
return key;
}
void IniDoc::setInteger(const std::string& section, const std::string& key, long value)
{
char buffer[BUFFER_LEN + 1];
ostrstream ostr(buffer, BUFFER_LEN);
ostr << value;
buffer[ostr.pcount()] = 0;
setString(section, key, buffer);
}
void IniDoc::setFloat(const std::string& section, const std::string& key, float value)
{
char buffer[BUFFER_LEN + 1];
ostrstream ostr(buffer, BUFFER_LEN);
ostr << value;
buffer[ostr.pcount()] = 0;
setString(section, key, buffer);
}
inline char bin2hex(char bin)
{
return bin < 10 ? bin + '0' : bin - 10 + 'A';
}
void IniDoc::setStruct(const std::string& section, const std::string& key, const void* buffer, long size)
{
char* dst = new char[size * 2 + 1];
if (dst)
{
const char* src = (const char*)buffer;
long i = 0;
for (i = 0; i < size; ++i)
{
dst[i << 1] = bin2hex((src[i] >> 4) & 0x0f);
dst[(i << 1) + 1] = bin2hex(src[i] & 0x0f);
}
dst[i << 1] = 0;
setString(section, key, dst);
delete[] dst;
}
}
void IniDoc::setString(const std::string& section, const std::string& key, const std::string& value)
{
m_map[section][key] = value;
m_modified = true;
}
bool IniDoc::delSection(const std::string& section)
{
SectionIterator itSection = m_map.find(section);
if (m_map.end() != itSection)
{
m_map.erase(itSection);
return true;
}
return false;
}
bool IniDoc::delKey(const std::string& section, const std::string& key)
{
SectionIterator itSection = m_map.find(section);
if (m_map.end() != itSection)
{
KeyIterator itKey = itSection->second.find(key);
if (itKey != itSection->second.end())
{
itSection->second.erase(itKey);
return true;
}
}
return false;
}
const IniDoc::KeyMap& IniDoc::getSection(const std::string& section) const
{
SectionMap::const_iterator itApp = m_map.find(section);
return m_map.end() == itApp ? ms_emptySection : itApp->second;
}
+107
View File
@@ -0,0 +1,107 @@
#pragma once
#include <map>
#include <string>
#include <string.h>
#if defined(_MSC_VER)
#define strcasecmp _stricmp
#endif
class IniDoc
{
public:
struct IgnoreCaseLT
{
bool operator()(const std::string& lhs, const std::string& rhs) const
{
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0;
}
};
public:
typedef std::map<std::string, std::string, IgnoreCaseLT> KeyMap;
typedef std::map<std::string, KeyMap, IgnoreCaseLT> SectionMap;
typedef KeyMap::iterator KeyIterator;
typedef SectionMap::iterator SectionIterator;
public:
// 默认的构造函数和析构函数
IniDoc();
~IniDoc();
// 构造函数 - 加载文件
IniDoc(const std::string& file_name);
// 加载一个ini文件, 如果之前的文件被修改, 那么之前的ini文件将会被保持。
bool load(const std::string& file_name);
// 从字符串中作为ini文件加载
bool loadString(const std::string& str);
// 保持到加载ini位置
bool save();
// 另存为一个和加载路径不一样的文件中
bool saveAs(const std::string& file_name);
// 返回ini是否被修改, 或者他最后一次操作是保存
bool isModified() const { return m_modified; }
public: // high level member function.
// 下面的成员函数是从Section中获得一些值
long getInteger(const std::string& section, const std::string& key, long def_val);
float getFloat(const std::string& section, const std::string& key, float def_val);
long getStruct(const std::string& section, const std::string& key, void* buffer, long size);
long getString(const std::string& section, const std::string& key, const std::string& def_val, std::string& buffer);
const std::string getString(const std::string& section, const std::string& key, const std::string& def_val);
void setInteger(const std::string& section, const std::string& key, long value);
void setFloat(const std::string& section, const std::string& key, float value);
void setStruct(const std::string& section, const std::string& key, const void* buffer, long size);
void setString(const std::string& section, const std::string& key, const std::string& value);
public:
bool delSection(const std::string& section);
bool delKey(const std::string& section, const std::string& key);
public:
// 返回一个section的map键值对
const KeyMap& getSection(const std::string& section) const;
// 返回整个ini的Sections
const SectionMap& getIni() const { return m_map; }
bool IsKeyExist(const std::string& section,std::string key);
private:
void saveBeforeLoad();
const char* key_value(const std::string& section, const std::string& key);
private:
// 禁止复制构造函数和赋值操作符。
IniDoc(const IniDoc& copy);
IniDoc& operator=(const IniDoc& rhs);
private:
static const KeyMap ms_emptySection;
static const char left_tag;
static const char right_tag;
static const char equal;
static const char cr;
static const char new_line;
static const char* empty_str;
static const int BUFFER_LEN;
SectionMap m_map;
std::string m_file_name;
bool m_modified;
public:
SectionMap& get_map() {
return m_map;
}
};
+141
View File
@@ -0,0 +1,141 @@
#include "nanolog.h"
namespace nanolog {
#if defined (_MSC_VER)
#define COMBINE_PATH(path,name) (path + "\\" + name)
#include <io.h>
std::vector<std::string> get_all_files_in_dir(std::string dir_path)
{
std::vector<std::string> retV;
std::string p;
intptr_t hFile = 0;
struct _finddata_t fileinfo;
if ((hFile = _findfirst(p.assign(dir_path).append("\\*").c_str(), &fileinfo)) != -1)
{
do{
if ((fileinfo.attrib & _A_ARCH))
{
if (strcmp(fileinfo.name, ".") != 0 && strcmp(fileinfo.name, "..") != 0)
{
retV.push_back(fileinfo.name);
}
}
} while (_findnext(hFile, &fileinfo) == 0);
_findclose(hFile);
}
return retV;
}
int remove_file(const char* pathname)
{
return remove(pathname);
}
#include <Windows.h>
int create_dir(const char* path)
{
int ret;
if (path == NULL)
return -1;
if (_access(path,0) == 0)
return 0;
ret = CreateDirectoryA(path,nullptr);
return ret;
}
#else
#define COMBINE_PATH(path,name) (path + "/" + name)
#include <sys/types.h>
#include <dirent.h>
std::vector<std::string> get_all_files_in_dir(std::string dir_path)
{
DIR *pDir;
struct dirent *ent;
std::vector<std::string> retV;
pDir = opendir(dir_path.c_str());
while((ent = readdir(pDir)) != NULL)
{
if(ent->d_type & DT_DIR){
//ignore
}
else{
retV.emplace_back(ent->d_name);
}
}
return retV;
}
#include <stdio.h>
int remove_file(const char* pathname)
{
return remove(pathname);
}
#include <sys/stat.h>
#include <sys/types.h>
//#include <stdio.h>
#include <unistd.h>
int create_dir(const char* path)
{
int ret;
if(path == NULL)
return -1;
if(access(path,F_OK) >= 0)
return 0;
ret = mkdir(path,0777);
return ret;
}
#endif
std::atomic <unsigned int> loglevel;
std::unique_ptr <NanoLogger> nanologger;
std::atomic < NanoLogger * > atomic_nanologger;
std::atomic<bool> flush_to_console;
void Logger::initialize(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num)
{
create_dir(log_directory.c_str());
nanologger.reset(new NanoLogger(gl, log_directory, log_file_name, log_file_roll_size_mb, file_num));
atomic_nanologger.store(nanologger.get(), std::memory_order_seq_cst);
}
void Logger::initialize(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num)
{
nanologger.reset(new NanoLogger(ngl, log_directory, log_file_name, log_file_roll_size_mb, file_num));
atomic_nanologger.store(nanologger.get(), std::memory_order_seq_cst);
}
void Logger::set_log_level(LogLevel level)
{
loglevel.store(static_cast<unsigned int>(level), std::memory_order_release);
}
void Logger::set_flush_to_console(bool v)
{
flush_to_console.store(v);
}
bool Logger::is_logged(LogLevel level)
{
return static_cast<unsigned int>(level) >= loglevel.load(std::memory_order_relaxed);
}
bool Logger::operator==(NanoLogLine & logline)
{
#ifdef NDEBUG
// nothing to do
#else
if (flush_to_console) {
std::ostringstream ostm{};
logline.stringify(ostm);
std::cout << ostm.str();
}
#endif
atomic_nanologger.load(std::memory_order_acquire)->add(std::move(logline));
return true;
}
}
+965
View File
@@ -0,0 +1,965 @@
#ifndef __NANOLOG_H
#define __NANOLOG_H
#pragma warning( disable : 4996 )
#include <cstdint>
#include <memory>
#include <string>
#include <iosfwd>
#include <type_traits>
#include <cstring>
#include <chrono>
#include <ctime>
#include <thread>
#include <tuple>
#include <atomic>
#include <queue>
#include <fstream>
#include <sstream>
#include <iostream>
#include <algorithm>
#include <vector>
#include <set>
namespace nanolog
{
#if defined max
#undef max
#endif
extern std::vector<std::string> get_all_files_in_dir(std::string dir_path);
extern int remove_file(const char* pathname);
extern int create_dir(const char* path);
#define YEAR (1900)
#define MONTH (1)
#define CCT (+8)
#if defined (_MSC_VER)
#define COMBINE_PATH(path,name) (path + "\\" + name)
#else
#define COMBINE_PATH(path,name) (path + "/" + name)
#endif
class NanologBase
{
public:
/* Returns microseconds since epoch */
static uint64_t timestamp_now()
{
// return std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::high_resolution_clock::now().time_since_epoch()).count();
return std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::system_clock::now().time_since_epoch()).count();
}
static std::string get_datetime(uint64_t timestamp)
{
char miliseconds[4];
sprintf(miliseconds, "%03llu", timestamp % 1000);
std::time_t time_t = timestamp / 1000;
tm* gmtime = std::localtime(&time_t);
std::ostringstream ostr;
if(nullptr == gmtime)
{
ostr<< miliseconds;
}
else
{
char datetime[32];
//sprintf(datetime, "%d%02d%02d%02d%02d%02d", gmtime->tm_year+YEAR, gmtime->tm_mon+MONTH,
// gmtime->tm_mday, gmtime->tm_hour + CCT, gmtime->tm_min, gmtime->tm_sec);
sprintf(datetime, "%d%02d%02d", gmtime->tm_year+YEAR, gmtime->tm_mon+MONTH, gmtime->tm_mday);
ostr<< datetime;
}
return ostr.str();
}
/* I want [2016-10-13 00:01:23.528514] */
static void format_timestamp(std::ostream & os, uint64_t timestamp)
{
// The next 3 lines do not work on MSVC!
// auto duration = std::chrono::microseconds(timestamp);
// std::chrono::high_resolution_clock::time_point time_point(duration);
// std::time_t time_t = std::chrono::high_resolution_clock::to_time_t(time_point);
char miliseconds[7];
sprintf(miliseconds, "%03llu", timestamp % 1000);
std::time_t time_t = timestamp / 1000;
tm* gmtime = localtime(&time_t);
if(nullptr == gmtime)
{
os << '[' << miliseconds << ']';
}
else
{
char datetime[32];
sprintf(datetime, "%d-%02d-%02d %02d:%02d:%02d", gmtime->tm_year+YEAR, gmtime->tm_mon+MONTH,
gmtime->tm_mday, gmtime->tm_hour, gmtime->tm_min, gmtime->tm_sec);
os << '['<< datetime<< "."<< miliseconds << ']';
}
}
static std::thread::id this_thread_id()
{
//static thread_local const std::thread::id id = std::this_thread::get_id();
std::thread::id id = std::this_thread::get_id();
return id;
}
};
enum class LogLevel : uint8_t { INFO, WARN, CRIT };
class NanoLogLine
{
public:
template < typename T, typename Tuple >
struct TupleIndex;
template < typename T,typename ... Types >
struct TupleIndex < T, std::tuple < T, Types... > >
{
static constexpr const std::size_t value = 0;
};
template < typename T, typename U, typename ... Types >
struct TupleIndex < T, std::tuple < U, Types... > >
{
static constexpr const std::size_t value = 1 + TupleIndex < T, std::tuple < Types... > >::value;
};
struct string_literal_t
{
explicit string_literal_t(char const * s) : m_s(s) {}
char const * m_s;
};
typedef std::tuple < char, uint32_t, uint64_t, int32_t, int64_t, double, NanoLogLine::string_literal_t, char * > SupportedTypes;
NanoLogLine(LogLevel level, char const * file, char const * function, uint32_t line)
: m_bytes_used(0)
, m_buffer_size(sizeof(m_stack_buffer))
{
encode < uint64_t >(NanologBase::timestamp_now());
encode < std::thread::id >(NanologBase::this_thread_id());
encode < string_literal_t >(string_literal_t(file));
encode < string_literal_t >(string_literal_t(function));
encode < uint32_t >(line);
encode < LogLevel >(level);
}
~NanoLogLine() = default;
NanoLogLine(NanoLogLine &&) = default;
NanoLogLine& operator=(NanoLogLine &&) = default;
void stringify(std::ostream & os)
{
char * b = !m_heap_buffer ? m_stack_buffer : m_heap_buffer.get();
char const * const end = b + m_bytes_used;
uint64_t timestamp = *reinterpret_cast < uint64_t * >(b); b += sizeof(uint64_t);
std::thread::id threadid = *reinterpret_cast < std::thread::id * >(b); b += sizeof(std::thread::id);
string_literal_t file = *reinterpret_cast < string_literal_t * >(b); b += sizeof(string_literal_t);
string_literal_t function = *reinterpret_cast < string_literal_t * >(b); b += sizeof(string_literal_t);
uint32_t line = *reinterpret_cast < uint32_t * >(b); b += sizeof(uint32_t);
LogLevel loglevel = *reinterpret_cast < LogLevel * >(b); b += sizeof(LogLevel);
NanologBase::format_timestamp(os, timestamp);
os << '[' << to_string(loglevel) << ']'
<< '[' << threadid << ']'
<< '[' << file.m_s << ':' << function.m_s << ':' << line << "] ";
stringify(os, b, end);
os << std::endl;
if (loglevel >= LogLevel::CRIT)
os.flush();
}
NanoLogLine& operator<<(char arg)
{
encode < char >(arg, TupleIndex < char, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(int32_t arg)
{
encode < int32_t >(arg, TupleIndex < int32_t, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(uint32_t arg)
{
encode < uint32_t >(arg, TupleIndex < uint32_t, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(int64_t arg)
{
encode < int64_t >(arg, TupleIndex < int64_t, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(uint64_t arg)
{
encode < uint64_t >(arg, TupleIndex < uint64_t, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(double arg)
{
encode < double >(arg, TupleIndex < double, SupportedTypes >::value);
return *this;
}
NanoLogLine& operator<<(std::string const & arg)
{
encode_c_string(arg.c_str(), arg.length());
return *this;
}
template < size_t N >
NanoLogLine& operator<<(const char (&arg)[N])
{
encode(string_literal_t(arg));
return *this;
}
template < typename Arg >
typename std::enable_if < std::is_same < Arg, char const * >::value, NanoLogLine& >::type
operator<<(Arg const & arg)
{
encode(arg);
return *this;
}
template < typename Arg >
typename std::enable_if < std::is_same < Arg, char * >::value, NanoLogLine& >::type
operator<<(Arg const & arg)
{
encode(arg);
return *this;
}
private:
char const * to_string(LogLevel loglevel)
{
switch (loglevel)
{
case LogLevel::INFO:
return "INFO";
case LogLevel::WARN:
return "WARN";
case LogLevel::CRIT:
return "CRIT";
}
return "XXXX";
}
char * buffer()
{
return !m_heap_buffer ? &m_stack_buffer[m_bytes_used] : &(m_heap_buffer.get())[m_bytes_used];
}
template < typename Arg >
void encode(Arg arg)
{
*reinterpret_cast<Arg*>(buffer()) = arg;
m_bytes_used += sizeof(Arg);
}
template < typename Arg >
void encode(Arg arg, uint8_t type_id)
{
resize_buffer_if_needed(sizeof(Arg) + sizeof(uint8_t));
encode < uint8_t >(type_id);
encode < Arg >(arg);
}
void encode(char * arg)
{
if (arg != nullptr)
encode_c_string(arg, strlen(arg));
}
void encode(char const * arg)
{
if (arg != nullptr)
encode_c_string(arg, strlen(arg));
}
void encode(string_literal_t arg)
{
encode < string_literal_t >(arg, TupleIndex < string_literal_t, SupportedTypes >::value);
}
void encode_c_string(char const * arg, size_t length)
{
if (length == 0)
return;
resize_buffer_if_needed(1 + length + 1);
char * b = buffer();
auto type_id = TupleIndex < char *, SupportedTypes >::value;
*reinterpret_cast<uint8_t*>(b++) = static_cast<uint8_t>(type_id);
memcpy(b, arg, length + 1);
m_bytes_used += 1 + length + 1;
}
void resize_buffer_if_needed(size_t additional_bytes)
{
size_t const required_size = m_bytes_used + additional_bytes;
if (required_size <= m_buffer_size)
return;
if (!m_heap_buffer)
{
m_buffer_size = std::max(static_cast<size_t>(512), required_size);
m_heap_buffer.reset(new char[m_buffer_size]);
memcpy(m_heap_buffer.get(), m_stack_buffer, m_bytes_used);
return;
}
else
{
m_buffer_size = std::max(static_cast<size_t>(2 * m_buffer_size), required_size);
std::unique_ptr < char [] > new_heap_buffer(new char[m_buffer_size]);
memcpy(new_heap_buffer.get(), m_heap_buffer.get(), m_bytes_used);
m_heap_buffer.swap(new_heap_buffer);
}
}
void stringify(std::ostream & os, char * start, char const * const end)
{
if (start == end)
return;
int type_id = static_cast < int >(*start); start++;
switch (type_id)
{
case 0:
stringify(os, decode(os, start, static_cast<std::tuple_element<0, SupportedTypes>::type*>(nullptr)), end);
return;
case 1:
stringify(os, decode(os, start, static_cast<std::tuple_element<1, SupportedTypes>::type*>(nullptr)), end);
return;
case 2:
stringify(os, decode(os, start, static_cast<std::tuple_element<2, SupportedTypes>::type*>(nullptr)), end);
return;
case 3:
stringify(os, decode(os, start, static_cast<std::tuple_element<3, SupportedTypes>::type*>(nullptr)), end);
return;
case 4:
stringify(os, decode(os, start, static_cast<std::tuple_element<4, SupportedTypes>::type*>(nullptr)), end);
return;
case 5:
stringify(os, decode(os, start, static_cast<std::tuple_element<5, SupportedTypes>::type*>(nullptr)), end);
return;
case 6:
stringify(os, decode(os, start, static_cast<std::tuple_element<6, SupportedTypes>::type*>(nullptr)), end);
return;
case 7:
stringify(os, decode(os, start, static_cast<std::tuple_element<7, SupportedTypes>::type*>(nullptr)), end);
return;
}
}
template < typename Arg >
char * decode(std::ostream & os, char * b, Arg * dummy)
{
Arg arg = *reinterpret_cast < Arg * >(b);
os << arg;
return b + sizeof(Arg);
}
char * decode(std::ostream & os, char * b, NanoLogLine::string_literal_t * dummy)
{
NanoLogLine::string_literal_t s = *reinterpret_cast < NanoLogLine::string_literal_t * >(b);
os << s.m_s;
return b + sizeof(NanoLogLine::string_literal_t);
}
char * decode(std::ostream & os, char * b, char ** dummy)
{
while (*b != '\0')
{
os << *b;
++b;
}
return ++b;
}
private:
size_t m_bytes_used;
size_t m_buffer_size;
std::unique_ptr < char [] > m_heap_buffer;
char m_stack_buffer[256 - 2 * sizeof(size_t) - sizeof(decltype(m_heap_buffer)) - 8 /* Reserved */];
};
struct BufferBase
{
virtual ~BufferBase() = default;
virtual void push(NanoLogLine && logline) = 0;
virtual bool try_pop(NanoLogLine & logline) = 0;
};
class SpinLock
{
public:
SpinLock(std::atomic_flag & flag) : m_flag(flag)
{
while (m_flag.test_and_set(std::memory_order_acquire));
}
~SpinLock()
{
m_flag.clear(std::memory_order_release);
}
private:
std::atomic_flag & m_flag;
};
/* Multi Producer Single Consumer Ring Buffer */
class RingBuffer : public BufferBase
{
public:
struct alignas(64) Item
{
Item()
: written(0)
, logline(LogLevel::INFO, nullptr, nullptr, 0)
{
}
std::atomic_flag flag = ATOMIC_FLAG_INIT;
char written;
char padding[256 - sizeof(std::atomic_flag) - sizeof(char) - sizeof(NanoLogLine)];
NanoLogLine logline;
};
RingBuffer(size_t const size)
: m_size(size)
, m_ring(static_cast<Item*>(std::malloc(size * sizeof(Item))))
, m_write_index(0)
, m_read_index(0)
{
for (size_t i = 0; i < m_size; ++i)
{
new (&m_ring[i]) Item();
}
static_assert(sizeof(Item) == 256, "Unexpected size != 256");
}
~RingBuffer()
{
for (size_t i = 0; i < m_size; ++i)
{
m_ring[i].~Item();
}
std::free(m_ring);
}
void push(NanoLogLine && logline) override
{
unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed) % m_size;
Item & item = m_ring[write_index];
SpinLock spinlock(item.flag);
item.logline = std::move(logline);
item.written = 1;
}
bool try_pop(NanoLogLine & logline) override
{
Item & item = m_ring[m_read_index % m_size];
SpinLock spinlock(item.flag);
if (item.written == 1)
{
logline = std::move(item.logline);
item.written = 0;
++m_read_index;
return true;
}
return false;
}
RingBuffer(RingBuffer const &) = delete;
RingBuffer& operator=(RingBuffer const &) = delete;
private:
size_t const m_size;
Item * m_ring;
std::atomic < unsigned int > m_write_index;
char pad[64];
unsigned int m_read_index;
};
class Buffer
{
public:
struct Item
{
Item(NanoLogLine && nanologline) : logline(std::move(nanologline)) {}
char padding[256 - sizeof(NanoLogLine)];
NanoLogLine logline;
};
static constexpr const size_t size = 32768; // 8MB. Helps reduce memory fragmentation
Buffer() : m_buffer(static_cast<Item*>(std::malloc(size * sizeof(Item))))
{
for (size_t i = 0; i <= size; ++i)
{
m_write_state[i].store(0, std::memory_order_relaxed);
}
static_assert(sizeof(Item) == 256, "Unexpected size != 256");
}
~Buffer()
{
unsigned int write_count = m_write_state[size].load();
for (size_t i = 0; i < write_count; ++i)
{
m_buffer[i].~Item();
}
std::free(m_buffer);
}
// Returns true if we need to switch to next buffer
bool push(NanoLogLine && logline, unsigned int const write_index)
{
new (&m_buffer[write_index]) Item(std::move(logline));
m_write_state[write_index].store(1, std::memory_order_release);
return m_write_state[size].fetch_add(1, std::memory_order_acquire) + 1 == size;
}
bool try_pop(NanoLogLine & logline, unsigned int const read_index)
{
if (m_write_state[read_index].load(std::memory_order_acquire))
{
Item & item = m_buffer[read_index];
logline = std::move(item.logline);
return true;
}
return false;
}
Buffer(Buffer const &) = delete;
Buffer& operator=(Buffer const &) = delete;
private:
Item * m_buffer;
std::atomic < unsigned int > m_write_state[size + 1];
};
class QueueBuffer : public BufferBase
{
public:
QueueBuffer(QueueBuffer const &) = delete;
QueueBuffer& operator=(QueueBuffer const &) = delete;
QueueBuffer() : m_current_read_buffer{nullptr}
, m_write_index(0)
, m_read_index(0)
{
setup_next_write_buffer();
}
void push(NanoLogLine && logline) override
{
unsigned int write_index = m_write_index.fetch_add(1, std::memory_order_relaxed);
if (write_index < Buffer::size)
{
if (m_current_write_buffer.load(std::memory_order_acquire)->push(std::move(logline), write_index))
{
setup_next_write_buffer();
}
}
else
{
while (m_write_index.load(std::memory_order_acquire) >= Buffer::size);
push(std::move(logline));
}
}
bool try_pop(NanoLogLine & logline) override
{
if (m_current_read_buffer == nullptr)
m_current_read_buffer = get_next_read_buffer();
Buffer* read_buffer = m_current_read_buffer;
if (read_buffer == nullptr)
return false;
if (read_buffer->try_pop(logline, m_read_index))
{
m_read_index++;
if (m_read_index == Buffer::size)
{
m_read_index = 0;
m_current_read_buffer = nullptr;
SpinLock spinlock(m_flag);
m_buffers.pop();
}
return true;
}
return false;
}
private:
void setup_next_write_buffer()
{
std::unique_ptr < Buffer > next_write_buffer(new Buffer());
m_current_write_buffer.store(next_write_buffer.get(), std::memory_order_release);
SpinLock spinlock(m_flag);
m_buffers.push(std::move(next_write_buffer));
m_write_index.store(0, std::memory_order_relaxed);
}
Buffer * get_next_read_buffer()
{
SpinLock spinlock(m_flag);
return m_buffers.empty() ? nullptr : m_buffers.front().get();
}
private:
std::queue < std::unique_ptr < Buffer > > m_buffers;
std::atomic < Buffer * > m_current_write_buffer;
Buffer * m_current_read_buffer;
std::atomic < unsigned int > m_write_index;
unsigned int m_read_index;
std::atomic_flag m_flag = ATOMIC_FLAG_INIT;
};
class FileWriter
{
public:
struct sfile_data
{
int64_t date = 0LL;
int64_t index = 0LL;
bool operator > (const sfile_data& right) const
{
if(date > right.date)
{
return true;
}
else if(date < right.date)
{
return false;
}
else
{
return index > right.index;
}
}
bool operator == (const sfile_data& right) const
{
return (date == right.date) && (index == right.index);
}
};
FileWriter(std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num)
: m_log_file_roll_size_bytes(log_file_roll_size_mb * 1024 * 1024)
, m_log_directory(log_directory)
, m_name(log_directory + "/" + log_file_name)
, m_filenum(file_num)
, m_first_create(true)
{
//roll_file();
init_fileset();
create_file();
}
void write(NanoLogLine & logline)
{
auto pos = m_os->tellp();
logline.stringify(*m_os);
m_bytes_written += m_os->tellp() - pos;
if (m_bytes_written > m_log_file_roll_size_bytes)
{
roll_file();
}
}
private:
void init_fileset()
{
auto files = get_all_files_in_dir(m_log_directory);
for(auto& p : files)
{
insert(COMBINE_PATH(m_log_directory,p));
}
}
void insert(sfile_data file_data)
{
if(file_data.date <= 0LL || file_data.index < 0LL)
{
return;
}
m_file_value_set.insert(file_data);
}
void insert(const std::string& filename)
{
sfile_data file_data = filename_to_file_data(filename);
if(file_data.date <= 0LL || file_data.index < 0LL)
{
return;
}
m_file_value_set.insert(file_data);
}
sfile_data filename_to_file_data(const std::string& filename){
sfile_data file_data;
size_t pos1 = filename.find_first_of("_");
if (pos1 == std::string::npos)
{
return file_data;
}
size_t pos2 = filename.find_last_of("_");
if (pos2 == std::string::npos)
{
return file_data;
}
std::string str_date = filename.substr(pos1 + 1, pos2 - pos1);
file_data.date = atoll(str_date.c_str());
std::string str_index = filename.substr(pos2 + 1);
file_data.index = atoll(str_index.c_str());
return file_data;
}
std::string get_logfilename()
{
std::string log_file_name = "_";
std::string date_time = NanologBase::get_datetime(NanologBase::timestamp_now());
log_file_name.append(date_time);
log_file_name.append("_");
log_file_name.append(std::to_string(++m_file_number));
return log_file_name;
}
void create_file()
{
if(m_first_create){
sfile_data file_data_max;
if(!m_file_value_set.empty())
{
file_data_max = *(m_file_value_set.begin());
}
sfile_data cur_file_data = filename_to_file_data(get_logfilename());
if(file_data_max > cur_file_data ||
file_data_max == cur_file_data)
{
cur_file_data.index = file_data_max.index;
}
const std::string logfile_type = ".txt";
std::string file_path = m_name + file_data_to_filename(cur_file_data) + logfile_type;
m_os.reset(new std::ofstream());
m_os->open(file_path, std::ofstream::out | std::ofstream::app);
insert(cur_file_data);
init_file_number(cur_file_data);
m_first_create = false;
}
else
{
const std::string logfile_type = ".txt";
std::string logfile_suffix = get_logfilename();
std::string file_path = m_name + logfile_suffix + logfile_type;
m_os.reset(new std::ofstream());
m_os->open(file_path, std::ofstream::out | std::ofstream::app);
insert(logfile_suffix);
}
delete_exceed_file();
}
void init_file_number(const sfile_data& cur_file_data){
m_file_number = cur_file_data.index;
}
void delete_exceed_file(){
if(m_filenum > 0 && m_file_value_set.size() > m_filenum)
{
int num = 0;
auto it_set = m_file_value_set.begin();
while(it_set != m_file_value_set.end())
{
if(num >= m_filenum)
{
const std::string logfile_type = ".txt";
std::string filename = m_name + file_data_to_filename(*it_set) + logfile_type;
try{
remove_file(filename.c_str());
}
catch(const std::exception& excep)
{
}
m_file_value_set.erase(it_set++);
}
else
{
++num;
++it_set;
}
}
}
}
void roll_file()
{
if (m_os)
{
m_os->flush();
m_os->close();
}
m_bytes_written = 0;
create_file();
}
std::string file_data_to_filename(const sfile_data& cur_file_data)
{
std::string filename = "_";
filename += std::to_string(cur_file_data.date);
filename += "_";
filename += std::to_string(cur_file_data.index);
return filename;
}
private:
int64_t m_file_number = 0;
std::streamoff m_bytes_written = 0;
uint32_t const m_log_file_roll_size_bytes;
std::string const m_log_directory;
std::string const m_name;
int m_filenum;
bool m_first_create;
std::unique_ptr < std::ofstream > m_os;
std::set<sfile_data, std::greater<sfile_data> > m_file_value_set;
};
/*
* Non guaranteed logging. Uses a ring buffer to hold log lines.
* When the ring gets full, the previous log line in the slot will be dropped.
* Does not block producer even if the ring buffer is full.
* ring_buffer_size_mb - LogLines are pushed into a mpsc ring buffer whose size
* is determined by this parameter. Since each LogLine is 256 bytes,
* ring_buffer_size = ring_buffer_size_mb * 1024 * 1024 / 256
*/
struct NonGuaranteedLogger
{
NonGuaranteedLogger(uint32_t ring_buffer_size_mb_) : ring_buffer_size_mb(ring_buffer_size_mb_) {}
uint32_t ring_buffer_size_mb;
};
/*
* Provides a guarantee log lines will not be dropped.
*/
struct GuaranteedLogger
{
};
class NanoLogger
{
public:
NanoLogger(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num)
: m_state(State::INIT)
, m_buffer_base(new RingBuffer(std::max(1u, ngl.ring_buffer_size_mb) * 1024 * 4))
, m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb), file_num)
, m_thread(&NanoLogger::pop, this)
{
m_state.store(State::READY, std::memory_order_release);
}
NanoLogger(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num)
: m_state(State::INIT)
, m_buffer_base(new QueueBuffer())
, m_file_writer(log_directory, log_file_name, std::max(1u, log_file_roll_size_mb), file_num)
, m_thread(&NanoLogger::pop, this)
{
m_state.store(State::READY, std::memory_order_release);
}
~NanoLogger()
{
m_state.store(State::SHUTDOWN);
m_thread.join();
}
void add(NanoLogLine && logline)
{
m_buffer_base->push(std::move(logline));
}
void pop()
{
// Wait for constructor to complete and pull all stores done there to this thread / core.
while (m_state.load(std::memory_order_acquire) == State::INIT)
std::this_thread::sleep_for(std::chrono::microseconds(50));
NanoLogLine logline(LogLevel::INFO, nullptr, nullptr, 0);
while (m_state.load() == State::READY)
{
if (m_buffer_base->try_pop(logline))
m_file_writer.write(logline);
else
std::this_thread::sleep_for(std::chrono::microseconds(50));
}
// Pop and log all remaining entries
while (m_buffer_base->try_pop(logline))
{
m_file_writer.write(logline);
}
}
private:
enum class State
{
INIT,
READY,
SHUTDOWN
};
std::atomic < State > m_state;
std::unique_ptr < BufferBase > m_buffer_base;
FileWriter m_file_writer;
std::thread m_thread;
};
//std::atomic <unsigned int> loglevel;
//std::unique_ptr <NanoLogger> nanologger;
//std::atomic < NanoLogger * > atomic_nanologger;
//std::atomic<bool> flush_to_console;
class Logger
{
public:
static void initialize(GuaranteedLogger gl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num);
static void initialize(NonGuaranteedLogger ngl, std::string const & log_directory, std::string const & log_file_name, uint32_t log_file_roll_size_mb, int file_num);
static void set_log_level(LogLevel level);
static void set_flush_to_console(bool v);
public:
static bool is_logged(LogLevel level);
bool operator==(NanoLogLine & logline);
};
} // namespace nanolog
#endif
#ifdef _WIN32
#define FILE_NAME(x) strrchr(x,'\\')?strrchr(x,'\\')+1:x
#else
#define FILE_NAME(x) x
#endif
#if defined (NDEBUG)
#define NANO_LOG(LEVEL) ::nanolog::Logger() == ::nanolog::NanoLogLine(LEVEL, "", __func__, __LINE__)
#else
#define NANO_LOG(LEVEL) ::nanolog::Logger() == ::nanolog::NanoLogLine(LEVEL, FILE_NAME(__FILE__), __func__, __LINE__)
#endif
#define LOG_INFO nanolog::Logger::is_logged(nanolog::LogLevel::INFO) && NANO_LOG(nanolog::LogLevel::INFO)
#define LOG_WARN nanolog::Logger::is_logged(nanolog::LogLevel::WARN) && NANO_LOG(nanolog::LogLevel::WARN)
#define LOG_CRIT nanolog::Logger::is_logged(nanolog::LogLevel::CRIT) && NANO_LOG(nanolog::LogLevel::CRIT)
+5
View File
@@ -0,0 +1,5 @@
// pch.cpp: 与预编译标头对应的源文件
#include "pch.h"
// 当使用预编译的头时,需要使用此源文件,编译才能成功。
+13
View File
@@ -0,0 +1,13 @@
// pch.h: 这是预编译标头文件。
// 下方列出的文件仅编译一次,提高了将来生成的生成性能。
// 这还将影响 IntelliSense 性能,包括代码完成和许多代码浏览功能。
// 但是,如果此处列出的文件中的任何一个在生成之间有更新,它们全部都将被重新编译。
// 请勿在此处添加要频繁更新的文件,这将使得性能优势无效。
#ifndef PCH_H
#define PCH_H
// 添加要在此处预编译的标头
#include "framework.h"
#endif //PCH_H
+345
View File
@@ -0,0 +1,345 @@
#include <iostream>
#include <list>
#include <mutex>
#include "post_helper.h"
#include "util_helper.h"
#include "app_config.h"
#include "nanolog.h"
#include <Shlwapi.h>
#pragma comment(lib, "libcurl.lib")
static size_t ret_data(void* buffer, size_t size, size_t nmemb, void* userp)
{
string* ststr = (string*)userp;
ststr->append((char*)buffer, size * nmemb);
return nmemb;
}
size_t write_callback(char* ptr, size_t size, size_t nmemb, void* userdata)
{
FILE* fp = static_cast<FILE*>(userdata);
if (!fp) return 0;
size_t length = fwrite(ptr, size, nmemb, fp);
if (length != nmemb) {
return length;
}
return size * nmemb;
}
bool PostHelper::PostLogin(const std::string& name, const std::string& passwd)
{
string strRes;
struct curl_slist* chunk = NULL;
//设置url
string strUrl = AppConfig::Instance()->DataServerUrl + "/business/system/auth/login";
curl_easy_setopt(curl_, CURLOPT_URL, strUrl.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, TRUE);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ret_data);
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type:application/json;charset=utf-8");
curl_easy_setopt(curl_, CURLOPT_POST, 1);
Json::Value root;
root["username"] = name;
root["password"] = passwd;
Json::StreamWriterBuilder builder;
const std::string strLoginPost = Json::writeString(builder, root);
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, strLoginPost.c_str());
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 2);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &strRes);
// 执行一次URL请求
CURLcode res = curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (!strRes.empty())
{
strRes = strRes.substr(strRes.find('{'), strRes.size() - 1);
bool res;
JSONCPP_STRING errs;
Json::Value root;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
res = jsonReader->parse(strRes.c_str(), strRes.c_str() + strRes.length(), &root, &errs);
if (!res || !errs.empty()) {
std::cout << "parseJson err. " << errs << std::endl;
return false;
}
if (root["code"].asInt() == 200) {
token_ = root["data"]["accessToken"].asString();
refresh_token_ = root["data"]["refreshToken"].asString();
}
return true;
}
return false;
}
bool PostHelper::RefreshToken() {
std::lock_guard guard(mutex_);
string strRes;
//设置url
string strUrl = AppConfig::Instance()->DataServerUrl + "/business/system/auth/refresh-token";
curl_easy_setopt(curl_, CURLOPT_URL, strUrl.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, TRUE);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ret_data);
struct curl_slist* headers = NULL;
string headerToken = "GeomativeAuthorization:" + token_;
headers = curl_slist_append(headers, headerToken.c_str());
curl_easy_setopt(curl_, CURLOPT_POST, 1);
struct curl_httppost* formpost = NULL;
struct curl_httppost* lastptr = NULL;
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "refreshToken", CURLFORM_COPYCONTENTS, PostHelper::refresh_token_.c_str(), CURLFORM_END);
curl_easy_setopt(curl_, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 2);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &strRes);
// 执行一次URL请求
CURLcode res = curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (!strRes.empty())
{
strRes = strRes.substr(strRes.find('{'), strRes.size() - 1);
bool res;
JSONCPP_STRING errs;
Json::Value root;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
res = jsonReader->parse(strRes.c_str(), strRes.c_str() + strRes.length(), &root, &errs);
if (!res || !errs.empty()) {
std::cout << "parseJson err. " << errs << std::endl;
return false;
}
if (root["code"].asInt() == 200) {
token_ = root["data"]["accessToken"].asString();
refresh_token_ = root["data"]["refreshToken"].asString();
}
return true;
}
return false;
}
bool PostHelper::Get(const std::string& url, Json::Value& res) {
std::lock_guard guard(mutex_);
std::string strRes;
//设置url
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, TRUE);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ret_data);
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Content-Type:application/json;charset=utf-8");
string headerToken = "GeomativeAuthorization:" + token_;
headers = curl_slist_append(headers, headerToken.c_str());
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &strRes);
// 执行一次URL请求
curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (!strRes.empty())
{
if (strRes.find('{') == string::npos)
return false;
strRes = strRes.substr(strRes.find('{'), strRes.size() - 1);
JSONCPP_STRING errs;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
if (!jsonReader->parse(strRes.c_str(), strRes.c_str() + strRes.length(), &res, &errs) || !errs.empty()) {
std::cout << "parseJson err. " << errs << std::endl;
return false;
}
return true;
}
return false;
}
bool PostHelper::Get(const std::string& url, const std::filesystem::path& file_path) {
std::lock_guard guard(mutex_);
FILE* fp = fopen(file_path.string().c_str(), "wb");
if (!fp) return false;
//设置url
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, false);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, write_callback);
struct curl_slist* headers = NULL;
string headerToken = "GeomativeAuthorization:" + token_;
headers = curl_slist_append(headers, headerToken.c_str());
curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L);
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, fp);
// 执行一次URL请求
CURLcode res = curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (fp) fclose(fp);
return res == CURLE_OK;
}
bool PostHelper::Post(const std::string& url, const Json::Value& arg, Json::Value& res)
{
std::lock_guard guard(mutex_);
string strRes;
//设置url
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, TRUE);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ret_data);
struct curl_slist* headers = NULL;
headers = curl_slist_append(headers, "Expect:");
headers = curl_slist_append(headers, "Content-Type:application/json;charset=utf-8");
if (!token_.empty()) {
string headerToken = "GeomativeAuthorization:" + token_;
headers = curl_slist_append(headers, headerToken.c_str());
}
//struct curl_httppost* formpost = NULL;
//struct curl_httppost* lastptr = NULL;
//for (auto it = arg.begin(); it != arg.end(); it++) {
// Json::StreamWriterBuilder builder;
// std::string& content = it->type() < Json::ValueType::arrayValue ? it->asString() : Json::writeString(builder, *it);
// curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, it.name().c_str(), CURLFORM_COPYCONTENTS, content.c_str(), CURLFORM_END);
//}
//curl_easy_setopt(curl_, CURLOPT_HTTPPOST, formpost);
Json::StreamWriterBuilder builder;
const std::string strLoginPost = Json::writeString(builder, arg);
curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, strLoginPost.c_str());
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 2);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &strRes);
// 执行一次URL请求
CURLcode res1 = curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (!strRes.empty())
{
strRes = strRes.substr(strRes.find('{'), strRes.size() - 1);
JSONCPP_STRING errs;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
if (!jsonReader->parse(strRes.c_str(), strRes.c_str() + strRes.length(), &res, &errs) || !errs.empty()) {
std::cout << "parseJson err. " << errs << std::endl;
return false;
}
return true;
}
return false;
}
bool PostHelper::Post(const std::string& url, const Json::Value& arg, const std::string& filePath, Json::Value& res) {
string strRes;
//设置url
curl_easy_setopt(curl_, CURLOPT_URL, url.c_str());
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, false); //忽略服务器验证
curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, false);
curl_easy_setopt(curl_, CURLOPT_HEADER, TRUE);
curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, ret_data);
struct curl_slist* headers = NULL;
string headerToken = "GeomativeAuthorization:" + token_;
headers = curl_slist_append(headers, headerToken.c_str());
struct curl_httppost* formpost = NULL;
struct curl_httppost* lastptr = NULL;
for (auto& it = arg.begin(); it != arg.end(); it++) {
Json::StreamWriterBuilder builder;
std::string& content = it->type() < Json::ValueType::arrayValue ? it->asString() : Json::writeString(builder, *it);
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, it.name().c_str(), CURLFORM_COPYCONTENTS, content.c_str(), CURLFORM_END);
}
if (!filePath.empty()) {
auto fileName = filePath.substr(filePath.find_last_of('\\') + 1, filePath.length());
curl_formadd(&formpost, &lastptr, CURLFORM_COPYNAME, "file", CURLFORM_FILENAME, fileName.c_str(), CURLFORM_FILE, filePath.c_str(), CURLFORM_END);
}
curl_easy_setopt(curl_, CURLOPT_HTTPPOST, formpost);
curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers);
curl_easy_setopt(curl_, CURLOPT_TIMEOUT, 10);
curl_easy_setopt(curl_, CURLOPT_WRITEDATA, &strRes);
// 执行一次URL请求
curl_easy_perform(curl_);
curl_slist_free_all(headers);
if (!strRes.empty())
{
strRes = strRes.substr(strRes.find('{'), strRes.size() - 1);
JSONCPP_STRING errs;
Json::CharReaderBuilder readerBuilder;
std::unique_ptr<Json::CharReader> const jsonReader(readerBuilder.newCharReader());
if (!jsonReader->parse(strRes.c_str(), strRes.c_str() + strRes.length(), &res, &errs) || !errs.empty()) {
return false;
}
return true;
}
return false;
}
void PostHelper::init_curl() {
curl_ = curl_easy_init();
}
void PostHelper::exit_curl() {
if (nullptr != curl_) {
curl_easy_cleanup(curl_);
}
}
+40
View File
@@ -0,0 +1,40 @@
#pragma once
#include <map>
#include <list>
#include <string>
#include <mutex>
#include <memory>
#include <filesystem>
#include "json/json.h"
#include "curl/curl.h"
class PostHelper
{
public:
PostHelper() {
init_curl();
}
~PostHelper() {
exit_curl();
}
public:
bool PostLogin(const std::string& name, const std::string& passwd);
bool RefreshToken();
bool Get(const std::string& api, Json::Value& res);
bool Get(const std::string& url, const std::filesystem::path& file_path);
bool Post(const std::string& api, const Json::Value& arg, Json::Value& res);
bool Post(const std::string& api, const Json::Value& arg, const std::string& filePath, Json::Value& res);
protected:
void init_curl();
void exit_curl();
private:
CURL* curl_;
std::string token_, refresh_token_;
std::mutex mutex_;
};
+5
View File
@@ -0,0 +1,5 @@
#pragma once
#include <string>
#include <vector>
+76
View File
@@ -0,0 +1,76 @@
#pragma once
#include <windows.h>
#include <shellapi.h>
class UtilHelper {
public:
static std::string StringFormat(const char* format, ...)
{
#if 1 // 最大长度限制:1024 - 1
char buff[1024] = { 0 };
va_list args;
va_start(args, format);
vsprintf_s(buff, sizeof(buff), format, args);
va_end(args);
std::string str(buff);
return str;
#else // 无长度限制
va_list args;
va_start(args, format);
int count = vsnprintf(NULL, 0, format, args); // 使用vsnprintfwarning C4996; 使用vsnprintf_s:无法自动计算长度
va_end(args);
va_start(args, format);
char* buff = (char*)malloc(count * sizeof(wchar_t));
vsnprintf(buff, count, format, args);
va_end(args);
std::string str(buff, count);
free(buff);
return str;
#endif
}
static std::string LaunchPath()
{
CHAR tszModule[MAX_PATH + 1] = { 0 };
::GetModuleFileNameA(NULL, tszModule, MAX_PATH); /* 获取结果为应用程序全路径名称 */
std::string filepath(tszModule);
std::string dir = filepath.substr(0, filepath.find_last_of(L'\\'));
return dir;
}
//UTF-8到GB2312的转换
static char* U2G(const char* utf8)
{
int len = MultiByteToWideChar(CP_UTF8, 0, utf8, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_UTF8, 0, utf8, -1, wstr, len);
len = WideCharToMultiByte(CP_ACP, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_ACP, 0, wstr, -1, str, len, NULL, NULL);
if (wstr) delete[] wstr;
return str;
}
//GB2312到UTF-8的转换
static char* G2U(const char* gb2312)
{
int len = MultiByteToWideChar(CP_ACP, 0, gb2312, -1, NULL, 0);
wchar_t* wstr = new wchar_t[len + 1];
memset(wstr, 0, len + 1);
MultiByteToWideChar(CP_ACP, 0, gb2312, -1, wstr, len);
len = WideCharToMultiByte(CP_UTF8, 0, wstr, -1, NULL, 0, NULL, NULL);
char* str = new char[len + 1];
memset(str, 0, len + 1);
WideCharToMultiByte(CP_UTF8, 0, wstr, -1, str, len, NULL, NULL);
if (wstr) delete[] wstr;
return str;
}
};
+2
View File
@@ -0,0 +1,2 @@
# GeomativeSDK
+34
View File
@@ -0,0 +1,34 @@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.haxx.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
pkginclude_HEADERS = \
curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \
typecheck-gcc.h system.h
pkgincludedir= $(includedir)/curl
checksrc:
@@PERL@ $(top_srcdir)/lib/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS)
if CURLDEBUG
# for debug builds, we scan the sources on all regular make invokes
all-local: checksrc
endif
+686
View File
@@ -0,0 +1,686 @@
# Makefile.in generated by automake 1.15.1 from Makefile.am.
# @configure_input@
# Copyright (C) 1994-2017 Free Software Foundation, Inc.
# This Makefile.in is free software; the Free Software Foundation
# gives unlimited permission to copy and/or distribute it,
# with or without modifications, as long as this notice is preserved.
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
# PARTICULAR PURPOSE.
@SET_MAKE@
VPATH = @srcdir@
am__is_gnu_make = { \
if test -z '$(MAKELEVEL)'; then \
false; \
elif test -n '$(MAKE_HOST)'; then \
true; \
elif test -n '$(MAKE_VERSION)' && test -n '$(CURDIR)'; then \
true; \
else \
false; \
fi; \
}
am__make_running_with_option = \
case $${target_option-} in \
?) ;; \
*) echo "am__make_running_with_option: internal error: invalid" \
"target option '$${target_option-}' specified" >&2; \
exit 1;; \
esac; \
has_opt=no; \
sane_makeflags=$$MAKEFLAGS; \
if $(am__is_gnu_make); then \
sane_makeflags=$$MFLAGS; \
else \
case $$MAKEFLAGS in \
*\\[\ \ ]*) \
bs=\\; \
sane_makeflags=`printf '%s\n' "$$MAKEFLAGS" \
| sed "s/$$bs$$bs[$$bs $$bs ]*//g"`;; \
esac; \
fi; \
skip_next=no; \
strip_trailopt () \
{ \
flg=`printf '%s\n' "$$flg" | sed "s/$$1.*$$//"`; \
}; \
for flg in $$sane_makeflags; do \
test $$skip_next = yes && { skip_next=no; continue; }; \
case $$flg in \
*=*|--*) continue;; \
-*I) strip_trailopt 'I'; skip_next=yes;; \
-*I?*) strip_trailopt 'I';; \
-*O) strip_trailopt 'O'; skip_next=yes;; \
-*O?*) strip_trailopt 'O';; \
-*l) strip_trailopt 'l'; skip_next=yes;; \
-*l?*) strip_trailopt 'l';; \
-[dEDm]) skip_next=yes;; \
-[JT]) skip_next=yes;; \
esac; \
case $$flg in \
*$$target_option*) has_opt=yes; break;; \
esac; \
done; \
test $$has_opt = yes
am__make_dryrun = (target_option=n; $(am__make_running_with_option))
am__make_keepgoing = (target_option=k; $(am__make_running_with_option))
pkgdatadir = $(datadir)/@PACKAGE@
pkglibdir = $(libdir)/@PACKAGE@
pkglibexecdir = $(libexecdir)/@PACKAGE@
am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
install_sh_DATA = $(install_sh) -c -m 644
install_sh_PROGRAM = $(install_sh) -c
install_sh_SCRIPT = $(install_sh) -c
INSTALL_HEADER = $(INSTALL_DATA)
transform = $(program_transform_name)
NORMAL_INSTALL = :
PRE_INSTALL = :
POST_INSTALL = :
NORMAL_UNINSTALL = :
PRE_UNINSTALL = :
POST_UNINSTALL = :
build_triplet = @build@
host_triplet = @host@
subdir = include/curl
ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
am__aclocal_m4_deps = $(top_srcdir)/m4/ax_code_coverage.m4 \
$(top_srcdir)/m4/curl-compilers.m4 \
$(top_srcdir)/m4/curl-confopts.m4 \
$(top_srcdir)/m4/curl-functions.m4 \
$(top_srcdir)/m4/curl-openssl.m4 \
$(top_srcdir)/m4/curl-override.m4 \
$(top_srcdir)/m4/curl-reentrant.m4 $(top_srcdir)/m4/libtool.m4 \
$(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \
$(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \
$(top_srcdir)/m4/xc-am-iface.m4 \
$(top_srcdir)/m4/xc-cc-check.m4 \
$(top_srcdir)/m4/xc-lt-iface.m4 \
$(top_srcdir)/m4/xc-translit.m4 \
$(top_srcdir)/m4/xc-val-flgs.m4 \
$(top_srcdir)/m4/zz40-xc-ovr.m4 \
$(top_srcdir)/m4/zz50-xc-ovr.m4 \
$(top_srcdir)/m4/zz60-xc-ovr.m4 $(top_srcdir)/acinclude.m4 \
$(top_srcdir)/configure.ac
am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
$(ACLOCAL_M4)
DIST_COMMON = $(srcdir)/Makefile.am $(pkginclude_HEADERS) \
$(am__DIST_COMMON)
mkinstalldirs = $(install_sh) -d
CONFIG_HEADER = $(top_builddir)/lib/curl_config.h
CONFIG_CLEAN_FILES =
CONFIG_CLEAN_VPATH_FILES =
AM_V_P = $(am__v_P_@AM_V@)
am__v_P_ = $(am__v_P_@AM_DEFAULT_V@)
am__v_P_0 = false
am__v_P_1 = :
AM_V_GEN = $(am__v_GEN_@AM_V@)
am__v_GEN_ = $(am__v_GEN_@AM_DEFAULT_V@)
am__v_GEN_0 = @echo " GEN " $@;
am__v_GEN_1 =
AM_V_at = $(am__v_at_@AM_V@)
am__v_at_ = $(am__v_at_@AM_DEFAULT_V@)
am__v_at_0 = @
am__v_at_1 =
SOURCES =
DIST_SOURCES =
am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \
n|no|NO) false;; \
*) (install-info --version) >/dev/null 2>&1;; \
esac
am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`;
am__vpath_adj = case $$p in \
$(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \
*) f=$$p;; \
esac;
am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`;
am__install_max = 40
am__nobase_strip_setup = \
srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'`
am__nobase_strip = \
for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||"
am__nobase_list = $(am__nobase_strip_setup); \
for p in $$list; do echo "$$p $$p"; done | \
sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \
$(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \
if (++n[$$2] == $(am__install_max)) \
{ print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \
END { for (dir in files) print dir, files[dir] }'
am__base_list = \
sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \
sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g'
am__uninstall_files_from_dir = { \
test -z "$$files" \
|| { test ! -d "$$dir" && test ! -f "$$dir" && test ! -r "$$dir"; } \
|| { echo " ( cd '$$dir' && rm -f" $$files ")"; \
$(am__cd) "$$dir" && rm -f $$files; }; \
}
am__installdirs = "$(DESTDIR)$(pkgincludedir)"
HEADERS = $(pkginclude_HEADERS)
am__tagged_files = $(HEADERS) $(SOURCES) $(TAGS_FILES) $(LISP)
# Read a list of newline-separated strings from the standard input,
# and print each of them once, without duplicates. Input order is
# *not* preserved.
am__uniquify_input = $(AWK) '\
BEGIN { nonempty = 0; } \
{ items[$$0] = 1; nonempty = 1; } \
END { if (nonempty) { for (i in items) print i; }; } \
'
# Make sure the list of sources is unique. This is necessary because,
# e.g., the same source file might be shared among _SOURCES variables
# for different programs/libraries.
am__define_uniq_tagged_files = \
list='$(am__tagged_files)'; \
unique=`for i in $$list; do \
if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
done | $(am__uniquify_input)`
ETAGS = etags
CTAGS = ctags
am__DIST_COMMON = $(srcdir)/Makefile.in
DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
pkgincludedir = $(includedir)/curl
ACLOCAL = @ACLOCAL@
AMTAR = @AMTAR@
AM_DEFAULT_VERBOSITY = @AM_DEFAULT_VERBOSITY@
AR = @AR@
AS = @AS@
AUTOCONF = @AUTOCONF@
AUTOHEADER = @AUTOHEADER@
AUTOMAKE = @AUTOMAKE@
AWK = @AWK@
BLANK_AT_MAKETIME = @BLANK_AT_MAKETIME@
CC = @CC@
CCDEPMODE = @CCDEPMODE@
CFLAGS = @CFLAGS@
CFLAG_CURL_SYMBOL_HIDING = @CFLAG_CURL_SYMBOL_HIDING@
CODE_COVERAGE_CFLAGS = @CODE_COVERAGE_CFLAGS@
CODE_COVERAGE_CPPFLAGS = @CODE_COVERAGE_CPPFLAGS@
CODE_COVERAGE_CXXFLAGS = @CODE_COVERAGE_CXXFLAGS@
CODE_COVERAGE_ENABLED = @CODE_COVERAGE_ENABLED@
CODE_COVERAGE_LDFLAGS = @CODE_COVERAGE_LDFLAGS@
CODE_COVERAGE_LIBS = @CODE_COVERAGE_LIBS@
CONFIGURE_OPTIONS = @CONFIGURE_OPTIONS@
CPP = @CPP@
CPPFLAGS = @CPPFLAGS@
CPPFLAG_CURL_STATICLIB = @CPPFLAG_CURL_STATICLIB@
CURLVERSION = @CURLVERSION@
CURL_CA_BUNDLE = @CURL_CA_BUNDLE@
CURL_CFLAG_EXTRAS = @CURL_CFLAG_EXTRAS@
CURL_DISABLE_DICT = @CURL_DISABLE_DICT@
CURL_DISABLE_FILE = @CURL_DISABLE_FILE@
CURL_DISABLE_FTP = @CURL_DISABLE_FTP@
CURL_DISABLE_GOPHER = @CURL_DISABLE_GOPHER@
CURL_DISABLE_HTTP = @CURL_DISABLE_HTTP@
CURL_DISABLE_IMAP = @CURL_DISABLE_IMAP@
CURL_DISABLE_LDAP = @CURL_DISABLE_LDAP@
CURL_DISABLE_LDAPS = @CURL_DISABLE_LDAPS@
CURL_DISABLE_POP3 = @CURL_DISABLE_POP3@
CURL_DISABLE_PROXY = @CURL_DISABLE_PROXY@
CURL_DISABLE_RTSP = @CURL_DISABLE_RTSP@
CURL_DISABLE_SMB = @CURL_DISABLE_SMB@
CURL_DISABLE_SMTP = @CURL_DISABLE_SMTP@
CURL_DISABLE_TELNET = @CURL_DISABLE_TELNET@
CURL_DISABLE_TFTP = @CURL_DISABLE_TFTP@
CURL_LT_SHLIB_VERSIONED_FLAVOUR = @CURL_LT_SHLIB_VERSIONED_FLAVOUR@
CURL_NETWORK_AND_TIME_LIBS = @CURL_NETWORK_AND_TIME_LIBS@
CURL_NETWORK_LIBS = @CURL_NETWORK_LIBS@
CYGPATH_W = @CYGPATH_W@
DEFS = @DEFS@
DEPDIR = @DEPDIR@
DLLTOOL = @DLLTOOL@
DSYMUTIL = @DSYMUTIL@
DUMPBIN = @DUMPBIN@
ECHO_C = @ECHO_C@
ECHO_N = @ECHO_N@
ECHO_T = @ECHO_T@
EGREP = @EGREP@
ENABLE_SHARED = @ENABLE_SHARED@
ENABLE_STATIC = @ENABLE_STATIC@
EXEEXT = @EXEEXT@
FGREP = @FGREP@
GCOV = @GCOV@
GENHTML = @GENHTML@
GREP = @GREP@
HAVE_GNUTLS_SRP = @HAVE_GNUTLS_SRP@
HAVE_LDAP_SSL = @HAVE_LDAP_SSL@
HAVE_LIBZ = @HAVE_LIBZ@
HAVE_OPENSSL_SRP = @HAVE_OPENSSL_SRP@
IDN_ENABLED = @IDN_ENABLED@
INSTALL = @INSTALL@
INSTALL_DATA = @INSTALL_DATA@
INSTALL_PROGRAM = @INSTALL_PROGRAM@
INSTALL_SCRIPT = @INSTALL_SCRIPT@
INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
IPV6_ENABLED = @IPV6_ENABLED@
LCOV = @LCOV@
LD = @LD@
LDFLAGS = @LDFLAGS@
LIBCURL_LIBS = @LIBCURL_LIBS@
LIBMETALINK_CPPFLAGS = @LIBMETALINK_CPPFLAGS@
LIBMETALINK_LDFLAGS = @LIBMETALINK_LDFLAGS@
LIBMETALINK_LIBS = @LIBMETALINK_LIBS@
LIBOBJS = @LIBOBJS@
LIBS = @LIBS@
LIBTOOL = @LIBTOOL@
LIPO = @LIPO@
LN_S = @LN_S@
LTLIBOBJS = @LTLIBOBJS@
LT_SYS_LIBRARY_PATH = @LT_SYS_LIBRARY_PATH@
MAINT = @MAINT@
MAKEINFO = @MAKEINFO@
MANIFEST_TOOL = @MANIFEST_TOOL@
MANOPT = @MANOPT@
MKDIR_P = @MKDIR_P@
NM = @NM@
NMEDIT = @NMEDIT@
NROFF = @NROFF@
NSS_LIBS = @NSS_LIBS@
OBJDUMP = @OBJDUMP@
OBJEXT = @OBJEXT@
OTOOL = @OTOOL@
OTOOL64 = @OTOOL64@
PACKAGE = @PACKAGE@
PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
PACKAGE_NAME = @PACKAGE_NAME@
PACKAGE_STRING = @PACKAGE_STRING@
PACKAGE_TARNAME = @PACKAGE_TARNAME@
PACKAGE_URL = @PACKAGE_URL@
PACKAGE_VERSION = @PACKAGE_VERSION@
PATH_SEPARATOR = @PATH_SEPARATOR@
PERL = @PERL@
PKGADD_NAME = @PKGADD_NAME@
PKGADD_PKG = @PKGADD_PKG@
PKGADD_VENDOR = @PKGADD_VENDOR@
PKGCONFIG = @PKGCONFIG@
RANDOM_FILE = @RANDOM_FILE@
RANLIB = @RANLIB@
REQUIRE_LIB_DEPS = @REQUIRE_LIB_DEPS@
SED = @SED@
SET_MAKE = @SET_MAKE@
SHELL = @SHELL@
SSL_ENABLED = @SSL_ENABLED@
SSL_LIBS = @SSL_LIBS@
STRIP = @STRIP@
SUPPORT_FEATURES = @SUPPORT_FEATURES@
SUPPORT_PROTOCOLS = @SUPPORT_PROTOCOLS@
USE_ARES = @USE_ARES@
USE_AXTLS = @USE_AXTLS@
USE_CYASSL = @USE_CYASSL@
USE_DARWINSSL = @USE_DARWINSSL@
USE_GNUTLS = @USE_GNUTLS@
USE_GNUTLS_NETTLE = @USE_GNUTLS_NETTLE@
USE_LIBRTMP = @USE_LIBRTMP@
USE_LIBSSH2 = @USE_LIBSSH2@
USE_MBEDTLS = @USE_MBEDTLS@
USE_NGHTTP2 = @USE_NGHTTP2@
USE_NSS = @USE_NSS@
USE_OPENLDAP = @USE_OPENLDAP@
USE_POLARSSL = @USE_POLARSSL@
USE_SCHANNEL = @USE_SCHANNEL@
USE_UNIX_SOCKETS = @USE_UNIX_SOCKETS@
USE_WINDOWS_SSPI = @USE_WINDOWS_SSPI@
VERSION = @VERSION@
VERSIONNUM = @VERSIONNUM@
ZLIB_LIBS = @ZLIB_LIBS@
ZSH_FUNCTIONS_DIR = @ZSH_FUNCTIONS_DIR@
abs_builddir = @abs_builddir@
abs_srcdir = @abs_srcdir@
abs_top_builddir = @abs_top_builddir@
abs_top_srcdir = @abs_top_srcdir@
ac_ct_AR = @ac_ct_AR@
ac_ct_CC = @ac_ct_CC@
ac_ct_DUMPBIN = @ac_ct_DUMPBIN@
am__include = @am__include@
am__leading_dot = @am__leading_dot@
am__quote = @am__quote@
am__tar = @am__tar@
am__untar = @am__untar@
bindir = @bindir@
build = @build@
build_alias = @build_alias@
build_cpu = @build_cpu@
build_os = @build_os@
build_vendor = @build_vendor@
builddir = @builddir@
datadir = @datadir@
datarootdir = @datarootdir@
docdir = @docdir@
dvidir = @dvidir@
exec_prefix = @exec_prefix@
host = @host@
host_alias = @host_alias@
host_cpu = @host_cpu@
host_os = @host_os@
host_vendor = @host_vendor@
htmldir = @htmldir@
includedir = @includedir@
infodir = @infodir@
install_sh = @install_sh@
libdir = @libdir@
libexecdir = @libexecdir@
libext = @libext@
localedir = @localedir@
localstatedir = @localstatedir@
mandir = @mandir@
mkdir_p = @mkdir_p@
oldincludedir = @oldincludedir@
pdfdir = @pdfdir@
prefix = @prefix@
program_transform_name = @program_transform_name@
psdir = @psdir@
runstatedir = @runstatedir@
sbindir = @sbindir@
sharedstatedir = @sharedstatedir@
srcdir = @srcdir@
subdirs = @subdirs@
sysconfdir = @sysconfdir@
target_alias = @target_alias@
top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@
top_srcdir = @top_srcdir@
#***************************************************************************
# _ _ ____ _
# Project ___| | | | _ \| |
# / __| | | | |_) | |
# | (__| |_| | _ <| |___
# \___|\___/|_| \_\_____|
#
# Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
#
# This software is licensed as described in the file COPYING, which
# you should have received as part of this distribution. The terms
# are also available at https://curl.haxx.se/docs/copyright.html.
#
# You may opt to use, copy, modify, merge, publish, distribute and/or sell
# copies of the Software, and permit persons to whom the Software is
# furnished to do so, under the terms of the COPYING file.
#
# This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
# KIND, either express or implied.
#
###########################################################################
pkginclude_HEADERS = \
curl.h curlver.h easy.h mprintf.h stdcheaders.h multi.h \
typecheck-gcc.h system.h
all: all-am
.SUFFIXES:
$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
@for dep in $?; do \
case '$(am__configure_deps)' in \
*$$dep*) \
( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \
&& { if test -f $@; then exit 0; else break; fi; }; \
exit 1;; \
esac; \
done; \
echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign include/curl/Makefile'; \
$(am__cd) $(top_srcdir) && \
$(AUTOMAKE) --foreign include/curl/Makefile
Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
@case '$?' in \
*config.status*) \
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
*) \
echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
esac;
$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
$(am__aclocal_m4_deps):
mostlyclean-libtool:
-rm -f *.lo
clean-libtool:
-rm -rf .libs _libs
install-pkgincludeHEADERS: $(pkginclude_HEADERS)
@$(NORMAL_INSTALL)
@list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
if test -n "$$list"; then \
echo " $(MKDIR_P) '$(DESTDIR)$(pkgincludedir)'"; \
$(MKDIR_P) "$(DESTDIR)$(pkgincludedir)" || exit 1; \
fi; \
for p in $$list; do \
if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \
echo "$$d$$p"; \
done | $(am__base_list) | \
while read files; do \
echo " $(INSTALL_HEADER) $$files '$(DESTDIR)$(pkgincludedir)'"; \
$(INSTALL_HEADER) $$files "$(DESTDIR)$(pkgincludedir)" || exit $$?; \
done
uninstall-pkgincludeHEADERS:
@$(NORMAL_UNINSTALL)
@list='$(pkginclude_HEADERS)'; test -n "$(pkgincludedir)" || list=; \
files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \
dir='$(DESTDIR)$(pkgincludedir)'; $(am__uninstall_files_from_dir)
ID: $(am__tagged_files)
$(am__define_uniq_tagged_files); mkid -fID $$unique
tags: tags-am
TAGS: tags
tags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
set x; \
here=`pwd`; \
$(am__define_uniq_tagged_files); \
shift; \
if test -z "$(ETAGS_ARGS)$$*$$unique"; then :; else \
test -n "$$unique" || unique=$$empty_fix; \
if test $$# -gt 0; then \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
"$$@" $$unique; \
else \
$(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
$$unique; \
fi; \
fi
ctags: ctags-am
CTAGS: ctags
ctags-am: $(TAGS_DEPENDENCIES) $(am__tagged_files)
$(am__define_uniq_tagged_files); \
test -z "$(CTAGS_ARGS)$$unique" \
|| $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
$$unique
GTAGS:
here=`$(am__cd) $(top_builddir) && pwd` \
&& $(am__cd) $(top_srcdir) \
&& gtags -i $(GTAGS_ARGS) "$$here"
cscopelist: cscopelist-am
cscopelist-am: $(am__tagged_files)
list='$(am__tagged_files)'; \
case "$(srcdir)" in \
[\\/]* | ?:[\\/]*) sdir="$(srcdir)" ;; \
*) sdir=$(subdir)/$(srcdir) ;; \
esac; \
for i in $$list; do \
if test -f "$$i"; then \
echo "$(subdir)/$$i"; \
else \
echo "$$sdir/$$i"; \
fi; \
done >> $(top_builddir)/cscope.files
distclean-tags:
-rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
distdir: $(DISTFILES)
@srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \
list='$(DISTFILES)'; \
dist_files=`for file in $$list; do echo $$file; done | \
sed -e "s|^$$srcdirstrip/||;t" \
-e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \
case $$dist_files in \
*/*) $(MKDIR_P) `echo "$$dist_files" | \
sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \
sort -u` ;; \
esac; \
for file in $$dist_files; do \
if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
if test -d $$d/$$file; then \
dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \
if test -d "$(distdir)/$$file"; then \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \
find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \
fi; \
cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \
else \
test -f "$(distdir)/$$file" \
|| cp -p $$d/$$file "$(distdir)/$$file" \
|| exit 1; \
fi; \
done
check-am: all-am
check: check-am
@CURLDEBUG_FALSE@all-local:
all-am: Makefile $(HEADERS) all-local
installdirs:
for dir in "$(DESTDIR)$(pkgincludedir)"; do \
test -z "$$dir" || $(MKDIR_P) "$$dir"; \
done
install: install-am
install-exec: install-exec-am
install-data: install-data-am
uninstall: uninstall-am
install-am: all-am
@$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
installcheck: installcheck-am
install-strip:
if test -z '$(STRIP)'; then \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
install; \
else \
$(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
"INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'" install; \
fi
mostlyclean-generic:
clean-generic:
distclean-generic:
-test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
-test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES)
maintainer-clean-generic:
@echo "This command is intended for maintainers to use"
@echo "it deletes files that may require special tools to rebuild."
clean: clean-am
clean-am: clean-generic clean-libtool mostlyclean-am
distclean: distclean-am
-rm -f Makefile
distclean-am: clean-am distclean-generic distclean-tags
dvi: dvi-am
dvi-am:
html: html-am
html-am:
info: info-am
info-am:
install-data-am: install-pkgincludeHEADERS
install-dvi: install-dvi-am
install-dvi-am:
install-exec-am:
install-html: install-html-am
install-html-am:
install-info: install-info-am
install-info-am:
install-man:
install-pdf: install-pdf-am
install-pdf-am:
install-ps: install-ps-am
install-ps-am:
installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f Makefile
maintainer-clean-am: distclean-am maintainer-clean-generic
mostlyclean: mostlyclean-am
mostlyclean-am: mostlyclean-generic mostlyclean-libtool
pdf: pdf-am
pdf-am:
ps: ps-am
ps-am:
uninstall-am: uninstall-pkgincludeHEADERS
.MAKE: install-am install-strip
.PHONY: CTAGS GTAGS TAGS all all-am all-local check check-am clean \
clean-generic clean-libtool cscopelist-am ctags ctags-am \
distclean distclean-generic distclean-libtool distclean-tags \
distdir dvi dvi-am html html-am info info-am install \
install-am install-data install-data-am install-dvi \
install-dvi-am install-exec install-exec-am install-html \
install-html-am install-info install-info-am install-man \
install-pdf install-pdf-am install-pkgincludeHEADERS \
install-ps install-ps-am install-strip installcheck \
installcheck-am installdirs maintainer-clean \
maintainer-clean-generic mostlyclean mostlyclean-generic \
mostlyclean-libtool pdf pdf-am ps ps-am tags tags-am uninstall \
uninstall-am uninstall-pkgincludeHEADERS
.PRECIOUS: Makefile
checksrc:
@@PERL@ $(top_srcdir)/lib/checksrc.pl -D$(top_srcdir)/include/curl $(pkginclude_HEADERS)
# for debug builds, we scan the sources on all regular make invokes
@CURLDEBUG_TRUE@all-local: checksrc
# Tell versions [3.59,3.63) of GNU make to not export all variables.
# Otherwise a system limit (for SysV at least) may be exceeded.
.NOEXPORT:
File diff suppressed because it is too large Load Diff
+77
View File
@@ -0,0 +1,77 @@
#ifndef __CURL_CURLVER_H
#define __CURL_CURLVER_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* This header file contains nothing but libcurl version info, generated by
a script at release-time. This was made its own header file in 7.11.2 */
/* This is the global package copyright */
#define LIBCURL_COPYRIGHT "1996 - 2017 Daniel Stenberg, <daniel@haxx.se>."
/* This is the version number of the libcurl package from which this header
file origins: */
#define LIBCURL_VERSION "7.55.1"
/* The numeric version number is also available "in parts" by using these
defines: */
#define LIBCURL_VERSION_MAJOR 7
#define LIBCURL_VERSION_MINOR 55
#define LIBCURL_VERSION_PATCH 1
/* This is the numeric version of the libcurl version number, meant for easier
parsing and comparions by programs. The LIBCURL_VERSION_NUM define will
always follow this syntax:
0xXXYYZZ
Where XX, YY and ZZ are the main version, release and patch numbers in
hexadecimal (using 8 bits each). All three numbers are always represented
using two digits. 1.2 would appear as "0x010200" while version 9.11.7
appears as "0x090b07".
This 6-digit (24 bits) hexadecimal number does not show pre-release number,
and it is always a greater number in a more recent release. It makes
comparisons with greater than and less than work.
Note: This define is the full hex number and _does not_ use the
CURL_VERSION_BITS() macro since curl's own configure script greps for it
and needs it to contain the full number.
*/
#define LIBCURL_VERSION_NUM 0x073701
/*
* This is the date and time when the full source package was created. The
* timestamp is not stored in git, as the timestamp is properly set in the
* tarballs by the maketgz script.
*
* The format of the date follows this template:
*
* "2007-11-23"
*/
#define LIBCURL_TIMESTAMP "2017-08-14"
#define CURL_VERSION_BITS(x,y,z) ((x)<<16|(y)<<8|z)
#define CURL_AT_LEAST_VERSION(x,y,z) \
(LIBCURL_VERSION_NUM >= CURL_VERSION_BITS(x, y, z))
#endif /* __CURL_CURLVER_H */
+102
View File
@@ -0,0 +1,102 @@
#ifndef __CURL_EASY_H
#define __CURL_EASY_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#ifdef __cplusplus
extern "C" {
#endif
CURL_EXTERN CURL *curl_easy_init(void);
CURL_EXTERN CURLcode curl_easy_setopt(CURL *curl, CURLoption option, ...);
CURL_EXTERN CURLcode curl_easy_perform(CURL *curl);
CURL_EXTERN void curl_easy_cleanup(CURL *curl);
/*
* NAME curl_easy_getinfo()
*
* DESCRIPTION
*
* Request internal information from the curl session with this function. The
* third argument MUST be a pointer to a long, a pointer to a char * or a
* pointer to a double (as the documentation describes elsewhere). The data
* pointed to will be filled in accordingly and can be relied upon only if the
* function returns CURLE_OK. This function is intended to get used *AFTER* a
* performed transfer, all results from this function are undefined until the
* transfer is completed.
*/
CURL_EXTERN CURLcode curl_easy_getinfo(CURL *curl, CURLINFO info, ...);
/*
* NAME curl_easy_duphandle()
*
* DESCRIPTION
*
* Creates a new curl session handle with the same options set for the handle
* passed in. Duplicating a handle could only be a matter of cloning data and
* options, internal state info and things like persistent connections cannot
* be transferred. It is useful in multithreaded applications when you can run
* curl_easy_duphandle() for each new thread to avoid a series of identical
* curl_easy_setopt() invokes in every thread.
*/
CURL_EXTERN CURL *curl_easy_duphandle(CURL *curl);
/*
* NAME curl_easy_reset()
*
* DESCRIPTION
*
* Re-initializes a CURL handle to the default values. This puts back the
* handle to the same state as it was in when it was just created.
*
* It does keep: live connections, the Session ID cache, the DNS cache and the
* cookies.
*/
CURL_EXTERN void curl_easy_reset(CURL *curl);
/*
* NAME curl_easy_recv()
*
* DESCRIPTION
*
* Receives data from the connected socket. Use after successful
* curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
*/
CURL_EXTERN CURLcode curl_easy_recv(CURL *curl, void *buffer, size_t buflen,
size_t *n);
/*
* NAME curl_easy_send()
*
* DESCRIPTION
*
* Sends data over the connected socket. Use after successful
* curl_easy_perform() with CURLOPT_CONNECT_ONLY option.
*/
CURL_EXTERN CURLcode curl_easy_send(CURL *curl, const void *buffer,
size_t buflen, size_t *n);
#ifdef __cplusplus
}
#endif
#endif
+439
View File
@@ -0,0 +1,439 @@
#ifndef __CURL_MULTI_H
#define __CURL_MULTI_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/*
This is an "external" header file. Don't give away any internals here!
GOALS
o Enable a "pull" interface. The application that uses libcurl decides where
and when to ask libcurl to get/send data.
o Enable multiple simultaneous transfers in the same thread without making it
complicated for the application.
o Enable the application to select() on its own file descriptors and curl's
file descriptors simultaneous easily.
*/
/*
* This header file should not really need to include "curl.h" since curl.h
* itself includes this file and we expect user applications to do #include
* <curl/curl.h> without the need for especially including multi.h.
*
* For some reason we added this include here at one point, and rather than to
* break existing (wrongly written) libcurl applications, we leave it as-is
* but with this warning attached.
*/
#include "curl.h"
#ifdef __cplusplus
extern "C" {
#endif
#if defined(BUILDING_LIBCURL) || defined(CURL_STRICTER)
typedef struct Curl_multi CURLM;
#else
typedef void CURLM;
#endif
typedef enum {
CURLM_CALL_MULTI_PERFORM = -1, /* please call curl_multi_perform() or
curl_multi_socket*() soon */
CURLM_OK,
CURLM_BAD_HANDLE, /* the passed-in handle is not a valid CURLM handle */
CURLM_BAD_EASY_HANDLE, /* an easy handle was not good/valid */
CURLM_OUT_OF_MEMORY, /* if you ever get this, you're in deep sh*t */
CURLM_INTERNAL_ERROR, /* this is a libcurl bug */
CURLM_BAD_SOCKET, /* the passed in socket argument did not match */
CURLM_UNKNOWN_OPTION, /* curl_multi_setopt() with unsupported option */
CURLM_ADDED_ALREADY, /* an easy handle already added to a multi handle was
attempted to get added - again */
CURLM_LAST
} CURLMcode;
/* just to make code nicer when using curl_multi_socket() you can now check
for CURLM_CALL_MULTI_SOCKET too in the same style it works for
curl_multi_perform() and CURLM_CALL_MULTI_PERFORM */
#define CURLM_CALL_MULTI_SOCKET CURLM_CALL_MULTI_PERFORM
/* bitmask bits for CURLMOPT_PIPELINING */
#define CURLPIPE_NOTHING 0L
#define CURLPIPE_HTTP1 1L
#define CURLPIPE_MULTIPLEX 2L
typedef enum {
CURLMSG_NONE, /* first, not used */
CURLMSG_DONE, /* This easy handle has completed. 'result' contains
the CURLcode of the transfer */
CURLMSG_LAST /* last, not used */
} CURLMSG;
struct CURLMsg {
CURLMSG msg; /* what this message means */
CURL *easy_handle; /* the handle it concerns */
union {
void *whatever; /* message-specific data */
CURLcode result; /* return code for transfer */
} data;
};
typedef struct CURLMsg CURLMsg;
/* Based on poll(2) structure and values.
* We don't use pollfd and POLL* constants explicitly
* to cover platforms without poll(). */
#define CURL_WAIT_POLLIN 0x0001
#define CURL_WAIT_POLLPRI 0x0002
#define CURL_WAIT_POLLOUT 0x0004
struct curl_waitfd {
curl_socket_t fd;
short events;
short revents; /* not supported yet */
};
/*
* Name: curl_multi_init()
*
* Desc: inititalize multi-style curl usage
*
* Returns: a new CURLM handle to use in all 'curl_multi' functions.
*/
CURL_EXTERN CURLM *curl_multi_init(void);
/*
* Name: curl_multi_add_handle()
*
* Desc: add a standard curl handle to the multi stack
*
* Returns: CURLMcode type, general multi error code.
*/
CURL_EXTERN CURLMcode curl_multi_add_handle(CURLM *multi_handle,
CURL *curl_handle);
/*
* Name: curl_multi_remove_handle()
*
* Desc: removes a curl handle from the multi stack again
*
* Returns: CURLMcode type, general multi error code.
*/
CURL_EXTERN CURLMcode curl_multi_remove_handle(CURLM *multi_handle,
CURL *curl_handle);
/*
* Name: curl_multi_fdset()
*
* Desc: Ask curl for its fd_set sets. The app can use these to select() or
* poll() on. We want curl_multi_perform() called as soon as one of
* them are ready.
*
* Returns: CURLMcode type, general multi error code.
*/
CURL_EXTERN CURLMcode curl_multi_fdset(CURLM *multi_handle,
fd_set *read_fd_set,
fd_set *write_fd_set,
fd_set *exc_fd_set,
int *max_fd);
/*
* Name: curl_multi_wait()
*
* Desc: Poll on all fds within a CURLM set as well as any
* additional fds passed to the function.
*
* Returns: CURLMcode type, general multi error code.
*/
CURL_EXTERN CURLMcode curl_multi_wait(CURLM *multi_handle,
struct curl_waitfd extra_fds[],
unsigned int extra_nfds,
int timeout_ms,
int *ret);
/*
* Name: curl_multi_perform()
*
* Desc: When the app thinks there's data available for curl it calls this
* function to read/write whatever there is right now. This returns
* as soon as the reads and writes are done. This function does not
* require that there actually is data available for reading or that
* data can be written, it can be called just in case. It returns
* the number of handles that still transfer data in the second
* argument's integer-pointer.
*
* Returns: CURLMcode type, general multi error code. *NOTE* that this only
* returns errors etc regarding the whole multi stack. There might
* still have occurred problems on invidual transfers even when this
* returns OK.
*/
CURL_EXTERN CURLMcode curl_multi_perform(CURLM *multi_handle,
int *running_handles);
/*
* Name: curl_multi_cleanup()
*
* Desc: Cleans up and removes a whole multi stack. It does not free or
* touch any individual easy handles in any way. We need to define
* in what state those handles will be if this function is called
* in the middle of a transfer.
*
* Returns: CURLMcode type, general multi error code.
*/
CURL_EXTERN CURLMcode curl_multi_cleanup(CURLM *multi_handle);
/*
* Name: curl_multi_info_read()
*
* Desc: Ask the multi handle if there's any messages/informationals from
* the individual transfers. Messages include informationals such as
* error code from the transfer or just the fact that a transfer is
* completed. More details on these should be written down as well.
*
* Repeated calls to this function will return a new struct each
* time, until a special "end of msgs" struct is returned as a signal
* that there is no more to get at this point.
*
* The data the returned pointer points to will not survive calling
* curl_multi_cleanup().
*
* The 'CURLMsg' struct is meant to be very simple and only contain
* very basic information. If more involved information is wanted,
* we will provide the particular "transfer handle" in that struct
* and that should/could/would be used in subsequent
* curl_easy_getinfo() calls (or similar). The point being that we
* must never expose complex structs to applications, as then we'll
* undoubtably get backwards compatibility problems in the future.
*
* Returns: A pointer to a filled-in struct, or NULL if it failed or ran out
* of structs. It also writes the number of messages left in the
* queue (after this read) in the integer the second argument points
* to.
*/
CURL_EXTERN CURLMsg *curl_multi_info_read(CURLM *multi_handle,
int *msgs_in_queue);
/*
* Name: curl_multi_strerror()
*
* Desc: The curl_multi_strerror function may be used to turn a CURLMcode
* value into the equivalent human readable error string. This is
* useful for printing meaningful error messages.
*
* Returns: A pointer to a zero-terminated error message.
*/
CURL_EXTERN const char *curl_multi_strerror(CURLMcode);
/*
* Name: curl_multi_socket() and
* curl_multi_socket_all()
*
* Desc: An alternative version of curl_multi_perform() that allows the
* application to pass in one of the file descriptors that have been
* detected to have "action" on them and let libcurl perform.
* See man page for details.
*/
#define CURL_POLL_NONE 0
#define CURL_POLL_IN 1
#define CURL_POLL_OUT 2
#define CURL_POLL_INOUT 3
#define CURL_POLL_REMOVE 4
#define CURL_SOCKET_TIMEOUT CURL_SOCKET_BAD
#define CURL_CSELECT_IN 0x01
#define CURL_CSELECT_OUT 0x02
#define CURL_CSELECT_ERR 0x04
typedef int (*curl_socket_callback)(CURL *easy, /* easy handle */
curl_socket_t s, /* socket */
int what, /* see above */
void *userp, /* private callback
pointer */
void *socketp); /* private socket
pointer */
/*
* Name: curl_multi_timer_callback
*
* Desc: Called by libcurl whenever the library detects a change in the
* maximum number of milliseconds the app is allowed to wait before
* curl_multi_socket() or curl_multi_perform() must be called
* (to allow libcurl's timed events to take place).
*
* Returns: The callback should return zero.
*/
typedef int (*curl_multi_timer_callback)(CURLM *multi, /* multi handle */
long timeout_ms, /* see above */
void *userp); /* private callback
pointer */
CURL_EXTERN CURLMcode curl_multi_socket(CURLM *multi_handle, curl_socket_t s,
int *running_handles);
CURL_EXTERN CURLMcode curl_multi_socket_action(CURLM *multi_handle,
curl_socket_t s,
int ev_bitmask,
int *running_handles);
CURL_EXTERN CURLMcode curl_multi_socket_all(CURLM *multi_handle,
int *running_handles);
#ifndef CURL_ALLOW_OLD_MULTI_SOCKET
/* This macro below was added in 7.16.3 to push users who recompile to use
the new curl_multi_socket_action() instead of the old curl_multi_socket()
*/
#define curl_multi_socket(x,y,z) curl_multi_socket_action(x,y,0,z)
#endif
/*
* Name: curl_multi_timeout()
*
* Desc: Returns the maximum number of milliseconds the app is allowed to
* wait before curl_multi_socket() or curl_multi_perform() must be
* called (to allow libcurl's timed events to take place).
*
* Returns: CURLM error code.
*/
CURL_EXTERN CURLMcode curl_multi_timeout(CURLM *multi_handle,
long *milliseconds);
#undef CINIT /* re-using the same name as in curl.h */
#ifdef CURL_ISOCPP
#define CINIT(name,type,num) CURLMOPT_ ## name = CURLOPTTYPE_ ## type + num
#else
/* The macro "##" is ISO C, we assume pre-ISO C doesn't support it. */
#define LONG CURLOPTTYPE_LONG
#define OBJECTPOINT CURLOPTTYPE_OBJECTPOINT
#define FUNCTIONPOINT CURLOPTTYPE_FUNCTIONPOINT
#define OFF_T CURLOPTTYPE_OFF_T
#define CINIT(name,type,number) CURLMOPT_/**/name = type + number
#endif
typedef enum {
/* This is the socket callback function pointer */
CINIT(SOCKETFUNCTION, FUNCTIONPOINT, 1),
/* This is the argument passed to the socket callback */
CINIT(SOCKETDATA, OBJECTPOINT, 2),
/* set to 1 to enable pipelining for this multi handle */
CINIT(PIPELINING, LONG, 3),
/* This is the timer callback function pointer */
CINIT(TIMERFUNCTION, FUNCTIONPOINT, 4),
/* This is the argument passed to the timer callback */
CINIT(TIMERDATA, OBJECTPOINT, 5),
/* maximum number of entries in the connection cache */
CINIT(MAXCONNECTS, LONG, 6),
/* maximum number of (pipelining) connections to one host */
CINIT(MAX_HOST_CONNECTIONS, LONG, 7),
/* maximum number of requests in a pipeline */
CINIT(MAX_PIPELINE_LENGTH, LONG, 8),
/* a connection with a content-length longer than this
will not be considered for pipelining */
CINIT(CONTENT_LENGTH_PENALTY_SIZE, OFF_T, 9),
/* a connection with a chunk length longer than this
will not be considered for pipelining */
CINIT(CHUNK_LENGTH_PENALTY_SIZE, OFF_T, 10),
/* a list of site names(+port) that are blacklisted from
pipelining */
CINIT(PIPELINING_SITE_BL, OBJECTPOINT, 11),
/* a list of server types that are blacklisted from
pipelining */
CINIT(PIPELINING_SERVER_BL, OBJECTPOINT, 12),
/* maximum number of open connections in total */
CINIT(MAX_TOTAL_CONNECTIONS, LONG, 13),
/* This is the server push callback function pointer */
CINIT(PUSHFUNCTION, FUNCTIONPOINT, 14),
/* This is the argument passed to the server push callback */
CINIT(PUSHDATA, OBJECTPOINT, 15),
CURLMOPT_LASTENTRY /* the last unused */
} CURLMoption;
/*
* Name: curl_multi_setopt()
*
* Desc: Sets options for the multi handle.
*
* Returns: CURLM error code.
*/
CURL_EXTERN CURLMcode curl_multi_setopt(CURLM *multi_handle,
CURLMoption option, ...);
/*
* Name: curl_multi_assign()
*
* Desc: This function sets an association in the multi handle between the
* given socket and a private pointer of the application. This is
* (only) useful for curl_multi_socket uses.
*
* Returns: CURLM error code.
*/
CURL_EXTERN CURLMcode curl_multi_assign(CURLM *multi_handle,
curl_socket_t sockfd, void *sockp);
/*
* Name: curl_push_callback
*
* Desc: This callback gets called when a new stream is being pushed by the
* server. It approves or denies the new stream.
*
* Returns: CURL_PUSH_OK or CURL_PUSH_DENY.
*/
#define CURL_PUSH_OK 0
#define CURL_PUSH_DENY 1
struct curl_pushheaders; /* forward declaration only */
CURL_EXTERN char *curl_pushheader_bynum(struct curl_pushheaders *h,
size_t num);
CURL_EXTERN char *curl_pushheader_byname(struct curl_pushheaders *h,
const char *name);
typedef int (*curl_push_callback)(CURL *parent,
CURL *easy,
size_t num_headers,
struct curl_pushheaders *headers,
void *userp);
#ifdef __cplusplus
} /* end of extern "C" */
#endif
#endif
+33
View File
@@ -0,0 +1,33 @@
#ifndef __STDC_HEADERS_H
#define __STDC_HEADERS_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2016, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
#include <sys/types.h>
size_t fread(void *, size_t, size_t, FILE *);
size_t fwrite(const void *, size_t, size_t, FILE *);
int strcasecmp(const char *, const char *);
int strncasecmp(const char *, const char *, size_t);
#endif /* __STDC_HEADERS_H */
+547
View File
@@ -0,0 +1,547 @@
#ifndef __CURL_SYSTEM_H
#define __CURL_SYSTEM_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/*
* Try to keep one section per platform, compiler and architecture, otherwise,
* if an existing section is reused for a different one and later on the
* original is adjusted, probably the piggybacking one can be adversely
* changed.
*
* In order to differentiate between platforms/compilers/architectures use
* only compiler built in predefined preprocessor symbols.
*
* curl_off_t
* ----------
*
* For any given platform/compiler curl_off_t must be typedef'ed to a 64-bit
* wide signed integral data type. The width of this data type must remain
* constant and independent of any possible large file support settings.
*
* As an exception to the above, curl_off_t shall be typedef'ed to a 32-bit
* wide signed integral data type if there is no 64-bit type.
*
* As a general rule, curl_off_t shall not be mapped to off_t. This rule shall
* only be violated if off_t is the only 64-bit data type available and the
* size of off_t is independent of large file support settings. Keep your
* build on the safe side avoiding an off_t gating. If you have a 64-bit
* off_t then take for sure that another 64-bit data type exists, dig deeper
* and you will find it.
*
*/
#if defined(__DJGPP__) || defined(__GO32__)
# if defined(__DJGPP__) && (__DJGPP__ > 1)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__SALFORDC__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__BORLANDC__)
# if (__BORLANDC__ < 0x520)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T i64
# define CURL_SUFFIX_CURL_OFF_TU ui64
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__TURBOC__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__WATCOMC__)
# if defined(__386__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T i64
# define CURL_SUFFIX_CURL_OFF_TU ui64
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__POCC__)
# if (__POCC__ < 280)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# elif defined(_MSC_VER)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T i64
# define CURL_SUFFIX_CURL_OFF_TU ui64
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__LCC__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__SYMBIAN32__)
# if defined(__EABI__) /* Treat all ARM compilers equally */
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(__CW32__)
# pragma longlong on
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(__VC32__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__MWERKS__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(_WIN32_WCE)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T i64
# define CURL_SUFFIX_CURL_OFF_TU ui64
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__MINGW32__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_WS2TCPIP_H 1
#elif defined(__VMS)
# if defined(__VAX)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T unsigned int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__OS400__)
# if defined(__ILEC400__)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
# endif
#elif defined(__MVS__)
# if defined(__IBMC__) || defined(__IBMCPP__)
# if defined(_ILP32)
# define CURL_SIZEOF_LONG 4
# elif defined(_LP64)
# define CURL_SIZEOF_LONG 8
# endif
# if defined(_LONG_LONG)
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(_LP64)
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# else
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
# endif
#elif defined(__370__)
# if defined(__IBMC__) || defined(__IBMCPP__)
# if defined(_ILP32)
# define CURL_SIZEOF_LONG 4
# elif defined(_LP64)
# define CURL_SIZEOF_LONG 8
# endif
# if defined(_LONG_LONG)
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(_LP64)
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# else
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
# endif
#elif defined(TPF)
# define CURL_SIZEOF_LONG 8
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
#elif defined(__TINYC__) /* also known as tcc */
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
#elif defined(__SUNPRO_C) /* Oracle Solaris Studio */
# if !defined(__LP64) && (defined(__ILP32) || \
defined(__i386) || defined(__sparcv8))
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(__LP64) || \
defined(__amd64) || defined(__sparcv9)
# define CURL_SIZEOF_LONG 8
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
/* ===================================== */
/* KEEP MSVC THE PENULTIMATE ENTRY */
/* ===================================== */
#elif defined(_MSC_VER)
# if (_MSC_VER >= 900) && (_INTEGRAL_MAX_BITS >= 64)
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T __int64
# define CURL_FORMAT_CURL_OFF_T "I64d"
# define CURL_FORMAT_CURL_OFF_TU "I64u"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T i64
# define CURL_SUFFIX_CURL_OFF_TU ui64
# else
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T int
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
/* ===================================== */
/* KEEP GENERIC GCC THE LAST ENTRY */
/* ===================================== */
#elif defined(__GNUC__)
# if !defined(__LP64__) && (defined(__ILP32__) || \
defined(__i386__) || defined(__powerpc__) || defined(__arm__) || \
defined(__sparc__) || defined(__mips__) || defined(__sh__) || \
defined(__XTENSA__) || (defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 4))
# define CURL_SIZEOF_LONG 4
# define CURL_TYPEOF_CURL_OFF_T long long
# define CURL_FORMAT_CURL_OFF_T "lld"
# define CURL_FORMAT_CURL_OFF_TU "llu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T LL
# define CURL_SUFFIX_CURL_OFF_TU ULL
# elif defined(__LP64__) || \
defined(__x86_64__) || defined(__ppc64__) || defined(__sparc64__) || \
(defined(__SIZEOF_LONG__) && __SIZEOF_LONG__ == 8)
# define CURL_SIZEOF_LONG 8
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SIZEOF_CURL_OFF_T 8
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# endif
# define CURL_TYPEOF_CURL_SOCKLEN_T socklen_t
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_PULL_SYS_TYPES_H 1
# define CURL_PULL_SYS_SOCKET_H 1
#else
/* generic "safe guess" on old 32 bit style */
# define CURL_SIZEOF_LONG 4
# define CURL_SIZEOF_CURL_SOCKLEN_T 4
# define CURL_SIZEOF_CURL_OFF_T 4
# define CURL_TYPEOF_CURL_OFF_T long
# define CURL_FORMAT_CURL_OFF_T "ld"
# define CURL_FORMAT_CURL_OFF_TU "lu"
# define CURL_SUFFIX_CURL_OFF_T L
# define CURL_SUFFIX_CURL_OFF_TU UL
# define CURL_TYPEOF_CURL_SOCKLEN_T int
#endif
/* CURL_PULL_WS2TCPIP_H is defined above when inclusion of header file */
/* ws2tcpip.h is required here to properly make type definitions below. */
#ifdef CURL_PULL_WS2TCPIP_H
# include <winsock2.h>
# include <windows.h>
# include <ws2tcpip.h>
#endif
/* CURL_PULL_SYS_TYPES_H is defined above when inclusion of header file */
/* sys/types.h is required here to properly make type definitions below. */
#ifdef CURL_PULL_SYS_TYPES_H
# include <sys/types.h>
#endif
/* CURL_PULL_SYS_SOCKET_H is defined above when inclusion of header file */
/* sys/socket.h is required here to properly make type definitions below. */
#ifdef CURL_PULL_SYS_SOCKET_H
# include <sys/socket.h>
#endif
/* Data type definition of curl_socklen_t. */
#ifdef CURL_TYPEOF_CURL_SOCKLEN_T
typedef CURL_TYPEOF_CURL_SOCKLEN_T curl_socklen_t;
#endif
/* Data type definition of curl_off_t. */
#ifdef CURL_TYPEOF_CURL_OFF_T
typedef CURL_TYPEOF_CURL_OFF_T curl_off_t;
#endif
/*
* CURL_ISOCPP and CURL_OFF_T_C definitions are done here in order to allow
* these to be visible and exported by the external libcurl interface API,
* while also making them visible to the library internals, simply including
* curl_setup.h, without actually needing to include curl.h internally.
* If some day this section would grow big enough, all this should be moved
* to its own header file.
*/
/*
* Figure out if we can use the ## preprocessor operator, which is supported
* by ISO/ANSI C and C++. Some compilers support it without setting __STDC__
* or __cplusplus so we need to carefully check for them too.
*/
#if defined(__STDC__) || defined(_MSC_VER) || defined(__cplusplus) || \
defined(__HP_aCC) || defined(__BORLANDC__) || defined(__LCC__) || \
defined(__POCC__) || defined(__SALFORDC__) || defined(__HIGHC__) || \
defined(__ILEC400__)
/* This compiler is believed to have an ISO compatible preprocessor */
#define CURL_ISOCPP
#else
/* This compiler is believed NOT to have an ISO compatible preprocessor */
#undef CURL_ISOCPP
#endif
/*
* Macros for minimum-width signed and unsigned curl_off_t integer constants.
*/
#if defined(__BORLANDC__) && (__BORLANDC__ == 0x0551)
# define __CURL_OFF_T_C_HLPR2(x) x
# define __CURL_OFF_T_C_HLPR1(x) __CURL_OFF_T_C_HLPR2(x)
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_T)
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val) ## \
__CURL_OFF_T_C_HLPR1(CURL_SUFFIX_CURL_OFF_TU)
#else
# ifdef CURL_ISOCPP
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val ## Suffix
# else
# define __CURL_OFF_T_C_HLPR2(Val,Suffix) Val/**/Suffix
# endif
# define __CURL_OFF_T_C_HLPR1(Val,Suffix) __CURL_OFF_T_C_HLPR2(Val,Suffix)
# define CURL_OFF_T_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_T)
# define CURL_OFF_TU_C(Val) __CURL_OFF_T_C_HLPR1(Val,CURL_SUFFIX_CURL_OFF_TU)
#endif
#endif /* __CURL_SYSTEM_H */
+677
View File
@@ -0,0 +1,677 @@
#ifndef __CURL_TYPECHECK_GCC_H
#define __CURL_TYPECHECK_GCC_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2017, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at https://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
***************************************************************************/
/* wraps curl_easy_setopt() with typechecking */
/* To add a new kind of warning, add an
* if(_curl_is_sometype_option(_curl_opt))
* if(!_curl_is_sometype(value))
* _curl_easy_setopt_err_sometype();
* block and define _curl_is_sometype_option, _curl_is_sometype and
* _curl_easy_setopt_err_sometype below
*
* NOTE: We use two nested 'if' statements here instead of the && operator, in
* order to work around gcc bug #32061. It affects only gcc 4.3.x/4.4.x
* when compiling with -Wlogical-op.
*
* To add an option that uses the same type as an existing option, you'll just
* need to extend the appropriate _curl_*_option macro
*/
#define curl_easy_setopt(handle, option, value) \
__extension__ ({ \
__typeof__(option) _curl_opt = option; \
if(__builtin_constant_p(_curl_opt)) { \
if(_curl_is_long_option(_curl_opt)) \
if(!_curl_is_long(value)) \
_curl_easy_setopt_err_long(); \
if(_curl_is_off_t_option(_curl_opt)) \
if(!_curl_is_off_t(value)) \
_curl_easy_setopt_err_curl_off_t(); \
if(_curl_is_string_option(_curl_opt)) \
if(!_curl_is_string(value)) \
_curl_easy_setopt_err_string(); \
if(_curl_is_write_cb_option(_curl_opt)) \
if(!_curl_is_write_cb(value)) \
_curl_easy_setopt_err_write_callback(); \
if((_curl_opt) == CURLOPT_READFUNCTION) \
if(!_curl_is_read_cb(value)) \
_curl_easy_setopt_err_read_cb(); \
if((_curl_opt) == CURLOPT_IOCTLFUNCTION) \
if(!_curl_is_ioctl_cb(value)) \
_curl_easy_setopt_err_ioctl_cb(); \
if((_curl_opt) == CURLOPT_SOCKOPTFUNCTION) \
if(!_curl_is_sockopt_cb(value)) \
_curl_easy_setopt_err_sockopt_cb(); \
if((_curl_opt) == CURLOPT_OPENSOCKETFUNCTION) \
if(!_curl_is_opensocket_cb(value)) \
_curl_easy_setopt_err_opensocket_cb(); \
if((_curl_opt) == CURLOPT_PROGRESSFUNCTION) \
if(!_curl_is_progress_cb(value)) \
_curl_easy_setopt_err_progress_cb(); \
if((_curl_opt) == CURLOPT_DEBUGFUNCTION) \
if(!_curl_is_debug_cb(value)) \
_curl_easy_setopt_err_debug_cb(); \
if((_curl_opt) == CURLOPT_SSL_CTX_FUNCTION) \
if(!_curl_is_ssl_ctx_cb(value)) \
_curl_easy_setopt_err_ssl_ctx_cb(); \
if(_curl_is_conv_cb_option(_curl_opt)) \
if(!_curl_is_conv_cb(value)) \
_curl_easy_setopt_err_conv_cb(); \
if((_curl_opt) == CURLOPT_SEEKFUNCTION) \
if(!_curl_is_seek_cb(value)) \
_curl_easy_setopt_err_seek_cb(); \
if(_curl_is_cb_data_option(_curl_opt)) \
if(!_curl_is_cb_data(value)) \
_curl_easy_setopt_err_cb_data(); \
if((_curl_opt) == CURLOPT_ERRORBUFFER) \
if(!_curl_is_error_buffer(value)) \
_curl_easy_setopt_err_error_buffer(); \
if((_curl_opt) == CURLOPT_STDERR) \
if(!_curl_is_FILE(value)) \
_curl_easy_setopt_err_FILE(); \
if(_curl_is_postfields_option(_curl_opt)) \
if(!_curl_is_postfields(value)) \
_curl_easy_setopt_err_postfields(); \
if((_curl_opt) == CURLOPT_HTTPPOST) \
if(!_curl_is_arr((value), struct curl_httppost)) \
_curl_easy_setopt_err_curl_httpost(); \
if(_curl_is_slist_option(_curl_opt)) \
if(!_curl_is_arr((value), struct curl_slist)) \
_curl_easy_setopt_err_curl_slist(); \
if((_curl_opt) == CURLOPT_SHARE) \
if(!_curl_is_ptr((value), CURLSH)) \
_curl_easy_setopt_err_CURLSH(); \
} \
curl_easy_setopt(handle, _curl_opt, value); \
})
/* wraps curl_easy_getinfo() with typechecking */
/* FIXME: don't allow const pointers */
#define curl_easy_getinfo(handle, info, arg) \
__extension__ ({ \
__typeof__(info) _curl_info = info; \
if(__builtin_constant_p(_curl_info)) { \
if(_curl_is_string_info(_curl_info)) \
if(!_curl_is_arr((arg), char *)) \
_curl_easy_getinfo_err_string(); \
if(_curl_is_long_info(_curl_info)) \
if(!_curl_is_arr((arg), long)) \
_curl_easy_getinfo_err_long(); \
if(_curl_is_double_info(_curl_info)) \
if(!_curl_is_arr((arg), double)) \
_curl_easy_getinfo_err_double(); \
if(_curl_is_slist_info(_curl_info)) \
if(!_curl_is_arr((arg), struct curl_slist *)) \
_curl_easy_getinfo_err_curl_slist(); \
if(_curl_is_tlssessioninfo_info(_curl_info)) \
if(!_curl_is_arr((arg), struct curl_tlssessioninfo *)) \
_curl_easy_getinfo_err_curl_tlssesssioninfo(); \
if(_curl_is_certinfo_info(_curl_info)) \
if(!_curl_is_arr((arg), struct curl_certinfo *)) \
_curl_easy_getinfo_err_curl_certinfo(); \
if(_curl_is_socket_info(_curl_info)) \
if(!_curl_is_arr((arg), curl_socket_t)) \
_curl_easy_getinfo_err_curl_socket(); \
if(_curl_is_off_t_info(_curl_info)) \
if(!_curl_is_arr((arg), curl_off_t)) \
_curl_easy_getinfo_err_curl_off_t(); \
} \
curl_easy_getinfo(handle, _curl_info, arg); \
})
/* TODO: typechecking for curl_share_setopt() and curl_multi_setopt(),
* for now just make sure that the functions are called with three
* arguments
*/
#define curl_share_setopt(share,opt,param) curl_share_setopt(share,opt,param)
#define curl_multi_setopt(handle,opt,param) curl_multi_setopt(handle,opt,param)
/* the actual warnings, triggered by calling the _curl_easy_setopt_err*
* functions */
/* To define a new warning, use _CURL_WARNING(identifier, "message") */
#define _CURL_WARNING(id, message) \
static void __attribute__((__warning__(message))) \
__attribute__((__unused__)) __attribute__((__noinline__)) \
id(void) { __asm__(""); }
_CURL_WARNING(_curl_easy_setopt_err_long,
"curl_easy_setopt expects a long argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_curl_off_t,
"curl_easy_setopt expects a curl_off_t argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_string,
"curl_easy_setopt expects a "
"string ('char *' or char[]) argument for this option"
)
_CURL_WARNING(_curl_easy_setopt_err_write_callback,
"curl_easy_setopt expects a curl_write_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_read_cb,
"curl_easy_setopt expects a curl_read_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_ioctl_cb,
"curl_easy_setopt expects a curl_ioctl_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_sockopt_cb,
"curl_easy_setopt expects a curl_sockopt_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_opensocket_cb,
"curl_easy_setopt expects a "
"curl_opensocket_callback argument for this option"
)
_CURL_WARNING(_curl_easy_setopt_err_progress_cb,
"curl_easy_setopt expects a curl_progress_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_debug_cb,
"curl_easy_setopt expects a curl_debug_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_ssl_ctx_cb,
"curl_easy_setopt expects a curl_ssl_ctx_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_conv_cb,
"curl_easy_setopt expects a curl_conv_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_seek_cb,
"curl_easy_setopt expects a curl_seek_callback argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_cb_data,
"curl_easy_setopt expects a "
"private data pointer as argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_error_buffer,
"curl_easy_setopt expects a "
"char buffer of CURL_ERROR_SIZE as argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_FILE,
"curl_easy_setopt expects a 'FILE *' argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_postfields,
"curl_easy_setopt expects a 'void *' or 'char *' argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_curl_httpost,
"curl_easy_setopt expects a 'struct curl_httppost *' "
"argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_curl_slist,
"curl_easy_setopt expects a 'struct curl_slist *' argument for this option")
_CURL_WARNING(_curl_easy_setopt_err_CURLSH,
"curl_easy_setopt expects a CURLSH* argument for this option")
_CURL_WARNING(_curl_easy_getinfo_err_string,
"curl_easy_getinfo expects a pointer to 'char *' for this info")
_CURL_WARNING(_curl_easy_getinfo_err_long,
"curl_easy_getinfo expects a pointer to long for this info")
_CURL_WARNING(_curl_easy_getinfo_err_double,
"curl_easy_getinfo expects a pointer to double for this info")
_CURL_WARNING(_curl_easy_getinfo_err_curl_slist,
"curl_easy_getinfo expects a pointer to 'struct curl_slist *' for this info")
_CURL_WARNING(_curl_easy_getinfo_err_curl_tlssesssioninfo,
"curl_easy_getinfo expects a pointer to "
"'struct curl_tlssessioninfo *' for this info")
_CURL_WARNING(_curl_easy_getinfo_err_curl_certinfo,
"curl_easy_getinfo expects a pointer to "
"'struct curl_certinfo *' for this info")
_CURL_WARNING(_curl_easy_getinfo_err_curl_socket,
"curl_easy_getinfo expects a pointer to curl_socket_t for this info")
_CURL_WARNING(_curl_easy_getinfo_err_curl_off_t,
"curl_easy_getinfo expects a pointer to curl_off_t for this info")
/* groups of curl_easy_setops options that take the same type of argument */
/* To add a new option to one of the groups, just add
* (option) == CURLOPT_SOMETHING
* to the or-expression. If the option takes a long or curl_off_t, you don't
* have to do anything
*/
/* evaluates to true if option takes a long argument */
#define _curl_is_long_option(option) \
(0 < (option) && (option) < CURLOPTTYPE_OBJECTPOINT)
#define _curl_is_off_t_option(option) \
((option) > CURLOPTTYPE_OFF_T)
/* evaluates to true if option takes a char* argument */
#define _curl_is_string_option(option) \
((option) == CURLOPT_ABSTRACT_UNIX_SOCKET || \
(option) == CURLOPT_ACCEPT_ENCODING || \
(option) == CURLOPT_CAINFO || \
(option) == CURLOPT_CAPATH || \
(option) == CURLOPT_COOKIE || \
(option) == CURLOPT_COOKIEFILE || \
(option) == CURLOPT_COOKIEJAR || \
(option) == CURLOPT_COOKIELIST || \
(option) == CURLOPT_CRLFILE || \
(option) == CURLOPT_CUSTOMREQUEST || \
(option) == CURLOPT_DEFAULT_PROTOCOL || \
(option) == CURLOPT_DNS_INTERFACE || \
(option) == CURLOPT_DNS_LOCAL_IP4 || \
(option) == CURLOPT_DNS_LOCAL_IP6 || \
(option) == CURLOPT_DNS_SERVERS || \
(option) == CURLOPT_EGDSOCKET || \
(option) == CURLOPT_FTPPORT || \
(option) == CURLOPT_FTP_ACCOUNT || \
(option) == CURLOPT_FTP_ALTERNATIVE_TO_USER || \
(option) == CURLOPT_INTERFACE || \
(option) == CURLOPT_ISSUERCERT || \
(option) == CURLOPT_KEYPASSWD || \
(option) == CURLOPT_KRBLEVEL || \
(option) == CURLOPT_LOGIN_OPTIONS || \
(option) == CURLOPT_MAIL_AUTH || \
(option) == CURLOPT_MAIL_FROM || \
(option) == CURLOPT_NETRC_FILE || \
(option) == CURLOPT_NOPROXY || \
(option) == CURLOPT_PASSWORD || \
(option) == CURLOPT_PINNEDPUBLICKEY || \
(option) == CURLOPT_PRE_PROXY || \
(option) == CURLOPT_PROXY || \
(option) == CURLOPT_PROXYPASSWORD || \
(option) == CURLOPT_PROXYUSERNAME || \
(option) == CURLOPT_PROXYUSERPWD || \
(option) == CURLOPT_PROXY_CAINFO || \
(option) == CURLOPT_PROXY_CAPATH || \
(option) == CURLOPT_PROXY_CRLFILE || \
(option) == CURLOPT_PROXY_KEYPASSWD || \
(option) == CURLOPT_PROXY_PINNEDPUBLICKEY || \
(option) == CURLOPT_PROXY_SERVICE_NAME || \
(option) == CURLOPT_PROXY_SSLCERT || \
(option) == CURLOPT_PROXY_SSLCERTTYPE || \
(option) == CURLOPT_PROXY_SSLKEY || \
(option) == CURLOPT_PROXY_SSLKEYTYPE || \
(option) == CURLOPT_PROXY_SSL_CIPHER_LIST || \
(option) == CURLOPT_PROXY_TLSAUTH_PASSWORD || \
(option) == CURLOPT_PROXY_TLSAUTH_USERNAME || \
(option) == CURLOPT_PROXY_TLSAUTH_TYPE || \
(option) == CURLOPT_RANDOM_FILE || \
(option) == CURLOPT_RANGE || \
(option) == CURLOPT_REFERER || \
(option) == CURLOPT_RTSP_SESSION_ID || \
(option) == CURLOPT_RTSP_STREAM_URI || \
(option) == CURLOPT_RTSP_TRANSPORT || \
(option) == CURLOPT_SERVICE_NAME || \
(option) == CURLOPT_SOCKS5_GSSAPI_SERVICE || \
(option) == CURLOPT_SSH_HOST_PUBLIC_KEY_MD5 || \
(option) == CURLOPT_SSH_KNOWNHOSTS || \
(option) == CURLOPT_SSH_PRIVATE_KEYFILE || \
(option) == CURLOPT_SSH_PUBLIC_KEYFILE || \
(option) == CURLOPT_SSLCERT || \
(option) == CURLOPT_SSLCERTTYPE || \
(option) == CURLOPT_SSLENGINE || \
(option) == CURLOPT_SSLKEY || \
(option) == CURLOPT_SSLKEYTYPE || \
(option) == CURLOPT_SSL_CIPHER_LIST || \
(option) == CURLOPT_TLSAUTH_PASSWORD || \
(option) == CURLOPT_TLSAUTH_TYPE || \
(option) == CURLOPT_TLSAUTH_USERNAME || \
(option) == CURLOPT_UNIX_SOCKET_PATH || \
(option) == CURLOPT_URL || \
(option) == CURLOPT_USERAGENT || \
(option) == CURLOPT_USERNAME || \
(option) == CURLOPT_USERPWD || \
(option) == CURLOPT_XOAUTH2_BEARER || \
0)
/* evaluates to true if option takes a curl_write_callback argument */
#define _curl_is_write_cb_option(option) \
((option) == CURLOPT_HEADERFUNCTION || \
(option) == CURLOPT_WRITEFUNCTION)
/* evaluates to true if option takes a curl_conv_callback argument */
#define _curl_is_conv_cb_option(option) \
((option) == CURLOPT_CONV_TO_NETWORK_FUNCTION || \
(option) == CURLOPT_CONV_FROM_NETWORK_FUNCTION || \
(option) == CURLOPT_CONV_FROM_UTF8_FUNCTION)
/* evaluates to true if option takes a data argument to pass to a callback */
#define _curl_is_cb_data_option(option) \
((option) == CURLOPT_CHUNK_DATA || \
(option) == CURLOPT_CLOSESOCKETDATA || \
(option) == CURLOPT_DEBUGDATA || \
(option) == CURLOPT_FNMATCH_DATA || \
(option) == CURLOPT_HEADERDATA || \
(option) == CURLOPT_INTERLEAVEDATA || \
(option) == CURLOPT_IOCTLDATA || \
(option) == CURLOPT_OPENSOCKETDATA || \
(option) == CURLOPT_PRIVATE || \
(option) == CURLOPT_PROGRESSDATA || \
(option) == CURLOPT_READDATA || \
(option) == CURLOPT_SEEKDATA || \
(option) == CURLOPT_SOCKOPTDATA || \
(option) == CURLOPT_SSH_KEYDATA || \
(option) == CURLOPT_SSL_CTX_DATA || \
(option) == CURLOPT_WRITEDATA || \
0)
/* evaluates to true if option takes a POST data argument (void* or char*) */
#define _curl_is_postfields_option(option) \
((option) == CURLOPT_POSTFIELDS || \
(option) == CURLOPT_COPYPOSTFIELDS || \
0)
/* evaluates to true if option takes a struct curl_slist * argument */
#define _curl_is_slist_option(option) \
((option) == CURLOPT_HTTP200ALIASES || \
(option) == CURLOPT_HTTPHEADER || \
(option) == CURLOPT_MAIL_RCPT || \
(option) == CURLOPT_POSTQUOTE || \
(option) == CURLOPT_PREQUOTE || \
(option) == CURLOPT_PROXYHEADER || \
(option) == CURLOPT_QUOTE || \
(option) == CURLOPT_RESOLVE || \
(option) == CURLOPT_TELNETOPTIONS || \
0)
/* groups of curl_easy_getinfo infos that take the same type of argument */
/* evaluates to true if info expects a pointer to char * argument */
#define _curl_is_string_info(info) \
(CURLINFO_STRING < (info) && (info) < CURLINFO_LONG)
/* evaluates to true if info expects a pointer to long argument */
#define _curl_is_long_info(info) \
(CURLINFO_LONG < (info) && (info) < CURLINFO_DOUBLE)
/* evaluates to true if info expects a pointer to double argument */
#define _curl_is_double_info(info) \
(CURLINFO_DOUBLE < (info) && (info) < CURLINFO_SLIST)
/* true if info expects a pointer to struct curl_slist * argument */
#define _curl_is_slist_info(info) \
(((info) == CURLINFO_SSL_ENGINES) || ((info) == CURLINFO_COOKIELIST))
/* true if info expects a pointer to struct curl_tlssessioninfo * argument */
#define _curl_is_tlssessioninfo_info(info) \
(((info) == CURLINFO_TLS_SSL_PTR) || ((info) == CURLINFO_TLS_SESSION))
/* true if info expects a pointer to struct curl_certinfo * argument */
#define _curl_is_certinfo_info(info) ((info) == CURLINFO_CERTINFO)
/* true if info expects a pointer to struct curl_socket_t argument */
#define _curl_is_socket_info(info) \
(CURLINFO_SOCKET < (info) && (info) < CURLINFO_OFF_T)
/* true if info expects a pointer to curl_off_t argument */
#define _curl_is_off_t_info(info) \
(CURLINFO_OFF_T < (info))
/* typecheck helpers -- check whether given expression has requested type*/
/* For pointers, you can use the _curl_is_ptr/_curl_is_arr macros,
* otherwise define a new macro. Search for __builtin_types_compatible_p
* in the GCC manual.
* NOTE: these macros MUST NOT EVALUATE their arguments! The argument is
* the actual expression passed to the curl_easy_setopt macro. This
* means that you can only apply the sizeof and __typeof__ operators, no
* == or whatsoever.
*/
/* XXX: should evaluate to true iff expr is a pointer */
#define _curl_is_any_ptr(expr) \
(sizeof(expr) == sizeof(void *))
/* evaluates to true if expr is NULL */
/* XXX: must not evaluate expr, so this check is not accurate */
#define _curl_is_NULL(expr) \
(__builtin_types_compatible_p(__typeof__(expr), __typeof__(NULL)))
/* evaluates to true if expr is type*, const type* or NULL */
#define _curl_is_ptr(expr, type) \
(_curl_is_NULL(expr) || \
__builtin_types_compatible_p(__typeof__(expr), type *) || \
__builtin_types_compatible_p(__typeof__(expr), const type *))
/* evaluates to true if expr is one of type[], type*, NULL or const type* */
#define _curl_is_arr(expr, type) \
(_curl_is_ptr((expr), type) || \
__builtin_types_compatible_p(__typeof__(expr), type []))
/* evaluates to true if expr is a string */
#define _curl_is_string(expr) \
(_curl_is_arr((expr), char) || \
_curl_is_arr((expr), signed char) || \
_curl_is_arr((expr), unsigned char))
/* evaluates to true if expr is a long (no matter the signedness)
* XXX: for now, int is also accepted (and therefore short and char, which
* are promoted to int when passed to a variadic function) */
#define _curl_is_long(expr) \
(__builtin_types_compatible_p(__typeof__(expr), long) || \
__builtin_types_compatible_p(__typeof__(expr), signed long) || \
__builtin_types_compatible_p(__typeof__(expr), unsigned long) || \
__builtin_types_compatible_p(__typeof__(expr), int) || \
__builtin_types_compatible_p(__typeof__(expr), signed int) || \
__builtin_types_compatible_p(__typeof__(expr), unsigned int) || \
__builtin_types_compatible_p(__typeof__(expr), short) || \
__builtin_types_compatible_p(__typeof__(expr), signed short) || \
__builtin_types_compatible_p(__typeof__(expr), unsigned short) || \
__builtin_types_compatible_p(__typeof__(expr), char) || \
__builtin_types_compatible_p(__typeof__(expr), signed char) || \
__builtin_types_compatible_p(__typeof__(expr), unsigned char))
/* evaluates to true if expr is of type curl_off_t */
#define _curl_is_off_t(expr) \
(__builtin_types_compatible_p(__typeof__(expr), curl_off_t))
/* evaluates to true if expr is abuffer suitable for CURLOPT_ERRORBUFFER */
/* XXX: also check size of an char[] array? */
#define _curl_is_error_buffer(expr) \
(_curl_is_NULL(expr) || \
__builtin_types_compatible_p(__typeof__(expr), char *) || \
__builtin_types_compatible_p(__typeof__(expr), char[]))
/* evaluates to true if expr is of type (const) void* or (const) FILE* */
#if 0
#define _curl_is_cb_data(expr) \
(_curl_is_ptr((expr), void) || \
_curl_is_ptr((expr), FILE))
#else /* be less strict */
#define _curl_is_cb_data(expr) \
_curl_is_any_ptr(expr)
#endif
/* evaluates to true if expr is of type FILE* */
#define _curl_is_FILE(expr) \
(_curl_is_NULL(expr) || \
(__builtin_types_compatible_p(__typeof__(expr), FILE *)))
/* evaluates to true if expr can be passed as POST data (void* or char*) */
#define _curl_is_postfields(expr) \
(_curl_is_ptr((expr), void) || \
_curl_is_arr((expr), char))
/* FIXME: the whole callback checking is messy...
* The idea is to tolerate char vs. void and const vs. not const
* pointers in arguments at least
*/
/* helper: __builtin_types_compatible_p distinguishes between functions and
* function pointers, hide it */
#define _curl_callback_compatible(func, type) \
(__builtin_types_compatible_p(__typeof__(func), type) || \
__builtin_types_compatible_p(__typeof__(func) *, type))
/* evaluates to true if expr is of type curl_read_callback or "similar" */
#define _curl_is_read_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), __typeof__(fread) *) || \
_curl_callback_compatible((expr), curl_read_callback) || \
_curl_callback_compatible((expr), _curl_read_callback1) || \
_curl_callback_compatible((expr), _curl_read_callback2) || \
_curl_callback_compatible((expr), _curl_read_callback3) || \
_curl_callback_compatible((expr), _curl_read_callback4) || \
_curl_callback_compatible((expr), _curl_read_callback5) || \
_curl_callback_compatible((expr), _curl_read_callback6))
typedef size_t (*_curl_read_callback1)(char *, size_t, size_t, void *);
typedef size_t (*_curl_read_callback2)(char *, size_t, size_t, const void *);
typedef size_t (*_curl_read_callback3)(char *, size_t, size_t, FILE *);
typedef size_t (*_curl_read_callback4)(void *, size_t, size_t, void *);
typedef size_t (*_curl_read_callback5)(void *, size_t, size_t, const void *);
typedef size_t (*_curl_read_callback6)(void *, size_t, size_t, FILE *);
/* evaluates to true if expr is of type curl_write_callback or "similar" */
#define _curl_is_write_cb(expr) \
(_curl_is_read_cb(expr) || \
_curl_callback_compatible((expr), __typeof__(fwrite) *) || \
_curl_callback_compatible((expr), curl_write_callback) || \
_curl_callback_compatible((expr), _curl_write_callback1) || \
_curl_callback_compatible((expr), _curl_write_callback2) || \
_curl_callback_compatible((expr), _curl_write_callback3) || \
_curl_callback_compatible((expr), _curl_write_callback4) || \
_curl_callback_compatible((expr), _curl_write_callback5) || \
_curl_callback_compatible((expr), _curl_write_callback6))
typedef size_t (*_curl_write_callback1)(const char *, size_t, size_t, void *);
typedef size_t (*_curl_write_callback2)(const char *, size_t, size_t,
const void *);
typedef size_t (*_curl_write_callback3)(const char *, size_t, size_t, FILE *);
typedef size_t (*_curl_write_callback4)(const void *, size_t, size_t, void *);
typedef size_t (*_curl_write_callback5)(const void *, size_t, size_t,
const void *);
typedef size_t (*_curl_write_callback6)(const void *, size_t, size_t, FILE *);
/* evaluates to true if expr is of type curl_ioctl_callback or "similar" */
#define _curl_is_ioctl_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_ioctl_callback) || \
_curl_callback_compatible((expr), _curl_ioctl_callback1) || \
_curl_callback_compatible((expr), _curl_ioctl_callback2) || \
_curl_callback_compatible((expr), _curl_ioctl_callback3) || \
_curl_callback_compatible((expr), _curl_ioctl_callback4))
typedef curlioerr (*_curl_ioctl_callback1)(CURL *, int, void *);
typedef curlioerr (*_curl_ioctl_callback2)(CURL *, int, const void *);
typedef curlioerr (*_curl_ioctl_callback3)(CURL *, curliocmd, void *);
typedef curlioerr (*_curl_ioctl_callback4)(CURL *, curliocmd, const void *);
/* evaluates to true if expr is of type curl_sockopt_callback or "similar" */
#define _curl_is_sockopt_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_sockopt_callback) || \
_curl_callback_compatible((expr), _curl_sockopt_callback1) || \
_curl_callback_compatible((expr), _curl_sockopt_callback2))
typedef int (*_curl_sockopt_callback1)(void *, curl_socket_t, curlsocktype);
typedef int (*_curl_sockopt_callback2)(const void *, curl_socket_t,
curlsocktype);
/* evaluates to true if expr is of type curl_opensocket_callback or
"similar" */
#define _curl_is_opensocket_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_opensocket_callback) || \
_curl_callback_compatible((expr), _curl_opensocket_callback1) || \
_curl_callback_compatible((expr), _curl_opensocket_callback2) || \
_curl_callback_compatible((expr), _curl_opensocket_callback3) || \
_curl_callback_compatible((expr), _curl_opensocket_callback4))
typedef curl_socket_t (*_curl_opensocket_callback1)
(void *, curlsocktype, struct curl_sockaddr *);
typedef curl_socket_t (*_curl_opensocket_callback2)
(void *, curlsocktype, const struct curl_sockaddr *);
typedef curl_socket_t (*_curl_opensocket_callback3)
(const void *, curlsocktype, struct curl_sockaddr *);
typedef curl_socket_t (*_curl_opensocket_callback4)
(const void *, curlsocktype, const struct curl_sockaddr *);
/* evaluates to true if expr is of type curl_progress_callback or "similar" */
#define _curl_is_progress_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_progress_callback) || \
_curl_callback_compatible((expr), _curl_progress_callback1) || \
_curl_callback_compatible((expr), _curl_progress_callback2))
typedef int (*_curl_progress_callback1)(void *,
double, double, double, double);
typedef int (*_curl_progress_callback2)(const void *,
double, double, double, double);
/* evaluates to true if expr is of type curl_debug_callback or "similar" */
#define _curl_is_debug_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_debug_callback) || \
_curl_callback_compatible((expr), _curl_debug_callback1) || \
_curl_callback_compatible((expr), _curl_debug_callback2) || \
_curl_callback_compatible((expr), _curl_debug_callback3) || \
_curl_callback_compatible((expr), _curl_debug_callback4) || \
_curl_callback_compatible((expr), _curl_debug_callback5) || \
_curl_callback_compatible((expr), _curl_debug_callback6) || \
_curl_callback_compatible((expr), _curl_debug_callback7) || \
_curl_callback_compatible((expr), _curl_debug_callback8))
typedef int (*_curl_debug_callback1) (CURL *,
curl_infotype, char *, size_t, void *);
typedef int (*_curl_debug_callback2) (CURL *,
curl_infotype, char *, size_t, const void *);
typedef int (*_curl_debug_callback3) (CURL *,
curl_infotype, const char *, size_t, void *);
typedef int (*_curl_debug_callback4) (CURL *,
curl_infotype, const char *, size_t, const void *);
typedef int (*_curl_debug_callback5) (CURL *,
curl_infotype, unsigned char *, size_t, void *);
typedef int (*_curl_debug_callback6) (CURL *,
curl_infotype, unsigned char *, size_t, const void *);
typedef int (*_curl_debug_callback7) (CURL *,
curl_infotype, const unsigned char *, size_t, void *);
typedef int (*_curl_debug_callback8) (CURL *,
curl_infotype, const unsigned char *, size_t, const void *);
/* evaluates to true if expr is of type curl_ssl_ctx_callback or "similar" */
/* this is getting even messier... */
#define _curl_is_ssl_ctx_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_ssl_ctx_callback) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback1) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback2) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback3) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback4) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback5) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback6) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback7) || \
_curl_callback_compatible((expr), _curl_ssl_ctx_callback8))
typedef CURLcode (*_curl_ssl_ctx_callback1)(CURL *, void *, void *);
typedef CURLcode (*_curl_ssl_ctx_callback2)(CURL *, void *, const void *);
typedef CURLcode (*_curl_ssl_ctx_callback3)(CURL *, const void *, void *);
typedef CURLcode (*_curl_ssl_ctx_callback4)(CURL *, const void *,
const void *);
#ifdef HEADER_SSL_H
/* hack: if we included OpenSSL's ssl.h, we know about SSL_CTX
* this will of course break if we're included before OpenSSL headers...
*/
typedef CURLcode (*_curl_ssl_ctx_callback5)(CURL *, SSL_CTX, void *);
typedef CURLcode (*_curl_ssl_ctx_callback6)(CURL *, SSL_CTX, const void *);
typedef CURLcode (*_curl_ssl_ctx_callback7)(CURL *, const SSL_CTX, void *);
typedef CURLcode (*_curl_ssl_ctx_callback8)(CURL *, const SSL_CTX,
const void *);
#else
typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback5;
typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback6;
typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback7;
typedef _curl_ssl_ctx_callback1 _curl_ssl_ctx_callback8;
#endif
/* evaluates to true if expr is of type curl_conv_callback or "similar" */
#define _curl_is_conv_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_conv_callback) || \
_curl_callback_compatible((expr), _curl_conv_callback1) || \
_curl_callback_compatible((expr), _curl_conv_callback2) || \
_curl_callback_compatible((expr), _curl_conv_callback3) || \
_curl_callback_compatible((expr), _curl_conv_callback4))
typedef CURLcode (*_curl_conv_callback1)(char *, size_t length);
typedef CURLcode (*_curl_conv_callback2)(const char *, size_t length);
typedef CURLcode (*_curl_conv_callback3)(void *, size_t length);
typedef CURLcode (*_curl_conv_callback4)(const void *, size_t length);
/* evaluates to true if expr is of type curl_seek_callback or "similar" */
#define _curl_is_seek_cb(expr) \
(_curl_is_NULL(expr) || \
_curl_callback_compatible((expr), curl_seek_callback) || \
_curl_callback_compatible((expr), _curl_seek_callback1) || \
_curl_callback_compatible((expr), _curl_seek_callback2))
typedef CURLcode (*_curl_seek_callback1)(void *, curl_off_t, int);
typedef CURLcode (*_curl_seek_callback2)(const void *, curl_off_t, int);
#endif /* __CURL_TYPECHECK_GCC_H */
+88
View File
@@ -0,0 +1,88 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ALLOCATOR_H_INCLUDED
#define JSON_ALLOCATOR_H_INCLUDED
#include <cstring>
#include <memory>
#pragma pack(push, 8)
namespace Json {
template <typename T> class SecureAllocator {
public:
// Type definitions
using value_type = T;
using pointer = T*;
using const_pointer = const T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
using difference_type = std::ptrdiff_t;
/**
* Allocate memory for N items using the standard allocator.
*/
pointer allocate(size_type n) {
// allocate using "global operator new"
return static_cast<pointer>(::operator new(n * sizeof(T)));
}
/**
* Release memory which was allocated for N items at pointer P.
*
* The memory block is filled with zeroes before being released.
*/
void deallocate(pointer p, size_type n) {
// memset_s is used because memset may be optimized away by the compiler
memset_s(p, n * sizeof(T), 0, n * sizeof(T));
// free using "global operator delete"
::operator delete(p);
}
/**
* Construct an item in-place at pointer P.
*/
template <typename... Args> void construct(pointer p, Args&&... args) {
// construct using "placement new" and "perfect forwarding"
::new (static_cast<void*>(p)) T(std::forward<Args>(args)...);
}
size_type max_size() const { return size_t(-1) / sizeof(T); }
pointer address(reference x) const { return std::addressof(x); }
const_pointer address(const_reference x) const { return std::addressof(x); }
/**
* Destroy an item in-place at pointer P.
*/
void destroy(pointer p) {
// destroy using "explicit destructor"
p->~T();
}
// Boilerplate
SecureAllocator() {}
template <typename U> SecureAllocator(const SecureAllocator<U>&) {}
template <typename U> struct rebind { using other = SecureAllocator<U>; };
};
template <typename T, typename U>
bool operator==(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return true;
}
template <typename T, typename U>
bool operator!=(const SecureAllocator<T>&, const SecureAllocator<U>&) {
return false;
}
} // namespace Json
#pragma pack(pop)
#endif // JSON_ALLOCATOR_H_INCLUDED
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_ASSERTIONS_H_INCLUDED
#define JSON_ASSERTIONS_H_INCLUDED
#include <cstdlib>
#include <sstream>
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
/** It should not be possible for a maliciously designed file to
* cause an abort() or seg-fault, so these macros are used only
* for pre-condition violations and internal logic errors.
*/
#if JSON_USE_EXCEPTION
// @todo <= add detail about condition in exception
#define JSON_ASSERT(condition) \
do { \
if (!(condition)) { \
Json::throwLogicError("assert json failed"); \
} \
} while (0)
#define JSON_FAIL_MESSAGE(message) \
do { \
OStringStream oss; \
oss << message; \
Json::throwLogicError(oss.str()); \
abort(); \
} while (0)
#else // JSON_USE_EXCEPTION
#define JSON_ASSERT(condition) assert(condition)
// The call to assert() will show the failure message in debug builds. In
// release builds we abort, for a core-dump or debugger.
#define JSON_FAIL_MESSAGE(message) \
{ \
OStringStream oss; \
oss << message; \
assert(false && oss.str().c_str()); \
abort(); \
}
#endif
#define JSON_ASSERT_MESSAGE(condition, message) \
do { \
if (!(condition)) { \
JSON_FAIL_MESSAGE(message); \
} \
} while (0)
#endif // JSON_ASSERTIONS_H_INCLUDED
+150
View File
@@ -0,0 +1,150 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_CONFIG_H_INCLUDED
#define JSON_CONFIG_H_INCLUDED
#include <cstddef>
#include <cstdint>
#include <istream>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include <type_traits>
// If non-zero, the library uses exceptions to report bad input instead of C
// assertion macros. The default is to use exceptions.
#ifndef JSON_USE_EXCEPTION
#define JSON_USE_EXCEPTION 1
#endif
// Temporary, tracked for removal with issue #982.
#ifndef JSON_USE_NULLREF
#define JSON_USE_NULLREF 1
#endif
/// If defined, indicates that the source file is amalgamated
/// to prevent private header inclusion.
/// Remarks: it is automatically defined in the generated amalgamated header.
// #define JSON_IS_AMALGAMATION
// Export macros for DLL visibility
#if defined(JSON_DLL_BUILD)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllexport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#elif defined(__GNUC__) || defined(__clang__)
#define JSON_API __attribute__((visibility("default")))
#endif // if defined(_MSC_VER)
#elif defined(JSON_DLL)
#if defined(_MSC_VER) || defined(__MINGW32__)
#define JSON_API __declspec(dllimport)
#define JSONCPP_DISABLE_DLL_INTERFACE_WARNING
#endif // if defined(_MSC_VER)
#endif // ifdef JSON_DLL_BUILD
#if !defined(JSON_API)
#define JSON_API
#endif
#if defined(_MSC_VER) && _MSC_VER < 1800
#error \
"ERROR: Visual Studio 12 (2013) with _MSC_VER=1800 is the oldest supported compiler with sufficient C++11 capabilities"
#endif
#if defined(_MSC_VER) && _MSC_VER < 1900
// As recommended at
// https://stackoverflow.com/questions/2915672/snprintf-and-visual-studio-2010
extern JSON_API int msvc_pre1900_c99_snprintf(char* outBuf, size_t size,
const char* format, ...);
#define jsoncpp_snprintf msvc_pre1900_c99_snprintf
#else
#define jsoncpp_snprintf std::snprintf
#endif
// If JSON_NO_INT64 is defined, then Json only support C++ "int" type for
// integer
// Storages, and 64 bits integer support is disabled.
// #define JSON_NO_INT64 1
// JSONCPP_OVERRIDE is maintained for backwards compatibility of external tools.
// C++11 should be used directly in JSONCPP.
#define JSONCPP_OVERRIDE override
#ifdef __clang__
#if __has_extension(attribute_deprecated_with_message)
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#endif
#elif defined(__GNUC__) // not clang (gcc comes later since clang emulates gcc)
#if (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 5))
#define JSONCPP_DEPRECATED(message) __attribute__((deprecated(message)))
#elif (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 1))
#define JSONCPP_DEPRECATED(message) __attribute__((__deprecated__))
#endif // GNUC version
#elif defined(_MSC_VER) // MSVC (after clang because clang on Windows emulates
// MSVC)
#define JSONCPP_DEPRECATED(message) __declspec(deprecated(message))
#endif // __clang__ || __GNUC__ || _MSC_VER
#if !defined(JSONCPP_DEPRECATED)
#define JSONCPP_DEPRECATED(message)
#endif // if !defined(JSONCPP_DEPRECATED)
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ >= 6))
#define JSON_USE_INT64_DOUBLE_CONVERSION 1
#endif
#if !defined(JSON_IS_AMALGAMATION)
#include "allocator.h"
#include "version.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
using Int = int;
using UInt = unsigned int;
#if defined(JSON_NO_INT64)
using LargestInt = int;
using LargestUInt = unsigned int;
#undef JSON_HAS_INT64
#else // if defined(JSON_NO_INT64)
// For Microsoft Visual use specific types as long long is not supported
#if defined(_MSC_VER) // Microsoft Visual Studio
using Int64 = __int64;
using UInt64 = unsigned __int64;
#else // if defined(_MSC_VER) // Other platforms, use long long
using Int64 = int64_t;
using UInt64 = uint64_t;
#endif // if defined(_MSC_VER)
using LargestInt = Int64;
using LargestUInt = UInt64;
#define JSON_HAS_INT64
#endif // if defined(JSON_NO_INT64)
template <typename T>
using Allocator =
typename std::conditional<JSONCPP_USING_SECURE_MEMORY, SecureAllocator<T>,
std::allocator<T>>::type;
using String = std::basic_string<char, std::char_traits<char>, Allocator<char>>;
using IStringStream =
std::basic_istringstream<String::value_type, String::traits_type,
String::allocator_type>;
using OStringStream =
std::basic_ostringstream<String::value_type, String::traits_type,
String::allocator_type>;
using IStream = std::istream;
using OStream = std::ostream;
} // namespace Json
// Legacy names (formerly macros).
using JSONCPP_STRING = Json::String;
using JSONCPP_ISTRINGSTREAM = Json::IStringStream;
using JSONCPP_OSTRINGSTREAM = Json::OStringStream;
using JSONCPP_ISTREAM = Json::IStream;
using JSONCPP_OSTREAM = Json::OStream;
#endif // JSON_CONFIG_H_INCLUDED
+43
View File
@@ -0,0 +1,43 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FORWARDS_H_INCLUDED
#define JSON_FORWARDS_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
namespace Json {
// writer.h
class StreamWriter;
class StreamWriterBuilder;
class Writer;
class FastWriter;
class StyledWriter;
class StyledStreamWriter;
// reader.h
class Reader;
class CharReader;
class CharReaderBuilder;
// json_features.h
class Features;
// value.h
using ArrayIndex = unsigned int;
class StaticString;
class Path;
class PathArgument;
class Value;
class ValueIteratorBase;
class ValueIterator;
class ValueConstIterator;
} // namespace Json
#endif // JSON_FORWARDS_H_INCLUDED
+15
View File
@@ -0,0 +1,15 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_JSON_H_INCLUDED
#define JSON_JSON_H_INCLUDED
#include "config.h"
#include "json_features.h"
#include "reader.h"
#include "value.h"
#include "writer.h"
#endif // JSON_JSON_H_INCLUDED
+61
View File
@@ -0,0 +1,61 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_FEATURES_H_INCLUDED
#define JSON_FEATURES_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#pragma pack(push, 8)
namespace Json {
/** \brief Configuration passed to reader and writer.
* This configuration object can be used to force the Reader or Writer
* to behave in a standard conforming way.
*/
class JSON_API Features {
public:
/** \brief A configuration that allows all features and assumes all strings
* are UTF-8.
* - C & C++ comments are allowed
* - Root object can be any JSON value
* - Assumes Value strings are encoded in UTF-8
*/
static Features all();
/** \brief A configuration that is strictly compatible with the JSON
* specification.
* - Comments are forbidden.
* - Root object must be either an array or an object value.
* - Assumes Value strings are encoded in UTF-8
*/
static Features strictMode();
/** \brief Initialize the configuration like JsonConfig::allFeatures;
*/
Features();
/// \c true if comments are allowed. Default: \c true.
bool allowComments_{true};
/// \c true if root must be either an array or an object value. Default: \c
/// false.
bool strictRoot_{false};
/// \c true if dropped null placeholders are allowed. Default: \c false.
bool allowDroppedNullPlaceholders_{false};
/// \c true if numeric object key are allowed. Default: \c false.
bool allowNumericKeys_{false};
};
} // namespace Json
#pragma pack(pop)
#endif // JSON_FEATURES_H_INCLUDED
File diff suppressed because it is too large Load Diff
+134
View File
@@ -0,0 +1,134 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#define LIB_JSONCPP_JSON_TOOL_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "config.h"
#endif
// Also support old flag NO_LOCALE_SUPPORT
#ifdef NO_LOCALE_SUPPORT
#define JSONCPP_NO_LOCALE_SUPPORT
#endif
#ifndef JSONCPP_NO_LOCALE_SUPPORT
#include <clocale>
#endif
/* This header provides common string manipulation support, such as UTF-8,
* portable conversion from/to string...
*
* It is an internal header that must not be exposed.
*/
namespace Json {
static inline char getDecimalPoint() {
#ifdef JSONCPP_NO_LOCALE_SUPPORT
return '\0';
#else
struct lconv* lc = localeconv();
return lc ? *(lc->decimal_point) : '\0';
#endif
}
/// Converts a unicode code-point to UTF-8.
static inline String codePointToUTF8(unsigned int cp) {
String result;
// based on description from http://en.wikipedia.org/wiki/UTF-8
if (cp <= 0x7f) {
result.resize(1);
result[0] = static_cast<char>(cp);
} else if (cp <= 0x7FF) {
result.resize(2);
result[1] = static_cast<char>(0x80 | (0x3f & cp));
result[0] = static_cast<char>(0xC0 | (0x1f & (cp >> 6)));
} else if (cp <= 0xFFFF) {
result.resize(3);
result[2] = static_cast<char>(0x80 | (0x3f & cp));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[0] = static_cast<char>(0xE0 | (0xf & (cp >> 12)));
} else if (cp <= 0x10FFFF) {
result.resize(4);
result[3] = static_cast<char>(0x80 | (0x3f & cp));
result[2] = static_cast<char>(0x80 | (0x3f & (cp >> 6)));
result[1] = static_cast<char>(0x80 | (0x3f & (cp >> 12)));
result[0] = static_cast<char>(0xF0 | (0x7 & (cp >> 18)));
}
return result;
}
enum {
/// Constant that specify the size of the buffer that must be passed to
/// uintToString.
uintToStringBufferSize = 3 * sizeof(LargestUInt) + 1
};
// Defines a char buffer for use with uintToString().
using UIntToStringBuffer = char[uintToStringBufferSize];
/** Converts an unsigned integer to string.
* @param value Unsigned integer to convert to string
* @param current Input/Output string buffer.
* Must have at least uintToStringBufferSize chars free.
*/
static inline void uintToString(LargestUInt value, char*& current) {
*--current = 0;
do {
*--current = static_cast<char>(value % 10U + static_cast<unsigned>('0'));
value /= 10;
} while (value != 0);
}
/** Change ',' to '.' everywhere in buffer.
*
* We had a sophisticated way, but it did not work in WinCE.
* @see https://github.com/open-source-parsers/jsoncpp/pull/9
*/
template <typename Iter> Iter fixNumericLocale(Iter begin, Iter end) {
for (; begin != end; ++begin) {
if (*begin == ',') {
*begin = '.';
}
}
return begin;
}
template <typename Iter> void fixNumericLocaleInput(Iter begin, Iter end) {
char decimalPoint = getDecimalPoint();
if (decimalPoint == '\0' || decimalPoint == '.') {
return;
}
for (; begin != end; ++begin) {
if (*begin == '.') {
*begin = decimalPoint;
}
}
}
/**
* Return iterator that would be the new end of the range [begin,end), if we
* were to delete zeros in the end of string, but not the last zero before '.'.
*/
template <typename Iter> Iter fixZerosInTheEnd(Iter begin, Iter end) {
for (; begin != end; --end) {
if (*(end - 1) != '0') {
return end;
}
// Don't delete the last zero before the decimal point.
if (begin != (end - 1) && *(end - 2) == '.') {
return end;
}
}
return end;
}
} // namespace Json
#endif // LIB_JSONCPP_JSON_TOOL_H_INCLUDED
File diff suppressed because it is too large Load Diff
+156
View File
@@ -0,0 +1,156 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
// included by json_value.cpp
namespace Json {
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIteratorBase
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIteratorBase::ValueIteratorBase() : current_() {}
ValueIteratorBase::ValueIteratorBase(
const Value::ObjectValues::iterator& current)
: current_(current), isNull_(false) {}
Value& ValueIteratorBase::deref() { return current_->second; }
const Value& ValueIteratorBase::deref() const { return current_->second; }
void ValueIteratorBase::increment() { ++current_; }
void ValueIteratorBase::decrement() { --current_; }
ValueIteratorBase::difference_type
ValueIteratorBase::computeDistance(const SelfType& other) const {
// Iterator for null value are initialized using the default
// constructor, which initialize current_ to the default
// std::map::iterator. As begin() and end() are two instance
// of the default std::map::iterator, they can not be compared.
// To allow this, we handle this comparison specifically.
if (isNull_ && other.isNull_) {
return 0;
}
// Usage of std::distance is not portable (does not compile with Sun Studio 12
// RogueWave STL,
// which is the one used by default).
// Using a portable hand-made version for non random iterator instead:
// return difference_type( std::distance( current_, other.current_ ) );
difference_type myDistance = 0;
for (Value::ObjectValues::iterator it = current_; it != other.current_;
++it) {
++myDistance;
}
return myDistance;
}
bool ValueIteratorBase::isEqual(const SelfType& other) const {
if (isNull_) {
return other.isNull_;
}
return current_ == other.current_;
}
void ValueIteratorBase::copy(const SelfType& other) {
current_ = other.current_;
isNull_ = other.isNull_;
}
Value ValueIteratorBase::key() const {
const Value::CZString czstring = (*current_).first;
if (czstring.data()) {
if (czstring.isStaticString())
return Value(StaticString(czstring.data()));
return Value(czstring.data(), czstring.data() + czstring.length());
}
return Value(czstring.index());
}
UInt ValueIteratorBase::index() const {
const Value::CZString czstring = (*current_).first;
if (!czstring.data())
return czstring.index();
return Value::UInt(-1);
}
String ValueIteratorBase::name() const {
char const* keey;
char const* end;
keey = memberName(&end);
if (!keey)
return String();
return String(keey, end);
}
char const* ValueIteratorBase::memberName() const {
const char* cname = (*current_).first.data();
return cname ? cname : "";
}
char const* ValueIteratorBase::memberName(char const** end) const {
const char* cname = (*current_).first.data();
if (!cname) {
*end = nullptr;
return nullptr;
}
*end = cname + (*current_).first.length();
return cname;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueConstIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueConstIterator::ValueConstIterator() = default;
ValueConstIterator::ValueConstIterator(
const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueConstIterator::ValueConstIterator(ValueIterator const& other)
: ValueIteratorBase(other) {}
ValueConstIterator& ValueConstIterator::
operator=(const ValueIteratorBase& other) {
copy(other);
return *this;
}
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// class ValueIterator
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
// //////////////////////////////////////////////////////////////////
ValueIterator::ValueIterator() = default;
ValueIterator::ValueIterator(const Value::ObjectValues::iterator& current)
: ValueIteratorBase(current) {}
ValueIterator::ValueIterator(const ValueConstIterator& other)
: ValueIteratorBase(other) {
throwRuntimeError("ConstIterator to Iterator should never be allowed.");
}
ValueIterator::ValueIterator(const ValueIterator& other) = default;
ValueIterator& ValueIterator::operator=(const SelfType& other) {
copy(other);
return *this;
}
} // namespace Json
File diff suppressed because it is too large Load Diff
+403
View File
@@ -0,0 +1,403 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_READER_H_INCLUDED
#define JSON_READER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "json_features.h"
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <deque>
#include <iosfwd>
#include <istream>
#include <stack>
#include <string>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
namespace Json {
/** \brief Unserialize a <a HREF="http://www.json.org">JSON</a> document into a
* Value.
*
* \deprecated Use CharReader and CharReaderBuilder.
*/
class JSONCPP_DEPRECATED(
"Use CharReader and CharReaderBuilder instead.") JSON_API Reader {
public:
using Char = char;
using Location = const Char*;
/** \brief An error tagged with where in the JSON text it was encountered.
*
* The offsets give the [start, limit) range of bytes within the text. Note
* that this is bytes, not codepoints.
*/
struct StructuredError {
ptrdiff_t offset_start;
ptrdiff_t offset_limit;
String message;
};
/** \brief Constructs a Reader allowing all features for parsing.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader();
/** \brief Constructs a Reader allowing the specified feature set for parsing.
*/
JSONCPP_DEPRECATED("Use CharReader and CharReaderBuilder instead")
Reader(const Features& features);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param document UTF-8 encoded string containing the document
* to read.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const std::string& document, Value& root,
bool collectComments = true);
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded
* string of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string
* of the document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it
* was successfully parsed.
* \param collectComments \c true to collect comment and allow writing
* them back during serialization, \c false to
* discard comments. This parameter is ignored
* if Features::allowComments_ is \c false.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
bool parse(const char* beginDoc, const char* endDoc, Value& root,
bool collectComments = true);
/// \brief Parse from input stream.
/// \see Json::operator>>(std::istream&, Json::Value&).
bool parse(IStream& is, Value& root, bool collectComments = true);
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
* \deprecated Use getFormattedErrorMessages() instead (typo fix).
*/
JSONCPP_DEPRECATED("Use getFormattedErrorMessages() instead.")
String getFormatedErrorMessages() const;
/** \brief Returns a user friendly string that list errors in the parsed
* document.
*
* \return Formatted error message with the list of errors with their
* location in the parsed document. An empty string is returned if no error
* occurred during parsing.
*/
String getFormattedErrorMessages() const;
/** \brief Returns a vector of structured errors encountered while parsing.
*
* \return A (possibly empty) vector of StructuredError objects. Currently
* only one error can be returned, but the caller should tolerate multiple
* errors. This can occur if the parser recovers from a non-fatal parse
* error and then encounters additional errors.
*/
std::vector<StructuredError> getStructuredErrors() const;
/** \brief Add a semantic error message.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \return \c true if the error was successfully added, \c false if the Value
* offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message);
/** \brief Add a semantic error message with extra context.
*
* \param value JSON Value location associated with the error
* \param message The error message.
* \param extra Additional JSON Value location to contextualize the error
* \return \c true if the error was successfully added, \c false if either
* Value offset exceeds the document size.
*/
bool pushError(const Value& value, const String& message, const Value& extra);
/** \brief Return whether there are any errors.
*
* \return \c true if there are no errors to report \c false if errors have
* occurred.
*/
bool good() const;
private:
enum TokenType {
tokenEndOfStream = 0,
tokenObjectBegin,
tokenObjectEnd,
tokenArrayBegin,
tokenArrayEnd,
tokenString,
tokenNumber,
tokenTrue,
tokenFalse,
tokenNull,
tokenArraySeparator,
tokenMemberSeparator,
tokenComment,
tokenError
};
class Token {
public:
TokenType type_;
Location start_;
Location end_;
};
class ErrorInfo {
public:
Token token_;
String message_;
Location extra_;
};
using Errors = std::deque<ErrorInfo>;
bool readToken(Token& token);
void skipSpaces();
bool match(const Char* pattern, int patternLength);
bool readComment();
bool readCStyleComment();
bool readCppStyleComment();
bool readString();
void readNumber();
bool readValue();
bool readObject(Token& token);
bool readArray(Token& token);
bool decodeNumber(Token& token);
bool decodeNumber(Token& token, Value& decoded);
bool decodeString(Token& token);
bool decodeString(Token& token, String& decoded);
bool decodeDouble(Token& token);
bool decodeDouble(Token& token, Value& decoded);
bool decodeUnicodeCodePoint(Token& token, Location& current, Location end,
unsigned int& unicode);
bool decodeUnicodeEscapeSequence(Token& token, Location& current,
Location end, unsigned int& unicode);
bool addError(const String& message, Token& token, Location extra = nullptr);
bool recoverFromError(TokenType skipUntilToken);
bool addErrorAndRecover(const String& message, Token& token,
TokenType skipUntilToken);
void skipUntilSpace();
Value& currentValue();
Char getNextChar();
void getLocationLineAndColumn(Location location, int& line,
int& column) const;
String getLocationLineAndColumn(Location location) const;
void addComment(Location begin, Location end, CommentPlacement placement);
void skipCommentTokens(Token& token);
static bool containsNewLine(Location begin, Location end);
static String normalizeEOL(Location begin, Location end);
using Nodes = std::stack<Value*>;
Nodes nodes_;
Errors errors_;
String document_;
Location begin_{};
Location end_{};
Location current_{};
Location lastValueEnd_{};
Value* lastValue_{};
String commentsBefore_;
Features features_;
bool collectComments_{};
}; // Reader
/** Interface for reading JSON from a char array.
*/
class JSON_API CharReader {
public:
virtual ~CharReader() = default;
/** \brief Read a Value from a <a HREF="http://www.json.org">JSON</a>
* document. The document must be a UTF-8 encoded string containing the
* document to read.
*
* \param beginDoc Pointer on the beginning of the UTF-8 encoded string
* of the document to read.
* \param endDoc Pointer on the end of the UTF-8 encoded string of the
* document to read. Must be >= beginDoc.
* \param[out] root Contains the root value of the document if it was
* successfully parsed.
* \param[out] errs Formatted error messages (if not NULL) a user
* friendly string that lists errors in the parsed
* document.
* \return \c true if the document was successfully parsed, \c false if an
* error occurred.
*/
virtual bool parse(char const* beginDoc, char const* endDoc, Value* root,
String* errs) = 0;
class JSON_API Factory {
public:
virtual ~Factory() = default;
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual CharReader* newCharReader() const = 0;
}; // Factory
}; // CharReader
/** \brief Build a CharReader implementation.
*
* Usage:
* \code
* using namespace Json;
* CharReaderBuilder builder;
* builder["collectComments"] = false;
* Value value;
* String errs;
* bool ok = parseFromStream(builder, std::cin, &value, &errs);
* \endcode
*/
class JSON_API CharReaderBuilder : public CharReader::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* These are case-sensitive.
* Available settings (case-sensitive):
* - `"collectComments": false or true`
* - true to collect comment and allow writing them back during
* serialization, false to discard comments. This parameter is ignored
* if allowComments is false.
* - `"allowComments": false or true`
* - true if comments are allowed.
* - `"allowTrailingCommas": false or true`
* - true if trailing commas in objects and arrays are allowed.
* - `"strictRoot": false or true`
* - true if root must be either an array or an object value
* - `"allowDroppedNullPlaceholders": false or true`
* - true if dropped null placeholders are allowed. (See
* StreamWriterBuilder.)
* - `"allowNumericKeys": false or true`
* - true if numeric object keys are allowed.
* - `"allowSingleQuotes": false or true`
* - true if '' are allowed for strings (both keys and values)
* - `"stackLimit": integer`
* - Exceeding stackLimit (recursive depth of `readValue()`) will cause an
* exception.
* - This is a security issue (seg-faults caused by deeply nested JSON), so
* the default is low.
* - `"failIfExtra": false or true`
* - If true, `parse()` returns false when extra non-whitespace trails the
* JSON value in the input string.
* - `"rejectDupKeys": false or true`
* - If true, `parse()` returns false when a key is duplicated within an
* object.
* - `"allowSpecialFloats": false or true`
* - If true, special float values (NaNs and infinities) are allowed and
* their values are lossfree restorable.
*
* You can examine 'settings_` yourself to see the defaults. You can also
* write and read them just like any JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
CharReaderBuilder();
~CharReaderBuilder() override;
CharReader* newCharReader() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
/** Same as old Features::strictMode().
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_reader.cpp CharReaderBuilderStrictMode
*/
static void strictMode(Json::Value* settings);
};
/** Consume entire stream and use its begin/end.
* Someday we might have a real StreamReader, but for now this
* is convenient.
*/
bool JSON_API parseFromStream(CharReader::Factory const&, IStream&, Value* root,
String* errs);
/** \brief Read from 'sin' into 'root'.
*
* Always keep comments from the input JSON.
*
* This can be used to read a file into a particular sub-object.
* For example:
* \code
* Json::Value root;
* cin >> root["dir"]["file"];
* cout << root;
* \endcode
* Result:
* \verbatim
* {
* "dir": {
* "file": {
* // The input stream JSON would be nested here.
* }
* }
* }
* \endverbatim
* \throw std::exception on parse error.
* \see Json::operator<<()
*/
JSON_API IStream& operator>>(IStream&, Value&);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_READER_H_INCLUDED
+935
View File
@@ -0,0 +1,935 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_H_INCLUDED
#define JSON_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "forwards.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
// Conditional NORETURN attribute on the throw functions would:
// a) suppress false positives from static code analysis
// b) possibly improve optimization opportunities.
#if !defined(JSONCPP_NORETURN)
#if defined(_MSC_VER) && _MSC_VER == 1800
#define JSONCPP_NORETURN __declspec(noreturn)
#else
#define JSONCPP_NORETURN [[noreturn]]
#endif
#endif
// Support for '= delete' with template declarations was a late addition
// to the c++11 standard and is rejected by clang 3.8 and Apple clang 8.2
// even though these declare themselves to be c++11 compilers.
#if !defined(JSONCPP_TEMPLATE_DELETE)
#if defined(__clang__) && defined(__apple_build_version__)
#if __apple_build_version__ <= 8000042
#define JSONCPP_TEMPLATE_DELETE
#endif
#elif defined(__clang__)
#if __clang_major__ == 3 && __clang_minor__ <= 8
#define JSONCPP_TEMPLATE_DELETE
#endif
#endif
#if !defined(JSONCPP_TEMPLATE_DELETE)
#define JSONCPP_TEMPLATE_DELETE = delete
#endif
#endif
#include <array>
#include <exception>
#include <map>
#include <memory>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
/** \brief JSON (JavaScript Object Notation).
*/
namespace Json {
#if JSON_USE_EXCEPTION
/** Base class for all exceptions we throw.
*
* We use nothing but these internally. Of course, STL can throw others.
*/
class JSON_API Exception : public std::exception {
public:
Exception(String msg);
~Exception() noexcept override;
char const* what() const noexcept override;
protected:
String msg_;
};
/** Exceptions which the user cannot easily avoid.
*
* E.g. out-of-memory (when we use malloc), stack-overflow, malicious input
*
* \remark derived from Json::Exception
*/
class JSON_API RuntimeError : public Exception {
public:
RuntimeError(String const& msg);
};
/** Exceptions thrown by JSON_ASSERT/JSON_FAIL macros.
*
* These are precondition-violations (user bugs) and internal errors (our bugs).
*
* \remark derived from Json::Exception
*/
class JSON_API LogicError : public Exception {
public:
LogicError(String const& msg);
};
#endif
/// used internally
JSONCPP_NORETURN void throwRuntimeError(String const& msg);
/// used internally
JSONCPP_NORETURN void throwLogicError(String const& msg);
/** \brief Type of the value held by a Value object.
*/
enum ValueType {
nullValue = 0, ///< 'null' value
intValue, ///< signed integer value
uintValue, ///< unsigned integer value
realValue, ///< double value
stringValue, ///< UTF-8 string value
booleanValue, ///< bool value
arrayValue, ///< array value (ordered list)
objectValue ///< object value (collection of name/value pairs).
};
enum CommentPlacement {
commentBefore = 0, ///< a comment placed on the line before a value
commentAfterOnSameLine, ///< a comment just after a value on the same line
commentAfter, ///< a comment on the line after a value (only make sense for
/// root value)
numberOfCommentPlacement
};
/** \brief Type of precision for formatting of real values.
*/
enum PrecisionType {
significantDigits = 0, ///< we set max number of significant digits in string
decimalPlaces ///< we set max number of digits after "." in string
};
/** \brief Lightweight wrapper to tag static string.
*
* Value constructor and objectValue member assignment takes advantage of the
* StaticString and avoid the cost of string duplication when storing the
* string or the member name.
*
* Example of usage:
* \code
* Json::Value aValue( StaticString("some text") );
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
class JSON_API StaticString {
public:
explicit StaticString(const char* czstring) : c_str_(czstring) {}
operator const char*() const { return c_str_; }
const char* c_str() const { return c_str_; }
private:
const char* c_str_;
};
/** \brief Represents a <a HREF="http://www.json.org">JSON</a> value.
*
* This class is a discriminated union wrapper that can represents a:
* - signed integer [range: Value::minInt - Value::maxInt]
* - unsigned integer (range: 0 - Value::maxUInt)
* - double
* - UTF-8 string
* - boolean
* - 'null'
* - an ordered list of Value
* - collection of name/value pairs (javascript object)
*
* The type of the held value is represented by a #ValueType and
* can be obtained using type().
*
* Values of an #objectValue or #arrayValue can be accessed using operator[]()
* methods.
* Non-const methods will automatically create the a #nullValue element
* if it does not exist.
* The sequence of an #arrayValue will be automatically resized and initialized
* with #nullValue. resize() can be used to enlarge or truncate an #arrayValue.
*
* The get() methods can be used to obtain default value in the case the
* required element does not exist.
*
* It is possible to iterate over the list of member keys of an object using
* the getMemberNames() method.
*
* \note #Value string-length fit in size_t, but keys must be < 2^30.
* (The reason is an implementation detail.) A #CharReader will raise an
* exception if a bound is exceeded to avoid security holes in your app,
* but the Value API does *not* check bounds. That is the responsibility
* of the caller.
*/
class JSON_API Value {
friend class ValueIteratorBase;
public:
using Members = std::vector<String>;
using iterator = ValueIterator;
using const_iterator = ValueConstIterator;
using UInt = Json::UInt;
using Int = Json::Int;
#if defined(JSON_HAS_INT64)
using UInt64 = Json::UInt64;
using Int64 = Json::Int64;
#endif // defined(JSON_HAS_INT64)
using LargestInt = Json::LargestInt;
using LargestUInt = Json::LargestUInt;
using ArrayIndex = Json::ArrayIndex;
// Required for boost integration, e. g. BOOST_TEST
using value_type = std::string;
#if JSON_USE_NULLREF
// Binary compatibility kludges, do not use.
static const Value& null;
static const Value& nullRef;
#endif
// null and nullRef are deprecated, use this instead.
static Value const& nullSingleton();
/// Minimum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt minLargestInt =
LargestInt(~(LargestUInt(-1) / 2));
/// Maximum signed integer value that can be stored in a Json::Value.
static constexpr LargestInt maxLargestInt = LargestInt(LargestUInt(-1) / 2);
/// Maximum unsigned integer value that can be stored in a Json::Value.
static constexpr LargestUInt maxLargestUInt = LargestUInt(-1);
/// Minimum signed int value that can be stored in a Json::Value.
static constexpr Int minInt = Int(~(UInt(-1) / 2));
/// Maximum signed int value that can be stored in a Json::Value.
static constexpr Int maxInt = Int(UInt(-1) / 2);
/// Maximum unsigned int value that can be stored in a Json::Value.
static constexpr UInt maxUInt = UInt(-1);
#if defined(JSON_HAS_INT64)
/// Minimum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 minInt64 = Int64(~(UInt64(-1) / 2));
/// Maximum signed 64 bits int value that can be stored in a Json::Value.
static constexpr Int64 maxInt64 = Int64(UInt64(-1) / 2);
/// Maximum unsigned 64 bits int value that can be stored in a Json::Value.
static constexpr UInt64 maxUInt64 = UInt64(-1);
#endif // defined(JSON_HAS_INT64)
/// Default precision for real value for string representation.
static constexpr UInt defaultRealPrecision = 17;
// The constant is hard-coded because some compiler have trouble
// converting Value::maxUInt64 to a double correctly (AIX/xlC).
// Assumes that UInt64 is a 64 bits integer.
static constexpr double maxUInt64AsDouble = 18446744073709551615.0;
// Workaround for bug in the NVIDIAs CUDA 9.1 nvcc compiler
// when using gcc and clang backend compilers. CZString
// cannot be defined as private. See issue #486
#ifdef __NVCC__
public:
#else
private:
#endif
#ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
class CZString {
public:
enum DuplicationPolicy { noDuplication = 0, duplicate, duplicateOnCopy };
CZString(ArrayIndex index);
CZString(char const* str, unsigned length, DuplicationPolicy allocate);
CZString(CZString const& other);
CZString(CZString&& other) noexcept;
~CZString();
CZString& operator=(const CZString& other);
CZString& operator=(CZString&& other) noexcept;
bool operator<(CZString const& other) const;
bool operator==(CZString const& other) const;
ArrayIndex index() const;
// const char* c_str() const; ///< \deprecated
char const* data() const;
unsigned length() const;
bool isStaticString() const;
private:
void swap(CZString& other);
struct StringStorage {
unsigned policy_ : 2;
unsigned length_ : 30; // 1GB max
};
char const* cstr_; // actually, a prefixed string, unless policy is noDup
union {
ArrayIndex index_;
StringStorage storage_;
};
};
public:
typedef std::map<CZString, Value> ObjectValues;
#endif // ifndef JSONCPP_DOC_EXCLUDE_IMPLEMENTATION
public:
/**
* \brief Create a default Value of the given type.
*
* This is a very useful constructor.
* To create an empty array, pass arrayValue.
* To create an empty object, pass objectValue.
* Another Value can then be set to this one by assignment.
* This is useful since clear() and resize() will not alter types.
*
* Examples:
* \code
* Json::Value null_value; // null
* Json::Value arr_value(Json::arrayValue); // []
* Json::Value obj_value(Json::objectValue); // {}
* \endcode
*/
Value(ValueType type = nullValue);
Value(Int value);
Value(UInt value);
#if defined(JSON_HAS_INT64)
Value(Int64 value);
Value(UInt64 value);
#endif // if defined(JSON_HAS_INT64)
Value(double value);
Value(const char* value); ///< Copy til first 0. (NULL causes to seg-fault.)
Value(const char* begin, const char* end); ///< Copy all, incl zeroes.
/**
* \brief Constructs a value from a static string.
*
* Like other value string constructor but do not duplicate the string for
* internal storage. The given string must remain alive after the call to
* this constructor.
*
* \note This works only for null-terminated strings. (We cannot change the
* size of this class, so we have nowhere to store the length, which might be
* computed later for various operations.)
*
* Example of usage:
* \code
* static StaticString foo("some text");
* Json::Value aValue(foo);
* \endcode
*/
Value(const StaticString& value);
Value(const String& value);
Value(bool value);
Value(std::nullptr_t ptr) = delete;
Value(const Value& other);
Value(Value&& other) noexcept;
~Value();
/// \note Overwrite existing comments. To preserve comments, use
/// #swapPayload().
Value& operator=(const Value& other);
Value& operator=(Value&& other) noexcept;
/// Swap everything.
void swap(Value& other);
/// Swap values but leave comments and source offsets in place.
void swapPayload(Value& other);
/// copy everything.
void copy(const Value& other);
/// copy values but leave comments and source offsets in place.
void copyPayload(const Value& other);
ValueType type() const;
/// Compare payload only, not comments etc.
bool operator<(const Value& other) const;
bool operator<=(const Value& other) const;
bool operator>=(const Value& other) const;
bool operator>(const Value& other) const;
bool operator==(const Value& other) const;
bool operator!=(const Value& other) const;
int compare(const Value& other) const;
const char* asCString() const; ///< Embedded zeroes could cause you trouble!
#if JSONCPP_USING_SECURE_MEMORY
unsigned getCStringLength() const; // Allows you to understand the length of
// the CString
#endif
String asString() const; ///< Embedded zeroes are possible.
/** Get raw char* of string-value.
* \return false if !string. (Seg-fault if str or end are NULL.)
*/
bool getString(char const** begin, char const** end) const;
Int asInt() const;
UInt asUInt() const;
#if defined(JSON_HAS_INT64)
Int64 asInt64() const;
UInt64 asUInt64() const;
#endif // if defined(JSON_HAS_INT64)
LargestInt asLargestInt() const;
LargestUInt asLargestUInt() const;
float asFloat() const;
double asDouble() const;
bool asBool() const;
bool isNull() const;
bool isBool() const;
bool isInt() const;
bool isInt64() const;
bool isUInt() const;
bool isUInt64() const;
bool isIntegral() const;
bool isDouble() const;
bool isNumeric() const;
bool isString() const;
bool isArray() const;
bool isObject() const;
/// The `as<T>` and `is<T>` member function templates and specializations.
template <typename T> T as() const JSONCPP_TEMPLATE_DELETE;
template <typename T> bool is() const JSONCPP_TEMPLATE_DELETE;
bool isConvertibleTo(ValueType other) const;
/// Number of values in array or object
ArrayIndex size() const;
/// \brief Return true if empty array, empty object, or null;
/// otherwise, false.
bool empty() const;
/// Return !isNull()
explicit operator bool() const;
/// Remove all object members and array elements.
/// \pre type() is arrayValue, objectValue, or nullValue
/// \post type() is unchanged
void clear();
/// Resize the array to newSize elements.
/// New elements are initialized to null.
/// May only be called on nullValue or arrayValue.
/// \pre type() is arrayValue or nullValue
/// \post type() is arrayValue
void resize(ArrayIndex newSize);
//@{
/// Access an array element (zero based index). If the array contains less
/// than index element, then null value are inserted in the array so that
/// its size is index+1.
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
Value& operator[](ArrayIndex index);
Value& operator[](int index);
//@}
//@{
/// Access an array element (zero based index).
/// (You may need to say 'value[0u]' to get your compiler to distinguish
/// this from the operator[] which takes a string.)
const Value& operator[](ArrayIndex index) const;
const Value& operator[](int index) const;
//@}
/// If the array contains at least index+1 elements, returns the element
/// value, otherwise returns defaultValue.
Value get(ArrayIndex index, const Value& defaultValue) const;
/// Return true if index < size().
bool isValidIndex(ArrayIndex index) const;
/// \brief Append value to array at the end.
///
/// Equivalent to jsonvalue[jsonvalue.size()] = value;
Value& append(const Value& value);
Value& append(Value&& value);
/// \brief Insert value in array at specific index
bool insert(ArrayIndex index, const Value& newValue);
bool insert(ArrayIndex index, Value&& newValue);
/// Access an object value by name, create a null member if it does not exist.
/// \note Because of our implementation, keys are limited to 2^30 -1 chars.
/// Exceeding that will cause an exception.
Value& operator[](const char* key);
/// Access an object value by name, returns null if there is no member with
/// that name.
const Value& operator[](const char* key) const;
/// Access an object value by name, create a null member if it does not exist.
/// \param key may contain embedded nulls.
Value& operator[](const String& key);
/// Access an object value by name, returns null if there is no member with
/// that name.
/// \param key may contain embedded nulls.
const Value& operator[](const String& key) const;
/** \brief Access an object value by name, create a null member if it does not
* exist.
*
* If the object has no entry for that name, then the member name used to
* store the new entry is not duplicated.
* Example of use:
* \code
* Json::Value object;
* static const StaticString code("code");
* object[code] = 1234;
* \endcode
*/
Value& operator[](const StaticString& key);
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
Value get(const char* key, const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \note key may contain embedded nulls.
Value get(const char* begin, const char* end,
const Value& defaultValue) const;
/// Return the member named key if it exist, defaultValue otherwise.
/// \note deep copy
/// \param key may contain embedded nulls.
Value get(const String& key, const Value& defaultValue) const;
/// Most general and efficient version of isMember()const, get()const,
/// and operator[]const
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
Value const* find(char const* begin, char const* end) const;
/// Most general and efficient version of object-mutators.
/// \note As stated elsewhere, behavior is undefined if (end-begin) >= 2^30
/// \return non-zero, but JSON_ASSERT if this is neither object nor nullValue.
Value* demand(char const* begin, char const* end);
/// \brief Remove and return the named member.
///
/// Do nothing if it did not exist.
/// \pre type() is objectValue or nullValue
/// \post type() is unchanged
void removeMember(const char* key);
/// Same as removeMember(const char*)
/// \param key may contain embedded nulls.
void removeMember(const String& key);
/// Same as removeMember(const char* begin, const char* end, Value* removed),
/// but 'key' is null-terminated.
bool removeMember(const char* key, Value* removed);
/** \brief Remove the named map member.
*
* Update 'removed' iff removed.
* \param key may contain embedded nulls.
* \return true iff removed (no exceptions)
*/
bool removeMember(String const& key, Value* removed);
/// Same as removeMember(String const& key, Value* removed)
bool removeMember(const char* begin, const char* end, Value* removed);
/** \brief Remove the indexed array element.
*
* O(n) expensive operations.
* Update 'removed' iff removed.
* \return true if removed (no exceptions)
*/
bool removeIndex(ArrayIndex index, Value* removed);
/// Return true if the object has a member named key.
/// \note 'key' must be null-terminated.
bool isMember(const char* key) const;
/// Return true if the object has a member named key.
/// \param key may contain embedded nulls.
bool isMember(const String& key) const;
/// Same as isMember(String const& key)const
bool isMember(const char* begin, const char* end) const;
/// \brief Return a list of the member names.
///
/// If null, return an empty list.
/// \pre type() is objectValue or nullValue
/// \post if type() was nullValue, it remains nullValue
Members getMemberNames() const;
/// \deprecated Always pass len.
JSONCPP_DEPRECATED("Use setComment(String const&) instead.")
void setComment(const char* comment, CommentPlacement placement) {
setComment(String(comment, strlen(comment)), placement);
}
/// Comments must be //... or /* ... */
void setComment(const char* comment, size_t len, CommentPlacement placement) {
setComment(String(comment, len), placement);
}
/// Comments must be //... or /* ... */
void setComment(String comment, CommentPlacement placement);
bool hasComment(CommentPlacement placement) const;
/// Include delimiters and embedded newlines.
String getComment(CommentPlacement placement) const;
String toStyledString() const;
const_iterator begin() const;
const_iterator end() const;
iterator begin();
iterator end();
// Accessors for the [start, limit) range of bytes within the JSON text from
// which this value was parsed, if any.
void setOffsetStart(ptrdiff_t start);
void setOffsetLimit(ptrdiff_t limit);
ptrdiff_t getOffsetStart() const;
ptrdiff_t getOffsetLimit() const;
private:
void setType(ValueType v) {
bits_.value_type_ = static_cast<unsigned char>(v);
}
bool isAllocated() const { return bits_.allocated_; }
void setIsAllocated(bool v) { bits_.allocated_ = v; }
void initBasic(ValueType type, bool allocated = false);
void dupPayload(const Value& other);
void releasePayload();
void dupMeta(const Value& other);
Value& resolveReference(const char* key);
Value& resolveReference(const char* key, const char* end);
// struct MemberNamesTransform
//{
// typedef const char *result_type;
// const char *operator()( const CZString &name ) const
// {
// return name.c_str();
// }
//};
union ValueHolder {
LargestInt int_;
LargestUInt uint_;
double real_;
bool bool_;
char* string_; // if allocated_, ptr to { unsigned, char[] }.
ObjectValues* map_;
} value_;
struct {
// Really a ValueType, but types should agree for bitfield packing.
unsigned int value_type_ : 8;
// Unless allocated_, string_ must be null-terminated.
unsigned int allocated_ : 1;
} bits_;
class Comments {
public:
Comments() = default;
Comments(const Comments& that);
Comments(Comments&& that) noexcept;
Comments& operator=(const Comments& that);
Comments& operator=(Comments&& that) noexcept;
bool has(CommentPlacement slot) const;
String get(CommentPlacement slot) const;
void set(CommentPlacement slot, String comment);
private:
using Array = std::array<String, numberOfCommentPlacement>;
std::unique_ptr<Array> ptr_;
};
Comments comments_;
// [start, limit) byte offsets in the source JSON text from which this Value
// was extracted.
ptrdiff_t start_;
ptrdiff_t limit_;
};
template <> inline bool Value::as<bool>() const { return asBool(); }
template <> inline bool Value::is<bool>() const { return isBool(); }
template <> inline Int Value::as<Int>() const { return asInt(); }
template <> inline bool Value::is<Int>() const { return isInt(); }
template <> inline UInt Value::as<UInt>() const { return asUInt(); }
template <> inline bool Value::is<UInt>() const { return isUInt(); }
#if defined(JSON_HAS_INT64)
template <> inline Int64 Value::as<Int64>() const { return asInt64(); }
template <> inline bool Value::is<Int64>() const { return isInt64(); }
template <> inline UInt64 Value::as<UInt64>() const { return asUInt64(); }
template <> inline bool Value::is<UInt64>() const { return isUInt64(); }
#endif
template <> inline double Value::as<double>() const { return asDouble(); }
template <> inline bool Value::is<double>() const { return isDouble(); }
template <> inline String Value::as<String>() const { return asString(); }
template <> inline bool Value::is<String>() const { return isString(); }
/// These `as` specializations are type conversions, and do not have a
/// corresponding `is`.
template <> inline float Value::as<float>() const { return asFloat(); }
template <> inline const char* Value::as<const char*>() const {
return asCString();
}
/** \brief Experimental and untested: represents an element of the "path" to
* access a node.
*/
class JSON_API PathArgument {
public:
friend class Path;
PathArgument();
PathArgument(ArrayIndex index);
PathArgument(const char* key);
PathArgument(String key);
private:
enum Kind { kindNone = 0, kindIndex, kindKey };
String key_;
ArrayIndex index_{};
Kind kind_{kindNone};
};
/** \brief Experimental and untested: represents a "path" to access a node.
*
* Syntax:
* - "." => root node
* - ".[n]" => elements at index 'n' of root node (an array value)
* - ".name" => member named 'name' of root node (an object value)
* - ".name1.name2.name3"
* - ".[0][1][2].name1[3]"
* - ".%" => member name is provided as parameter
* - ".[%]" => index is provided as parameter
*/
class JSON_API Path {
public:
Path(const String& path, const PathArgument& a1 = PathArgument(),
const PathArgument& a2 = PathArgument(),
const PathArgument& a3 = PathArgument(),
const PathArgument& a4 = PathArgument(),
const PathArgument& a5 = PathArgument());
const Value& resolve(const Value& root) const;
Value resolve(const Value& root, const Value& defaultValue) const;
/// Creates the "path" to access the specified node and returns a reference on
/// the node.
Value& make(Value& root) const;
private:
using InArgs = std::vector<const PathArgument*>;
using Args = std::vector<PathArgument>;
void makePath(const String& path, const InArgs& in);
void addPathInArg(const String& path, const InArgs& in,
InArgs::const_iterator& itInArg, PathArgument::Kind kind);
static void invalidPath(const String& path, int location);
Args args_;
};
/** \brief base class for Value iterators.
*
*/
class JSON_API ValueIteratorBase {
public:
using iterator_category = std::bidirectional_iterator_tag;
using size_t = unsigned int;
using difference_type = int;
using SelfType = ValueIteratorBase;
bool operator==(const SelfType& other) const { return isEqual(other); }
bool operator!=(const SelfType& other) const { return !isEqual(other); }
difference_type operator-(const SelfType& other) const {
return other.computeDistance(*this);
}
/// Return either the index or the member name of the referenced value as a
/// Value.
Value key() const;
/// Return the index of the referenced Value, or -1 if it is not an
/// arrayValue.
UInt index() const;
/// Return the member name of the referenced Value, or "" if it is not an
/// objectValue.
/// \note Avoid `c_str()` on result, as embedded zeroes are possible.
String name() const;
/// Return the member name of the referenced Value. "" if it is not an
/// objectValue.
/// \deprecated This cannot be used for UTF-8 strings, since there can be
/// embedded nulls.
JSONCPP_DEPRECATED("Use `key = name();` instead.")
char const* memberName() const;
/// Return the member name of the referenced Value, or NULL if it is not an
/// objectValue.
/// \note Better version than memberName(). Allows embedded nulls.
char const* memberName(char const** end) const;
protected:
/*! Internal utility functions to assist with implementing
* other iterator functions. The const and non-const versions
* of the "deref" protected methods expose the protected
* current_ member variable in a way that can often be
* optimized away by the compiler.
*/
const Value& deref() const;
Value& deref();
void increment();
void decrement();
difference_type computeDistance(const SelfType& other) const;
bool isEqual(const SelfType& other) const;
void copy(const SelfType& other);
private:
Value::ObjectValues::iterator current_;
// Indicates that iterator is for a null value.
bool isNull_{true};
public:
// For some reason, BORLAND needs these at the end, rather
// than earlier. No idea why.
ValueIteratorBase();
explicit ValueIteratorBase(const Value::ObjectValues::iterator& current);
};
/** \brief const iterator for object and array value.
*
*/
class JSON_API ValueConstIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = const Value;
// typedef unsigned int size_t;
// typedef int difference_type;
using reference = const Value&;
using pointer = const Value*;
using SelfType = ValueConstIterator;
ValueConstIterator();
ValueConstIterator(ValueIterator const& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueConstIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const ValueIteratorBase& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
reference operator*() const { return deref(); }
pointer operator->() const { return &deref(); }
};
/** \brief Iterator for object and array value.
*/
class JSON_API ValueIterator : public ValueIteratorBase {
friend class Value;
public:
using value_type = Value;
using size_t = unsigned int;
using difference_type = int;
using reference = Value&;
using pointer = Value*;
using SelfType = ValueIterator;
ValueIterator();
explicit ValueIterator(const ValueConstIterator& other);
ValueIterator(const ValueIterator& other);
private:
/*! \internal Use by Value to create an iterator.
*/
explicit ValueIterator(const Value::ObjectValues::iterator& current);
public:
SelfType& operator=(const SelfType& other);
SelfType operator++(int) {
SelfType temp(*this);
++*this;
return temp;
}
SelfType operator--(int) {
SelfType temp(*this);
--*this;
return temp;
}
SelfType& operator--() {
decrement();
return *this;
}
SelfType& operator++() {
increment();
return *this;
}
/*! The return value of non-const iterators can be
* changed, so the these functions are not const
* because the returned references/pointers can be used
* to change state of the base class.
*/
reference operator*() { return deref(); }
pointer operator->() { return &deref(); }
};
inline void swap(Value& a, Value& b) { a.swap(b); }
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_H_INCLUDED
+28
View File
@@ -0,0 +1,28 @@
#ifndef JSON_VERSION_H_INCLUDED
#define JSON_VERSION_H_INCLUDED
// Note: version must be updated in three places when doing a release. This
// annoying process ensures that amalgamate, CMake, and meson all report the
// correct version.
// 1. /meson.build
// 2. /include/json/version.h
// 3. /CMakeLists.txt
// IMPORTANT: also update the SOVERSION!!
#define JSONCPP_VERSION_STRING "1.9.4"
#define JSONCPP_VERSION_MAJOR 1
#define JSONCPP_VERSION_MINOR 9
#define JSONCPP_VERSION_PATCH 4
#define JSONCPP_VERSION_QUALIFIER
#define JSONCPP_VERSION_HEXA \
((JSONCPP_VERSION_MAJOR << 24) | (JSONCPP_VERSION_MINOR << 16) | \
(JSONCPP_VERSION_PATCH << 8))
#ifdef JSONCPP_USING_SECURE_MEMORY
#undef JSONCPP_USING_SECURE_MEMORY
#endif
#define JSONCPP_USING_SECURE_MEMORY 0
// If non-zero, the library zeroes any memory that it has allocated before
// it frees its memory.
#endif // JSON_VERSION_H_INCLUDED
+367
View File
@@ -0,0 +1,367 @@
// Copyright 2007-2010 Baptiste Lepilleur and The JsonCpp Authors
// Distributed under MIT license, or public domain if desired and
// recognized in your jurisdiction.
// See file LICENSE for detail or copy at http://jsoncpp.sourceforge.net/LICENSE
#ifndef JSON_WRITER_H_INCLUDED
#define JSON_WRITER_H_INCLUDED
#if !defined(JSON_IS_AMALGAMATION)
#include "value.h"
#endif // if !defined(JSON_IS_AMALGAMATION)
#include <ostream>
#include <string>
#include <vector>
// Disable warning C4251: <data member>: <type> needs to have dll-interface to
// be used by...
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING) && defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4251)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma pack(push, 8)
namespace Json {
class Value;
/**
*
* Usage:
* \code
* using namespace Json;
* void writeToStdout(StreamWriter::Factory const& factory, Value const& value)
* { std::unique_ptr<StreamWriter> const writer( factory.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* }
* \endcode
*/
class JSON_API StreamWriter {
protected:
OStream* sout_; // not owned; will not delete
public:
StreamWriter();
virtual ~StreamWriter();
/** Write Value into document as configured in sub-class.
* Do not take ownership of sout, but maintain a reference during function.
* \pre sout != NULL
* \return zero on success (For now, we always return zero, so check the
* stream instead.) \throw std::exception possibly, depending on
* configuration
*/
virtual int write(Value const& root, OStream* sout) = 0;
/** \brief A simple abstract factory.
*/
class JSON_API Factory {
public:
virtual ~Factory();
/** \brief Allocate a CharReader via operator new().
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
virtual StreamWriter* newStreamWriter() const = 0;
}; // Factory
}; // StreamWriter
/** \brief Write into stringstream, then return string, for convenience.
* A StreamWriter will be created from the factory, used, and then deleted.
*/
String JSON_API writeString(StreamWriter::Factory const& factory,
Value const& root);
/** \brief Build a StreamWriter implementation.
* Usage:
* \code
* using namespace Json;
* Value value = ...;
* StreamWriterBuilder builder;
* builder["commentStyle"] = "None";
* builder["indentation"] = " "; // or whatever you like
* std::unique_ptr<Json::StreamWriter> writer(
* builder.newStreamWriter());
* writer->write(value, &std::cout);
* std::cout << std::endl; // add lf and flush
* \endcode
*/
class JSON_API StreamWriterBuilder : public StreamWriter::Factory {
public:
// Note: We use a Json::Value so that we can add data-members to this class
// without a major version bump.
/** Configuration of this builder.
* Available settings (case-sensitive):
* - "commentStyle": "None" or "All"
* - "indentation": "<anything>".
* - Setting this to an empty string also omits newline characters.
* - "enableYAMLCompatibility": false or true
* - slightly change the whitespace around colons
* - "dropNullPlaceholders": false or true
* - Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
* - "useSpecialFloats": false or true
* - If true, outputs non-finite floating point values in the following way:
* NaN values as "NaN", positive infinity as "Infinity", and negative
* infinity as "-Infinity".
* - "precision": int
* - Number of precision digits for formatting of real values.
* - "precisionType": "significant"(default) or "decimal"
* - Type of precision for formatting of real values.
* You can examine 'settings_` yourself
* to see the defaults. You can also write and read them just like any
* JSON Value.
* \sa setDefaults()
*/
Json::Value settings_;
StreamWriterBuilder();
~StreamWriterBuilder() override;
/**
* \throw std::exception if something goes wrong (e.g. invalid settings)
*/
StreamWriter* newStreamWriter() const override;
/** \return true if 'settings' are legal and consistent;
* otherwise, indicate bad settings via 'invalid'.
*/
bool validate(Json::Value* invalid) const;
/** A simple way to update a specific setting.
*/
Value& operator[](const String& key);
/** Called by ctor, but you can use this to reset settings_.
* \pre 'settings' != NULL (but Json::null is fine)
* \remark Defaults:
* \snippet src/lib_json/json_writer.cpp StreamWriterBuilderDefaults
*/
static void setDefaults(Json::Value* settings);
};
/** \brief Abstract class for writers.
* \deprecated Use StreamWriter. (And really, this is an implementation detail.)
*/
class JSONCPP_DEPRECATED("Use StreamWriter instead") JSON_API Writer {
public:
virtual ~Writer();
virtual String write(const Value& root) = 0;
};
/** \brief Outputs a Value in <a HREF="http://www.json.org">JSON</a> format
*without formatting (not human friendly).
*
* The JSON document is written in a single line. It is not intended for 'human'
*consumption,
* but may be useful to support feature such as RPC where bandwidth is limited.
* \sa Reader, Value
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API FastWriter
: public Writer {
public:
FastWriter();
~FastWriter() override = default;
void enableYAMLCompatibility();
/** \brief Drop the "null" string from the writer's output for nullValues.
* Strictly speaking, this is not valid JSON. But when the output is being
* fed to a browser's JavaScript, it makes for smaller output and the
* browser can handle the output just fine.
*/
void dropNullPlaceholders();
void omitEndingLineFeed();
public: // overridden from Writer
String write(const Value& root) override;
private:
void writeValue(const Value& value);
String document_;
bool yamlCompatibilityEnabled_{false};
bool dropNullPlaceholders_{false};
bool omitEndingLineFeed_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
*human friendly way.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
*line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
*types,
* and all the values fit on one lines, then print the array on a single
*line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
*#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
StyledWriter : public Writer {
public:
StyledWriter();
~StyledWriter() override = default;
public: // overridden from Writer
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param root Value to serialize.
* \return String containing the JSON document that represents the root value.
*/
String write(const Value& root) override;
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
String document_;
String indentString_;
unsigned int rightMargin_{74};
unsigned int indentSize_{3};
bool addChildValues_{false};
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
/** \brief Writes a Value in <a HREF="http://www.json.org">JSON</a> format in a
human friendly way,
to a stream rather than to a string.
*
* The rules for line break and indent are as follow:
* - Object value:
* - if empty then print {} without indent and line break
* - if not empty the print '{', line break & indent, print one value per
line
* and then unindent and line break and print '}'.
* - Array value:
* - if empty then print [] without indent and line break
* - if the array contains no object value, empty array or some other value
types,
* and all the values fit on one lines, then print the array on a single
line.
* - otherwise, it the values do not fit on one line, or the array contains
* object or non empty array, then print one value per line.
*
* If the Value have comments then they are outputed according to their
#CommentPlacement.
*
* \sa Reader, Value, Value::setComment()
* \deprecated Use StreamWriterBuilder.
*/
#if defined(_MSC_VER)
#pragma warning(push)
#pragma warning(disable : 4996) // Deriving from deprecated class
#endif
class JSONCPP_DEPRECATED("Use StreamWriterBuilder instead") JSON_API
StyledStreamWriter {
public:
/**
* \param indentation Each level will be indented by this amount extra.
*/
StyledStreamWriter(String indentation = "\t");
~StyledStreamWriter() = default;
public:
/** \brief Serialize a Value in <a HREF="http://www.json.org">JSON</a> format.
* \param out Stream to write to. (Can be ostringstream, e.g.)
* \param root Value to serialize.
* \note There is no point in deriving from Writer, since write() should not
* return a value.
*/
void write(OStream& out, const Value& root);
private:
void writeValue(const Value& value);
void writeArrayValue(const Value& value);
bool isMultilineArray(const Value& value);
void pushValue(const String& value);
void writeIndent();
void writeWithIndent(const String& value);
void indent();
void unindent();
void writeCommentBeforeValue(const Value& root);
void writeCommentAfterValueOnSameLine(const Value& root);
static bool hasCommentForValue(const Value& value);
static String normalizeEOL(const String& text);
using ChildValues = std::vector<String>;
ChildValues childValues_;
OStream* document_;
String indentString_;
unsigned int rightMargin_{74};
String indentation_;
bool addChildValues_ : 1;
bool indented_ : 1;
};
#if defined(_MSC_VER)
#pragma warning(pop)
#endif
#if defined(JSON_HAS_INT64)
String JSON_API valueToString(Int value);
String JSON_API valueToString(UInt value);
#endif // if defined(JSON_HAS_INT64)
String JSON_API valueToString(LargestInt value);
String JSON_API valueToString(LargestUInt value);
String JSON_API valueToString(
double value, unsigned int precision = Value::defaultRealPrecision,
PrecisionType precisionType = PrecisionType::significantDigits);
String JSON_API valueToString(bool value);
String JSON_API valueToQuotedString(const char* value);
/// \brief Output using the StyledStreamWriter.
/// \see Json::operator>>()
JSON_API OStream& operator<<(OStream&, const Value& root);
} // namespace Json
#pragma pack(pop)
#if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#pragma warning(pop)
#endif // if defined(JSONCPP_DISABLE_DLL_INTERFACE_WARNING)
#endif // JSON_WRITER_H_INCLUDED
Binary file not shown.
+69
View File
@@ -0,0 +1,69 @@
# Prerequisites
*.d
# Compiled Object files
*.slo
*.lo
*.o
*.obj
# 忽略所有的本地配置文件
local_config.h
# 忽略所有的二进制可执行文件
**/bin/*
**/Debug/*
**/build/*
# 忽略所有的日志文件
**/logs/*
# 忽略所有的IDE配置目录
**/.vs/*
**/.vs/*
**/.idea/*
**/.svn/*
# 忽略所有的依赖管理文件和目录
**/deps/*
**/vcpkg/*
# 忽略所有的文档和示例文件
doc/
examples/
# 忽略所有的外部库文件和文档
**/external/*
# 忽略所有的测试结果文件
test_results/
# 忽略所有的版本控制文件
.git/
# 忽略所有的系统文件和可执行文件
.DS_Store
# Executables
*.exe
*.out
*.app
# Precompiled Headers
*.gch
*.pch
# Compiled Dynamic libraries
*.so
*.dylib
*.dll
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
*.la
*.a
*.lib
@@ -0,0 +1,902 @@
# API参考
<cite>
**本文档引用的文件**
- [DevManager.h](file://h\DevManager.h)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp)
- [ProManager.h](file://h\ProManager.h)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp)
- [SptManager.h](file://h\SptManager.h)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp)
- [TdManager.h](file://h\TdManager.h)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp)
- [IOManager.h](file://h\IOManager.h)
- [IOManager.cpp](file://cpp\Managers\IOManager.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [DevManager API](#devmanager-api)
3. [ProManager API](#promanager-api)
4. [SptManager API](#sptmanager-api)
5. [TdManager API](#tdmanager-api)
6. [IOManager API](#iomanager-api)
7. [API调用链与依赖关系](#api调用链与依赖关系)
8. [线程安全性说明](#线程安全性说明)
9. [使用示例](#使用示例)
## 简介
本文档为Geomative Studio软件系统中的核心管理器类提供详细的API参考。文档涵盖DevManager、ProManager、SptManager、TdManager和IOManager五个核心管理器类的公共接口,详细说明了各方法的签名、参数、返回值、异常情况以及使用场景。同时,文档解释了API的调用顺序、线程安全性,并提供了实际代码示例,为集成开发和自动化脚本提供了必要的技术参考。
## DevManager API
DevManager类负责管理设备的生命周期,包括设备的添加、删除、查询和状态更新。该管理器维护设备在内存中的链接列表,并处理与数据库的交互。
### 公共方法
#### GetDeviceByID
根据设备ID获取设备对象。
**方法签名**
```cpp
CDevice* GetDeviceByID(DWORD dwDevID)
```
**参数**
- `dwDevID`: 设备的唯一标识符
**返回值**
- 成功时返回指向CDevice对象的指针,失败时返回NULL
**异常情况**
- 无显式异常抛出,但会通过调试输出记录操作日志
**使用场景**
- 当需要根据设备ID查找特定设备时使用
**Section sources**
- [DevManager.h](file://h\DevManager.h#L21)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L48-L68)
#### GetDevice
根据设备句柄获取设备对象。
**方法签名**
```cpp
CDevice* GetDevice(DWORD dwHandle)
```
**参数**
- `dwHandle`: 设备的句柄
**返回值**
- 成功时返回指向CDevice对象的指针,失败时返回NULL
**异常情况**
- 无显式异常抛出
**使用场景**
- 在设备操作过程中,通过句柄快速获取设备对象
**Section sources**
- [DevManager.h](file://h\DevManager.h#L22)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L82-L89)
#### AddDevice
向系统中添加新设备。
**方法签名**
```cpp
BOOL AddDevice(CDevice* const pDev)
```
**参数**
- `pDev`: 指向要添加的设备对象的常量指针
**返回值**
- 成功时返回TRUE,失败时返回FALSE
**异常情况**
- 在数据库事务处理中捕获_com_error异常,并显示错误消息
- 操作失败时会回滚数据库事务
**使用场景**
- 当检测到新设备并需要将其注册到系统中时使用
**Section sources**
- [DevManager.h](file://h\DevManager.h#L30)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L203-L280)
#### DeleteDevice
从系统中删除设备。
**方法签名**
```cpp
BOOL DeleteDevice(DWORD dwHandle)
```
**参数**
- `dwHandle`: 要删除设备的句柄
**返回值**
- 成功时返回TRUE,失败时返回FALSE
**异常情况**
- 捕获所有异常,操作失败时回滚数据库事务
**使用场景**
- 当设备被移除或不再需要时,从系统中彻底删除设备
**Section sources**
- [DevManager.h](file://h\DevManager.h#L37)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L383-L432)
#### InitialDevLinkList
初始化设备链接列表。
**方法签名**
```cpp
void InitialDevLinkList()
```
**参数**
-
**返回值**
-
**异常情况**
- 无显式异常处理
**使用场景**
- 在系统启动或设备管理器初始化时,重新加载所有设备到内存中
**Section sources**
- [DevManager.h](file://h\DevManager.h#L29)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L160-L201)
#### UpdateDevInfo
更新设备信息。
**方法签名**
```cpp
void UpdateDevInfo(STSynDevParam stDevParam, BYTE bRemoteDeveTyp)
```
**参数**
- `stDevParam`: 包含设备参数的结构体
- `bRemoteDeveTyp`: 远程设备类型
**返回值**
-
**异常情况**
- 捕获_com_error异常并显示错误信息
**使用场景**
- 当从设备接收到同步参数时,更新数据库中的设备信息
**Section sources**
- [DevManager.h](file://h\DevManager.h#L39)
- [DevManager.cpp](file://cpp\Managers\DevManager.cpp#L592-L641)
## ProManager API
ProManager类负责管理项目(Project)和测区(Testing Zone)的层次结构,处理项目与设备之间的同步。
### 公共方法
#### CreateProjectInDB
在数据库中创建新项目。
**方法签名**
```cpp
UINT CreateProjectInDB(DWORD& dwID)
```
**参数**
- `dwID`: 输出参数,返回创建项目的ID
**返回值**
- APP_SUCCESS: 创建成功
- APP_CANCLE: 用户取消操作
- APP_FAIL: 创建失败
**异常情况**
- 在数据库操作中捕获_com_error异常,显示适当的错误消息并回滚事务
**使用场景**
- 当用户通过UI创建新项目时,此方法处理数据库层面的创建逻辑
**Section sources**
- [ProManager.h](file://h\ProManager.h#L51)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L249-L316)
#### CreateProjectInDev
在设备中创建项目。
**方法签名**
```cpp
UINT CreateProjectInDev(DWORD dwID, CDevice* const pDev)
```
**参数**
- `dwID`: 项目的数据库ID
- `pDev`: 指向目标设备的常量指针
**返回值**
- APP_SUCCESS: 创建成功
- APP_DUPLICATE: 项目已存在
- APP_FAIL: 创建失败
- APP_OVERFLOW: 项目数量超出限制
**异常情况**
- 处理文件传输失败的情况,删除已创建的项目以保持一致性
**使用场景**
- 当项目在数据库中创建后,需要将项目同步到指定设备时使用
**Section sources**
- [ProManager.h](file://h\ProManager.h#L50)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L343-L482)
#### CreateDefaultTzInDev
在设备中为项目创建默认测区。
**方法签名**
```cpp
UINT CreateDefaultTzInDev(DWORD dwProID, CDevice* const pDev)
```
**参数**
- `dwProID`: 项目的ID
- `pDev`: 指向目标设备的常量指针
**返回值**
- APP_SUCCESS: 创建成功
- APP_DUPLICATE: 测区已存在
- APP_FAIL: 创建失败
**异常情况**
- 在数据库操作中捕获_com_error异常,操作失败时回滚事务
**使用场景**
- 当项目在设备中创建后,自动创建默认测区以确保项目结构完整性
**Section sources**
- [ProManager.h](file://h\ProManager.h#L60)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L485-L706)
#### ShowProList
显示设备关联的项目列表。
**方法签名**
```cpp
bool ShowProList(DWORD dwDevHandle, CListCtrl& proList)
```
**参数**
- `dwDevHandle`: 设备句柄
- `proList`: 引用CListCtrl控件,用于显示项目列表
**返回值**
- 操作成功返回true,失败返回false
**异常情况**
- 无显式异常处理
**使用场景**
- 在UI中显示特定设备关联的所有项目列表
**Section sources**
- [ProManager.h](file://h\ProManager.h#L56)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L184-L246)
#### ShowTzList
显示项目关联的测区列表。
**方法签名**
```cpp
bool ShowTzList(DWORD dwProHandle, CListCtrl& tzList)
```
**参数**
- `dwProHandle`: 项目句柄
- `tzList`: 引用CListCtrl控件,用于显示测区列表
**返回值**
- 操作成功返回true,失败返回false
**异常情况**
- 无显式异常处理
**使用场景**
- 在UI中显示特定项目下的所有测区列表
**Section sources**
- [ProManager.h](file://h\ProManager.h#L58)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L53-L92)
#### DeleteProjectInDB
从数据库中删除项目。
**方法签名**
```cpp
void DeleteProjectInDB(DWORD dwID)
```
**参数**
- `dwID`: 要删除项目的ID
**返回值**
-
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要从数据库中永久删除项目及其相关数据时使用
**Section sources**
- [ProManager.h](file://h\ProManager.h#L48)
- [ProManager.cpp](file://cpp\Managers\ProManager.cpp#L485-L706)
## SptManager API
SptManager类负责管理脚本(Script)的创建、删除和同步,支持1D、2D和3D测量脚本。
### 公共方法
#### CreateCESConInDB
在数据库中创建1D脚本。
**方法签名**
```cpp
UINT CreateCESConInDB(DWORD& dwSConID)
```
**参数**
- `dwSConID`: 输出参数,返回创建脚本的ID
**返回值**
- APP_SUCCESS: 创建成功
- APP_CANCLE: 用户取消操作
- APP_FAIL: 创建失败
**异常情况**
- 在数据库操作中捕获_com_error异常,操作失败时回滚事务
**使用场景**
- 当用户创建新的1D测量脚本时,此方法处理数据库存储
**Section sources**
- [SptManager.h](file://h\SptManager.h#L41)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L548-L795)
#### Create2DSConInDB
在数据库中创建2D脚本。
**方法签名**
```cpp
UINT Create2DSConInDB(DWORD& dwSConID)
```
**参数**
- `dwSConID`: 输出参数,返回创建脚本的ID
**返回值**
- APP_SUCCESS: 创建成功
- APP_CANCLE: 用户取消操作
- APP_FAIL: 创建失败
**异常情况**
- 在数据库操作中捕获_com_error异常,操作失败时回滚事务
**使用场景**
- 当用户创建新的2D测量脚本时,此方法处理数据库存储
**Section sources**
- [SptManager.h](file://h\SptManager.h#L44)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L257-L486)
#### PutScriptToDev
将脚本下传至设备。
**方法签名**
```cpp
UINT PutScriptToDev(DWORD dwSCID, CDevice* const pDev)
```
**参数**
- `dwSCID`: 脚本的数据库ID
- `pDev`: 指向目标设备的常量指针
**返回值**
- APP_SUCCESS: 传输成功
- APP_DUPLICATE: 脚本已存在
- APP_FAIL: 传输失败
**异常情况**
- 处理文件传输失败的情况,删除设备上已创建的脚本以保持一致性
**使用场景**
- 当需要将数据库中的脚本同步到测量设备时使用
**Section sources**
- [SptManager.h](file://h\SptManager.h#L34)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L257-L486)
#### LoadScriptFromDev
从设备加载脚本。
**方法签名**
```cpp
UINT LoadScriptFromDev(CString szSptFileName, CDevice* const pDev)
```
**参数**
- `szSptFileName`: 脚本文件名
- `pDev`: 指向源设备的常量指针
**返回值**
- APP_SUCCESS: 加载成功
- APP_FAIL: 加载失败
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要从设备上载脚本到数据库时使用
**Section sources**
- [SptManager.h](file://h\SptManager.h#L33)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L257-L486)
#### GetScript
获取脚本对象。
**方法签名**
```cpp
CScript* GetScript(DWORD dwHandle)
```
**参数**
- `dwHandle`: 脚本句柄
**返回值**
- 成功时返回指向CScript对象的指针,失败时返回NULL
**异常情况**
- 无显式异常抛出
**使用场景**
- 在脚本操作过程中,通过句柄获取脚本对象进行操作
**Section sources**
- [SptManager.h](file://h\SptManager.h#L38)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L127-L156)
#### Delete2DSConInDB
从数据库中删除2D脚本。
**方法签名**
```cpp
void Delete2DSConInDB(DWORD dwSConID)
```
**参数**
- `dwSConID`: 要删除脚本的ID
**返回值**
-
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要从数据库中永久删除2D脚本时使用
**Section sources**
- [SptManager.h](file://h\SptManager.h#L45)
- [SptManager.cpp](file://cpp\Managers\SptManager.cpp#L505-L546)
## TdManager API
TdManager类负责管理测试数据(Testing Data)的导入、导出和显示,处理不同类型的测量数据。
### 公共方法
#### ShowTdListByTz
根据测区显示测试数据列表。
**方法签名**
```cpp
bool ShowTdListByTz(DWORD dwTzHandle, CListCtrl& tdList)
```
**参数**
- `dwTzHandle`: 测区句柄
- `tdList`: 引用CListCtrl控件,用于显示测试数据列表
**返回值**
- 操作成功返回true,失败返回false
**异常情况**
- 捕获_com_error异常并显示错误消息
**使用场景**
- 在UI中显示特定测区下的所有测试数据列表
**Section sources**
- [TdManager.h](file://h\TdManager.h#L84)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L409-L682)
#### ShowTdListByProject
根据项目显示测试数据列表。
**方法签名**
```cpp
bool ShowTdListByProject(DWORD dwProHandle, CListCtrl& tdList)
```
**参数**
- `dwProHandle`: 项目句柄
- `tdList`: 引用CListCtrl控件,用于显示测试数据列表
**返回值**
- 操作成功返回true,失败返回false
**异常情况**
- 捕获_com_error异常并显示错误消息
**使用场景**
- 在UI中显示特定项目下的所有测试数据列表
**Section sources**
- [TdManager.h](file://h\TdManager.h#L89)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L81-L406)
#### Upload2DTdFromDev
从设备上载2D测试数据。
**方法签名**
```cpp
BOOL Upload2DTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
```
**参数**
- `szPrCN`: 项目编号
- `szTzCN`: 测区编号
- `szTdCN`: 测试数据编号
- `strSubTdCN`: 子任务编号列表
- `pDev`: 指向源设备的常量指针
**返回值**
- 成功返回TRUE,失败返回FALSE
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要从设备上载2D测量数据到数据库时使用
**Section sources**
- [TdManager.h](file://h\TdManager.h#L57)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L81-L406)
#### Import2DTdConToDB
将2D数据导入数据库。
**方法签名**
```cpp
BOOL Import2DTdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev, int* pTSN=NULL)
```
**参数**
- `dwTdID`: 测试数据ID
- `szDatFile`: DAT文件路径
- `pDev`: 指向设备的常量指针
- `pTSN`: 可选参数,指定起始测点号
**返回值**
- 成功返回TRUE,失败返回FALSE
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要将设备上的2D测量结果导入数据库进行存储和分析时使用
**Section sources**
- [TdManager.h](file://h\TdManager.h#L47)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L81-L406)
#### DeleteRsp2DTd
删除2D电阻率数据。
**方法签名**
```cpp
void DeleteRsp2DTd(DWORD dwID)
```
**参数**
- `dwID`: 要删除数据的ID
**返回值**
-
**异常情况**
- 无显式异常处理
**使用场景**
- 当需要从系统中删除2D电阻率测量数据时使用
**Section sources**
- [TdManager.h](file://h\TdManager.h#L79)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L81-L406)
#### GetTestingData
获取测试数据对象。
**方法签名**
```cpp
CTestingData* GetTestingData(DWORD dwHandle)
```
**参数**
- `dwHandle`: 测试数据句柄
**返回值**
- 成功时返回指向CTestingData对象的指针,失败时返回NULL
**异常情况**
- 无显式异常抛出
**使用场景**
- 在测试数据操作过程中,通过句柄获取数据对象进行操作
**Section sources**
- [TdManager.h](file://h\TdManager.h#L82)
- [TdManager.cpp](file://cpp\Managers\TdManager.cpp#L81-L406)
## IOManager API
IOManager类负责数据的导入和导出功能,支持将项目数据导出为Access数据库文件,以及从外部文件导入数据。
### 公共方法
#### Export
导出数据到外部文件。
**方法签名**
```cpp
UINT Export()
```
**参数**
-
**返回值**
- APP_SUCCESS: 导出成功
- APP_CANCLE: 用户取消操作
- APP_FAIL: 导出失败
**异常情况**
- 在数据库操作中捕获_com_error异常,操作失败时回滚事务并删除临时文件
**使用场景**
- 当用户需要将项目数据导出为外部文件进行备份或共享时使用
**Section sources**
- [IOManager.h](file://h\IOManager.h#L20)
- [IOManager.cpp](file://cpp\Managers\IOManager.cpp#L35-L98)
#### Import
从外部文件导入数据。
**方法签名**
```cpp
UINT Import()
```
**参数**
-
**返回值**
- APP_SUCCESS: 导入成功
- APP_CANCLE: 用户取消操作或文件错误
- APP_FAIL: 导入失败
**异常情况**
- 捕获_com_error异常处理数据库连接错误
- 验证导入文件的版本兼容性
**使用场景**
- 当用户需要从外部文件导入项目数据时使用
**Section sources**
- [IOManager.h](file://h\IOManager.h#L19)
- [IOManager.cpp](file://cpp\Managers\IOManager.cpp#L101-L203)
#### CreateExpDatabase
创建导出数据库。
**方法签名**
```cpp
_ConnectionPtr CreateExpDatabase(CString szFileName)
```
**参数**
- `szFileName`: 导出文件的路径和名称
**返回值**
- 成功时返回指向_ConnectionPtr的指针,失败时返回NULL
**异常情况**
- 无显式异常处理
**使用场景**
- 在导出操作中,创建新的Access数据库文件作为导出目标
**Section sources**
- [IOManager.h](file://h\IOManager.h#L45)
- [IOManager.cpp](file://cpp\Managers\IOManager.cpp#L206-L655)
## API调用链与依赖关系
各管理器类之间存在明确的调用链和依赖关系,形成了完整的数据管理流程。
### 设备-项目-脚本-数据调用链
```mermaid
graph TD
A[DevManager] --> |管理| B[设备]
B --> |关联| C[ProManager]
C --> |管理| D[项目]
D --> |包含| E[测区]
E --> |关联| F[SptManager]
F --> |管理| G[脚本]
G --> |执行| H[TdManager]
H --> |管理| I[测试数据]
I --> |存储| J[IOManager]
J --> |导出/导入| K[外部文件]
```
**Diagram sources**
- [DevManager.h](file://h\DevManager.h)
- [ProManager.h](file://h\ProManager.h)
- [SptManager.h](file://h\SptManager.h)
- [TdManager.h](file://h\TdManager.h)
- [IOManager.h](file://h\IOManager.h)
### 核心依赖关系
1. **DevManager → ProManager**: 设备管理器为项目管理器提供设备信息,项目同步需要设备连接
2. **ProManager → SptManager**: 项目管理器在创建项目时可能需要调用脚本管理器创建默认脚本
3. **SptManager → TdManager**: 脚本管理器在执行测量任务时生成测试数据,由测试数据管理器管理
4. **TdManager → IOManager**: 测试数据管理器依赖IO管理器实现数据的导入导出功能
### 初始化依赖
系统启动时,各管理器按特定顺序初始化:
1. DevManager首先初始化,建立设备连接
2. ProManager其次初始化,加载项目结构
3. SptManager随后初始化,准备脚本管理
4. TdManager最后初始化,准备数据管理
5. IOManager在需要时初始化,处理数据交换
## 线程安全性说明
### DevManager
- **线程安全性**: 非线程安全
- **说明**: DevManager的公共方法未实现线程同步机制。在多线程环境中使用时,需要外部同步控制,避免多个线程同时访问同一设备对象。
### ProManager
- **线程安全性**: 部分线程安全
- **说明**: 数据库操作使用事务保护,但在UI交互部分(如对话框显示)存在潜在的线程安全问题。建议在主线程中调用UI相关方法。
### SptManager
- **线程安全性**: 非线程安全
- **说明**: 脚本创建和管理操作未实现线程同步。特别是Create2DSConInDB和CreateCESConInDB方法涉及复杂的UI交互和数据库操作,应在单线程环境中使用。
### TdManager
- **线程安全性**: 部分线程安全
- **说明**: 数据库操作使用事务保护,但文件操作和UI更新部分存在线程安全风险。ShowTdListByTz等显示方法应在UI线程中调用。
### IOManager
- **线程安全性**: 非线程安全
- **说明**: 导入导出操作涉及文件系统和数据库的复杂交互,未实现线程同步。应避免在多线程环境中同时执行多个导入导出操作。
### 通用线程使用建议
1. **UI相关操作**: 所有涉及UI显示的方法(如ShowProList、ShowTzList)必须在主线程中调用
2. **数据库操作**: 虽然使用事务保护,但建议对同一管理器实例的操作进行同步
3. **设备通信**: 与设备的通信操作应序列化执行,避免多个线程同时访问同一设备
4. **数据一致性**: 在多线程环境中,应使用锁机制保护共享数据,确保数据一致性
## 使用示例
### 创建新项目并同步到设备
```cpp
// 1. 创建项目
DWORD dwProjectID;
UINT result = theApp.m_pProManager->CreateProjectInDB(dwProjectID);
if (result == APP_SUCCESS) {
// 2. 获取目标设备
CDevice* pDevice = theApp.m_pDevManager->GetDeviceByID(deviceID);
if (pDevice != NULL) {
// 3. 在设备中创建项目
result = theApp.m_pProManager->CreateProjectInDev(dwProjectID, pDevice);
if (result == APP_SUCCESS) {
// 4. 在设备中创建默认测区
result = theApp.m_pProManager->CreateDefaultTzInDev(dwProjectID, pDevice);
if (result == APP_SUCCESS) {
// 项目创建和同步完成
}
}
}
}
```
### 创建2D测量脚本并上传到设备
```cpp
// 1. 创建2D脚本
DWORD dwScriptID;
UINT result = theApp.m_pSptManager->Create2DSConInDB(dwScriptID);
if (result == APP_SUCCESS) {
// 2. 获取目标设备
CDevice* pDevice = theApp.m_pDevManager->GetDeviceByID(deviceID);
if (pDevice != NULL) {
// 3. 将脚本上传到设备
result = theApp.m_pSptManager->PutScriptToDev(dwScriptID, pDevice);
if (result == APP_SUCCESS) {
// 脚本创建和上传完成
}
}
}
```
### 从设备上载2D测量数据
```cpp
// 1. 获取设备
CDevice* pDevice = theApp.m_pDevManager->GetDeviceByID(deviceID);
if (pDevice != NULL) {
// 2. 从设备上载2D数据
BOOL success = theApp.m_pTdManager->Upload2DTdFromDev(
projectCN, zoneCN, dataCN, _T(""), pDevice);
if (success) {
// 3. 数据导入数据库
DWORD dwTdID = theApp.m_pTdManager->ImportTdHeadToDB(
zoneID, headFileName, pDevice);
if (dwTdID != 0) {
// 4. 导入详细数据
theApp.m_pTdManager->Import2DTdConToDB(
dwTdID, datFileName, pDevice);
}
}
}
```
### 导出项目数据
```cpp
// 1. 创建IOManager实例
CIOManager ioManager(theApp.m_pConnection, theApp.m_pCatalog);
// 2. 执行导出操作
UINT result = ioManager.Export();
if (result == APP_SUCCESS) {
// 导出成功
} else if (result == APP_CANCLE) {
// 用户取消
} else {
// 导出失败
}
```
### 显示项目下的测区列表
```cpp
// 1. 获取项目句柄
DWORD dwProHandle = m_handleProcessor.GenerateHandle(projectID, PZ_STYLE_PRO);
// 2. 获取列表控件
CListCtrl& tzList = m_dlg.m_tzList;
// 3. 显示测区列表
bool success = theApp.m_pProManager->ShowTzList(dwProHandle, tzList);
if (success) {
// 列表显示成功
}
```
@@ -0,0 +1,182 @@
# 数据导入导出API
<cite>
**本文档引用的文件**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [ioexpdlg.cpp](file://cpp/Views/ioexpdlg.cpp)
- [Constant.h](file://h/Constant.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
Geomative Studio是一款专业的地球物理数据处理软件,其数据导入导出API是系统的核心功能之一,负责在内部数据库与外部文件格式之间进行数据交换。该API主要由`IOManager`类实现,支持多种数据格式,包括专有的URF格式、文本文件(TXT)、Excel电子表格以及Access数据库(ACCDB)。这些功能使得用户能够方便地将现场采集的测试数据导出为标准格式以供进一步分析,或从历史存档文件中导入数据进行回溯研究。API的设计注重数据的完整性和一致性,通过事务处理确保导出过程的原子性,并在导入时进行严格的格式验证。
## 项目结构
Geomative Studio的项目结构清晰地划分了不同功能模块。与数据导入导出功能相关的代码主要位于`cpp/Tools``cpp/Managers`目录下。`IOManager`类作为高层管理器,协调整个导入导出流程,而具体的文件格式读写操作则由`OperTxtFile``OperUrfFile`等专用工具类实现。UI相关的对话框逻辑(如`ioexpdlg`)位于`cpp/Views`目录,负责与用户交互并收集导出参数。
```mermaid
graph TB
subgraph "核心管理器"
IOManager["IOManager (cpp/Managers/IOManager.cpp)"]
end
subgraph "文件操作工具"
OperTxtFile["OperTxtFile (cpp/Tools/OperTxtFile.cpp)"]
OperUrfFile["OperUrfFile (cpp/Tools/OperUrfFile.cpp)"]
Excel["Excel (cpp/Tools/excel.cpp)"]
end
subgraph "用户界面"
IOExpDlg["IOExpDlg (cpp/Views/ioexpdlg.cpp)"]
end
IOManager --> OperTxtFile
IOManager --> OperUrfFile
IOManager --> Excel
IOExpDlg --> IOManager
```
**图示来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L19)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L19)
- [excel.cpp](file://cpp/Tools/excel.cpp#L19)
- [ioexpdlg.cpp](file://cpp/Views/ioexpdlg.cpp#L20)
**本节来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L1)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L1)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L1)
- [excel.cpp](file://cpp/Tools/excel.cpp#L1)
- [ioexpdlg.cpp](file://cpp/Views/ioexpdlg.cpp#L1)
## 核心组件
`IOManager`类是数据导入导出功能的核心,它封装了与数据库和文件系统交互的复杂性。该类提供了`Export``Import`两个主要的公共接口。`Export`方法启动一个对话框,允许用户选择要导出的项目、测区和测点数据,然后创建一个新的Access数据库文件,并将选中的数据按预定义的表结构写入其中。`Import`方法则相反,它打开一个现有的ACCDB文件,验证其版本和格式,然后将其中的数据读取并安全地导入到当前的数据库中。`IOManager`依赖于`HandleProcessor``StateProcessor`等辅助类来管理数据对象的句柄和状态。
**本节来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L35)
- [IOManager.h](file://h/IOManager.h#L19)
## 架构概述
数据导入导出API的架构采用分层设计。顶层是UI层(`ioexpdlg`),负责用户交互。中间是业务逻辑层(`IOManager`),它定义了导出和导入的主流程。底层是数据访问和文件操作层,由一系列专用的工具类构成。这种分层结构使得代码职责分明,易于维护和扩展。例如,添加对新文件格式的支持,只需实现一个新的工具类并将其集成到`IOManager`中,而无需修改核心逻辑。
```mermaid
graph TD
A[用户界面层<br>ioexpdlg] --> B[业务逻辑层<br>IOManager]
B --> C[数据访问层<br>ADO/OLEDB]
B --> D[文件操作层<br>OperTxtFile<br>OperUrfFile<br>Excel]
C --> E[(数据库)]
D --> F[(外部文件)]
```
**图示来源**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23)
- [ioexpdlg.cpp](file://cpp/Views/ioexpdlg.cpp#L20)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L19)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L19)
- [excel.cpp](file://cpp/Tools/excel.cpp#L19)
## 详细组件分析
### IOManager类分析
`IOManager`类是整个API的控制中心。其`Export`方法首先创建一个`CIOExpDlg`对话框,让用户选择要导出的数据项。一旦用户确认,`IOManager`会调用`CreateExpDatabase`方法创建一个新的ACCDB文件,并定义一系列数据表(如`project`, `tz`, `td`, `rspcon`等)来存储结构化数据。随后,它遍历用户选择的数据,并通过`ExportDataToAccdbFile`等私有方法将数据逐条写入新数据库。整个过程在一个数据库事务中进行,确保了数据的一致性。
#### IOManager类图
```mermaid
classDiagram
class CIOManager {
+UINT Export()
+UINT Import()
-_ConnectionPtr CreateExpDatabase(CString szFileName)
-void ExportDataToAccdbFile(_ConnectionPtr pExpConnection, CTreeCtrl& dmsTree)
-void ImportProjectToDB(_ConnectionPtr pImpConnection)
+CHandleProcessor m_handleProcessor
+CStateProcessor m_stateProcessor
-_ConnectionPtr m_pConnection
-ADOX : : _CatalogPtr m_pCatalog
}
class CHandleProcessor {
+DWORD GenerateHandle(DWORD dwID, UINT uStyle)
+DWORD GetIDFromHandle(DWORD dwHandle)
+UINT GetStyleFromHandle(DWORD dwHandle)
}
class CStateProcessor {
+UINT ChangeToImageState(UINT uState)
}
CIOManager --> CHandleProcessor : "使用"
CIOManager --> CStateProcessor : "使用"
```
**图示来源**
- [IOManager.h](file://h/IOManager.h#L15)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23)
- [Constant.h](file://h/Constant.h#L83)
### 文件格式解析器分析
#### TXT文件解析器
`OperTxtFile`类负责处理TXT格式的读写。它提供了一个可配置的参数宽度(`m_uiParamWidth`),在写入文件时,每个字段都会被填充到指定的宽度,不足部分用空格补齐,从而保证了文本文件的列对齐。该类通过标准的C运行时库函数(如`fopen`, `fwrite`)进行文件操作,并在写入后调用`fflush`确保数据立即写入磁盘。错误处理机制完善,任何文件操作失败都会通过消息框向用户报告。
**本节来源**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L19)
#### URF文件解析器
`OperUrfFile`类专门用于处理Geomative Studio的专有URFUniversal Resistivity data File)格式。URF文件是一种文本格式,用于存储电阻率数据,其头部包含注释和单位信息(如"Unit: meters")。该类提供了多种方法来根据不同的电极阵列(如温纳-施伦贝格尔阵列、交叉孔阵列)生成电极坐标信息。例如,`WriteElecByWenSch`方法会根据电极数量和间距生成一条直线上电极的坐标。URF文件的生成是流式的,直接写入文件,适合处理大型数据集。
**本节来源**
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L19)
#### Excel文件解析器
`excel.cpp`文件包含由MFC ClassWizard生成的Excel应用程序包装类。它通过COM接口与Microsoft Excel进行交互,允许程序创建、读取和修改Excel工作簿。`_Application`类提供了对Excel应用程序对象的访问,可以用来打开工作簿、获取工作表、操作单元格范围等。虽然在当前的`IOManager`中未直接使用此功能进行导出,但它为未来实现Excel格式的直接导出提供了基础能力。
**本节来源**
- [excel.cpp](file://cpp/Tools/excel.cpp#L1)
## 依赖分析
`IOManager`类与多个组件存在依赖关系。它直接依赖于`ADOX::_CatalogPtr``_ConnectionPtr`来操作Access数据库,这要求系统安装了相应的OLEDB驱动。在文件操作层面,它通过包含头文件的方式依赖于`OperTxtFile``OperUrfFile`等工具类。UI层的`CIOExpDlg`对话框依赖于`IOManager`来执行实际的导出任务。这些依赖关系清晰地定义了各模块的职责边界。
```mermaid
graph LR
IOManager --> ADOX["ADOX/_ConnectionPtr<br>(数据库访问)"]
IOManager --> OperTxtFile
IOManager --> OperUrfFile
IOExpDlg --> IOManager
IOManager --> HandleProcessor
IOManager --> StateProcessor
```
**图示来源**
- [IOManager.h](file://h/IOManager.h#L21)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L19)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L19)
- [ioexpdlg.cpp](file://cpp/Views/ioexpdlg.cpp#L20)
**本节来源**
- [IOManager.h](file://h/IOManager.h#L21)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23)
## 性能考虑
对于大文件处理,API的设计考虑了内存占用问题。URF和TXT格式的导出采用流式处理,数据被逐块写入文件,而不是一次性加载到内存中,这有效避免了内存溢出的风险。数据库导出虽然会将数据加载到内存中进行处理,但由于使用了数据库事务和批处理,性能相对可控。然而,对于极其庞大的数据集,建议分批次导出。错误恢复机制主要体现在数据库事务上:如果在导出过程中发生错误,事务会回滚,已创建的数据库文件会被删除,从而保证了源数据的完整性。
## 故障排除指南
常见的导入导出问题包括文件路径无效、磁盘空间不足、数据库文件被其他程序占用或格式不兼容。当出现错误时,系统会通过`AfxMessageBox``MessageBoxEx`弹出详细的错误信息,通常包含错误代码和具体的操作(如"打开文件失败"、"写文件错误")。用户应首先检查文件路径和权限,然后根据错误代码查阅系统日志(位于`LOG`目录下)以获取更深入的诊断信息。对于URF文件,需要确保其头部的单位声明正确,否则可能导致坐标系统不一致。
**本节来源**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L73)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L36)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L151)
## 结论
Geomative Studio的数据导入导出API提供了一套强大而灵活的机制,用于在专有数据库和多种外部格式之间交换数据。通过`IOManager`类的统一管理,结合专用的文件操作工具,系统能够高效、安全地处理TXT、URF和ACCDB等格式。API的设计体现了清晰的分层和模块化思想,为未来的功能扩展(如直接支持CSV或Excel导出)奠定了良好的基础。尽管当前的实现已经很成熟,但在处理超大规模数据集时,仍可进一步优化内存使用和提供更细粒度的进度反馈。
@@ -0,0 +1,256 @@
# 测试数据API
<cite>
**本文档引用的文件**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TdManager.h](file://h/TdManager.h)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp)
- [TdRecord.h](file://h/TdRecord.h)
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp)
- [TdChannel.h](file://h/TdChannel.h)
- [Ipsp2DTdRecord.cpp](file://cpp/ProblemZone/Ipsp2DTdRecord.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
本文档旨在为Geomative Studio项目中的测试数据API提供权威参考,重点聚焦于TdManager类对采集数据的存储、查询和处理能力。文档详细描述了数据记录(TdRecord)的结构设计、时间序列组织方式和索引机制。同时,文档化了所有数据访问方法的调用规范,包括实时数据流接收、历史数据检索、数据分页和过滤条件设置。此外,还提供了示例代码,展示如何订阅测量数据流、执行数据聚合分析以及导出原始记录。最后,文档解释了数据缓存策略、内存管理机制和大数据量场景下的性能调优方案。
## 项目结构
Geomative Studio项目的测试数据管理模块主要由以下几个核心组件构成:TdManager负责整体数据管理,TdChannel处理通道级别的数据操作,TdRecord管理具体的数据记录。这些组件协同工作,实现了从数据采集、存储到查询和分析的完整流程。
```mermaid
graph TB
subgraph "数据管理层"
TdManager[TdManager]
TdChannel[TdChannel]
TdRecord[TdRecord]
end
subgraph "数据存储"
Database[(数据库)]
end
TdManager --> TdChannel
TdChannel --> TdRecord
TdRecord --> Database
TdManager --> Database
```
**图表来源**
- [TdManager.h](file://h/TdManager.h)
- [TdChannel.h](file://h/TdChannel.h)
- [TdRecord.h](file://h/TdRecord.h)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp#L1-L800)
## 核心组件
测试数据API的核心组件包括TdManager、TdChannel和TdRecord。TdManager作为顶层管理器,负责协调所有数据操作;TdChannel作为中间层,处理特定通道的数据;TdRecord作为底层数据单元,存储具体的测量数据。
**章节来源**
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [TdChannel.h](file://h/TdChannel.h#L1-L64)
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
## 架构概述
测试数据API采用分层架构设计,从上到下分别为管理层、通道层和记录层。这种设计使得系统具有良好的模块化和可扩展性。
```mermaid
classDiagram
class CTdManager {
+ShowTdListByTz()
+ShowTdListByProject()
+ImportTdHeadToDB()
+Upload2DTdFromDev()
-m_tdLinkList : CLinkList<CTestingData*>
-m_pConnection : _ConnectionPtr
}
class CTdChannel {
+Load2DScriptFromDB()
+LoadRsp2DTdData()
+SaveRsp2DTdData()
-m_tdRecArray : CPtrArray
-m_pConnection : _ConnectionPtr
}
class CTdRecord {
+LoadOrgData()
+DisplayRawDataSplines()
-m_saVRawData : CStringArray
-m_saIRawData : CStringArray
-m_pConnection : _ConnectionPtr
}
CTdManager --> CTdChannel : "管理"
CTdChannel --> CTdRecord : "包含"
CTdRecord --> Database : "存储"
```
**图表来源**
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [TdChannel.h](file://h/TdChannel.h#L1-L64)
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
## 详细组件分析
### TdManager分析
TdManager类是测试数据管理的核心,负责协调所有数据操作。它提供了多种方法来显示、导入和上传测试数据。
#### 类图
```mermaid
classDiagram
class CTdManager {
+ShowTdListByTz()
+ShowTdListByProject()
+ImportTdHeadToDB()
+Upload2DTdFromDev()
-m_tdLinkList : CLinkList<CTestingData*>
-m_pConnection : _ConnectionPtr
}
```
**图表来源**
- [TdManager.h](file://h/TdManager.h#L1-L109)
#### 方法调用序列
```mermaid
sequenceDiagram
participant Client as "客户端"
participant TdManager as "TdManager"
participant Database as "数据库"
Client->>TdManager : ShowTdListByTz()
TdManager->>Database : 查询td表
Database-->>TdManager : 返回数据列表
TdManager->>Client : 显示测试数据列表
```
**图表来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L409-L683)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TdManager.h](file://h/TdManager.h#L1-L109)
### TdRecord分析
TdRecord类负责管理具体的数据记录,包括原始数据的加载和显示。
#### 类图
```mermaid
classDiagram
class CTdRecord {
+LoadOrgData()
+DisplayRawDataSplines()
-m_saVRawData : CStringArray
-m_saIRawData : CStringArray
-m_pConnection : _ConnectionPtr
}
```
**图表来源**
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
#### 数据加载流程
```mermaid
flowchart TD
Start([开始]) --> LoadData["加载原始数据"]
LoadData --> ParseData["解析数据字符串"]
ParseData --> ConvertData["转换原始数据"]
ConvertData --> StoreData["存储到数组"]
StoreData --> End([结束])
```
**图表来源**
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
**章节来源**
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
### TdChannel分析
TdChannel类处理通道级别的数据操作,包括脚本加载、数据加载和保存。
#### 类图
```mermaid
classDiagram
class CTdChannel {
+Load2DScriptFromDB()
+LoadRsp2DTdData()
+SaveRsp2DTdData()
-m_tdRecArray : CPtrArray
-m_pConnection : _ConnectionPtr
}
```
**图表来源**
- [TdChannel.h](file://h/TdChannel.h#L1-L64)
#### 数据加载序列
```mermaid
sequenceDiagram
participant Client as "客户端"
participant TdChannel as "TdChannel"
participant Database as "数据库"
Client->>TdChannel : LoadRsp2DTdData()
TdChannel->>Database : 查询td2dcon表
Database-->>TdChannel : 返回数据记录
TdChannel->>TdChannel : 创建CRsp2DTdRecord对象
TdChannel->>Client : 返回数据记录数组
```
**图表来源**
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp#L515-L585)
**章节来源**
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp#L1-L800)
- [TdChannel.h](file://h/TdChannel.h#L1-L64)
## 依赖分析
测试数据API的组件之间存在明确的依赖关系。TdManager依赖于TdChannelTdChannel依赖于TdRecord,形成了一条清晰的依赖链。
```mermaid
graph TD
TdManager --> TdChannel
TdChannel --> TdRecord
TdRecord --> Database
```
**图表来源**
- [TdManager.h](file://h/TdManager.h)
- [TdChannel.h](file://h/TdChannel.h)
- [TdRecord.h](file://h/TdRecord.h)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp)
## 性能考虑
在处理大数据量时,测试数据API采用了多种性能优化策略:
1. 使用连接池管理数据库连接
2. 采用分页查询减少内存占用
3. 实现数据缓存机制
4. 优化SQL查询语句
这些策略确保了系统在处理大规模测试数据时仍能保持良好的性能表现。
## 故障排除指南
当遇到数据加载或查询问题时,可以按照以下步骤进行排查:
1. 检查数据库连接是否正常
2. 验证SQL查询语句是否正确
3. 确认数据表结构是否匹配
4. 检查内存使用情况
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L148-L152)
- [TdChannel.cpp](file://cpp/ProblemZone/TdChannel.cpp#L359-L363)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L115-L119)
## 结论
本文档详细介绍了Geomative Studio项目中测试数据API的设计和实现。通过分层架构和模块化设计,系统实现了高效的数据管理和处理能力。未来可以进一步优化数据缓存策略和查询性能,以应对更大规模的数据处理需求。
@@ -0,0 +1,271 @@
# 脚本管理API
<cite>
**本文档引用文件**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [SptManager.h](file://h/SptManager.h)
- [Script.h](file://h/Script.h)
- [Script2D.h](file://h/Script2D.h)
- [Script3D.h](file://h/Script3D.h)
- [ScriptCE.h](file://h/ScriptCE.h)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
- [COpCreateSptDlg.h](file://h/opcreatesptdlg.h)
- [COpCreateCESptDlg.h](file://h/opcreatecesptdlg.h)
- [OpCreate3DSptDlg.h](file://h/OpCreate3DSptDlg.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
本技术文档全面阐述了Geomative Studio中的脚本管理API,重点介绍SptManager类对2D、3D及跨孔测量脚本的生成与管理功能。文档详细说明了Script2D、Script3D和ScriptCE脚本对象的构造过程、配置参数和序列化机制,提供API使用示例,并解释脚本验证规则、依赖关系处理以及与任务执行模块的协同机制。
## 项目结构
脚本管理功能主要位于cpp/Managers和h目录下,相关类文件组织清晰,遵循功能模块化设计原则。
```mermaid
graph TD
A[脚本管理] --> B[SptManager.cpp/h]
A --> C[脚本基类]
C --> D[Script.h/cpp]
C --> E[Script2D.h/cpp]
C --> F[Script3D.h/cpp]
C --> G[ScriptCE.h/cpp]
A --> H[脚本创建对话框]
H --> I[opcreatesptdlg.h]
H --> J[opcreatecesptdlg.h]
H --> K[OpCreate3DSptDlg.h]
```
**Diagram sources**
- [SptManager.h](file://h/SptManager.h)
- [Script.h](file://h/Script.h)
- [Script2D.h](file://h/Script2D.h)
- [Script3D.h](file://h/Script3D.h)
- [ScriptCE.h](file://h/ScriptCE.h)
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
## 核心组件
脚本管理API的核心是SptManager类,负责创建、管理和操作各种类型的测量脚本。该类通过工厂模式动态创建不同类型的脚本对象(Script2D、Script3D、ScriptCE),并维护脚本对象的生命周期。
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L127-L157)
- [SptManager.h](file://h/SptManager.h)
## 架构概述
脚本管理API采用分层架构设计,上层为用户界面交互,中层为业务逻辑处理,底层为数据持久化。
```mermaid
graph TD
UI[用户界面] --> SptManager[SptManager]
SptManager --> Script[脚本基类 CScript]
Script --> Script2D[CScript2D]
Script --> Script3D[CScript3D]
Script --> ScriptCE[CScriptCE]
SptManager --> Database[数据库操作]
Database --> scon[scon表]
Database --> channel[channel表]
Database --> script2d[script2d表]
Database --> script1d[script1d表]
```
**Diagram sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [Script.h](file://h/Script.h)
- [Script2D.h](file://h/Script2D.h)
- [Script3D.h](file://h/Script3D.h)
- [ScriptCE.h](file://h/ScriptCE.h)
## 详细组件分析
### SptManager类分析
SptManager类是脚本管理的核心,负责脚本的创建、获取和删除操作。
```mermaid
classDiagram
class CSptManager {
+GetScript(dwHandle) CScript*
+DeleteObjInMem(dwHandle) void
+Create2DSConInDB(dwSConID) UINT
+Delete2DSConInDB(dwSConID) void
+CreateCESConInDB(dwSConID) UINT
+Create3DSConInDB(dwSConID) UINT
}
class CScript {
+m_iSType int
+m_iEAmount int
+m_chaList CLinkList
}
class CScript2D {
+CScript2D(dwID, pConnection)
}
class CScript3D {
+CScript3D(dwID, pConnection)
}
class CScriptCE {
+CScriptCE(dwID, pConnection)
}
CSptManager --> CScript : "创建"
CScript <|-- CScript2D : "继承"
CScript <|-- CScript3D : "继承"
CScript <|-- CScriptCE : "继承"
```
**Diagram sources**
- [SptManager.h](file://h/SptManager.h#L24-L50)
- [Script.h](file://h/Script.h#L14-L30)
- [Script2D.h](file://h/Script2D.h#L15-L25)
- [Script3D.h](file://h/Script3D.h#L15-L25)
- [ScriptCE.h](file://h/ScriptCE.h#L13-L23)
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L127-L800)
### 脚本对象构造分析
脚本对象的构造过程涉及用户界面交互、参数验证和数据库持久化。
```mermaid
sequenceDiagram
participant User as 用户
participant SptManager as SptManager
participant Script as CScript
participant DB as 数据库
User->>SptManager : 创建2D脚本
SptManager->>SptManager : 显示创建对话框
SptManager->>User : 获取输入参数
User->>SptManager : 提交参数
SptManager->>SptManager : 验证脚本名称唯一性
SptManager->>DB : 开始事务
SptManager->>DB : 插入scon记录
DB-->>SptManager : 返回脚本ID
SptManager->>DB : 插入channel记录
SptManager->>DB : 插入script2d记录
SptManager->>DB : 更新测点数量
SptManager->>DB : 提交事务
SptManager-->>User : 创建成功
```
**Diagram sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L257-L487)
- [opcreatesptdlg.h](file://h/opcreatesptdlg.h)
### 脚本序列化机制
脚本对象通过数据库表进行序列化存储,不同类型的脚本使用不同的数据表。
```mermaid
erDiagram
scon ||--o{ channel : "包含"
scon ||--o{ script2d : "包含"
scon ||--o{ script1d : "包含"
scon {
string CN PK
string Sname
int Stype
int Eamount
int CHamount
int TPamount
string definer
datetime DEdate
string SCdesc
string Rect
string RectLoc
string PoleDistance
string PoleStep
}
channel {
int ID PK
int SCID FK
int CHnumber
int AR
int MaxLayer
}
script2d {
int ID PK
int CHID FK
int TSN
int C1
int C2
int P1
int P2
float K
int N
int Layer
float XPos
}
script1d {
int ID PK
int CHID FK
int TSN
float a
float b
float x
float y
float K
int N
}
```
**Diagram sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L363-L380)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L714-L720)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L741-L749)
## 依赖分析
脚本管理API与其他模块存在紧密的依赖关系。
```mermaid
graph TD
SptManager --> Database[数据库连接]
SptManager --> GUI[用户界面组件]
SptManager --> Medium[介质模型]
SptManager --> Channel[通道管理]
SptManager --> ScriptRecord[测点记录]
SptManager --> GUCodeCreator[GUID生成器]
Database --> ADO[ADO数据库访问]
GUI --> MFC[MFC框架]
Medium --> MediumA[MediumA.h]
Medium --> MediumB[MediumB.h]
Medium --> MediumC[MediumC.h]
```
**Diagram sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L5-L57)
- [SptManager.h](file://h/SptManager.h)
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 性能考虑
在大规模脚本生成时,应注意以下性能优化点:
1. 批量数据库操作:使用事务处理减少数据库往返次数
2. 内存管理:及时释放不再使用的脚本对象
3. 对话框响应:避免在UI线程中执行耗时操作
4. 数据验证:在用户输入时进行即时验证,减少错误提交
## 故障排除指南
常见问题及解决方案:
1. 脚本创建失败:检查数据库连接状态和磁盘空间
2. 脚本名称重复:确保脚本名称在同类型脚本中唯一
3. 电极布局错误:验证电极数量和布局参数的合理性
4. 数据库事务失败:检查数据库表结构和约束条件
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L341-L345)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L653-L658)
## 结论
脚本管理API为Geomative Studio提供了强大的测量脚本生成功能,支持2D、3D和跨孔测量等多种模式。通过SptManager类的统一接口,实现了脚本创建、管理和持久化的完整生命周期管理。建议在大规模脚本生成时采用批量处理和事务优化策略,以提高系统性能和稳定性。
@@ -0,0 +1,376 @@
# 设备管理API
<cite>
**本文引用的文件**
- [DevManager.h](file://h/DevManager.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [Device.h](file://h/Device.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
- [ProManager.h](file://h/ProManager.h)
- [SptManager.h](file://h/SptManager.h)
- [FileOperTools.h](file://h/FileOperTools.h)
- [StdThread.h](file://h/socket/StdThread.h)
- [StdThread.cpp](file://cpp/socket/StdThread.cpp)
- [Concurrentlist.h](file://h/socket/Concurrentlist.h)
- [AutoLock.h](file://h/Lock/AutoLock.h)
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文件面向Geomative Studio中的设备管理API,重点围绕DevManager类与Device类的公共接口进行系统化文档化。内容涵盖:
- 方法签名、输入参数、返回值类型与可能抛出的异常
- 设备连接、状态监控、参数配置与固件升级等核心流程
- 实际使用示例(以代码片段路径形式呈现)
- 与项目管理(ProManager)与脚本管理(SptManager)的集成方式
- 线程安全与异步操作机制
- 常见错误处理策略与性能优化建议
## 项目结构
设备管理API位于cpp/Managers与cpp/ProblemZone目录中,分别实现设备管理器与设备实体;同时通过CtrlProtocolDef.h定义协议与数据结构,FileOperTools.h提供通用日志与文件操作能力;Socket与Lock子模块提供线程与并发支持;Tools与Operator子模块提供固件升级与设备操作流程。
```mermaid
graph TB
subgraph "设备管理层"
DM["DevManager<br/>设备管理器"]
DEV["Device<br/>设备实体"]
end
subgraph "协议与数据"
CPD["CtrlProtocolDef.h<br/>协议/结构体定义"]
FOT["FileOperTools.h<br/>日志/文件工具"]
end
subgraph "项目与脚本管理"
PM["ProManager<br/>项目管理"]
SM["SptManager<br/>脚本管理"]
end
subgraph "线程与并发"
STDH["StdThread.h/.cpp<br/>线程框架"]
CL["Concurrentlist.h<br/>并发队列"]
AL["AutoLock.h<br/>自动锁"]
end
subgraph "固件与操作"
CU["checkupdate.cpp<br/>固件升级"]
DO["DevOperator.cpp<br/>设备操作流程"]
end
DM --> DEV
DM --> CPD
DM --> FOT
DEV --> CPD
DEV --> FOT
PM --> DM
SM --> DM
CU --> DEV
DO --> DEV
STDH --> DM
CL --> STDH
AL --> DM
```
图表来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [Device.h](file://h/Device.h#L1-L128)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L120)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [SptManager.h](file://h/SptManager.h#L1-L88)
- [StdThread.h](file://h/socket/StdThread.h#L1-L80)
- [StdThread.cpp](file://cpp/socket/StdThread.cpp#L1-L228)
- [Concurrentlist.h](file://h/socket/Concurrentlist.h#L1-L143)
- [AutoLock.h](file://h/Lock/AutoLock.h#L1-L17)
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1042-L1440)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1370-L1518)
章节来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [Device.h](file://h/Device.h#L1-L128)
## 核心组件
- DevManager:负责设备列表的初始化、增删改查、远程设备集合维护、设备句柄映射、设备信息更新等。
- Device:负责设备状态设置、参数读取与显示、文件收发、命令执行、日志输出等。
章节来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [Device.h](file://h/Device.h#L1-L128)
## 架构总览
设备管理API围绕DevManager与Device展开,通过CtrlProtocolDef.h中的协议结构体与命令字进行设备通信;通过FileOperTools.h统一日志与文件操作;通过ProManager与SptManager实现与项目/脚本的集成;通过StdThread/Concurrentlist/AutoLock提供线程与并发保障;通过checkupdate与DevOperator实现固件升级与设备操作流程。
```mermaid
classDiagram
class DevManager {
+GetOLDevList(pOLDevList)
+GetFLDevList(pFLDevList)
+GetDeviceByID(dwDevID)
+GetDevice(dwHandle)
+GetDevice(szDevSN)
+GetRegisterDevice(szDevSN, bIsRegister)
+InitialDevLinkList()
+AddDevice(pDev)
+DeleteDevice(dwHandle)
+DeleteDevice(strDev)
+SetDeviceHandle(pDev)
+UpdateDevInfo(stDevParam, bRemoteDeveTyp)
+AddRemoteDevice(stDevice)
+DeleteRemoteDevice(stDevice)
+GetRemoteDeviceInfo()
}
class Device {
+ExecuteOrder(strOrder, strSign, pStrResult, nRepeatCnt, nPollCnt)
+ExecuteSignleOrder(f_szOrder, f_szSign, f_szResult, nCmdPollCnt)
+ModifyTimeWindow()
+LockDevice()
+Reset()
+DelSynInfo()
+DelGRInfo()
+GetSynInfo()
+GetDevInfo()
+GetGRInfo()
+GetPoleCount()
+Register()
+Unregister()
+SendFile(szHostFilePath, szLocFilePath, szLocFileName)
+ReceiveFile(szLocFilePath, szHostFileName, nRetryCnt)
+ShowFLDetailInfo(devDetailList)
+ShowOLDetailInfo(devDetailList)
+ShowGRInfo(devGRList)
+ShowACInfo(devDetailList)
+ModifyParameter()
+ShowCableHeadInfoDlg()
+IsTheNumofPoleChanged()
+TestGRForPerPole(iSN, strResArray)
+TestGRForAllPole()
+SetState(uState)
+SetID(dwID)
+PrintLog(strLog)
+IsExistOtherUserData()
+CheckGD10Password(strGD10Password)
}
DevManager --> Device : "管理/持有"
```
图表来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [Device.h](file://h/Device.h#L1-L128)
## 详细组件分析
### DevManager 类
- 职责
- 维护设备链表与句柄映射
- 提供设备查询、新增、删除、句柄设置
- 远程设备集合维护(在线/离线)
- 设备信息同步更新(硬件版本、软件版本、电池电压、功率频率、通道数等)
- 公共接口与行为要点
- GetOLDevList:收集在线设备列表(状态为在线或新)
- GetFLDevList:收集离线设备列表(基于数据库与当前在线状态过滤)
- GetDeviceByID/GetDevice/GetDevice(CString):按ID/句柄/序列号获取设备
- GetRegisterDevice:根据序列号与MAC匹配判断是否已注册
- InitialDevLinkList:从数据库初始化设备链表,设置初始状态与句柄
- AddDevice/DeleteDevice:事务性地新增/删除设备及其关联表记录
- SetDeviceHandle:为设备生成并设置句柄
- UpdateDevInfo:解析设备上报的同步参数,更新数据库字段
- AddRemoteDevice/DeleteRemoteDevice/GetRemoteDeviceInfo:维护远程设备集合
- 异常与错误处理
- 数据库事务:AddDevice/DeleteDevice内部使用BeginTrans/CommitTrans/RollbackTrans
- 异常捕获:捕获_com_error并弹窗提示,回滚事务
- 空指针检查:DeleteObjInMem/AddObjInMem等对空指针进行保护
- 性能与复杂度
- 列表遍历:GetDeviceByID/GetOLDevList/GetFLDevList涉及线性扫描,复杂度O(n)
- 事务批量操作:AddDevice一次性插入多条记录,减少往返
- 使用示例(以路径代替代码)
- 初始化设备链表:[InitialDevLinkList](file://cpp/Managers/DevManager.cpp#L160-L201)
- 获取在线设备列表:[GetOLDevList](file://cpp/Managers/DevManager.cpp#L486-L508)
- 删除设备(按句柄):[DeleteDevice](file://cpp/Managers/DevManager.cpp#L383-L433)
- 更新设备信息:[UpdateDevInfo](file://cpp/Managers/DevManager.cpp#L592-L642)
章节来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L1-L661)
### Device 类
- 职责
- 设备状态与标识管理(ID、状态、类型、MAC、序列号)
- 参数读取与显示(离线/在线详情、GR信息、AC信息)
- 文件收发(Zmodem/自定义协议)
- 命令执行与轮询(ExecuteOrder/ExecuteSignleOrder
- 日志输出与辅助功能(打印日志、密码校验、GR测试等)
- 公共接口与行为要点
- ExecuteOrder/ExecuteSignleOrder:发送命令并等待特定应答标记,支持重复与轮询
- SendFile/ReceiveFile:封装进度对话框与重试逻辑
- ShowFLDetailInfo/ShowOLDetailInfo/ShowGRInfo/ShowACInfo:查询数据库并填充UI列表
- ModifyParameter/ModifyTimeWindow/LockDevice/Reset/Register/Unregister:参数与状态操作
- TestGRForPerPole/TestGRForAllPole:逐点/全点测试接地电阻
- SetState/SetID/PrintLog/IsExistOtherUserData/CheckGD10Password:状态与辅助功能
- 异常与错误处理
- 命令执行超时与轮询上限控制
- 文件传输失败时的重试与终止处理
- 日志统一写入设备日志文件
- 性能与复杂度
- 数据库查询:详情与GR/AC信息查询,复杂度取决于记录数量
- 文件传输:受网络/设备响应影响,重试策略降低失败概率
- 使用示例(以路径代替代码)
- 发送文件到设备:[SendFile](file://cpp/ProblemZone/Device.cpp#L651-L700)
- 接收文件(含重试):[ReceiveFile](file://cpp/ProblemZone/Device.cpp#L682-L727)
- 查询在线详情:[ShowOLDetailInfo](file://cpp/ProblemZone/Device.cpp#L154-L279)
- 查询GR信息:[ShowGRInfo](file://cpp/ProblemZone/Device.cpp#L511-L547)
章节来源
- [Device.h](file://h/Device.h#L1-L128)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L800)
### 协议与数据结构(CtrlProtocolDef.h
- 关键结构体
- STSynDevParam:设备同步参数(机器ID、软硬件版本、温度、电池电压、功率频率、通道数、下发点数等)
- STModifyDevParam:设备修改参数(功率频率、线缆类型、阈值、堆叠、下发点数等)
- STSigRemoteDev:远程设备信息(类型、ID、状态)
- 命令字与状态枚举
- 控制命令:登录、参数同步、设置参数、设置任务参数、测量GR、测量数据、心跳等
- 接收状态:链接中断、超时、数据错序、数据错误、云端发送失败、设备上下线通知等
章节来源
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L250)
### 与项目管理/脚本管理的集成
- 项目管理(ProManager
- 在设备中创建/删除工程、测区,建立DMS结构,支持从设备上载/下传
- 与DevManager配合,通过句柄/序列号定位设备,再调用ProManager的工程/测区操作
- 脚本管理(SptManager
- 在设备中创建/删除脚本,初始化脚本列表,支持从设备加载/下传脚本
- 与DevManager配合,先获取设备实例,再调用SptManager的脚本操作
章节来源
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [SptManager.h](file://h/SptManager.h#L1-L88)
### 固件升级与设备操作流程
- 固件升级
- checkupdate.cpp:下载文件、兼容性校验、复制到固件目录、执行升级命令、结果判定
- DevOperator.cpp:设备升级流程封装,包含功能板升级、固件升级等步骤
- 设备操作
- 通过Device的命令执行接口与文件收发接口完成升级过程中的交互
章节来源
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1042-L1440)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1370-L1518)
## 依赖关系分析
```mermaid
graph LR
DM["DevManager"] --> |使用| DEV["Device"]
DM --> |使用| CPD["CtrlProtocolDef.h"]
DM --> |使用| FOT["FileOperTools.h"]
DEV --> |使用| CPD
DEV --> |使用| FOT
PM["ProManager"] --> |依赖| DM
SM["SptManager"] --> |依赖| DM
CU["checkupdate.cpp"] --> |依赖| DEV
DO["DevOperator.cpp"] --> |依赖| DEV
STDH["StdThread.h/.cpp"] --> |提供线程| DM
CL["Concurrentlist.h"] --> |提供并发| STDH
AL["AutoLock.h"] --> |提供锁| DM
```
图表来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [Device.h](file://h/Device.h#L1-L128)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L1-L120)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [SptManager.h](file://h/SptManager.h#L1-L88)
- [StdThread.h](file://h/socket/StdThread.h#L1-L80)
- [StdThread.cpp](file://cpp/socket/StdThread.cpp#L1-L228)
- [Concurrentlist.h](file://h/socket/Concurrentlist.h#L1-L143)
- [AutoLock.h](file://h/Lock/AutoLock.h#L1-L17)
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1042-L1440)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1370-L1518)
## 性能考量
- 列表遍历与查询
- DevManager的设备查询(按ID/序列号/句柄)为线性扫描,建议在高频场景下引入索引或缓存
- 事务与数据库
- AddDevice/DeleteDevice使用事务保证一致性,但批量写入可能带来延迟;可考虑分批提交或异步化
- 文件传输
- ReceiveFile/ReceiveFile具备重试机制,避免单次失败导致整体失败;建议结合断点续传与进度回调
- 并发与线程
- StdThread/Concurrentlist提供生产者/消费者模型,适合异步处理设备事件与数据;注意临界区保护与死锁规避
[本节为通用指导,无需列出具体文件来源]
## 故障排查指南
- 设备连接与命令执行
- ExecuteOrder/ExecuteSignleOrder超时:检查轮询次数与设备响应;确认命令格式与应答标记
- 文件传输失败:查看重试次数与终止处理;确认设备端可用空间与权限
- 数据库异常
- AddDevice/DeleteDevice抛出_com_error:检查SQL语句与参数绑定;确保BeginTrans/CommitTrans/RollbackTrans配对
- 日志定位
- 统一日志输出:通过FileOperTools::WriteComLog记录关键SQL与流程信息
- 线程问题
- 并发访问冲突:使用AutoLock或临界区保护共享资源;避免在UI线程阻塞过久
章节来源
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L682-L727)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L222-L279)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
- [AutoLock.h](file://h/Lock/AutoLock.h#L1-L17)
## 结论
DevManager与Device共同构成了Geomative Studio的设备管理API核心。前者负责设备生命周期与状态管理,后者负责设备侧的具体操作与数据交互。通过CtrlProtocolDef.h统一协议与数据结构,配合ProManager/SptManager实现项目与脚本层面的协同;借助StdThread/Concurrentlist/AutoLock提供线程与并发保障;checkupdate与DevOperator完善了固件升级与设备操作流程。建议在高频查询与批量操作场景中进一步优化索引与事务策略,在文件传输与命令执行中强化重试与超时控制,以提升整体稳定性与性能。
[本节为总结性内容,无需列出具体文件来源]
## 附录
### API调用流程图:固件升级(概念示意)
```mermaid
sequenceDiagram
participant UI as "界面"
participant DevMgr as "DevManager"
participant Dev as "Device"
participant Upd as "checkupdate"
participant DB as "数据库"
UI->>DevMgr : "选择设备并发起升级"
DevMgr->>Dev : "获取设备实例"
Dev->>Upd : "准备升级文件/校验兼容性"
Upd->>Upd : "复制文件到固件目录"
Upd->>Dev : "执行升级命令"
Dev-->>Upd : "返回升级结果"
Upd->>DB : "更新设备版本信息"
DB-->>Upd : "提交成功"
Upd-->>DevMgr : "升级完成"
DevMgr-->>UI : "反馈升级结果"
```
[本图为概念示意,不直接映射具体源文件,故无图表来源]
### API调用流程图:枚举连接设备(概念示意)
```mermaid
flowchart TD
Start(["开始"]) --> Init["InitialDevLinkList()<br/>从数据库加载设备并初始化链表"]
Init --> List["GetOLDevList()/GetFLDevList()<br/>筛选在线/离线设备"]
List --> Show["展示设备列表<br/>UI层绑定数据"]
Show --> End(["结束"])
```
[本图为概念示意,不直接映射具体源文件,故无图表来源]
@@ -0,0 +1,322 @@
# 项目管理API
<cite>
**本文档引用的文件**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [ProManager.h](file://h/ProManager.h)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp)
- [Project.h](file://h/Project.h)
- [TestingZone.cpp](file://cpp/ProblemZone/TestingZone.cpp)
- [TestingZone.h](file://h/TestingZone.h)
- [DataMngStruct.cpp](file://cpp/ProblemZone/DataMngStruct.cpp)
- [DataMngStruct.h](file://h/DataMngStruct.h)
- [HandleProcessor.h](file://h/HandleProcessor.h)
- [opcreateprojectdlg.h](file://h/opcreateprojectdlg.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
本文档深入探讨了Geomative Studio项目中的项目管理API,重点分析了ProManager类如何管理项目创建、打开、保存及测区配置等操作。文档详细记录了每个公共方法的技术规范,包括参数约束、状态转换逻辑和持久化行为。通过使用示例展示了如何通过API创建新项目、添加测区、配置任务参数并保存到缓存或数据库。同时解释了Project类的数据结构设计及其与脚本和测试数据的关联关系,说明了在多项目环境下的资源管理策略,并提供了错误恢复和数据一致性保障的最佳实践。
## 项目结构
Geomative Studio项目采用分层架构设计,主要分为以下几个目录:
- **CACHE**: 存放项目和测区的XML缓存文件
- **DB**: 数据库相关文件和记录
- **Install**: 安装程序和配置文件
- **cpp**: C++源代码,包含Managers、ProblemZone等关键模块
- **h**: 头文件,定义了各类接口和数据结构
- **res**: 资源文件
项目管理功能主要集中在`cpp/Managers/ProManager.cpp``h/ProManager.h`中,通过ProManager类提供统一的项目管理接口。
```mermaid
graph TB
subgraph "核心管理模块"
ProManager[ProManager类]
Project[Project类]
TestingZone[TestingZone类]
end
subgraph "数据结构"
DataMngStruct[DataMngStruct基类]
HandleProcessor[HandleProcessor]
end
subgraph "持久化"
Database[(数据库)]
Cache[(缓存文件)]
end
ProManager --> Project
ProManager --> TestingZone
ProManager --> HandleProcessor
Project --> DataMngStruct
TestingZone --> DataMngStruct
ProManager --> Database
ProManager --> Cache
```
**图表来源**
- [ProManager.h](file://h/ProManager.h#L30-L74)
- [Project.h](file://h/Project.h#L16-L38)
- [TestingZone.h](file://h/TestingZone.h#L14-L30)
**本节来源**
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [Project.h](file://h/Project.h#L1-L41)
## 核心组件
项目管理API的核心组件包括ProManager类、Project类和TestingZone类。ProManager作为项目管理的中枢,负责协调项目和测区的创建、删除、同步等操作。Project类代表一个工程项目,包含项目的基本信息和属性。TestingZone类代表项目中的测区,包含测区的配置和参数。
ProManager类通过句柄系统管理项目和测区对象,使用HandleProcessor生成和解析句柄,确保对象的唯一性和可追溯性。所有项目和测区数据都持久化存储在数据库和XML缓存文件中,实现了数据的双重保障。
**本节来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L1-L81)
- [TestingZone.cpp](file://cpp/ProblemZone/TestingZone.cpp#L1-L81)
## 架构概述
项目管理API采用分层架构设计,分为接口层、业务逻辑层和数据持久层。接口层提供统一的API接口,业务逻辑层实现具体的项目管理功能,数据持久层负责数据的存储和读取。
```mermaid
classDiagram
class CProManager {
+ShowProList(dwDevHandle, proList) bool
+ShowTzList(dwProHandle, tzList) bool
+CreateProjectInDB(dwID) UINT
+CreateProjectInDev(dwID, pDev) UINT
+CreateTzInDB(dwID, dwPrID) UINT
+CreateTzInDev(dwID, pDev) UINT
+DeleteProjectInDB(dwID) void
+DeleteProjectInDev(szMac, szPrCN, pDev) UINT
+DeleteTzInDB(dwID) void
+DeleteTzInDev(szPrCN, szTzCN, pDev) UINT
+GetDMS(dwHandle) CDataMngStruct*
-m_handleProcessor CHandleProcessor
-m_dmsLinkList CLinkList<CDataMngStruct*>
-m_pConnection _ConnectionPtr
}
class CProject {
+ShowDetailInfo(proDetailList) bool
+CProject(dwID, pConnection)
+~CProject()
-m_dwID DWORD
-m_pConnection _ConnectionPtr
-m_szCN CString
-m_szPRname CString
-m_szDesc CString
-m_szLocation CString
-m_szPRdate CString
-m_szDuration CString
-m_szPS CString
-m_szCS CString
-m_szPM CString
-m_szQAS CString
-m_szStandard CString
}
class CTestingZone {
+ShowDetailInfo(tzDetailList) bool
+CTestingZone(dwID, pConnection)
+~CTestingZone()
-m_dwID DWORD
-m_pConnection _ConnectionPtr
-m_szTZname CString
-m_szCDate CString
-m_szDesc CString
-m_szLocation CString
-m_szCN CString
-m_szTZtype CString
}
class CDataMngStruct {
+ShowDetailInfo(dmsDetailList) virtual bool
+CDataMngStruct()
+~CDataMngStruct()
}
class CHandleProcessor {
+GenerateHandle(dwID, uStyle) DWORD
+GetIDFromHandle(dwHandle) DWORD
+GetStyleFromHandle(dwHandle) UINT
+AnalyseHandle(dwHandle, dwID, uStyle) void
}
CProManager --> CProject : "创建"
CProManager --> CTestingZone : "创建"
CProManager --> CHandleProcessor : "使用"
CProject --> CDataMngStruct : "继承"
CTestingZone --> CDataMngStruct : "继承"
```
**图表来源**
- [ProManager.h](file://h/ProManager.h#L30-L74)
- [Project.h](file://h/Project.h#L16-L38)
- [TestingZone.h](file://h/TestingZone.h#L14-L30)
- [DataMngStruct.h](file://h/DataMngStruct.h#L14-L20)
- [HandleProcessor.h](file://h/HandleProcessor.h#L12-L26)
## 详细组件分析
### ProManager类分析
ProManager类是项目管理的核心,负责协调项目和测区的生命周期管理。它通过数据库连接和设备通信,实现了项目数据的持久化和同步。
#### 项目创建流程
```mermaid
flowchart TD
Start([开始创建项目]) --> ShowDialog["显示项目创建对话框"]
ShowDialog --> ValidateInput["验证输入参数"]
ValidateInput --> CheckExistence["检查项目名称是否存在"]
CheckExistence --> |存在| ShowError["显示错误信息"]
CheckExistence --> |不存在| CreateInDB["在数据库创建项目"]
CreateInDB --> GenerateCN["生成项目编号"]
GenerateCN --> InsertDB["插入项目记录"]
InsertDB --> GetID["获取项目ID"]
GetID --> CreateDefaultTz["创建默认测区"]
CreateDefaultTz --> CreateInDev["在设备创建项目"]
CreateInDev --> CreateDir["创建项目目录"]
CreateDir --> GenerateXML["生成project.xml"]
GenerateXML --> SendToDevice["发送到设备"]
SendToDevice --> |成功| UpdateSync["更新同步状态"]
SendToDevice --> |失败| Rollback["回滚操作"]
UpdateSync --> End([项目创建完成])
Rollback --> End
ShowError --> End
```
**图表来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L341)
- [opcreateprojectdlg.h](file://h/opcreateprojectdlg.h#L15-L67)
#### 测区管理
ProManager类提供了完整的测区管理功能,包括测区的创建、删除和同步。测区数据既存储在数据库中,也以XML格式缓存在本地,确保数据的可靠性和可恢复性。
```mermaid
sequenceDiagram
participant UI as "用户界面"
participant ProManager as "ProManager"
participant Database as "数据库"
participant Device as "设备"
participant Cache as "缓存"
UI->>ProManager : 创建测区请求
ProManager->>Database : 检查测区是否存在
Database-->>ProManager : 返回检查结果
ProManager->>Database : 插入测区记录
Database-->>ProManager : 返回测区ID
ProManager->>Cache : 生成testzone.xml
Cache-->>ProManager : 返回文件路径
ProManager->>Device : 发送测区文件
Device-->>ProManager : 返回操作结果
ProManager->>Database : 更新同步状态
Database-->>ProManager : 返回结果
ProManager-->>UI : 返回操作结果
```
**图表来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L709-L943)
- [TestingZone.h](file://h/TestingZone.h#L14-L30)
**本节来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
- [ProManager.h](file://h/ProManager.h#L1-L77)
### Project类分析
Project类代表一个工程项目,封装了项目的所有属性和行为。它是项目管理的基本单元,通过数据库持久化存储项目信息。
#### 数据结构设计
```mermaid
classDiagram
class CProject {
-m_dwID DWORD
-m_pConnection _ConnectionPtr
-m_szCN CString
-m_szPRname CString
-m_szDesc CString
-m_szLocation CString
-m_szPRdate CString
-m_szDuration CString
-m_szPS CString
-m_szCS CString
-m_szPM CString
-m_szQAS CString
-m_szStandard CString
}
note right of CProject
项目数据结构包含 :
- 项目编号(CN)
- 项目名称(PRname)
- 描述(PRdesc)
- 位置(location)
- 创建日期(PRdate)
- 持续时间(duration)
- 项目负责人(PS)
- 客户(CS)
- 项目经理(PM)
- 质量保证(QAS)
- 标准(standard)
end note
```
**图表来源**
- [Project.h](file://h/Project.h#L16-L38)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L1-L81)
**本节来源**
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L1-L81)
- [Project.h](file://h/Project.h#L1-L41)
## 依赖分析
项目管理API的组件之间存在明确的依赖关系,形成了一个层次化的架构。
```mermaid
graph TD
ProManager[ProManager] --> Project[Project]
ProManager --> TestingZone[TestingZone]
ProManager --> HandleProcessor[HandleProcessor]
ProManager --> DataMngStruct[DataMngStruct]
Project --> DataMngStruct
TestingZone --> DataMngStruct
ProManager --> Database[(数据库)]
ProManager --> Cache[(缓存)]
Database --> SQLServer[SQL Server]
Cache --> XML[XML文件]
style ProManager fill:#f9f,stroke:#333
style Project fill:#bbf,stroke:#333
style TestingZone fill:#bbf,stroke:#333
style HandleProcessor fill:#f96,stroke:#333
style DataMngStruct fill:#9f9,stroke:#333
```
**图表来源**
- [ProManager.h](file://h/ProManager.h#L8-L13)
- [Project.h](file://h/Project.h#L15-L16)
- [TestingZone.h](file://h/TestingZone.h#L12-L13)
## 性能考虑
项目管理API在设计时考虑了性能优化,主要体现在以下几个方面:
- 使用句柄系统快速定位项目和测区对象
- 采用缓存机制减少数据库访问频率
- 批量操作减少设备通信次数
- 异步处理耗时操作,提高响应速度
在多项目环境下,建议合理规划项目结构,避免单个项目包含过多测区,以保持系统性能。
## 故障排除指南
当项目管理操作失败时,可以按照以下步骤进行排查:
1. 检查数据库连接是否正常
2. 验证设备通信是否畅通
3. 确认缓存目录权限是否正确
4. 检查XML文件格式是否正确
5. 查看日志文件获取详细错误信息
对于数据一致性问题,系统提供了回滚机制,在操作失败时自动回滚到之前的状态,确保数据完整性。
**本节来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1014-L1208)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1892-L1994)
## 结论
项目管理API通过ProManager类提供了一套完整的项目生命周期管理功能,实现了项目和测区的创建、删除、同步等操作。系统采用分层架构设计,具有良好的可维护性和扩展性。通过数据库和缓存的双重持久化机制,确保了数据的可靠性和一致性。在多项目环境下,系统能够有效管理资源,提供稳定的项目管理服务。
@@ -0,0 +1,499 @@
# 安装与配置
<cite>
**本文档中引用的文件**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi)
- [config.ini](file://Install/Geomative Studio/config.ini)
- [database_modify.xml](file://Install/Geomative Studio/database_modify.xml)
- [version_info.txt](file://Install/Geomative Studio/version_info.txt)
- [数据库字段修改记录.txt](file://DB/数据库字段修改记录.txt)
- [IAP-GD10.bat](file://Install/Geomative Studio/tools/IAP-GD10.bat)
- [License-GD10.bat](file://Install/Geomative Studio/tools/License-GD10.bat)
- [project.xml](file://CACHE/project.xml)
- [testzone.xml](file://CACHE/testzone.xml)
</cite>
## 目录
1. [系统安装要求](#系统安装要求)
2. [安装程序工作流程](#安装程序工作流程)
3. [安装目录结构](#安装目录结构)
4. [配置文件详解](#配置文件详解)
5. [数据库字段修改记录](#数据库字段修改记录)
6. [配置最佳实践](#配置最佳实践)
7. [常见配置错误排查](#常见配置错误排查)
8. [不同部署环境的配置差异](#不同部署环境的配置差异)
## 系统安装要求
Geomative Studio 应用程序的安装需要满足以下系统要求:
- **操作系统**Windows 7/8/1032位或64位)
- **权限要求**:必须以管理员身份运行安装程序,NSIS安装脚本通过`RequestExecutionLevel admin`指令确保安装过程具有管理员权限
- **运行时依赖**:安装程序会自动安装Visual C++ Redistributablevc_redist.x64.exe和vc_redist.x86.exe),这是应用程序运行所必需的C++运行时库
- **数据库引擎**:安装程序包含AccessDatabaseEngine.exe,用于支持Access数据库文件(.accdb)的连接和操作
- **硬件驱动**:安装程序包含GD-10设备的驱动程序,包括STM32 USB CDC驱动(stmcdc.inf)和ST Tube驱动(STtube.inf),支持x86和x64架构
**Section sources**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L45)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L81-L84)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L142)
## 安装程序工作流程
Geomative Studio的安装程序使用NSISNullsoft Scriptable Install System)创建,其工作流程如下:
1. **初始化阶段**
- 设置产品信息(名称、版本、发布者、网站)
- 配置安装向导界面,包括欢迎页、安装进度页和完成页
- 设置安装目录为`$PROGRAMFILES\GeomativeStudio`
- 验证管理员权限,通过`EnsureAdminRights`宏检查当前用户是否具有管理员权限
2. **安装阶段**
- **主程序安装**Geomative Studio部分):复制主程序文件、配置文件、数据库文件、日志文件和工具脚本到安装目录
- **驱动程序安装**Drivers部分):复制x86和x64架构的设备驱动程序到相应目录
- **数据库连接引擎安装**DBConntEngine部分):复制Access数据库引擎安装程序
- **管理员权限配置**(ASADMIN部分):在注册表中设置应用程序以管理员身份运行
3. **条件执行阶段**
- 根据系统架构(x64或x86)执行相应的Visual C++ Redistributable安装程序
- 根据系统架构执行相应的驱动程序安装程序(dpinst_amd64.exe用于64位系统,dpinst_x86.exe用于32位系统)
- 执行Access数据库引擎安装程序
4. **安装后处理阶段**
- 创建卸载程序(uninst.exe
- 在注册表中写入安装信息,包括程序路径和卸载信息
- 创建开始菜单快捷方式和桌面快捷方式
5. **卸载流程**
- 删除所有安装的文件和目录
- 删除开始菜单和桌面快捷方式
- 删除注册表中的安装信息
- 清理安装目录
```mermaid
flowchart TD
A[开始安装] --> B[验证管理员权限]
B --> C[安装主程序文件]
C --> D[安装驱动程序]
D --> E[安装数据库引擎]
E --> F[配置管理员运行权限]
F --> G[根据系统架构安装VC++运行库]
G --> H[根据系统架构安装驱动]
H --> I[安装数据库连接引擎]
I --> J[创建快捷方式]
J --> K[写入注册表信息]
K --> L[完成安装]
```
**Diagram sources**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L58-L61)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L69-L287)
**Section sources**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L1-L287)
## 安装目录结构
安装完成后,Geomative Studio的目录结构如下:
```
GeomativeStudio/
├── DB/ # 数据库目录
│ └── GeoMativeDB.accdb # 主数据库文件
├── LOG/ # 日志目录
│ ├── commLog.txt # 通信日志
│ ├── detect_gd20_log.txt # GD20设备检测日志
│ ├── device_log.txt # 设备日志
│ ├── td_time_error.txt # 时间错误日志
│ ├── update_db.log # 数据库更新日志
│ └── general/ # 通用日志目录
│ └── 20211110.txt # 日期日志文件
├── tools/ # 工具目录
│ └── dfu_tools/ # DFU工具目录
│ ├── COPYING.txt # 版权信息
│ ├── dfu-suffix.exe # DFU后缀工具
│ ├── dfu-util.exe # DFU实用工具
│ ├── DfuCreateFile.exe # DFU文件创建工具
│ ├── STDFU.dll # ST DFU动态链接库
│ ├── STDFUFiles.dll # ST DFU文件处理库
│ ├── STDFUPRT.dll # ST DFU协议库
│ └── STTubeDevice30.dll # ST Tube设备库
├── x64/ # 64位驱动目录
│ ├── dpinst.xml # 驱动安装配置
│ ├── dpinst_amd64.exe # 64位驱动安装程序
│ ├── mdmcpq.inf # MDM/COM端口驱动信息
│ ├── stmcdc.cat # STM32 CDC驱动证书
│ ├── stmcdc.inf # STM32 CDC驱动信息
│ ├── stmcdc_g.inf # STM32 CDC通用驱动信息
│ ├── STTub30.sys # ST Tube驱动系统文件
│ ├── sttube.cat # ST Tube驱动证书
│ ├── STtube.inf # ST Tube驱动信息
│ └── usbser.sys # USB串行驱动系统文件
├── x86/ # 32位驱动目录
│ ├── dpinst.xml # 驱动安装配置
│ ├── dpinst_x86.exe # 32位驱动安装程序
│ ├── mdmcpq.inf # MDM/COM端口驱动信息
│ ├── stmcdc.cat # STM32 CDC驱动证书
│ ├── stmcdc.inf # STM32 CDC驱动信息
│ ├── stmcdc_g.inf # STM32 CDC通用驱动信息
│ ├── STTub30.sys # ST Tube驱动系统文件
│ ├── sttube.cat # ST Tube驱动证书
│ ├── STtube.inf # ST Tube驱动信息
│ └── usbser.sys # USB串行驱动系统文件
├── config.ini # 主配置文件
├── database_modify.xml # 数据库修改记录文件
├── Geomative Studio.exe # 主程序可执行文件
├── msado15.dll # ADO数据对象库
├── msadox.dll # ADOX扩展库
├── upGeoMative.exe # 更新程序
├── vc_redist.x64.exe # 64位VC++运行库安装程序
├── vc_redist.x86.exe # 32位VC++运行库安装程序
└── version_info.txt # 版本信息文件
```
**Section sources**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L71-L108)
## 配置文件详解
### config.ini 文件结构
`config.ini`是Geomative Studio的主要配置文件,采用INI文件格式,包含多个节区(section),每个节区包含特定功能的配置参数。
#### [UI] 节区
用户界面相关配置:
- **Language**:语言设置,1表示中文,2表示英文。此参数控制应用程序的界面语言。
#### [TRANSFER_INFO] 节区
数据传输和网络相关配置:
- **Url**:软件更新服务器地址,用于检查和下载新版本。
- **HomePage**:公司主页URL,可能在应用程序中作为链接提供。
#### [EXPORT_DATA] 节区
数据导出相关配置:
- **2dDataExpStyle**2D数据导出样式,0表示默认导出格式。
#### [ONLINE_DEVICE] 节区
在线设备连接配置:
- **RemoteType**:远程设备类型,2表示特定类型的在线设备。
- **IP**:远程设备的IP地址。
- **Port**:远程设备的通信端口。
#### [ONLINE_VERSION] 节区
在线版本配置:
- **VERID**:版本ID,用于标识当前版本系列。
#### [ONLINE_TIMEZONE] 节区
时区配置:
- **TIMEZONE**:时区设置,8表示UTC+8(中国标准时间)。
#### [CHANNEL_INFO] 节区
通道信息配置:
- **IsMultiChannel**:是否多通道模式,0表示单通道,1表示多通道。
#### [USER_INFO] 和 [USER_Pwd] 节区
用户认证配置:
- **UserID**:用户ID,用于身份验证。
- **UserPwd**:用户密码,明文存储(存在安全风险)。
- **UserSave**:是否保存用户信息,1表示保存,0表示不保存。
- **UserAuto**:是否自动登录,1表示自动登录,0表示需要手动登录。
#### [CROSS_HOLE_CFG] 和 [CFG_X] 节区
跨孔测量配置:
- **Number**:跨孔配置数量,6表示有6种预设配置。
- **[CFG_X]**:具体的跨孔配置,每个配置包含:
- **Name**:配置名称
- **FirstPoleDep**:第一电极深度
- **HoleSpace**:孔间距
#### [PLC_SET] 节区
PLC(可编程逻辑控制器)设置:
- **TimeInterval**:时间间隔,600表示600秒(10分钟)的间隔。
```mermaid
classDiagram
class ConfigFile {
+string filePath
+readConfig()
+writeConfig()
+validateConfig()
}
class Section {
+string name
+Map~string,string~ parameters
+getParameter(key)
+setParameter(key, value)
}
class UISection {
+int language
+setLanguage(lang)
+getLanguage()
}
class TransferInfoSection {
+string url
+string homePage
+setUrl(url)
+setHomePage(homePage)
}
class UserInfoSection {
+string userId
+string userPwd
+int userSave
+int userAuto
+setUserId(id)
+setUserPwd(pwd)
+setUserSave(save)
+setUserAuto(auto)
}
class OnlineDeviceSection {
+int remoteType
+string ip
+int port
+setRemoteType(type)
+setIp(ip)
+setPort(port)
}
class CrossHoleConfig {
+int number
+CFGSection[] cfgList
+setNumber(num)
+addCFG(cfg)
}
class CFGSection {
+string name
+double firstPoleDep
+double holeSpace
+setName(name)
+setFirstPoleDep(dep)
+setHoleSpace(space)
}
class PLCSetSection {
+int timeInterval
+setTimeInterval(interval)
+getTimeInterval()
}
ConfigFile --> Section : "contains"
Section <|-- UISection
Section <|-- TransferInfoSection
Section <|-- UserInfoSection
Section <|-- OnlineDeviceSection
Section <|-- CrossHoleConfig
Section <|-- PLCSetSection
CrossHoleConfig --> CFGSection : "contains"
```
**Diagram sources**
- [config.ini](file://Install/Geomative Studio/config.ini#L1-L73)
**Section sources**
- [config.ini](file://Install/Geomative Studio/config.ini#L1-L73)
- [config.ini](file://config.ini#L1-L73)
## 数据库字段修改记录
### database_modify.xml 文件
`database_modify.xml`文件用于记录数据库结构的修改历史,采用XML格式,包含当前版本和预版本的修改信息。
文件结构:
- **current_version**:当前数据库版本号
- **pre_version**:前一个版本号
- **table**:表修改信息,包含:
- **name**:表名
- **modify_type**:修改类型,1表示增加表,2表示删除表,3表示修改表
- **column**:列信息,包含:
- **name**:列名
- **is_primary_key**:是否为主键
- **value_type**:值类型(1:text, 2:Memo, 3:Byte, 4:Integer, 5:Long, 6:Single, 7:Double, 8:Currency, 9:AutoNumber, 10:Date/Time, 11:Yes/No等)
- **attribute_value**:属性值(对于文本类型表示长度)
- **index**:索引类型(0:无索引, 1:允许重复, 2:不允许重复)
- **is_empty**:是否允许为空
- **is_compress**:是否压缩
### 数据库字段修改记录.txt 文件
`数据库字段修改记录.txt`文件以文本形式记录了数据库字段的修改历史,按日期组织,便于追溯数据库结构的演变。
主要修改记录包括:
- **2015年6月2日**:在td2dcon、td3dcon、td1dcon表中增加bUse字段,用于标记记录的有效性
- **2015年6月12日**:在Td表中增加rect和rect_loc字段,用于记录测试任务的矩形大小和位置;修改Espace和Edistance字段为文本类型
- **2016年12月2日**:在ac、acds、td_spc_attr表中增加10个时窗信息;在cm表中增加发射频率0.03125Hz
- **2017年9月12日**:在project和tz表中加入默认的工程和测区
- **2017年9月18日**:在device表中增加GPS字段;在desetting表中增加CableType、LowPowerAlarm、AutoStack字段
- **2017年9月23日**:新增task_timer表
- **2017年9月25日**:在td表中添加IsAutoGenerate字段
- **2017年9月26日**:在timer_task表中增加PLCID字段
- **2019年7月4日**medium表新增加强梯度装置
- **2019年7月14日**medium表新增跨孔装置;新增TTaskInfoCoordinetes表用于保存电极坐标信息
- **2019年12月3日**:新增TPictureInfo表存放图片信息;新增TPictureCode表存放岩性编辑图片二维码
- **2019年12月11日**:新增TLoggingTaskInfo表存放新建任务信息;新增TLoggingTaskContent表保存测点数据信息
```mermaid
erDiagram
table "test" {
id AutoNumber PK
col1 Memo
col2 Long
col3 Long
}
table "td2dcon" {
bUse Yes/No
}
table "td3dcon" {
bUse Yes/No
}
table "td1dcon" {
bUse Yes/No
}
table "Td" {
rect Text
rect_loc Text
SkipCable Yes/No
IsAutoGenerate Yes/No
}
table "device" {
GPS Text
}
table "desetting" {
CableType Text
LowPowerAlarm Yes/No
AutoStack Yes/No
}
table "task_timer" {
PLCID Text
}
table "medium" {
加强梯度装置 Yes/No
跨孔装置 Yes/No
}
table "TTaskInfoCoordinetes" {
电极坐标信息 Text
}
table "TPictureInfo" {
图片信息 Text
}
table "TPictureCode" {
二维码信息 Text
}
table "TLoggingTaskInfo" {
测试任务信息 Text
}
table "TLoggingTaskContent" {
测点数据信息 Text
}
```
**Diagram sources**
- [database_modify.xml](file://Install/Geomative Studio/database_modify.xml#L1-L25)
- [数据库字段修改记录.txt](file://DB/数据库字段修改记录.txt#L1-L61)
**Section sources**
- [database_modify.xml](file://Install/Geomative Studio/database_modify.xml#L1-L25)
- [database_modify.xml](file://database_modify.xml#L1-L25)
- [数据库字段修改记录.txt](file://DB/数据库字段修改记录.txt#L1-L61)
## 配置最佳实践
### 安全配置
1. **用户凭证保护**:避免在config.ini中明文存储密码,建议使用加密存储或外部认证机制。
2. **管理员权限**:确保安装程序以管理员身份运行,但运行时尽量使用普通用户权限。
3. **驱动程序签名**:确保所有驱动程序都经过数字签名,防止恶意驱动安装。
### 性能优化
1. **日志管理**:定期清理LOG目录中的日志文件,避免占用过多磁盘空间。
2. **数据库维护**:定期备份GeoMativeDB.accdb数据库文件,防止数据丢失。
3. **缓存配置**:合理配置CACHE目录中的project.xml和testzone.xml文件,提高项目加载速度。
### 网络配置
1. **服务器地址**:确保TRANSFER_INFO节区中的URL和IP地址正确,能够访问更新服务器和远程设备。
2. **端口配置**:确保ONLINE_DEVICE节区中的端口未被其他应用程序占用。
3. **时区设置**:根据实际地理位置正确设置ONLINE_TIMEZONE节区的TIMEZONE值。
### 跨孔测量配置
1. **预设配置**:根据实际测量需求,在CFG_X节区中配置常用的跨孔测量参数。
2. **参数验证**:确保HoleSpace(孔间距)和FirstPoleDep(第一电极深度)的值在合理范围内。
3. **配置管理**:通过CROSS_HOLE_CFG节区的Number参数控制预设配置的数量。
## 常见配置错误排查
### 安装问题
1. **权限不足**:如果安装失败,确保以管理员身份运行安装程序。NSIS脚本会检查管理员权限并提示用户。
2. **驱动安装失败**:检查系统是否禁用了驱动程序安装,或尝试手动运行dpinst_amd64.exe或dpinst_x86.exe。
3. **VC++运行库缺失**:如果程序无法启动,尝试手动安装vc_redist.x64.exe或vc_redist.x86.exe。
### 配置文件问题
1. **config.ini格式错误**:确保文件使用正确的INI格式,节区用方括号包围,参数用等号分隔。
2. **参数值错误**:检查关键参数如IP地址、端口号、用户ID等是否正确。
3. **文件编码问题**:确保config.ini文件保存为ANSI或UTF-8编码,避免中文乱码。
### 数据库问题
1. **数据库连接失败**:确保AccessDatabaseEngine.exe已正确安装,且GeoMativeDB.accdb文件存在。
2. **字段缺失**:如果程序报错字段不存在,检查数据库字段修改记录,确保数据库结构与程序版本匹配。
3. **权限问题**:确保程序对DB目录有读写权限。
### 网络连接问题
1. **无法连接远程设备**:检查IP地址和端口是否正确,防火墙是否阻止了连接。
2. **更新失败**:检查Url参数是否正确,网络连接是否正常。
3. **时区错误**:如果时间显示不正确,检查TIMEZONE参数是否与本地时区匹配。
## 不同部署环境的配置差异
### 开发环境
- **配置特点**
- 启用详细日志记录,便于调试
- 使用测试服务器地址和端口
- 用户认证可能简化或禁用
- 数据库使用测试数据
- **示例配置**
```ini
[TRANSFER_INFO]
Url=http://dev.update.geomative.com/Upgrade/software/V2.0
[ONLINE_DEVICE]
IP=192.168.1.100
Port=8769
[USER_INFO]
UserID=testuser
UserPwd=test123
```
### 测试环境
- **配置特点**
- 使用预生产服务器
- 启用部分日志记录
- 使用测试用户账户
- 数据库使用模拟生产数据
- **示例配置**
```ini
[TRANSFER_INFO]
Url=http://test.update.geomative.com/Upgrade/software/V2.0
[ONLINE_DEVICE]
IP=10.0.1.50
Port=8769
[USER_INFO]
UserID=testuser
UserPwd=securepassword
```
### 生产环境
- **配置特点**
- 最小化日志记录,仅记录关键信息
- 使用正式服务器地址和端口
- 严格的安全认证
- 数据库使用真实生产数据
- 定期备份和监控
- **示例配置**
```ini
[TRANSFER_INFO]
Url=http://update.geomative.com/Upgrade/software/V2.0
[ONLINE_DEVICE]
IP=120.31.131.181
Port=8769
[USER_INFO]
UserID=productionuser
UserPwd=strongpassword123!
```
**Section sources**
- [config.ini](file://Install/Geomative Studio/config.ini#L1-L73)
- [config.ini](file://config.ini#L1-L73)
- [config.ini](file://Release/config.ini#L1-L3)
@@ -0,0 +1,237 @@
# 安装指南
<cite>
**本文引用的文件**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi)
- [dpinst.xmlx64](file://Install/GD_10/x64/dpinst.xml)
- [dpinst.xmlx86](file://Install/GD_10/x86/dpinst.xml)
- [config.ini](file://Install/Geomative Studio/config.ini)
- [version_info.txt](file://Install/Geomative Studio/version_info.txt)
- [Setup.rul](file://Install/test1/Script Files/Setup.rul)
- [Setup.map](file://Install/test1/Script Files/Setup.map)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构与安装包组成](#项目结构与安装包组成)
3. [系统要求与兼容性](#系统要求与兼容性)
4. [安装流程总览](#安装流程总览)
5. [NSIS安装脚本工作机制](#nsis安装脚本工作机制)
6. [组件部署顺序与职责](#组件部署顺序与职责)
7. [驱动程序安装与dpinst.xml作用](#驱动程序安装与dpinstxml作用)
8. [卸载程序生成与注册表清理](#卸载程序生成与注册表清理)
9. [安装失败常见原因与排查](#安装失败常见原因与排查)
10. [故障排除清单](#故障排除清单)
11. [结论](#结论)
## 简介
本指南面向GeomativeStudio桌面应用的完整安装流程,基于仓库中的NSIS安装脚本与配套驱动、运行库、数据库引擎等组件,系统化说明安装目录设置、注册表写入、快捷方式创建、卸载程序生成,以及驱动安装时dpinst.xml的自动安装逻辑。同时提供安装前权限要求、兼容性配置(RUNASADMIN)说明,并结合实际脚本代码路径,帮助用户与运维人员快速定位问题并完成安装。
## 项目结构与安装包组成
- 安装脚本与组件:
- NSIS安装脚本:Install/geomativestudio.nsi
- GD10设备驱动(x64/x86):Install/GD_10/x64、Install/GD_10/x86
- 运行库:vc_redist.x64.exe、vc_redist.x86.exe
- Access数据库引擎:AccessDatabaseEngine.exe
- 应用程序与工具:Geomative Studio.exe、tools、LOG、DB等
- 兼容性配置:RUNASADMIN注册表项(脚本中预留)
```mermaid
graph TB
A["安装包根目录<br/>Install/"] --> B["NSIS脚本<br/>geomativestudio.nsi"]
A --> C["GD10驱动(x64/x86)"]
A --> D["VC++运行库<br/>vc_redist.x64.exe / vc_redist.x86.exe"]
A --> E["Access数据库引擎<br/>AccessDatabaseEngine.exe"]
A --> F["应用与工具<br/>Geomative Studio.exe / tools / LOG / DB"]
C --> C1["x64/dpinst.xml"]
C --> C2["x86/dpinst.xml"]
```
**图表来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L70-L110)
- [dpinst.xmlx64](file://Install/GD_10/x64/dpinst.xml#L1-L19)
- [dpinst.xmlx86](file://Install/GD_10/x86/dpinst.xml#L1-L19)
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L70-L110)
## 系统要求与兼容性
- 权限要求:安装需以管理员身份运行,脚本在初始化阶段强制校验管理员权限。
- 兼容性配置:脚本中包含为可执行文件写入“以管理员身份运行”的注册表项(RUNASADMIN),便于后续兼容旧版系统行为。
- 架构匹配:根据系统位数选择对应组件(x64或x86),确保驱动与运行库正确安装。
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L41-L61)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L145-L149)
## 安装流程总览
安装流程由NSIS脚本控制,按以下顺序执行:
1. 初始化与权限检查(管理员)
2. 部署核心程序与工具
3. 部署VC++运行库(按系统架构选择)
4. 部署Access数据库引擎
5. 部署GD10驱动(按系统架构选择)
6. 写入注册表与卸载程序
7. 创建开始菜单与桌面快捷方式
```mermaid
sequenceDiagram
participant U as "用户"
participant NSIS as "NSIS安装器"
participant OS as "操作系统/注册表/文件系统"
U->>NSIS : 运行安装包
NSIS->>NSIS : 初始化并请求管理员权限
NSIS->>OS : 写入安装目录与注册表
NSIS->>OS : 部署核心程序与工具
NSIS->>OS : 部署VC++运行库x64/x86
NSIS->>OS : 部署Access数据库引擎
NSIS->>OS : 部署GD10驱动x64/x86
NSIS->>OS : 写入卸载程序与注册表项
NSIS->>OS : 创建开始菜单/桌面快捷方式
NSIS-->>U : 安装完成
```
**图表来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L58-L61)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L70-L110)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L111-L138)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L140-L143)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L163)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L166-L175)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L191-L200)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L185-L189)
## NSIS安装脚本工作机制
- 安装目录与注册表:
- 默认安装目录为Program Files下的GeomativeStudio。
- 写入应用路径到注册表,供卸载程序与开始菜单使用。
- 快捷方式:
- 在开始菜单创建程序组,并在开始菜单与桌面创建快捷方式。
- 卸载程序:
- 生成uninst.exe并在注册表中登记卸载字符串,支持卸载流程。
- 权限与兼容性:
- 请求管理员权限;可选写入RUNASADMIN注册表项以提升兼容性。
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L41-L45)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L58-L61)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L185-L189)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L191-L200)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L145-L149)
## 组件部署顺序与职责
- 核心程序与工具(Section “Geomative Studio” SEC01):
- 配置文件、数据库、日志、工具集、版本信息等。
- 驱动程序(Section “Drivers” SEC02):
- x64与x86目录下分别包含dpinst.xml、驱动INF与SYS文件。
- 数据库引擎(Section “DBConntEngine” SEC03):
- AccessDatabaseEngine.exe用于支持Access数据库连接。
- 兼容性配置(Section “ASADMIN” SEC04):
- 可选写入RUNASADMIN注册表项,提升兼容性。
- 运行库安装(Section 自定义段):
- 按系统架构选择vc_redist.x64.exe或vc_redist.x86.exe执行安装。
- 驱动安装(Section 自定义段):
- 调用dpinst_amd64.exe或dpinst_x86.exe,指定安装路径参数。
- 卸载清理(Section 卸载):
- 删除所有已安装文件与注册表项,清理开始菜单与桌面快捷方式。
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L70-L110)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L111-L138)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L140-L143)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L145-L149)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L163)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L166-L175)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L213-L287)
## 驱动程序安装与dpinst.xml作用
- dpinst.xml的作用:
- 提供驱动安装向导的标题、欢迎语、安装头信息与完成提示。
- 启用静默安装模式与严格静默策略,减少交互,便于自动化安装。
- 自动安装逻辑:
- NSIS在安装阶段调用dpinst_amd64.exex64)或dpinst_x86.exex86),并传入安装路径参数,使驱动INF/SYS文件被自动安装。
- 该逻辑通过NSIS内置命令实现,无需用户手动确认。
```mermaid
flowchart TD
Start(["开始"]) --> DetectArch["检测系统架构"]
DetectArch --> IsX64{"是否x64系统?"}
IsX64 --> |是| RunDpinst64["执行dpinst_amd64.exe<br/>参数:/PATH \"$INSTDIR\\x64\""]
IsX64 --> |否| RunDpinst32["执行dpinst_x86.exe<br/>参数:/PATH \"$INSTDIR\\x86\""]
RunDpinst64 --> ReadXml64["读取x64/dpinst.xml<br/>启用静默安装策略"]
RunDpinst32 --> ReadXml32["读取x86/dpinst.xml<br/>启用静默安装策略"]
ReadXml64 --> InstallDrv64["安装驱动INF/SYSx64"]
ReadXml32 --> InstallDrv32["安装驱动INF/SYSx86"]
InstallDrv64 --> End(["结束"])
InstallDrv32 --> End
```
**图表来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L163)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L166-L175)
- [dpinst.xmlx64](file://Install/GD_10/x64/dpinst.xml#L1-L19)
- [dpinst.xmlx86](file://Install/GD_10/x86/dpinst.xml#L1-L19)
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L175)
- [dpinst.xmlx64](file://Install/GD_10/x64/dpinst.xml#L1-L19)
- [dpinst.xmlx86](file://Install/GD_10/x86/dpinst.xml#L1-L19)
## 卸载程序生成与注册表清理
- 卸载程序生成:
- 在Post阶段生成uninst.exe并写入注册表卸载字符串。
- 注册表清理:
- 卸载时删除应用路径、卸载字符串、显示图标、显示版本、发布者等注册表项。
- 文件与快捷方式清理:
- 删除所有安装文件、开始菜单与桌面快捷方式、空目录。
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L191-L200)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L213-L287)
## 安装失败常见原因与排查
- 权限不足(需要管理员):
- 现象:安装启动即退出或提示无权限。
- 排查:右键安装包选择“以管理员身份运行”,确保当前用户属于管理员组。
- 参考:脚本在初始化阶段强制校验管理员权限。
- 系统架构不匹配:
- 现象:VC++运行库或驱动安装失败。
- 排查:确认目标系统为x64或x86,确保对应组件被正确选择与安装。
- 参考:脚本根据系统架构选择vc_redist与dpinst。
- 依赖组件安装失败:
- 现象:Access数据库引擎或VC++运行库安装中断。
- 排查:检查系统是否已安装更高版本的运行库或数据库引擎;必要时先卸载冲突版本再重试。
- 目标路径无写权限:
- 现象:安装到Program Files失败或部分文件无法写入。
- 排查:确保安装目录对当前用户具有完全控制权;避免安装到受保护路径。
- 驱动安装异常:
- 现象:驱动安装界面弹出或失败。
- 排查:确认dpinst.xml静默策略生效;检查系统安全策略(如驱动签名);必要时临时关闭驱动强制签名限制(仅限测试环境)。
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L41-L61)
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L175)
- [Setup.rul](file://Install/test1/Script Files/Setup.rul#L232-L248)
## 故障排除清单
- 安装前准备
- 以管理员身份运行安装包
- 关闭杀毒软件或防火墙(临时)
- 确认磁盘空间充足
- 安装中关注
- VC++运行库安装是否出现静默窗口
- Access数据库引擎是否静默完成
- 驱动安装是否静默完成(dpinst.xml启用quietInstallStrict
- 安装后验证
- 开始菜单与桌面快捷方式是否存在
- 应用程序可正常启动
- 数据库连接功能可用
- 设备驱动已加载(可在设备管理器查看)
**章节来源**
- [geomativestudio.nsi](file://Install/geomativestudio.nsi#L158-L175)
- [dpinst.xmlx64](file://Install/GD_10/x64/dpinst.xml#L1-L19)
- [dpinst.xmlx86](file://Install/GD_10/x86/dpinst.xml#L1-L19)
## 结论
本指南基于仓库中的NSIS安装脚本与驱动、运行库、数据库引擎等组件,给出了完整的安装流程说明与排障建议。通过理解安装目录设置、注册表写入、快捷方式创建、卸载程序生成以及驱动安装的自动逻辑,用户与运维人员可以更高效地完成GeomativeStudio的部署与维护工作。遇到问题时,优先检查管理员权限、系统架构匹配与依赖组件状态,通常可快速定位并解决问题。
@@ -0,0 +1,254 @@
# 应用配置详解
<cite>
**本文档引用的文件**
- [config.ini](file://config.ini)
- [Install/Geomative Studio/config.ini](file://Install/Geomative Studio/config.ini)
- [Release/config.ini](file://Release/config.ini)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
- [cpp/Views/DailLogin.cpp](file://cpp/Views/DailLogin.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [配置文件结构](#配置文件结构)
3. [核心配置节分析](#核心配置节分析)
4. [多环境配置示例](#多环境配置示例)
5. [配置加载优先级](#配置加载优先级)
6. [配置维护最佳实践](#配置维护最佳实践)
7. [结论](#结论)
## 简介
GeomativeStudio是一款专业的地球物理数据处理软件,其行为和功能通过`config.ini`配置文件进行控制。本文档深入解析该配置文件的结构与各项参数的具体含义,涵盖用户界面、数据传输、设备连接、用户认证及跨孔测量等关键配置。通过分析代码实现,我们将揭示配置参数如何影响系统行为,并提供实际应用场景下的配置建议。
## 配置文件结构
GeomativeStudio的`config.ini`文件采用标准的INI文件格式,由多个节(section)组成,每个节包含若干键值对(key-value pairs)。配置文件定义了软件的用户界面语言、数据传输设置、导出格式、远程设备连接信息、用户认证凭据以及跨孔测量参数等核心功能。
```mermaid
flowchart TD
A[config.ini] --> B[UI]
A --> C[TRANSFER_INFO]
A --> D[EXPORT_DATA]
A --> E[ONLINE_DEVICE]
A --> F[USER_INFO]
A --> G[CROSS_HOLE_CFG]
B --> B1[Language]
C --> C1[Url]
C --> C2[HomePage]
D --> D1[2dDataExpStyle]
E --> E1[RemoteType]
E --> E2[IP]
E --> E3[Port]
F --> F1[UserID]
F --> F2[UserPwd]
G --> G1[Number]
G --> G2[CFG_1...CFG_N]
```
**Diagram sources**
- [config.ini](file://config.ini)
## 核心配置节分析
### [UI] 节:界面语言控制
`[UI]`节中的`Language`参数控制软件界面的显示语言。
- **Language=1**:表示界面语言为中文
- **Language=2**:表示界面语言为英文
该参数在软件启动时被读取,并通过`WM_CHANGE_LANGUAGE`消息通知主窗口切换语言。代码实现中,系统会根据当前线程的区域设置(LCID)和配置值共同决定最终的语言显示。
```cpp
int iLanguage = GetPrivateProfileInt(_T("UI"), _T("Language"), 0, _T("./config.ini"));
if (LANG_ZHCN == iLanguage && (LANG_CHINESE_SIMPLIFIED == PRIMARYLANGID(LANGIDFROMLCID(lcidNew))))
{
WritePrivateProfileString(_T("UI"), _T("Language"), _T("1"), _T("./config.ini"));
m_pMainWnd->SendMessage(WM_CHANGE_LANGUAGE, (WPARAM)LANG_ZHCN, 0);
}
else
{
WritePrivateProfileString(_T("UI"), _T("Language"), _T("2"), _T("./config.ini"));
m_pMainWnd->SendMessage(WM_CHANGE_LANGUAGE, (WPARAM)LANG_ENUS, 0);
}
```
**Section sources**
- [config.ini](file://config.ini#L1-L3)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L446-L458)
### [TRANSFER_INFO] 节:数据传输与系统更新
`[TRANSFER_INFO]`节定义了软件的数据传输和系统更新相关URL。
- **Url**:系统更新服务器地址,用于检查和下载软件更新
- **HomePage**:公司主页URL,可能用于用户引导或信息展示
这些参数在`GetCfgUrl()`函数中被读取,如果获取失败,系统会弹出错误提示。该配置支持软件的在线升级功能,确保用户能够及时获取最新版本。
```cpp
::GetPrivateProfileString(_T("TRANSFER_INFO"), _T("Url"),_T("NULL"), chUrl, 150,_T("./config.ini"));
::GetPrivateProfileString(_T("TRANSFER_INFO"), _T("HomePage"),_T("NULL"), chHomePage, 150,_T("./config.ini"));
```
**Section sources**
- [config.ini](file://config.ini#L4-L7)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1116-L1135)
### [EXPORT_DATA] 节:导出数据格式
`[EXPORT_DATA]`节中的`2dDataExpStyle`参数控制二维数据的导出格式。
- **2dDataExpStyle=0**:表示一种特定的二维数据导出样式
该参数在软件初始化时被读取,影响数据导出模块的行为。不同的值可能对应不同的文件格式、坐标系统或数据组织方式。
```cpp
m_i2dDataExpStyle = GetPrivateProfileInt(_T("EXPORT_DATA"), _T("2dDataExpStyle"), 0, _T("./config.ini"));
```
**Section sources**
- [config.ini](file://config.ini#L8-L10)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L624)
### [ONLINE_DEVICE] 节:远程设备连接
`[ONLINE_DEVICE]`节配置了与远程设备的网络连接参数。
- **RemoteType**:传输模式,如USB、云端或WiFi
- **IP**:远程服务器或设备的IP地址
- **Port**:通信端口号
在软件启动时,如果选择云端或WiFi模式,系统会读取这些参数并尝试建立网络连接。如果IP或端口配置错误,会弹出相应的错误提示。
```cpp
char chIP[30]="NULL";
GetPrivateProfileString(_T("ONLINE_DEVICE"),_T("IP"), "NULL",chIP, sizeof(chIP),_T("./config.ini"));
WORD wPort = GetPrivateProfileInt(_T("ONLINE_DEVICE"), _T("Port"), 0, _T("./config.ini"));
```
**Section sources**
- [config.ini](file://config.ini#L11-L15)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L492-L502)
### [USER_INFO] 节:用户认证机制
`[USER_INFO]`节包含用户认证相关的信息。
- **UserID**:用户的唯一标识符
- **UserPwd**:用户密码,存储在单独的`[USER_Pwd]`节中
- **UserSave**:是否保存用户凭据
- **UserAuto**:是否自动登录
用户在登录界面输入凭据后,如果选择保存,系统会将用户名和密码写回配置文件。认证机制通过`DailLogin`对话框实现,确保只有授权用户才能访问系统。
```cpp
WritePrivateProfileString(_T("USER_INFO"), _T("UserID"), csID, _T("./config.ini"));
WritePrivateProfileString(_T("USER_Pwd"), _T("UserPwd"), csPwd, _T("./config.ini"));
```
**Section sources**
- [config.ini](file://config.ini#L25-L38)
- [cpp/Views/DailLogin.cpp](file://cpp/Views/DailLogin.cpp#L137-L140)
### [CROSS_HOLE_CFG] 节:跨孔测量参数
`[CROSS_HOLE_CFG]`节定义了跨孔测量的相关物理参数。
- **Number**:配置组的数量
- **CFG_1...CFG_N**:每个配置组的具体参数
- **Name**:配置名称
- **FirstPoleDep**:第一电极深度(单位:米)
- **HoleSpace**:孔间距(单位:米)
这些参数通过`GeCrossHoleCfg()`函数解析,用于地质勘探中的跨孔电阻率成像。系统会根据配置数量循环读取每个`CFG_X`节的参数,并存储在内存映射中供后续处理使用。
```cpp
int iNumber = ::GetPrivateProfileInt(_T("CROSS_HOLE_CFG"), _T("Number"),255,_T("./config.ini"));
for (int i = 1; i <= iNumber; i++)
{
if (!ParserSigCrossHole(i))
{
bRes = false;
break;
}
}
```
**Section sources**
- [config.ini](file://config.ini#L39-L73)
- [cpp/Main/GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1182-L1221)
## 多环境配置示例
### 开发环境配置
```ini
[UI]
Language=1
[TRANSFER_INFO]
Url=http://dev.update.geomative.com/Upgrade/software/V2.0
HomePage=http://dev.geomative.com/
[ONLINE_DEVICE]
RemoteType=2
IP=192.168.1.100
Port=8769
```
### 测试环境配置
```ini
[UI]
Language=2
[TRANSFER_INFO]
Url=http://test.update.geomative.com/Upgrade/software/V2.0
HomePage=http://test.geomative.com/
[ONLINE_DEVICE]
RemoteType=2
IP=10.0.0.50
Port=8769
```
### 生产环境配置
```ini
[UI]
Language=1
[TRANSFER_INFO]
Url=http://update.geomative.com/Upgrade/software/V2.0
HomePage=http://www.geomative.com/
[ONLINE_DEVICE]
RemoteType=2
IP=120.31.131.181
Port=8769
```
## 配置加载优先级
GeomativeStudio遵循特定的配置文件加载优先级:
1. **根目录优先**:位于软件根目录的`config.ini`具有最高优先级
2. **安装目录次之**`Install/Geomative Studio/`目录下的配置文件作为备选
3. **运行时动态更新**:某些配置(如用户凭据)可在运行时修改并立即生效
这种设计允许用户在不同环境中灵活切换配置,同时确保关键设置不会被意外覆盖。例如,`Release/config.ini`中的`Language=2`会覆盖安装目录中相同文件的设置。
## 配置维护最佳实践
### 参数修改后的生效机制
- **重启生效**:大多数配置需要重启软件才能生效
- **即时生效**:用户界面语言等少数配置可在运行时动态切换
- **验证机制**:关键参数(如IP地址)在读取后会进行有效性验证
### 常见配置错误排查方法
1. **连接失败**:检查`ONLINE_DEVICE`节的IP和Port是否正确
2. **语言不切换**:确认`UI`节的`Language`值是否为1或2
3. **更新失败**:验证`TRANSFER_INFO`中的URL是否可访问
4. **认证失败**:检查`USER_INFO``USER_Pwd`节的凭据是否正确
5. **跨孔数据异常**:核对`CROSS_HOLE_CFG`中各孔的间距和深度设置
建议在修改配置前备份原文件,并在修改后通过日志文件(位于`LOG/`目录)验证配置是否正确加载。
**Section sources**
- [config.ini](file://config.ini)
- [Release/config.ini](file://Release/config.ini)
- [Install/Geomative Studio/config.ini](file://Install/Geomative Studio/config.ini)
## 结论
GeomativeStudio的`config.ini`文件是控制系统行为的核心组件。通过深入分析其结构和参数含义,我们可以更好地理解软件的工作机制,并根据实际需求进行定制化配置。遵循最佳实践进行配置维护,可以确保系统的稳定运行和高效管理。
@@ -0,0 +1,238 @@
# 版本与数据库配置
<cite>
**本文档中引用的文件**
- [version_info.txt](file://version_info.txt)
- [database_modify.xml](file://database_modify.xml)
- [DB\数据库字段修改记录.txt](file://DB/数据库字段修改记录.txt)
- [cpp\Operator\UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
- [h\UpdateDataBase.h](file://h/UpdateDataBase.h)
- [cpp\Tools\checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
- [h\checkupdate.h](file://h/checkupdate.h)
</cite>
## 目录
1. [版本控制机制](#版本控制机制)
2. [数据库变更管理](#数据库变更管理)
3. [XML与文本格式对比](#xml与文本格式对比)
4. [系统升级流程](#系统升级流程)
5. [部署与维护建议](#部署与维护建议)
## 版本控制机制
`version_info.txt` 文件是系统版本控制的核心元数据文件,其主要作用是存储当前软件的版本信息,支持自动更新功能。该文件位于安装目录中,其内容结构简单,仅包含一个版本号字段。
根据文件内容分析,当前版本号为 `2.4.1`。该版本号在系统启动时被读取,并与服务器端的最新版本进行比对,以判断是否需要执行更新操作。
版本比对逻辑主要在 `checkupdate.cpp` 文件中实现。系统通过 `GetGeoMativeVersion()` 函数从可执行文件的资源中提取版本信息,并通过 HTTP 请求从服务器获取最新的版本号(`GeomativeVersion.txt`)。如果本地版本低于服务器版本,则触发更新流程。
版本控制流程如下:
1. 系统启动时读取 `version_info.txt` 中的版本号
2. 通过 HTTP 请求获取服务器端最新版本号
3. 比较本地与远程版本号
4. 若本地版本较旧,则提示用户更新或自动下载更新包
5. 更新完成后,更新本地版本信息
该机制确保了软件能够及时获取最新功能和安全补丁,同时避免了不必要的更新操作。
**本节来源**
- [version_info.txt](file://version_info.txt)
- [cpp\Tools\checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
- [h\checkupdate.h](file://h/checkupdate.h)
## 数据库变更管理
`database_modify.xml` 文件是数据库结构变更管理的核心配置文件,采用 XML 格式存储数据库的变更历史。该文件详细记录了数据库表结构的增删改操作,包括字段属性、主键、索引、外键等元数据信息。
### XML结构分析
该文件采用分层结构,根节点为 `current_version`,包含当前版本号。其子节点 `pre_version` 记录了历史版本的变更信息。每个 `pre_version` 节点包含一个 `value` 属性,表示该版本号。
`pre_version` 节点下,`table` 元素表示对数据库表的操作,其 `modify_type` 属性定义了操作类型:
- `1`: 增加表
- `2`: 删除表
- `3`: 修改表
`column` 元素描述了字段的详细属性:
- `name`: 字段名称
- `is_primary_key`: 是否为主键
- `value_type`: 字段类型(1:text, 2:Memo, 3:Byte, 4:Integer, 5:Long, 6:Single, 7:Double, 8:Currency, 9:AutoNumber, 10:Date/Time, 11:Yes/No
- `attribute_value`: 字段长度(仅对文本类型有效)
- `index`: 索引类型(0:无索引, 1:允许重复, 2:不允许重复)
- `is_foreign_key`: 是否为外键
- `is_empty`: 是否允许为空
- `is_compress`: 是否压缩
### 变更执行流程
数据库变更的执行由 `UpdateDataBase.cpp` 中的 `CUpdateDataBase` 类负责。其工作流程如下:
```mermaid
flowchart TD
A[启动系统] --> B{是否存在database_modify.xml?}
B --> |否| C[无需更新数据库]
B --> |是| D[解析XML文件]
D --> E[获取当前数据库版本]
E --> F[查找匹配的pre_version]
F --> G[提取变更指令]
G --> H{遍历变更指令}
H --> I[增加表]
H --> J[删除表]
H --> K[修改表]
I --> L[生成CREATE TABLE语句]
J --> M[生成DROP TABLE语句]
K --> N[生成ALTER TABLE语句]
L --> O[执行SQL]
M --> O
N --> O
O --> P[更新versioninfo表]
P --> Q[完成数据库更新]
```
**图示来源**
- [database_modify.xml](file://database_modify.xml)
- [cpp\Operator\UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
- [h\UpdateDataBase.h](file://h/UpdateDataBase.h)
当系统检测到数据库需要更新时,会按照以下步骤执行:
1. 读取 `database_modify.xml` 文件
2. 解析当前版本号,查找对应的变更记录
3. 根据 `modify_type` 执行相应的数据库操作
4. 更新数据库中的 `versioninfo` 表,记录新的版本号
5. 记录更新日志到 `LOG\update_db.log`
**本节来源**
- [database_modify.xml](file://database_modify.xml)
- [cpp\Operator\UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
- [h\UpdateDataBase.h](file://h/UpdateDataBase.h)
## XML与文本格式对比
系统中存在两种数据库变更记录格式:结构化的 XML 格式和纯文本格式。`database_modify.xml` 采用 XML 格式,而 `DB\数据库字段修改记录.txt` 则采用纯文本格式。
### 文本格式分析
纯文本格式的变更记录位于 `DB\数据库字段修改记录.txt` 文件中,其内容为人工编写的变更日志,按时间顺序记录了数据库的变更历史。例如:
```
2015年6月2日---
1td2dcon增加字段bUse,用于标记该条记录是否有效
2td3dcon增加字段bUse,用于标记该条记录是否有效
3: td1dcon增加字段bUse,用于标记该条记录是否有效
```
这种格式的优点是易于人类阅读和编写,但存在以下局限性:
- 缺乏结构化,难以自动化解析
- 没有标准化的字段类型定义
- 无法精确描述索引、外键等复杂约束
- 容易出现格式不一致的问题
### XML格式优势
相比之下,`database_modify.xml` 的 XML 格式具有显著优势:
1. **结构化存储**:采用标准化的标签和属性,便于程序解析和处理
2. **自动化处理**:可以直接映射到数据库操作指令,实现自动化迁移
3. **完整性**:包含完整的字段属性定义,包括数据类型、长度、约束等
4. **可验证性**:可以通过 XML Schema 进行格式验证
5. **版本控制**:支持多版本变更记录的嵌套管理
XML 格式特别适合在自动化部署和持续集成环境中使用,能够确保数据库结构变更的准确性和一致性。
**本节来源**
- [database_modify.xml](file://database_modify.xml)
- [DB\数据库字段修改记录.txt](file://DB/数据库字段修改记录.txt)
## 系统升级流程
当系统需要进行数据库结构升级时,会依据 `version_info.txt``database_modify.xml` 两个文件执行完整的迁移流程。
### 升级触发条件
系统在启动时会检查以下条件来决定是否需要升级:
1. 本地版本号与服务器版本号不一致
2. 数据库版本号与 `database_modify.xml` 中的版本号不一致
3. 存在未应用的 `pre_version` 变更记录
### 升级执行过程
升级过程由 `CUpdateDataBase` 类的 `ParserUpdateDBXml()` 方法驱动,其核心逻辑如下:
1. **版本比对**:比较 `m_strCurVer`(当前版本)与 XML 中的 `current_version`
2. **变更查找**:遍历 `pre_version` 节点,找到与当前数据库版本匹配的变更记录
3. **指令解析**:根据 `modify_type` 解析表结构变更指令
4. **SQL生成**:将 XML 指令转换为具体的 SQL 语句
5. **事务执行**:在数据库事务中执行所有变更操作
6. **版本更新**:更新 `versioninfo` 表中的版本号
### 实际案例
假设系统从版本 `1.1.1` 升级到 `1.1.2`,需要在 `test` 表中添加新字段。`database_modify.xml` 中的配置如下:
```xml
<table name="test" modify_type="1">
<column name="id" is_primary_key="1" value_type="9" attribute_value="1"/>
<column name="col1" value_type="2" attribute_value="20" is_empty="1" is_compress="1"/>
<column name="col2" value_type="5" is_foreign_key="0" refer_table_name="FK_test" refer_table_col="fk1"/>
<column name="col3" value_type="5" index="2"/>
</table>
```
系统将生成以下 SQL 语句并执行:
```sql
CREATE TABLE test (
id counter primary key,
col1 text(20) with compression null,
col2 integer,
col3 integer
);
CREATE UNIQUE INDEX iCol3 ON test (col3);
ALTER TABLE test ADD CONSTRAINT FK_col2 FOREIGN KEY (col2) REFERENCES FK_test(fk1) ON UPDATE CASCADE ON DELETE CASCADE;
```
此过程确保了数据库结构变更的原子性和一致性。
**本节来源**
- [version_info.txt](file://version_info.txt)
- [database_modify.xml](file://database_modify.xml)
- [cpp\Operator\UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
## 部署与维护建议
`version_info.txt``database_modify.xml` 作为关键的元数据文件,在部署、备份和故障恢复中扮演着重要角色。
### 部署注意事项
1. **文件完整性**:确保两个文件在部署包中完整存在
2. **版本同步**:确保 `version_info.txt` 中的版本号与实际软件版本一致
3. **权限设置**:确保应用程序有读写这两个文件的权限
4. **路径正确**:确认文件位于正确的安装目录中
### 备份策略
建议将这两个文件纳入常规备份范围:
- `version_info.txt`:记录当前系统状态,便于版本追溯
- `database_modify.xml`:包含数据库变更历史,是数据恢复的关键
### 故障恢复
当系统出现故障时,这两个文件可帮助快速恢复:
1. 通过 `version_info.txt` 确定系统版本
2. 通过 `database_modify.xml` 重建数据库结构
3. 按照变更历史逐步恢复数据
### 维护建议
为确保文件的准确性和完整性,建议:
1. **变更审计**:每次数据库结构变更都必须更新 `database_modify.xml`
2. **版本递增**:每次发布新版本都必须更新 `version_info.txt`
3. **格式验证**:使用 XML 验证工具确保 `database_modify.xml` 的格式正确
4. **文档同步**:保持 XML 文件与数据库实际结构的一致性
5. **测试验证**:在测试环境中验证变更脚本的正确性
通过严格维护这两个元数据文件,可以确保系统的稳定运行和可维护性。
**本节来源**
- [version_info.txt](file://version_info.txt)
- [database_modify.xml](file://database_modify.xml)
- [cpp\Operator\UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
@@ -0,0 +1,352 @@
# 配置说明
<cite>
**本文档引用的文件**
- [config.ini](file://config.ini)
- [Install\Geomative Studio\config.ini](file://Install/Geomative Studio/config.ini)
- [database_modify.xml](file://database_modify.xml)
- [version_info.txt](file://version_info.txt)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
- [cpp\Tools\checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
</cite>
## 目录
1. [项目结构](#项目结构)
2. [核心配置文件分析](#核心配置文件分析)
3. [配置加载与优先级](#配置加载与优先级)
4. [版本管理与更新机制](#版本管理与更新机制)
5. [数据库变更管理](#数据库变更管理)
6. [配置最佳实践](#配置最佳实践)
## 项目结构
GeomativeStudio项目的目录结构清晰地分为多个功能区域,包括缓存、数据库、安装包、日志、发布版本和源代码等。项目根目录下的`config.ini`文件是主要的配置文件,而`Install`目录下也包含了一个`config.ini`副本,用于安装过程中的配置。
```mermaid
graph TD
A[GeomativeStudio] --> B[CACHE]
A --> C[DB]
A --> D[Install]
A --> E[LOG]
A --> F[Release]
A --> G[cpp]
A --> H[h]
A --> I[res]
A --> J[tools]
A --> K[config.ini]
A --> L[database_modify.xml]
A --> M[version_info.txt]
D --> N[GD_10]
D --> O[Geomative Studio]
O --> P[LOG]
O --> Q[tools]
O --> R[config.ini]
O --> S[database_modify.xml]
O --> T[version_info.txt]
F --> U[DB]
F --> V[config.ini]
```
**Diagram sources**
- [config.ini](file://config.ini)
- [Install\Geomative Studio\config.ini](file://Install/Geomative Studio/config.ini)
- [database_modify.xml](file://database_modify.xml)
- [version_info.txt](file://version_info.txt)
**Section sources**
- [config.ini](file://config.ini)
- [Install\Geomative Studio\config.ini](file://Install/Geomative Studio/config.ini)
- [database_modify.xml](file://database_modify.xml)
- [version_info.txt](file://version_info.txt)
## 核心配置文件分析
### UI配置节
[UI]配置节主要控制用户界面的语言设置。Language参数决定了应用程序的显示语言。
- **Language**: 语言设置参数,值为1表示中文,2表示英文。该参数在程序启动时被读取,并通过`WM_CHANGE_LANGUAGE`消息通知主窗口切换语言。
```ini
[UI]
Language=1
```
**Section sources**
- [config.ini](file://config.ini#L1-L3)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L447-L458)
### 传输信息配置节
[TRANSFER_INFO]配置节定义了软件更新和主页的URL地址。
- **Url**: 自动更新服务器的URL地址,用于检查和下载新版本。
- **HomePage**: 公司官方网站地址,用于用户访问更多信息。
```ini
[TRANSFER_INFO]
Url=http://update.geomative.com/Upgrade/software/V2.0
HomePage=http://www.geomative.com/
```
**Section sources**
- [config.ini](file://config.ini#L4-L7)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1116-L1135)
### 数据导出配置节
[EXPORT_DATA]配置节控制数据导出的样式设置。
- **2dDataExpStyle**: 二维数据导出样式,值为0表示默认样式。
```ini
[EXPORT_DATA]
2dDataExpStyle=0
```
**Section sources**
- [config.ini](file://config.ini#L8-L10)
### 在线设备配置节
[ONLINE_DEVICE]配置节定义了远程设备连接的网络参数。
- **RemoteType**: 远程传输类型,值为2表示云传输模式。
- **IP**: 远程服务器IP地址。
- **Port**: 远程服务器端口号。
```ini
[ONLINE_DEVICE]
RemoteType=2
IP=120.31.131.181
Port=8769
```
**Section sources**
- [config.ini](file://config.ini#L11-L15)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L492-L500)
### 用户信息配置节
[USER_INFO]、[USER_Pwd]、[USER_Save]和[USER_Auto]配置节共同管理用户认证信息。
- **UserID**: 用户ID,用于身份识别。
- **UserPwd**: 用户密码,用于身份验证。
- **UserSave**: 是否保存密码,1表示保存,0表示不保存。
- **UserAuto**: 是否自动登录,1表示自动登录,0表示手动登录。
```ini
[USER_INFO]
UserID=12345637
[USER_Pwd]
UserPwd=123456
[USER_Save]
UserSave=1
[USER_Auto]
UserAuto=0
```
**Section sources**
- [config.ini](file://config.ini#L25-L38)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L434-L442)
- [cpp\Views\DailLogin.cpp](file://cpp/Views/DailLogin.cpp#L19-L30)
### 跨孔测量配置节
[CROSS_HOLE_CFG]配置节定义了跨孔测量的配置组,包含多个CFG_n子节。
- **Number**: 配置组数量,表示有多少个CFG_n配置节。
- **CFG_n**: 每个配置组包含Name、FirstPoleDep和HoleSpace三个参数。
```ini
[CROSS_HOLE_CFG]
Number=6
[CFG_1]
Name=1_2
FirstPoleDep=1
HoleSpace=2.66
[CFG_2]
Name=1_3
FirstPoleDep=1
HoleSpace=2.78
```
**Section sources**
- [config.ini](file://config.ini#L39-L73)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1138-L1221)
## 配置加载与优先级
GeomativeStudio的配置加载遵循特定的优先级规则。根目录下的`config.ini`文件优先于安装目录中的副本。程序启动时,会从根目录的`config.ini`文件中读取配置信息。
配置加载过程如下:
1. 程序启动时,首先读取`config.ini`文件中的各种配置参数。
2. 使用Windows API `GetPrivateProfileString``GetPrivateProfileInt`函数读取INI文件中的字符串和整数参数。
3. 对于跨孔测量配置,程序会先读取`[CROSS_HOLE_CFG]`节的`Number`参数,然后循环读取每个`[CFG_n]`节的配置。
4. 如果配置文件中缺少必要的参数,程序会记录错误日志并可能显示错误消息。
```mermaid
sequenceDiagram
participant App as 应用程序
participant Config as config.ini
participant Memory as 内存
App->>Config : 读取Language参数
Config-->>App : 返回1(中文)
App->>Memory : 设置语言为中文
App->>Config : 读取Transfer_Info
Config-->>App : 返回URL和主页
App->>Config : 读取Online_Device
Config-->>App : 返回IP和端口
App->>Config : 读取Cross_Hole_Cfg
Config-->>App : 返回配置数量
loop 每个CFG_n配置
App->>Config : 读取CFG_n参数
Config-->>App : 返回Name, FirstPoleDep, HoleSpace
App->>Memory : 存储配置
end
```
**Diagram sources**
- [config.ini](file://config.ini)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L433-L624)
**Section sources**
- [config.ini](file://config.ini)
- [cpp\Main\GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L433-L624)
## 版本管理与更新机制
### version_info.txt文件
`version_info.txt`文件用于存储软件的版本信息,支持自动更新功能。
```txt
geomative_version=2.4.1
```
该文件包含一个简单的键值对,`geomative_version`表示当前软件版本。在自动更新过程中,程序会读取此文件的版本号,并与服务器上的最新版本进行比较,以决定是否需要更新。
**Section sources**
- [version_info.txt](file://version_info.txt)
- [cpp\Tools\checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L833-L867)
### 自动更新流程
自动更新流程如下:
1. 程序启动时读取`version_info.txt`中的当前版本号。
2. 从`config.ini`中获取更新服务器URL。
3. 连接到更新服务器,获取最新版本信息。
4. 比较本地版本和服务器版本,如果服务器版本更高,则提示用户更新。
5. 下载并安装新版本。
## 数据库变更管理
### database_modify.xml文件
`database_modify.xml`文件记录了数据库结构的变更历史,用于数据库版本控制和迁移。
```xml
<?xml version="1.0" encoding="UTF-8"?>
<current_version value = "1.1.2">
<pre_version value = "1.1.2">
<!-- modify_type值含义 1 : 增加表 2 : 删除表 3 : 修改表 -->
<!-- 列的属性在这里只为名称,主键,值的属性,索引信息,外键,和是否为空,其中是否压缩.
其中 vallue_type的值域 1: text 最多255个字节 2: Memo 最多65535个字节 3: Byte
4: Integer 5: Long 6: Single 7: Double 8:Currency 9 AutoNumber 10 Date/Time
11 Yes/No 12 Ole Object 13 Hyperlink 暂时不支持Lookup Wizard
其中在1,2 中,attribute_value表示的是字符的长度,其他值时,attribute_value没有含义。
index值得含义: 0 : 表示没有索引, 1: 有索引并允许重复 2: 有索引不允许重复
is_primary_key index is_foreign_keyis_empty is_compress默认值为0 -->
<table name = "test" modify_type= "1" >
<column name = "id" is_primary_key = "1" value_type = "9" attribute_value="1" is_foreign_key = "0" is_empty="0" is_compress="0"/>
<column name = "col1" value_type = "2" attribute_value="20" is_empty="1" is_compress="1"/>
<column name = "col2" value_type = "5" attribute_value="30" is_foreign_key = "0" refer_table_name="FK_test" refer_table_col="fk1"/>
<column name = "col3" value_type = "5" index ="2" />
</table>
</pre_version>
</current_version>
```
该XML文件的结构说明:
- **current_version**: 当前数据库版本号。
- **pre_version**: 前一版本号。
- **table**: 表示一个数据库表的变更。
- `name`: 表名
- `modify_type`: 变更类型,1表示新增,2表示删除,3表示修改
- **column**: 表示表中的一列。
- `name`: 列名
- `value_type`: 数据类型
- `is_primary_key`: 是否为主键
- `index`: 索引类型
**Section sources**
- [database_modify.xml](file://database_modify.xml)
- [cpp\ProblemZone\Device.cpp](file://cpp/ProblemZone/Device.cpp#L1144-L1202)
## 配置最佳实践
### 敏感信息保护
1. **密码保护**: 避免在配置文件中明文存储敏感密码,建议使用加密存储或环境变量。
2. **用户ID管理**: 用户ID不应包含敏感信息,建议使用随机生成的唯一标识符。
3. **配置文件权限**: 确保配置文件的访问权限受到限制,防止未授权访问。
### 多环境配置管理
1. **开发/生产分离**: 为不同环境(开发、测试、生产)维护不同的配置文件。
2. **配置模板**: 使用模板文件,通过脚本生成特定环境的配置。
3. **环境变量**: 将环境特定的配置(如数据库连接、API密钥)通过环境变量注入。
### 跨孔测量参数设置
正确设置跨孔测量参数的示例:
```ini
[CROSS_HOLE_CFG]
Number=3
[CFG_1]
Name=A_B
FirstPoleDep=1.5
HoleSpace=3.0
[CFG_2]
Name=A_C
FirstPoleDep=1.5
HoleSpace=4.5
[CFG_3]
Name=B_C
FirstPoleDep=1.5
HoleSpace=3.8
```
关键参数说明:
- **FirstPoleDep**: 第一电极深度,单位为米,表示测量起始深度。
- **HoleSpace**: 孔间距,单位为米,表示两个测量孔之间的距离。
### 网络通信参数设置
正确设置网络通信参数的示例:
```ini
[ONLINE_DEVICE]
RemoteType=2
IP=192.168.1.100
Port=8769
[TRANSFER_INFO]
Url=http://your-update-server.com/Upgrade/software/V2.0
HomePage=http://your-company.com/
```
注意事项:
1. 确保IP地址和端口号正确无误。
2. 更新服务器URL应使用HTTPS以保证安全。
3. 在防火墙设置中开放指定端口。
@@ -0,0 +1,382 @@
# Main模块
<cite>
**本文引用的文件**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
- [Global.cpp](file://cpp/Main/Global.cpp)
- [GeoMative.h](file://h/GeoMative.h)
- [DevManager.h](file://h/DevManager.h)
- [TdManager.h](file://h/TdManager.h)
- [ProManager.h](file://h/ProManager.h)
- [ExecManager.h](file://h/ExecManager.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
## 引言
本文件聚焦于GeomativeStudio项目的Main模块,系统性阐述CGeoMativeApp作为MFC应用程序主类的职责与实现,包括:
- 应用程序初始化流程(InitInstance)与生命周期管理(ExitInstance
- 全局变量与全局函数的定义与作用域
- 各管理器(m_pDevManager、m_pProManager、m_pTdManager、m_pExecManager、m_pSptManager、m_pIOManager)的创建与依赖关系
- 通过AfxGetApp()全局函数访问应用程序实例并在不同模块间传递调用模式
## 项目结构
Main模块位于cpp/Main目录,包含两个关键文件:
- GeoMative.cppMFC应用主类CGeoMativeApp的实现,涵盖初始化、消息处理、退出清理等
- Global.cpp:全局变量、全局函数与跨模块共享的数据结构定义
```mermaid
graph TB
subgraph "Main模块"
A["GeoMative.cpp<br/>CGeoMativeApp实现"]
B["Global.cpp<br/>全局变量/函数"]
H["GeoMative.h<br/>CGeoMativeApp声明"]
end
subgraph "管理器模块"
D["DevManager.h"]
T["TdManager.h"]
P["ProManager.h"]
E["ExecManager.h"]
end
A --> H
A --> D
A --> T
A --> P
A --> E
B --> A
```
图表来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1-L120)
- [Global.cpp](file://cpp/Main/Global.cpp#L1-L60)
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
章节来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1-L120)
- [Global.cpp](file://cpp/Main/Global.cpp#L1-L60)
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
## 核心组件
- CGeoMativeApp:MFC应用主类,负责应用初始化、主窗口创建、资源加载、全局对象创建、网络与数据库初始化、线程与定时器管理、退出清理等
- 全局变量与函数:集中定义跨模块使用的全局状态、工具函数、线程入口与回调等
- 管理器集合:设备、项目、测试数据、执行、脚本、IO等管理器,由CGeoMativeApp统一创建与协调
章节来源
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L139-L210)
- [Global.cpp](file://cpp/Main/Global.cpp#L20-L70)
## 架构总览
CGeoMativeApp作为应用入口,承担以下职责:
- 初始化MFC环境、OLE、控件容器、3D控件支持
- 设置工作目录、创建缓存目录、加载主框架窗口
- 建立数据库连接(ADO/ADOX)、初始化日志、诊断信息、跨孔配置
- 创建各管理器实例并注入数据库连接
- 读取配置(config.ini)、语言切换、用户登录、网络连接与心跳
- 启动扫描线程、系统时间线程、定时器
- 在退出时释放所有资源与线程
```mermaid
classDiagram
class CGeoMativeApp {
+InitInstance() bool
+ExitInstance() int
+PreTranslateMessage(pMsg) bool
+OnFileExit() void
+OnHelpAbout() void
+GetDBPasswordInfo() void
+GetPasswordPtr(map) void
+GetDevPassword(name, info) void
+OnCfgTerrain() void
+GetCfgUrl() bool
+Get2dDataExpStyle() int
-InitalDiagnosisInfo() void
-SetDBAttributeUpdateInfo() void
-ProcConPtrCloseExpect() void
-ParserSigCrossHole(index) bool
-GeCrossHoleCfg() bool
+m_pDevManager : CDevManager*
+m_pProManager : CProManager*
+m_pTdManager : CTdManager*
+m_pExecManager : CExecManager*
+m_pSptManager : CSptManager*
+m_pIOManager : CIOManager*
+m_pConnection : _ConnectionPtr
+m_pCatalog : _CatalogPtr
+m_NetWorkOper : CNetWorkOper
+m_uiUserID : UINT32
+m_ucIsMultiChannel : EN_CHANNEL_INFO
+m_mapCrossHole : map
}
class CDevManager
class CProManager
class CTdManager
class CExecManager
class CSptManager
class CIOManager
CGeoMativeApp --> CDevManager : "创建并持有"
CGeoMativeApp --> CProManager : "创建并持有"
CGeoMativeApp --> CTdManager : "创建并持有"
CGeoMativeApp --> CExecManager : "创建并持有"
CGeoMativeApp --> CSptManager : "创建并持有"
CGeoMativeApp --> CIOManager : "创建并持有"
```
图表来源
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
## 详细组件分析
### CGeoMativeApp类与生命周期管理
- 构造与析构
- 构造函数初始化各管理器指针为空,清理计数器与路径等成员
- 析构函数负责释放数据库连接、主窗口、各管理器实例、菜单/加速键资源、COM/临界区等
- 初始化(InitInstance
- OLE初始化、控件容器启用、3D控件支持
- 设置注册表键、设置当前工作目录、创建CACHE目录结构
- 加载主框架窗口、显示最大化、初始化COM、清空跨孔配置映射
- 建立数据库连接(ADO/ADOX),设置锁数量上限
- 初始化网络(WSAStartup)、日志、诊断信息、跨孔配置
- 密码验证(数据库密码、几何密码)、系统时间线程、数据库升级
- 创建各管理器实例(Dev/Pro/Td/Exec/Spt/IO),读取config.ini配置(传输方式、用户ID、多通道、语言、时区等)
- 在线模式:登录对话框、心跳定时器、网络连接、设备通知注册、工作线程启动
- 离线/USB模式:设备扫描线程、设备链接初始化、MAC地址生成、扫描线程
- 资源清理与菜单刷新
- 退出(ExitInstance
- 注销网络回调、释放主窗口、管理器、关闭数据库连接、释放ADO/ADOX、停止扫描线程、反初始化COM、删除临界区、调用基类退出
```mermaid
sequenceDiagram
participant App as "CGeoMativeApp"
participant Frame as "主框架窗口"
participant DB as "数据库(ADO/ADOX)"
participant Net as "网络(CNetWorkOper)"
participant Th as "线程(扫描/系统时间)"
App->>App : 构造函数初始化
App->>App : InitInstance()
App->>App : OLE/控件容器初始化
App->>Frame : 创建并加载主框架
App->>App : 设置工作目录/CACHE目录
App->>DB : 建立连接/设置锁上限
App->>App : 日志/诊断/跨孔配置初始化
App->>App : 密码验证/系统时间线程
App->>App : 数据库升级
App->>App : 创建管理器实例
App->>App : 读取config.ini/语言切换
alt 在线模式
App->>Net : 初始化/连接/心跳/注册通知
else 离线/USB模式
App->>Th : 启动扫描线程
end
App-->>Frame : 显示并更新窗口
App->>App : ExitInstance()
App->>Th : 释放线程/临界区/COM
App->>DB : 关闭连接/释放ADO/ADOX
App->>Frame : 释放主窗口
App-->>App : 返回基类ExitInstance
```
图表来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L139-L210)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L211-L420)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L421-L642)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L644-L737)
章节来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L139-L210)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L211-L420)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L421-L642)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L644-L737)
### 全局变量与全局函数的作用
- 全局变量
- 系统语言、UI偏移、传输模式、UI语言、系统时间、扫描标志、MAC地址、设备序列号、PLC/设备ID、时区、在线传输开关、任务状态、时间戳等
- 设备链接表(aDevLinkTable[256])、扫描临界区(g_ScanTabSection)、系统时间(g_sysCurTime
- 全局函数
- 列表排序比较函数(ListStrCompare、SptListStrCompare
- 消息泵辅助(DoEvent
- 延迟与事件循环(TransDelay
- 字符串处理(SplitterString、StatStringCount、GetSubStringInHeadAndTail
- 数学与信号处理(多项式拟合、积分、二分法求根)
- 状态文本与编码转换(GetGRStatus、GetGrCodeText、U2G/G2U
- 时间转换(Tm2GmtStr/Tm2LocalStr、Str2GmtTm/Str2LocalTm
- 网络序转换(tcp_htonf/tcp_ntohf
- 在线任务状态文本(OnLineLocalTaskState/OnLineTask
- 测试失败原因文本(GetTestFailedReason
- 设备枚举(DeviceEnumCommPort
- 扫描线程入口(DevLinkScanThreadFunction
- 系统时间线程入口(GetSysTimeFunction
- 错误钩子(CBTHookProc
这些全局变量与函数贯穿于设备扫描、网络通信、UI交互、数据处理等模块,确保模块间共享状态与工具能力。
章节来源
- [Global.cpp](file://cpp/Main/Global.cpp#L20-L120)
- [Global.cpp](file://cpp/Main/Global.cpp#L120-L320)
- [Global.cpp](file://cpp/Main/Global.cpp#L320-L600)
- [Global.cpp](file://cpp/Main/Global.cpp#L600-L1000)
- [Global.cpp](file://cpp/Main/Global.cpp#L1000-L1539)
### 管理器创建与依赖关系
- 管理器创建顺序与注入
- 数据库连接(m_pConnection)与目录(m_pCatalog)在InitInstance中建立
- 管理器构造函数均接收_connection指针,确保所有管理器共享同一数据库上下文
- 管理器职责概览
- CDevManager:设备发现、在线/离线设备管理、远程设备集合维护
- CProManager:工程/测区管理、DMS树构建与同步、默认测区创建
- CTdManager:测试数据导入/导出、在线同步、2D/3D转换
- CExecManager:测试执行流程控制(2D/CE电阻率测试)
- CSptManager:脚本管理(2D/3D/CE
- CIOManagerIO与外部接口(含数据库与Catalog)
- 依赖关系
- CGeoMativeApp持有各管理器指针,负责创建与销毁
- 各管理器内部持有_connection指针,避免重复连接
- 网络层(CNetWorkOper)与扫描线程(DevLinkScanThreadFunction)通过CGeoMativeApp间接协作
```mermaid
flowchart TD
Start(["InitInstance入口"]) --> DBInit["建立数据库连接<br/>ADO/ADOX"]
DBInit --> CreateMgrs["创建管理器实例<br/>Dev/Pro/Td/Exec/Spt/IO"]
CreateMgrs --> ConfigRead["读取config.ini<br/>传输方式/用户ID/语言/时区"]
ConfigRead --> OnlineCheck{"在线模式?"}
OnlineCheck --> |是| NetInit["网络初始化/连接/心跳/注册"]
OnlineCheck --> |否| ScanThread["启动扫描线程"]
NetInit --> SysTime["启动系统时间线程"]
ScanThread --> SysTime
SysTime --> Done(["初始化完成"])
```
图表来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L421-L642)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
章节来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L421-L642)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
### AfxGetApp()的应用与模块间调用模式
- theApp全局实例
- Global.cpp中声明并定义CGeoMativeApp theApp,供全工程使用
- 各模块通过extern声明theApp,从而在任意位置访问CGeoMativeApp实例
- AfxGetApp()调用模式
- 在扫描线程入口DevLinkScanThreadFunction中,通过AfxGetApp()->GetMainWnd()向主窗口发送WM_NEWLINK/WM_DETECT_LINE消息
- 在线任务状态文本函数OnLineLocalTaskState/OnLineTask中,根据UI语言选择中文或英文提示
- 调用链示意
```mermaid
sequenceDiagram
participant Thread as "扫描线程"
participant App as "CGeoMativeApp"
participant MainWnd as "主窗口"
participant Handlers as "消息处理器"
Thread->>App : AfxGetApp()
App-->>Thread : 返回theApp实例
Thread->>App : GetMainWnd()
App-->>Thread : 返回主窗口指针
Thread->>MainWnd : SendMessage(WM_NEWLINK/WM_DETECT_LINE)
MainWnd->>Handlers : 分发消息并处理
```
图表来源
- [Global.cpp](file://cpp/Main/Global.cpp#L1000-L1166)
- [Global.cpp](file://cpp/Main/Global.cpp#L20-L30)
章节来源
- [Global.cpp](file://cpp/Main/Global.cpp#L20-L30)
- [Global.cpp](file://cpp/Main/Global.cpp#L1000-L1166)
## 依赖关系分析
- 组件耦合
- CGeoMativeApp与各管理器之间为“聚合”关系,通过指针持有,便于解耦与独立扩展
- 管理器之间无直接依赖,通过CGeoMativeApp协调,降低环状依赖风险
- 外部依赖
- 数据库:ADO/ADOX连接与事务
- 网络:CNetWorkOper封装的网络操作与线程
- 系统服务:COM初始化/反初始化、线程与临界区、文件系统(CACHE目录)
- 潜在循环依赖
- 通过全局theApp与AfxGetApp()访问主类,避免了管理器对主类的直接包含,降低循环依赖概率
```mermaid
graph LR
App["CGeoMativeApp"] --> DevMgr["CDevManager"]
App --> ProMgr["CProManager"]
App --> TdMgr["CTdManager"]
App --> ExecMgr["CExecManager"]
App --> SptMgr["CSptManager"]
App --> IOMgr["CIOManager"]
App --> Net["CNetWorkOper"]
App --> DB["ADO/ADOX 连接"]
```
图表来源
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
章节来源
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [ExecManager.h](file://h/ExecManager.h#L1-L42)
## 性能考量
- 数据库连接与锁
- 初始化时设置最大锁数量,避免高并发下的锁竞争
- 线程与临界区
- 设备扫描线程与系统时间线程分离,扫描线程使用临界区保护共享表
- 网络与心跳
- 在线模式下启用心跳定时器与网络工作线程,需注意线程安全与异常恢复
- 文件系统
- 启动阶段创建CACHE目录与子目录,避免运行期频繁创建导致的性能抖动
[本节为通用指导,无需具体文件分析]
## 故障排查指南
- 数据库连接失败
- 现象:InitInstance中捕获_com_error并提示连接失败
- 排查:确认ACCDB路径、密码、Jet OLEDB驱动安装情况
- 网络初始化失败
- 现象:WSAStartup失败或网络连接失败
- 排查:检查IP/端口配置、防火墙、网络连通性
- 扫描线程创建失败
- 现象:创建扫描线程失败或无法发现设备
- 排查:检查传输模式、USB/GD设备状态、权限与驱动
- 退出异常
- 现象:ExitInstance中关闭连接抛出异常
- 排查:查看错误钩子与日志,确保线程安全关闭与资源释放顺序
章节来源
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L293-L310)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L311-L320)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L578-L622)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L644-L737)
- [Global.cpp](file://cpp/Main/Global.cpp#L1168-L1181)
## 结论
CGeoMativeApp作为GeomativeStudio的主入口,承担了完整的应用生命周期管理职责。通过清晰的初始化流程、严格的资源管理与模块化设计,实现了数据库、网络、设备扫描、UI交互等功能的有序协同。全局变量与函数为跨模块共享提供了基础,而theApp/AfxGetApp()的使用模式确保了模块间松耦合与可维护性。建议在后续演进中持续关注线程安全、异常恢复与性能优化,以提升整体稳定性与用户体验。
@@ -0,0 +1,419 @@
# DevManager
<cite>
**本文档中引用的文件**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [DevManager.h](file://h/DevManager.h)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [Device.h](file://h/Device.h)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp)
- [SComPort.h](file://h/SComPort.h)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
- [checkupdate.h](file://h/checkupdate.h)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
</cite>
## 目录
1. [介绍](#介绍)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 介绍
DevManager模块是Geomative Studio系统中的核心组件,负责管理与GD10设备的连接、状态监控、参数配置和固件升级。该模块通过串口与GD10设备通信,使用DetcGD10Dev类进行设备检测,以及Device类对设备状态进行封装。DevManager与其他管理器(如ProManager、TdManager)协同工作,确保设备连接成功后能够同步项目信息和测量数据。本文档将详细说明DevManager的职责和实现,提供设备连接失败、通信超时等常见问题的解决方案,并分析其与其他管理器的数据交互流程。
## 项目结构
DevManager模块位于`cpp/Managers/`目录下,主要由`DevManager.cpp``DevManager.h`文件组成。该模块依赖于`Operator`目录下的`DetcGD10Dev.cpp``Device.cpp`文件,以及`Tools`目录下的`SComPort.cpp``GD10OperCmd.cpp`文件。`DetcGD10Dev`类负责检测GD10设备的连接状态,`Device`类封装了设备的状态和操作,`SComPort`类提供了串口通信功能,而`GD10OperCmd`类则处理与GD10设备的命令交互。
```mermaid
graph TD
subgraph "Managers"
DevManager[DevManager.cpp/h]
ProManager[ProManager.cpp/h]
TdManager[TdManager.cpp/h]
end
subgraph "Operator"
DetcGD10Dev[DetcGD10Dev.cpp/h]
Device[Device.cpp/h]
end
subgraph "Tools"
SComPort[SComPort.cpp/h]
GD10OperCmd[GD10OperCmd.cpp/h]
checkupdate[checkupdate.cpp/h]
end
DevManager --> DetcGD10Dev
DevManager --> Device
DevManager --> SComPort
DevManager --> GD10OperCmd
DevManager --> ProManager
DevManager --> TdManager
```
**图源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L1-L661)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L3893)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L1-L1199)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L1213)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L6839)
## 核心组件
DevManager模块的核心组件包括`CDevManager`类、`CDetcGD10Dev`类和`CDevice`类。`CDevManager`类负责管理设备的生命周期,包括设备的添加、删除、状态更新等操作。`CDetcGD10Dev`类用于检测GD10设备的连接状态,`CDevice`类则封装了设备的状态和操作。
**组件源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L1-L661)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L3893)
## 架构概述
DevManager模块的架构设计遵循分层原则,将设备管理、通信、状态监控等功能分离。`CDevManager`类作为顶层管理器,负责协调各个子组件的工作。`CDetcGD10Dev`类负责设备检测,`CDevice`类负责设备状态的封装和操作,`SComPort`类提供串口通信功能,`GD10OperCmd`类处理与GD10设备的命令交互。
```mermaid
classDiagram
class CDevManager {
+InitialDevLinkList()
+AddDevice(CDevice* const pDev)
+DeleteDevice(DWORD dwHandle)
+GetDeviceByID(DWORD dwDevID)
+GetDevice(DWORD dwHandle)
+GetDevice(CString szDevSN)
+GetRegisterDevice(CString szDevSN, bool &bIsRegister)
+GetOLDevList(CPtrArray *pOLDevList)
+GetFLDevList(CPtrArray* pFLDevList)
+UpdateDevInfo(STSynDevParam stDevParam, BYTE bRemoteDeveTyp)
+SetDeviceHandle(CDevice* const pDev)
+DeleteObjInMem(DWORD dwHandle)
+DeleteObjInMem(CString szDevSN)
+AddObjInMem(CString szDevSN)
+AddOfflineObjInMem(CString szDevSN)
+DeleteDevice(CString strDev)
+AddRemoteDevice(STSigRemoteDev stDevice)
+DeleteRemoteDevice(STSigRemoteDev stDevice)
+GetRemoteDeviceInfo()
}
class CDetcGD10Dev {
+GetInstance()
+DetectGD10Dev()
+GetGD10DevAddr()
+IsGD10DevConnect()
+FindUsbDevice(CString strDevName)
+CompareDriverName(CString strDriName, CString strDriverAddr)
+PrintLog(CString strLog)
}
class CDevice {
+CDevice(DWORD dwID, _ConnectionPtr& pConnection)
+~CDevice()
+Reset()
+ShowFLDetailInfo(CListCtrl &devDetailList)
+ShowOLDetailInfo(CListCtrl& devDetailList)
+ShowGRInfo(CListCtrl& devGRList)
+ShowACInfo(CListCtrl& devDetailList)
+ShowCableHeadInfoDlg()
+ReceiveFile(const CString &szLocFilePath, const CString& szHostFileName, int nRetryCnt)
+SendFile(const CString &szHostFilePath, const CString &szLocFilePath, const CString& szLocFileName)
+ExecuteOrder(CString strOrder, CString strSign, CString *pStrResult, int nRepeatCnt, int nPollCnt)
+ExecuteSignleOrder(CString f_szOrder, CString f_szSign, CString *f_szResult, int nCmdPollCnt)
+ModifyTimeWindow()
+LockDevice()
+GetSynInfo()
+GetDevInfo()
+GetGRInfo()
+GetPoleCount()
+Register()
+Unregister()
+ModifyParameter()
+TestGRForPerPole(int iSN, CStringArray& strResArray)
+TestGRForAllPole()
+SetState(UINT uState)
+SetID(DWORD dwID)
+PrintLog(CString& strLog)
+IsExistOtherUserData()
+CheckGD10Password(CString strGD10Password)
+EndTransfer()
}
class CSComPort {
+OpenComm(CString szComName)
+CloseComm()
+SendDataDirectly(char *pDataBuff, int iDataSize)
+ReceiveDataDirectly(char *pDataBuff, int* iDataSize)
+ZmodemReceiveDataDirectly(char *pDataBuff, int *iDataSize)
+ZmodemSendDataDirectly(char *pDataBuff, int iDataSize)
+ExecuteOrder(CString strOrder, CString strSign, CString *pStrResult, int nRepeatCnt)
+ExecuteSignleOrder(CString f_szOrder, CString f_szSign, CString *f_szResult)
+ExecuteNoResOrder(CString f_szOrder)
+ClearCommReceiveBuff()
+ClearCommSendBuff()
+SetScanBreakSign(BOOL bIsScanBreak)
+DetectLine()
+SetOwnerWnd(HWND hOwnerWnd)
+SetCommID(long lCommID)
+SComPortDetectBreakThreadFun(LPVOID pParam)
+FindComName(CStringArray* pStringArray)
}
class CGD10OperCmd {
+GetInstance()
+project_add(const char *mac, const char *projectcn)
+AddProjectInLocalHost(const char *pProjectCN)
+AddProjectInMacXml(const char* pMac, const char *pProjectCN)
+project_del(const char *mac, const char *projectcn)
+project_rename(const char *mac, const char *old_projectcn, const char *new_projectcn)
+tz_add(const char *mac, const char *projectcn, const char *tzcn)
+tz_del(const char *mac, const char *projectcn, const char *tzcn)
+tz_rename(const char *mac, const char *projectcn, const char *old_tzcn, const char *new_tzcn)
+task_add(const char *mac, const char *projectcn, const char *tzcn, const char *taskcn)
+task_del(const char *mac, const char *projectcn, const char *tzcn, const char *taskcn)
+task_rename(const char *mac, const char *projectcn, const char *tzcn, const char *old_taskcn, const char *new_taskcn)
+sync_project(const char *mac, const char *projectcn)
+sync_tz(const char *mac, const char *projectcn, const char *tzcn)
+sync_task(const char *mac, const char *projectcn, const char *tzcn, const char *taskcn)
+sync_all(const char *mac)
+get_project_list(const char *mac, CStringArray &project_list)
+get_tz_list(const char *mac, const char *projectcn, CStringArray &tz_list)
+get_task_list(const char *mac, const char *projectcn, const char *tzcn, CStringArray &task_list)
+get_sync_status(const char *mac, const char *projectcn, const char *tzcn, const char *taskcn)
+get_device_info(const char *mac, STSynDevParam &dev_info)
+get_gr_info(const char *mac, CStringArray &gr_info)
+get_ac_info(const char *mac, CStringArray &ac_info)
+get_cable_head_info(const char *mac, CStringArray &cable_head_info)
+get_time_info(const char *mac, SYSTEMTIME &time_info)
+set_time_info(const char *mac, SYSTEMTIME &time_info)
+get_parameter_info(const char *mac, STParameterInfo &param_info)
+set_parameter_info(const char *mac, STParameterInfo &param_info)
+get_firmware_version(const char *mac, CString &version)
+upgrade_firmware(const char *mac, const char *firmware_path)
+reset_device(const char *mac)
+lock_device(const char *mac)
+unlock_device(const char *mac)
+get_battery_info(const char *mac, float &battery_vol, int &battery_alarm)
+get_signal_info(const char *mac, int &signal_strength)
+get_network_info(const char *mac, CString &network_status)
+get_storage_info(const char *mac, int &total_space, int &used_space)
+get_temperature_info(const char *mac, float &temperature)
+get_humidity_info(const char *mac, float &humidity)
+get_pressure_info(const char *mac, float &pressure)
+get_gps_info(const char *mac, double &latitude, double &longitude, double &altitude)
+get_accelerometer_info(const char *mac, float &x, float &y, float &z)
+get_gyroscope_info(const char *mac, float &x, float &y, float &z)
+get_magnetometer_info(const char *mac, float &x, float &y, float &z)
+get_barometer_info(const char *mac, float &pressure)
+get_light_sensor_info(const char *mac, float &light_level)
+get_proximity_sensor_info(const char *mac, float &proximity)
+get_ambient_temperature_info(const char *mac, float &temperature)
+get_relative_humidity_info(const char *mac, float &humidity)
+get_air_quality_info(const char *mac, int &air_quality)
+get_noise_level_info(const char *mac, float &noise_level)
+get_vibration_info(const char *mac, float &vibration)
+get_motion_info(const char *mac, float &motion)
+get_orientation_info(const char *mac, float &orientation)
+get_rotation_info(const char *mac, float &rotation)
+get_acceleration_info(const char *mac, float &acceleration)
+get_velocity_info(const char *mac, float &velocity)
+get_distance_info(const char *mac, float &distance)
+get_speed_info(const char *mac, float &speed)
+get_direction_info(const char *mac, float &direction)
+get_altitude_info(const char *mac, float &altitude)
+get_pressure_info(const char *mac, float &pressure)
+get_temperature_info(const char *mac, float &temperature)
+get_humidity_info(const char *mac, float &humidity)
+get_light_level_info(const char *mac, float &light_level)
+get_proximity_info(const char *mac, float &proximity)
+get_ambient_temperature_info(const char *mac, float &temperature)
+get_relative_humidity_info(const char *mac, float &humidity)
+get_air_quality_info(const char *mac, int &air_quality)
+get_noise_level_info(const char *mac, float &noise_level)
+get_vibration_info(const char *mac, float &vibration)
+get_motion_info(const char *mac, float &motion)
+get_orientation_info(const char *mac, float &orientation)
+get_rotation_info(const char *mac, float &rotation)
+get_acceleration_info(const char *mac, float &acceleration)
+get_velocity_info(const char *mac, float &velocity)
+get_distance_info(const char *mac, float &distance)
+get_speed_info(const char *mac, float &speed)
+get_direction_info(const char *mac, float &direction)
+get_altitude_info(const char *mac, float &altitude)
}
CDevManager --> CDetcGD10Dev
CDevManager --> CDevice
CDevManager --> CSComPort
CDevManager --> CGD10OperCmd
CDevice --> CSComPort
CDevice --> CGD10OperCmd
```
**图源**
- [DevManager.h](file://h/DevManager.h#L1-L69)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L1-L38)
- [Device.h](file://h/Device.h#L1-L128)
- [SComPort.h](file://h/SComPort.h#L1-L74)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L1213)
## 详细组件分析
### DevManager分析
`CDevManager`类是设备管理的核心,负责管理所有设备的生命周期。它通过`InitialDevLinkList`方法初始化设备列表,从数据库中读取设备信息并创建`CDevice`对象。`AddDevice`方法用于添加新设备,`DeleteDevice`方法用于删除设备。`GetDeviceByID``GetDevice``GetDevice`方法用于根据不同的标识符获取设备对象。`GetOLDevList``GetFLDevList`方法分别获取在线和离线设备列表。`UpdateDevInfo`方法用于更新设备信息,`SetDeviceHandle`方法用于设置设备句柄。
#### 设备初始化和状态轮询
设备初始化和状态轮询是DevManager模块的重要功能。设备初始化通过`InitialDevLinkList`方法完成,该方法从数据库中读取设备信息并创建`CDevice`对象。状态轮询通过`GetOLDevList``GetFLDevList`方法实现,定期检查设备的连接状态并更新设备列表。
```mermaid
sequenceDiagram
participant DevManager as "DevManager"
participant Database as "数据库"
participant Device as "Device"
DevManager->>Database : 查询设备信息
Database-->>DevManager : 返回设备信息
DevManager->>Device : 创建CDevice对象
Device-->>DevManager : 返回设备对象
DevManager->>DevManager : 更新设备列表
```
**图源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L160-L201)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L486-L590)
### DetcGD10Dev分析
`CDetcGD10Dev`类负责检测GD10设备的连接状态。`DetectGD10Dev`方法通过调用`FindUsbDevice`方法查找USB设备,`GetGD10DevAddr`方法返回设备的地址,`IsGD10DevConnect`方法返回设备是否已连接。
#### 设备检测流程
设备检测流程通过`DetectGD10Dev`方法实现,该方法首先清空设备地址,然后调用`FindUsbDevice`方法查找USB设备。如果找到设备,`m_strDevAddr`将被设置为设备地址,`m_bGD10DevIsCon`将被设置为`true`
```mermaid
flowchart TD
Start([开始]) --> DetectDevice["DetectGD10Dev()"]
DetectDevice --> ClearAddr["清空设备地址"]
ClearAddr --> FindDevice["FindUsbDevice(strDevName)"]
FindDevice --> CheckResult{"找到设备?"}
CheckResult --> |是| SetAddr["设置设备地址"]
SetAddr --> SetConnected["设置连接状态为true"]
SetConnected --> End([结束])
CheckResult --> |否| SetDisconnected["设置连接状态为false"]
SetDisconnected --> End
```
**图源**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L61-L68)
### Device分析
`CDevice`类封装了设备的状态和操作。`CDevice`类的构造函数接收设备ID和数据库连接指针,初始化设备对象。`Reset`方法用于重置设备,`ShowFLDetailInfo``ShowOLDetailInfo`方法用于显示设备的详细信息,`ShowGRInfo``ShowACInfo`方法用于显示接地电阻和视极化率信息。`ReceiveFile``SendFile`方法用于文件传输,`ExecuteOrder``ExecuteSignleOrder`方法用于执行命令。
#### 文件传输流程
文件传输流程通过`ReceiveFile``SendFile`方法实现。`ReceiveFile`方法接收文件,`SendFile`方法发送文件。文件传输过程中,`CProgressDlg`对话框用于显示传输进度。
```mermaid
sequenceDiagram
participant Device as "Device"
participant ProgressDlg as "CProgressDlg"
participant SComPort as "SComPort"
Device->>ProgressDlg : 创建CProgressDlg对象
ProgressDlg->>Device : 返回对话框对象
Device->>ProgressDlg : 设置设备、传输类型、文件路径
ProgressDlg->>Device : 显示对话框
Device->>SComPort : 发送文件传输命令
SComPort-->>Device : 返回传输结果
Device->>ProgressDlg : 更新传输进度
ProgressDlg-->>Device : 传输完成
Device->>ProgressDlg : 销毁对话框对象
```
**图源**
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L682-L725)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L59-L66)
### 与其他管理器的数据交互
DevManager模块与其他管理器(如ProManager、TdManager)协同工作,确保设备连接成功后能够同步项目信息和测量数据。`ProManager`类负责管理项目信息,`TdManager`类负责管理测量数据。
#### 项目信息同步
项目信息同步通过`ProManager`类的`ShowProList`方法实现。当设备连接成功后,`ProManager`会从数据库中读取项目信息,并将其同步到设备上。
```mermaid
sequenceDiagram
participant DevManager as "DevManager"
participant ProManager as "ProManager"
participant Database as "数据库"
DevManager->>ProManager : 请求项目列表
ProManager->>Database : 查询项目信息
Database-->>ProManager : 返回项目信息
ProManager-->>DevManager : 返回项目列表
DevManager->>Device : 同步项目信息
```
**图源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L184-L200)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L592-L642)
#### 测量数据同步
测量数据同步通过`TdManager`类的`ShowTdListByProject`方法实现。当设备连接成功后,`TdManager`会从数据库中读取测量数据,并将其同步到设备上。
```mermaid
sequenceDiagram
participant DevManager as "DevManager"
participant TdManager as "TdManager"
participant Database as "数据库"
DevManager->>TdManager : 请求测量数据列表
TdManager->>Database : 查询测量数据信息
Database-->>TdManager : 返回测量数据信息
TdManager-->>DevManager : 返回测量数据列表
DevManager->>Device : 同步测量数据
```
**图源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L81-L200)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L592-L642)
## 依赖分析
DevManager模块依赖于多个其他模块,包括`DetcGD10Dev``Device``SComPort``GD10OperCmd``ProManager``TdManager`。这些模块共同协作,确保设备管理功能的正常运行。
```mermaid
graph TD
DevManager[DevManager] --> DetcGD10Dev[DetcGD10Dev]
DevManager --> Device[Device]
DevManager --> SComPort[SComPort]
DevManager --> GD10OperCmd[GD10OperCmd]
DevManager --> ProManager[ProManager]
DevManager --> TdManager[TdManager]
DetcGD10Dev --> SComPort
Device --> SComPort
Device --> GD10OperCmd
ProManager --> Database[(数据库)]
TdManager --> Database
```
**图源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L1-L661)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L3893)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L1-L1199)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L1213)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L6839)
## 性能考虑
DevManager模块在设计时考虑了性能优化。例如,`InitialDevLinkList`方法在初始化设备列表时,会从数据库中批量读取设备信息,减少数据库查询次数。`GetOLDevList``GetFLDevList`方法在获取设备列表时,会缓存设备信息,减少重复查询。此外,`CDevice`类的`ExecuteOrder``ExecuteSignleOrder`方法支持重试机制,确保命令执行的可靠性。
## 故障排除指南
### 设备连接失败
设备连接失败可能是由于USB线缆问题、设备驱动问题或设备未正确连接。解决方法包括检查USB线缆、重新安装设备驱动、重新连接设备。
### 通信超时
通信超时可能是由于串口配置错误、设备响应慢或网络问题。解决方法包括检查串口配置、增加超时时间、检查网络连接。
### 文件传输失败
文件传输失败可能是由于文件路径错误、存储空间不足或设备忙。解决方法包括检查文件路径、清理存储空间、等待设备空闲。
**组件源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L383-L433)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L682-L725)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L127-L187)
## 结论
DevManager模块是Geomative Studio系统中的核心组件,负责管理与GD10设备的连接、状态监控、参数配置和固件升级。通过详细的分析,我们了解了DevManager模块的职责和实现,包括设备初始化、状态轮询、文件传输、与其他管理器的数据交互等。此外,我们还提供了设备连接失败、通信超时等常见问题的解决方案。DevManager模块的设计考虑了性能优化,确保了系统的高效运行。未来的工作可以进一步优化设备管理功能,提高系统的稳定性和用户体验。
@@ -0,0 +1,278 @@
# IOManager
<cite>
**本文档引用的文件**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [DataOperator.h](file://h/DataOperator.h)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperTxtFile.h](file://h/OperTxtFile.h)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
- [OperUrfFile.h](file://h/OperUrfFile.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
IOManager模块是Geomative Studio项目中的核心组件,负责处理数据的导入导出功能。该模块支持多种文件格式,包括Excel、CSV、TXT和URF等,能够协调DataOperator进行数据格式转换,并利用excel.cpp、OperTxtFile.cpp、OperUrfFile.cpp等工具类实现具体的文件读写操作。此外,IOManager还负责文件传输过程中的进度监控和错误处理机制,确保数据传输的可靠性和完整性。
## 项目结构
IOManager模块位于`cpp/Managers/`目录下,主要由`IOManager.cpp``IOManager.h`两个文件组成。该模块依赖于`DataOperator`类进行数据操作,并通过`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类实现具体的文件读写功能。`DataOperator`类位于`cpp/Operator/`目录下,而工具类则位于`cpp/Tools/`目录下。
```mermaid
graph TD
subgraph "IOManager模块"
IOManager[IOManager]
DataOperator[DataOperator]
end
subgraph "工具类"
Excel[excel.cpp]
OperTxtFile[OperTxtFile.cpp]
OperUrfFile[OperUrfFile.cpp]
end
IOManager --> DataOperator
DataOperator --> Excel
DataOperator --> OperTxtFile
DataOperator --> OperUrfFile
```
**Diagram sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
## 核心组件
IOManager模块的核心功能包括数据的导入导出、格式转换、文件读写以及进度监控和错误处理。该模块通过调用`DataOperator`类的方法来执行具体的数据操作,并利用`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类来实现不同格式的文件读写。
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
## 架构概述
IOManager模块的架构设计遵循了分层原则,将数据操作与文件读写分离,提高了代码的可维护性和可扩展性。`IOManager`类负责协调整个数据导入导出流程,`DataOperator`类负责具体的数据操作,而`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类则负责具体的文件读写操作。
```mermaid
graph TD
IOManager[IOManager] --> DataOperator[DataOperator]
DataOperator --> Excel[excel.cpp]
DataOperator --> OperTxtFile[OperTxtFile.cpp]
DataOperator --> OperUrfFile[OperUrfFile.cpp]
```
**Diagram sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
## 详细组件分析
### IOManager分析
`IOManager`类的主要职责是协调数据的导入导出流程。它通过调用`DataOperator`类的方法来执行具体的数据操作,并利用`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类来实现不同格式的文件读写。
#### 类图
```mermaid
classDiagram
class IOManager {
+Export()
+Import()
-CreateExpDatabase()
-ExportDataToAccdbFile()
-ImportProjectToDB()
-ImportTzToDB()
-ImportSptToDB()
-ImportRspTdToDB()
-ImportIpspTdToDB()
}
class DataOperator {
+ExportRsp2DTdToExcel()
+ExportRsp2DTdToCSV()
+ExportRsp2DTdToUrf()
+ExportRsp2DTdToTxt()
+ExportRspCETdToExcel()
+ExportRspCETdToCSV()
+ExportRspCETdToUrf()
+ExportRspCETdToTxt()
+ExportIpCETdToExcel()
+ExportIpCETdToCSV()
+ExportIpCETdToTxt()
+ExportIp2DTdToExcel()
+ExportIp2DTdToCSV()
+ExportIp2DTdToTxt()
+ExportSPCETdToExcel()
+ExportSPCETdToCSV()
+ExportSPCETdToTxt()
+ExportSP2DTdToExcel()
+ExportSP2DTdToCSV()
+ExportSP2DTdToTxt()
}
class excel {
+_Application
+_Workbook
+_Worksheet
}
class OperTxtFile {
+OpenFileforWrite()
+CloseFile()
+SetParamWidth()
+WriteFileContent()
+WriteEmptyRow()
}
class OperUrfFile {
+OpenUrfFileForWrite()
+WriteUrfHeadInfo()
+Write3DUrfHeadInfo()
+WriteUrfPoleInfo()
+WriteUrfDataInfo()
+CloseFile()
+WriteElecByWenSch()
+WriteElecByWenSch_AMN()
+WriteElecByWenSch_MNB()
+WriteElecByWenSch_AM()
+WriteElecByCrossHole()
+WriteElecByCrossHoleGeomative()
+WriteElecByCrossHoleGeomativeAM()
+WriteElecByAR()
+WriteUrfPoleInfo()
}
IOManager --> DataOperator
DataOperator --> excel
DataOperator --> OperTxtFile
DataOperator --> OperUrfFile
```
**Diagram sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
### DataOperator分析
`DataOperator`类负责具体的数据操作,包括数据的导出和导入。它提供了多种方法来导出不同格式的数据,如Excel、CSV、TXT和URF等。
#### 序列图
```mermaid
sequenceDiagram
participant IOManager as "IOManager"
participant DataOperator as "DataOperator"
participant Excel as "excel.cpp"
participant OperTxtFile as "OperTxtFile.cpp"
participant OperUrfFile as "OperUrfFile.cpp"
IOManager->>DataOperator : ExportRsp2DTdToExcel()
DataOperator->>Excel : SaveTdToExcelFile()
Excel-->>DataOperator : 返回结果
DataOperator-->>IOManager : 返回结果
IOManager->>DataOperator : ExportRsp2DTdToCSV()
DataOperator->>DataOperator : SaveTdToCsvFile()
DataOperator-->>IOManager : 返回结果
IOManager->>DataOperator : ExportRsp2DTdToUrf()
DataOperator->>OperUrfFile : ExportDataToUrf()
OperUrfFile-->>DataOperator : 返回结果
DataOperator-->>IOManager : 返回结果
IOManager->>DataOperator : ExportRsp2DTdToTxt()
DataOperator->>OperTxtFile : ExportResDataToTxt()
OperTxtFile-->>DataOperator : 返回结果
DataOperator-->>IOManager : 返回结果
```
**Diagram sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**Section sources**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [DataOperator.h](file://h/DataOperator.h)
### 工具类分析
`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类负责具体的文件读写操作。这些类提供了丰富的API来支持不同格式的文件读写。
#### 流程图
```mermaid
flowchart TD
Start([开始]) --> ValidateInput["验证输入参数"]
ValidateInput --> InputValid{"输入有效?"}
InputValid --> |否| ReturnError["返回错误响应"]
InputValid --> |是| CheckCache["检查缓存"]
CheckCache --> CacheHit{"缓存命中?"}
CacheHit --> |是| ReturnCache["返回缓存数据"]
CacheHit --> |否| QueryDB["查询数据库"]
QueryDB --> DBResult{"查询成功?"}
DBResult --> |否| HandleError["处理数据库错误"]
DBResult --> |是| ProcessData["处理原始数据"]
ProcessData --> UpdateCache["更新缓存"]
UpdateCache --> ReturnResult["返回处理结果"]
HandleError --> ReturnError
ReturnCache --> End([结束])
ReturnResult --> End
ReturnError --> End
```
**Diagram sources**
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**Section sources**
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
## 依赖分析
IOManager模块依赖于`DataOperator`类进行数据操作,并通过`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类实现具体的文件读写操作。这些依赖关系确保了模块的高内聚和低耦合,提高了代码的可维护性和可扩展性。
```mermaid
graph TD
IOManager[IOManager] --> DataOperator[DataOperator]
DataOperator --> Excel[excel.cpp]
DataOperator --> OperTxtFile[OperTxtFile.cpp]
DataOperator --> OperUrfFile[OperUrfFile.cpp]
```
**Diagram sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
## 性能考虑
IOManager模块在设计时充分考虑了性能因素。通过使用高效的文件读写操作和缓存机制,确保了数据传输的高效性。此外,模块还支持批量导出功能,进一步提高了数据处理的效率。
## 故障排除指南
在使用IOManager模块时,可能会遇到文件编码错误或格式不兼容的问题。为了解决这些问题,建议检查文件的编码格式,并确保文件格式与目标系统兼容。如果问题仍然存在,可以尝试使用不同的文件格式或编码方式。
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
## 结论
IOManager模块是Geomative Studio项目中的关键组件,负责处理数据的导入导出功能。通过协调`DataOperator`类和利用`excel.cpp``OperTxtFile.cpp``OperUrfFile.cpp`等工具类,该模块实现了对多种文件格式的支持,并确保了数据传输的可靠性和完整性。未来的工作可以进一步优化性能,提高用户体验。
@@ -0,0 +1,84 @@
# Managers模块
<cite>
**Referenced Files in This Document**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [ExecManager.cpp](file://cpp/Managers/ExecManager.cpp)
- [PasswordMng.cpp](file://cpp/Managers/PasswordMng.cpp)
- [TestManager.cpp](file://cpp/Managers/TestManager.cpp)
</cite>
## 目录
1. [DevManager职责与实现](#devmanager职责与实现)
2. [ProManager职责与实现](#promanager职责与实现)
3. [SptManager职责与实现](#sptmanager职责与实现)
4. [TdManager职责与实现](#tdmanager职责与实现)
5. [IOManager职责与实现](#iomanager职责与实现)
6. [ExecManager职责与实现](#execmanager职责与实现)
7. [PasswordMng职责与实现](#passwordmng职责与实现)
8. [TestManager职责与实现](#testmanager职责与实现)
9. [管理器间协作示例](#管理器间协作示例)
## DevManager职责与实现
DevManager模块负责设备连接管理、状态监控和固件升级。该管理器通过`CDevManager`类实现,主要功能包括设备的添加、删除、状态更新和在线/离线设备列表的管理。它维护一个设备链表`m_devLinkList`,用于存储和管理所有设备对象。当设备连接时,DevManager会通过`AddDevice`方法将设备信息写入数据库,并在内存中创建设备对象。设备的状态(在线、离线)通过`m_uState`字段进行跟踪,状态更新通过`UpdateDevInfo`方法实现,该方法会将设备的硬件版本、软件版本、电池电压等信息同步到数据库中。对于固件升级,虽然具体实现未在代码中直接体现,但通过设备通信机制,可以实现固件的远程更新。
**Section sources**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L24-#L642)
## ProManager职责与实现
ProManager模块负责项目创建、测区管理和任务配置。该管理器通过`CProManager`类实现,提供了创建、显示和管理项目、测区和任务的接口。`CreateProjectInDB`方法用于在数据库中创建新项目,包括项目名称、描述、位置等信息,并确保项目名称的唯一性。`CreateProjectInDev`方法则负责将项目信息同步到设备端,通过生成XML文件并传输到设备的SD卡目录下。对于测区管理,`InsertDefaultTzToProject`方法会在项目创建时自动添加一个默认测区。任务配置方面,ProManager通过`ShowTzList``ShowProList`等方法提供项目和测区的列表显示功能,支持用户进行任务的配置和选择。
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L32-#L707)
## SptManager职责与实现
SptManager模块负责生成和管理2D/3D/跨孔测量脚本。该管理器通过`CSptManager`类实现,支持多种测量模式的脚本创建。`Create2DSConInDB`方法用于在数据库中创建二维测量脚本,包括脚本名称、电极数量、通道数等参数,并将脚本记录写入`script2d`表。对于跨孔测量,`CreateCESConInDB`方法实现了1D跨孔脚本的创建,支持自定义的测量参数。SptManager还维护了一个介质链表`m_medLinkList`,用于存储不同测量模式对应的介质对象,如`CMediumA``CMediumB`等,这些介质对象定义了具体的测量几何配置。脚本的生成过程涉及用户界面交互,通过`COpCreateSptDlg`等对话框收集用户输入,并最终将配置信息持久化到数据库。
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L74-#L800)
## TdManager职责与实现
TdManager模块负责测试数据的采集、存储和分析。该管理器通过`CTdManager`类实现,提供了测试数据的列表显示、数据采集和存储功能。`ShowTdListByProject``ShowTdListByTz`方法用于根据项目或测区显示相关的测试数据列表,支持多种测试类型(如2D电阻率、IP等)。测试数据的存储通过`CTdManager`与数据库的交互实现,将测量结果写入`td``rspcon``ipspcon`等数据表中。对于数据分析,TdManager提供了数据导出功能,可以将测试数据导出为Access数据库文件,便于后续处理。此外,该管理器还负责管理测试数据的元信息,如测量时间、天气条件、操作人员等。
**Section sources**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L57-#L800)
## IOManager职责与实现
IOManager模块负责数据导入导出功能,支持Excel、CSV、TXT、URF格式。该管理器通过`CIOManager`类实现,提供了`Export``Import`两个主要方法。`Export`方法允许用户将项目、测区和测试数据导出为Access数据库文件(.accdb),通过`CreateExpDatabase`方法创建目标数据库,并使用`ExportDataToAccdbFile`方法将数据写入。`Import`方法则从Access文件中读取数据,并将其导入到本地数据库中,支持项目、测区、脚本和测试数据的完整导入。虽然代码中主要实现了Access格式的导入导出,但通过扩展`OperTxtFile``OperUrfFile`等工具类,可以支持其他格式如TXT和URF。对于Excel和CSV格式,系统可能通过外部库或工具进行转换。
**Section sources**
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp#L23-#L800)
## ExecManager职责与实现
ExecManager模块负责任务执行流程的管理。该管理器通过`CExecManager`类实现,协调测试任务的执行。`Exec2DRSPTest`方法是执行2D电阻率测量的核心,它首先通过`Save2DRSPSetInfo`方法保存测试设置,然后将配置文件发送到设备端,最后初始化测量过程。任务执行流程包括:1) 用户在界面设置测量参数;2) ExecManager生成配置文件并发送到设备;3) 设备接收指令并开始测量;4) 测量数据实时回传并存储。`InitialMeasure`方法负责与设备通信,发送`meas_add`等命令来启动测量。整个流程通过非模态对话框`COpExec2DRSPTestDlg`进行监控,确保用户可以实时查看测量状态。
**Section sources**
- [ExecManager.cpp](file://cpp/Managers/ExecManager.cpp#L49-#L613)
## PasswordMng职责与实现
PasswordMng模块负责密码管理机制。该管理器通过`CPasswordMng`类实现,提供密码的创建、修改和删除功能。密码信息存储在数据库的`password_info`表中,包含设备类型、设备名称和密码字段。`OperatePassword`方法根据操作类型(创建、修改、删除)生成相应的SQL语句,并执行数据库事务。对于GD-10设备,该管理器还通过串口命令与设备通信,使用`check_password`命令验证旧密码,并使用`set_param("password,...")`命令在设备端设置新密码,确保数据库和设备端的密码同步。密码的输入通过对话框界面完成,`OnOK`方法负责验证用户输入,确保新密码与确认密码一致,并调用`VerifyPassword`方法检查密码格式(必须由字母和数字组成)。
**Section sources**
- [PasswordMng.cpp](file://cpp/Managers/PasswordMng.cpp#L21-#L623)
## TestManager职责与实现
TestManager模块负责测试任务的调度。该管理器通过`CTestManager`类实现,目前代码中仅包含构造函数和析构函数的定义,具体实现细节未在提供的文件中体现。根据命名和模块化设计原则,TestManager可能负责管理测试任务队列、调度任务执行顺序、处理任务间的依赖关系以及监控任务执行状态。它可能与其他管理器(如ExecManager、TdManager)协同工作,确保测试任务能够按照预定计划高效执行。
**Section sources**
- [TestManager.cpp](file://cpp/Managers/TestManager.cpp#L18-#L27)
## 管理器间协作示例
各个管理器之间通过紧密协作实现完整的测量工作流。例如,当用户创建一个新项目时,ProManager首先在数据库中创建项目记录,然后触发SptManager生成默认的测量脚本。项目创建完成后,如果设备已连接,DevManager会检测到设备在线状态,并通知ProManager将项目数据同步到设备。ProManager调用`CreateProjectInDev`方法,生成项目配置文件并通过DevManager的设备对象发送到设备端。当用户开始执行测量任务时,ExecManager从ProManager获取项目和测区信息,从SptManager获取测量脚本,配置测试参数后,通过DevManager与设备通信,启动测量。测量过程中,TdManager负责接收和存储从设备传回的数据。整个流程体现了各管理器职责分明又相互协作的设计理念。
@@ -0,0 +1,216 @@
# ProManager
<cite>
**本文档引用的文件**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [ProManager.h](file://h/ProManager.h)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp)
- [Project.h](file://h/Project.h)
- [TestingZone.cpp](file://cpp/ProblemZone/TestingZone.cpp)
- [TestingZone.h](file://h/TestingZone.h)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [SptManager.h](file://h/SptManager.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目生命周期管理](#项目生命周期管理)
3. [测区管理机制](#测区管理机制)
4. [数据持久化流程](#数据持久化流程)
5. [与SptManager的协调](#与sptmanager的协调)
6. [恢复策略](#恢复策略)
7. [代码示例](#代码示例)
8. [结论](#结论)
## 简介
ProManager模块是Geomative Studio软件的核心组件之一,负责管理项目的整个生命周期。该模块提供了创建、打开、保存和关闭项目的基本功能,同时管理与项目相关的测区(TestingZone)和任务包配置。ProManager通过协调数据库和XML缓存文件之间的数据同步,确保项目数据的完整性和一致性。此外,它还与SptManager模块紧密协作,生成与项目关联的测量脚本。
**Section sources**
- [ProManager.h](file://h/ProManager.h#L30-L64)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L32-L37)
## 项目生命周期管理
ProManager模块实现了完整的项目生命周期管理功能,包括项目创建、打开、保存和关闭操作。当用户创建新项目时,ProManager首先通过`CreateProjectInDB`方法在数据库中创建项目记录,然后通过`CreateProjectInDev`方法在设备上创建相应的项目结构。项目创建过程中,系统会自动生成一个唯一的项目编号(CN),并创建一个默认的测区。
项目打开操作通过`LoadProjectFromDev`方法实现,该方法从设备加载项目数据并同步到本地数据库。项目保存操作则通过将内存中的项目数据序列化为XML格式并保存到缓存文件中来实现。项目关闭时,ProManager会清理内存中的项目对象,释放相关资源。
```mermaid
flowchart TD
Start([开始]) --> CreateProject["创建项目"]
CreateProject --> CheckExist["检查项目名称是否存在"]
CheckExist --> |存在| ShowError["显示错误信息"]
CheckExist --> |不存在| InsertDB["在数据库插入记录"]
InsertDB --> GenerateCN["生成项目编号"]
GenerateCN --> CreateDefaultTz["创建默认测区"]
CreateDefaultTz --> CreateDev["在设备创建项目"]
CreateDev --> SaveXML["保存project.xml"]
SaveXML --> End([结束])
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L317)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L317)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L19-L50)
## 测区管理机制
测区(TestingZone)是项目中的重要组成部分,用于组织和管理测量任务。ProManager通过`CreateTzInDB``CreateTzInDev`方法实现测区的创建。每个测区都有一个类型属性(TZtype),用于区分不同类型的测量任务,如1D VES、2D ERI或3D ERT。
测区配置逻辑包括设置测区名称、描述、位置和创建日期等基本信息。ProManager通过`ShowTzList`方法显示指定项目下的所有测区列表,用户可以通过界面操作选择和管理测区。当创建新测区时,系统会自动为其分配一个唯一的测区编号(CN),并在数据库和设备上同步创建相应的结构。
```mermaid
classDiagram
class CProManager {
+ShowTzList(dwProHandle, tzList)
+CreateTzInDB(dwID, dwPrID)
+CreateTzInDev(dwID, pDev)
+DeleteTzInDB(dwID)
+DeleteTzInDev(szPrCN, szTzCN, pDev)
}
class CTestingZone {
+m_szTZname
+m_szCDate
+m_szDesc
+m_szLocation
+m_szCN
+m_szTZtype
}
CProManager --> CTestingZone : "创建"
CProManager --> CTestingZone : "管理"
```
**Diagram sources**
- [ProManager.h](file://h/ProManager.h#L53-L58)
- [TestingZone.h](file://h/TestingZone.h#L14-L30)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L53-L92)
- [TestingZone.cpp](file://cpp/ProblemZone/TestingZone.cpp#L18-L44)
- [TestingZone.h](file://h/TestingZone.h#L14-L30)
## 数据持久化流程
ProManager模块采用混合持久化策略,将项目数据存储在数据库和XML缓存文件中。核心项目数据(如项目名称、描述、位置等)存储在Access数据库的project表中,而项目结构和配置信息则以XML格式存储在CACHE目录下的project.xml文件中。
数据持久化流程如下:当项目创建或修改时,ProManager首先更新数据库中的记录,然后生成或更新project.xml文件。XML文件包含项目的基本信息、测区列表和配置参数。系统使用CMarkup类来处理XML文件的读写操作。项目数据在内存、数据库和XML文件之间保持同步,确保数据的一致性和完整性。
```mermaid
flowchart LR
Memory[内存对象] --> Database[(数据库)]
Memory --> XML[project.xml]
Database --> Memory
XML --> Memory
subgraph "数据流"
Database
XML
Memory
end
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L398-L426)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1746-L1863)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L398-L426)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1746-L1863)
- [Project.h](file://h/Project.h#L16-L38)
## 与SptManager的协调
ProManager与SptManager模块紧密协作,共同完成项目相关的测量脚本管理。当需要为项目生成测量脚本时,ProManager会调用SptManager的相关方法。SptManager负责具体的脚本创建、编辑和验证逻辑,而ProManager则负责提供项目上下文和持久化支持。
这种协作关系体现在项目创建后的脚本生成流程中:ProManager首先创建项目结构,然后通知SptManager为该项目生成相应的测量脚本。脚本数据存储在独立的数据库表中,但与项目数据通过外键关联。这种设计实现了功能分离,同时保持了数据的完整性。
```mermaid
sequenceDiagram
participant User as "用户"
participant ProManager as "ProManager"
participant SptManager as "SptManager"
participant Database as "数据库"
User->>ProManager : 创建新项目
ProManager->>Database : 插入项目记录
ProManager->>SptManager : 请求生成脚本
SptManager->>SptManager : 创建脚本配置
SptManager->>Database : 保存脚本数据
SptManager-->>ProManager : 脚本创建完成
ProManager-->>User : 项目创建成功
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L257-L487)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L257-L487)
- [SptManager.h](file://h/SptManager.h#L30-L48)
## 恢复策略
ProManager模块实现了多种恢复策略,以应对项目文件损坏或版本不兼容的情况。当检测到project.xml文件损坏时,系统可以从数据库中重建XML文件。恢复流程首先验证数据库中的项目记录完整性,然后重新生成XML文件结构。
对于版本不兼容问题,ProManager采用向后兼容的设计原则。新版本的软件可以读取旧版本创建的项目文件,系统会自动进行必要的数据转换和升级。在极端情况下,如果项目数据无法恢复,系统会创建一个新的空白项目,并尝试从备份中恢复尽可能多的数据。
```mermaid
flowchart TD
Start([开始]) --> CheckXML["检查project.xml"]
CheckXML --> |有效| LoadNormal["正常加载"]
CheckXML --> |无效| CheckDB["检查数据库"]
CheckDB --> |完整| RebuildXML["重建XML文件"]
CheckDB --> |不完整| CreateNew["创建新项目"]
RebuildXML --> Validate["验证数据"]
Validate --> |成功| LoadRebuilt["加载重建文件"]
Validate --> |失败| CreateNew
CreateNew --> Initialize["初始化新项目"]
Initialize --> End([结束])
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1746-L1863)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1865-L1999)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1746-L1863)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1865-L1999)
## 代码示例
以下代码示例展示了项目创建和测区配置的核心方法调用。`CreateProjectInDB`方法处理数据库层面的项目创建,而`CreateProjectInDev`方法负责在设备上创建项目结构。测区配置通过`InsertDefaultTzToProject`方法实现,该方法为新项目创建一个默认的测区。
```mermaid
flowchart TD
A["CreateProjectInDB"] --> B["显示创建对话框"]
B --> C["验证项目名称"]
C --> |已存在| D["显示错误信息"]
C --> |不存在| E["生成项目编号"]
E --> F["插入数据库记录"]
F --> G["创建默认测区"]
G --> H["返回成功状态"]
I["CreateProjectInDev"] --> J["获取项目信息"]
J --> K["创建本地目录"]
K --> L["生成project.xml"]
L --> M["发送到设备"]
M --> |成功| N["更新同步状态"]
M --> |失败| O["清理并返回错误"]
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L317)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L319-L341)
**Section sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L317)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L343-L483)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L319-L341)
## 结论
ProManager模块作为Geomative Studio项目管理的核心,实现了完整的项目生命周期管理功能。通过精心设计的架构,该模块有效地协调了数据库存储和XML文件缓存,确保了项目数据的持久性和一致性。其与SptManager模块的协作机制,为测量脚本的生成和管理提供了坚实的基础。完善的恢复策略进一步增强了系统的可靠性和用户体验。
@@ -0,0 +1,222 @@
# SptManager
<cite>
**本文档引用的文件**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [SptManager.h](file://h/SptManager.h)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
- [Script.h](file://h/Script.h)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [testzone.xml](file://CACHE/testzone.xml)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
SptManager模块是Geomative Studio软件中的核心组件,负责生成和管理各种测量脚本(包括2D、3D和跨孔测量)。该模块根据ProManager中定义的项目和测区信息,调用Script2D、Script3D、ScriptCE等具体类来生成符合测量协议的脚本数据。SptManager不仅处理脚本的创建和验证,还负责脚本的存储(如testzone.xml)和导出流程。本文档将深入分析SptManager的职责和实现,提供脚本生成失败或逻辑错误的排查方法,并通过代码示例展示从测区参数到最终脚本数据的转换过程。
## 项目结构
SptManager模块位于`cpp/Managers/`目录下,其主要功能是通过调用不同类型的脚本类(如Script2D、Script3D、ScriptCE)来生成测量脚本。这些脚本类位于`cpp/ProblemZone/`目录下,每个类负责特定类型的测量脚本生成。SptManager还与ProManager模块紧密协作,获取项目和测区信息,确保生成的脚本符合实际测量需求。
```mermaid
graph TB
subgraph "核心模块"
SptManager[SptManager]
ProManager[ProManager]
end
subgraph "脚本生成类"
Script2D[Script2D]
Script3D[Script3D]
ScriptCE[ScriptCE]
end
SptManager --> ProManager
SptManager --> Script2D
SptManager --> Script3D
SptManager --> ScriptCE
```
**图表来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L1-L3589)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp#L1-L237)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp#L1-L221)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp#L1-L146)
**章节来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L1-L3589)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
## 核心组件
SptManager的核心功能包括脚本的创建、验证、存储和导出。它通过`Create2DSConInDB``CreateCESConInDB`等方法创建不同类型的测量脚本,并将这些脚本存储在数据库中。SptManager还负责将脚本导出到设备,并从设备加载脚本。
**章节来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L257-L800)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp#L1-L237)
## 架构概述
SptManager的架构设计遵循模块化原则,通过继承和多态机制实现不同类型的脚本生成。CScript类作为基类,定义了所有脚本的通用接口,而CScript2D、CScript3D、CScriptCE等派生类则实现了特定类型的脚本生成逻辑。SptManager通过`GetScript`方法根据句柄动态创建相应的脚本对象,实现了灵活的脚本管理。
```mermaid
classDiagram
class CScript {
+DWORD m_dwID
+_ConnectionPtr m_pConnection
+int m_iAR
+int m_iEAmount
+CScript(DWORD dwID, _ConnectionPtr& pConnection)
+~CScript()
+virtual bool ShowSptDetailInfo(CListCtrl &sptDetailList)
+virtual bool ShowChannelList(CListCtrl &sptChannelList)
+virtual bool ShowSptConInfo(CListCtrl &sptConList)
+virtual void AdjustRecListColumn(int iAR, CListCtrl& sptConListInfo)
}
class CScript2D {
+CScript2D(DWORD dwID, _ConnectionPtr& pConnection)
+~CScript2D()
+bool ShowSptDetailInfo(CListCtrl &sptDetailList)
+bool ShowChannelList(CListCtrl &sptChannelList)
+bool ShowSptConInfo(CListCtrl &sptConList)
+void AdjustRecListColumn(int iAR, CListCtrl& sptConListInfo)
}
class CScript3D {
+CScript3D(DWORD dwID, _ConnectionPtr& pConnection)
+~CScript3D()
+bool ShowSptDetailInfo(CListCtrl &sptDetailList)
+bool ShowChannelList(CListCtrl &sptChannelList)
+bool ShowSptConInfo(CListCtrl &sptConList)
+void AdjustRecListColumn(int iAR, CListCtrl& sptConListInfo)
}
class CScriptCE {
+CScriptCE(DWORD dwID, _ConnectionPtr& pConnection)
+~CScriptCE()
+bool ShowSptDetailInfo(CListCtrl &sptDetailList)
+bool ShowChannelList(CListCtrl &sptChannelList)
+bool ShowSptConInfo(CListCtrl &sptConList)
}
CScript <|-- CScript2D
CScript <|-- CScript3D
CScript <|-- CScriptCE
```
**图表来源**
- [Script.h](file://h/Script.h)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp#L1-L237)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp#L1-L221)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp#L1-L146)
## 详细组件分析
### SptManager分析
SptManager模块的主要职责是协调脚本的生成和管理。它通过`Create2DSConInDB`方法创建2D测量脚本,通过`CreateCESConInDB`方法创建跨孔测量脚本。这些方法首先通过对话框获取用户输入的脚本参数,然后在数据库中创建相应的记录。
#### 脚本创建流程
```mermaid
sequenceDiagram
participant 用户 as "用户"
participant SptManager as "SptManager"
participant 数据库 as "数据库"
用户->>SptManager : 请求创建2D脚本
SptManager->>SptManager : 显示创建对话框
SptManager->>用户 : 获取脚本参数
SptManager->>数据库 : 检查脚本名称是否已存在
alt 脚本名称已存在
数据库-->>SptManager : 返回存在
SptManager-->>用户 : 显示错误消息
else 脚本名称不存在
数据库-->>SptManager : 返回不存在
SptManager->>数据库 : 插入scon记录
SptManager->>数据库 : 插入channel记录
SptManager->>数据库 : 插入script2d记录
数据库-->>SptManager : 确认插入成功
SptManager-->>用户 : 显示成功消息
end
```
**图表来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L257-L800)
#### 脚本验证与存储
SptManager在创建脚本时会进行严格的验证,确保脚本名称的唯一性。如果脚本名称已存在,系统会提示用户并取消创建操作。脚本数据存储在数据库的`scon``channel``script2d`表中,其中`scon`表存储脚本的基本信息,`channel`表存储通道信息,`script2d`表存储具体的测量点数据。
**章节来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L334-L351)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L363-L380)
### Script2D分析
Script2D类负责生成2D测量脚本。它继承自CScript类,并实现了特定于2D测量的逻辑。Script2D的主要功能包括显示脚本详细信息、显示通道列表和显示脚本配置信息。
#### 显示脚本详细信息
```mermaid
flowchart TD
Start([开始]) --> GetSConData["从scon表获取数据"]
GetSConData --> CheckRecordCount{"记录数 > 0?"}
CheckRecordCount --> |是| SetDetailInfo["设置详细信息"]
CheckRecordCount --> |否| End([结束])
SetDetailInfo --> End
```
**图表来源**
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp#L28-L71)
#### 显示通道列表
Script2D通过查询`channel``medium`表来获取通道信息,并将其显示在列表控件中。每个通道的装置类型编号(AR)也会被获取并存储。
**章节来源**
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp#L74-L109)
### Script3D分析
Script3D类负责生成3D测量脚本。它的实现与Script2D类似,但针对3D测量的特点进行了优化。Script3D同样实现了显示脚本详细信息、显示通道列表和显示脚本配置信息的功能。
**章节来源**
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp#L28-L151)
### ScriptCE分析
ScriptCE类负责生成跨孔测量脚本。它继承自CScript类,并实现了特定于跨孔测量的逻辑。ScriptCE的主要功能包括显示脚本配置信息、显示脚本详细信息和显示通道列表。
**章节来源**
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp#L28-L146)
## 依赖分析
SptManager模块依赖于多个其他模块和类,包括ProManager、Script2D、Script3D、ScriptCE等。这些依赖关系确保了SptManager能够获取项目和测区信息,并生成符合实际测量需求的脚本。
```mermaid
graph TD
SptManager --> ProManager
SptManager --> Script2D
SptManager --> Script3D
SptManager --> ScriptCE
SptManager --> CMarkup
SptManager --> _ConnectionPtr
```
**图表来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L1-L3589)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
## 性能考虑
SptManager在处理大量脚本数据时需要考虑性能问题。为了避免数据库操作的性能瓶颈,SptManager采用了事务处理机制,确保脚本创建的原子性。此外,SptManager还通过缓存机制减少对数据库的频繁访问,提高整体性能。
## 故障排除指南
当脚本生成失败或出现逻辑错误时,可以按照以下步骤进行排查:
1. **检查脚本名称**:确保脚本名称在数据库中是唯一的,避免名称冲突。
2. **验证数据库连接**:确保数据库连接正常,能够正确执行SQL语句。
3. **检查参数输入**:确认用户输入的脚本参数是否正确,特别是电极数量、通道数量等关键参数。
4. **查看日志文件**:检查系统日志文件,查找可能的错误信息或异常堆栈。
**章节来源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L334-L351)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L488-L498)
## 结论
SptManager模块是Geomative Studio软件中不可或缺的一部分,负责生成和管理各种测量脚本。通过深入分析SptManager的职责和实现,我们可以更好地理解其工作原理,并在实际应用中更有效地使用和维护该模块。未来的工作可以进一步优化SptManager的性能,提高脚本生成的效率和可靠性。
@@ -0,0 +1,513 @@
# TdManager
<cite>
**本文档引用的文件**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TdManager.h](file://h/TdManager.h)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TaskDataOper.h](file://h/TaskDataOper.h)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp)
- [TdRecord.h](file://h/TdRecord.h)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp)
- [TestingData.h](file://h/TestingData.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
10. [附录](#附录)(如有必要)
## 简介
TdManager模块是GeomativeStudio软件中负责测试数据采集、存储和管理的核心组件。该模块主要负责接收来自设备或文件的原始测量数据,通过TaskDataOper进行处理,并将结果持久化到TdRecord和TestingData对象中。它在测试数据与项目、脚本的关联机制中起着关键作用,确保数据的完整性和一致性。本文档将详细介绍TdManager模块的职责和实现,包括数据解析错误或存储失败的解决方案,并提供代码示例展示数据接收和记录的核心流程。
## 项目结构
GeomativeStudio项目的结构清晰,主要分为以下几个部分:
- **CACHE**: 存储缓存文件,如`project.xml``testzone.xml`
- **DB**: 包含数据库相关的文件,如`数据库字段修改记录.txt`
- **Install**: 包含安装文件和配置文件,如`config.ini``version_info.txt`
- **LOG**: 存储日志文件,如`upg_geo_filetrans_log.txt`
- **Release**: 包含发布版本的文件,如`config.ini``msado15.tlh`
- **cpp**: 源代码目录,包含多个子目录,如`Managers``Operator``ProblemZone`等。
- **h**: 头文件目录,包含与源代码对应的头文件。
- **res**: 资源文件目录,包含资源文件如`GeoMative.rc2`
- **tools**: 工具脚本目录,包含批处理文件如`IAP-GD10.bat`
```mermaid
graph TD
subgraph "根目录"
CACHE["CACHE"]
DB["DB"]
Install["Install"]
LOG["LOG"]
Release["Release"]
cpp["cpp"]
h["h"]
res["res"]
tools["tools"]
end
subgraph "cpp"
Managers["Managers"]
Operator["Operator"]
ProblemZone["ProblemZone"]
end
subgraph "Managers"
TdManager["TdManager.cpp"]
TaskDataOper["TaskDataOper.cpp"]
end
subgraph "ProblemZone"
TdRecord["TdRecord.cpp"]
TestingData["TestingData.cpp"]
end
subgraph "h"
TdManagerH["TdManager.h"]
TaskDataOperH["TaskDataOper.h"]
TdRecordH["TdRecord.h"]
TestingDataH["TestingData.h"]
end
TdManager --> TdManagerH
TaskDataOper --> TaskDataOperH
TdRecord --> TdRecordH
TestingData --> TestingDataH
```
**图表来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 核心组件
TdManager模块的核心组件包括`TdManager``TaskDataOper``TdRecord``TestingData`。这些组件协同工作,确保测试数据的高效采集、处理和存储。
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 架构概述
TdManager模块的架构设计旨在实现高效的数据管理和处理。模块通过`TdManager`类接收来自设备或文件的原始测量数据,然后通过`TaskDataOper`类进行数据处理,最终将结果持久化到`TdRecord``TestingData`对象中。这种设计确保了数据的完整性和一致性,同时提供了灵活的数据处理和存储机制。
```mermaid
graph TD
subgraph "数据采集"
Device["设备"]
File["文件"]
end
subgraph "数据处理"
TdManager["TdManager"]
TaskDataOper["TaskDataOper"]
end
subgraph "数据存储"
TdRecord["TdRecord"]
TestingData["TestingData"]
end
Device --> TdManager
File --> TdManager
TdManager --> TaskDataOper
TaskDataOper --> TdRecord
TaskDataOper --> TestingData
```
**图表来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 详细组件分析
### TdManager 分析
`TdManager`类是测试数据管理的核心,负责接收和处理来自设备或文件的原始测量数据。它通过`ImportTdHeadToDB``Import2DTdOrgToDB``ImportCETdOrgToDB`等方法将数据导入数据库,并通过`UploadCETdFromDev``Upload2DTdFromDev`等方法从设备上载数据。
#### 类图
```mermaid
classDiagram
class CTdManager {
+void DeleteSPCETd(DWORD dwID)
+void DeleteSP3DTd(DWORD dwID)
+void DeleteSP2DTd(DWORD dwID)
+void DeleteRsp3DTd(DWORD dwID)
+void DeleteIpsp3DTd(DWORD dwID)
+BOOL CheckTdExist(CString szPrCN, CString szTzCN, CString szTdCN, CDevice* const pDev, DWORD *TdId)
+BOOL OnlineCheckTdExist(CString szTdID, CString szDevSN, DWORD *TdId)
+BOOL OnlineCheckElecInfoExist(CString strTdID, CString szDevSN)
+DWORD ImportTdHeadToDB(DWORD dwTzID, CString szHeadFile, CDevice* const pDev, DWORD* pExTdID = NULL)
+BOOL Import2DTdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+BOOL ImportCETdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+BOOL Import3DTdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+BOOL Import2DTdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev, int* pTSN=NULL)
+BOOL ImportCETdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev)
+BOOL Import3DTdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev, int* pTSN = NULL)
+BOOL Import2DGRToDB(DWORD dwTdID, CString szGRFile, CDevice* const pDev)
+BOOL Import3DGRToDB(DWORD dwTdID, CString szGRFile, CDevice* const pDev)
+BOOL ImportCETGRToDB(DWORD dwTdID, CString szGRFile)
+BOOL UploadCETdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+BOOL Upload2DTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+BOOL Upload3DTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+BOOL UploadWellTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+BOOL DeleteTdInDev(CString szPrCN, CString szTzCN, CString szTdCN, CDevice* const pDev)
+BOOL InitialTDListByTzForSyn(CListCtrl &tdList, CString szDeSN, CString szTzCN)
+BOOL InitialTDListByProForSyn(CListCtrl &tdList, CString szDeSN, CString szPrCN)
+BOOL InitialTDListByOnLineForSyn(CListCtrl &tdList, CString szDeSN, UINT32 &uiTotNum)
+BOOL InitialDevListByOnLineForSyn(CListCtrl &tdList, CString szDeSN, STSynDevParam *ptLocalDevParam)
+BOOL InitialCableListByOnLineForSyn(CListCtrl &tdList, CString szDeSN, STRemCableCallInfo *ptLocalCableInfo)
+BOOL OnlineSevTDListSynToDB(STRemTaskArg *tTaskArg, CString szDeSN)
+DWORD OnLineImportTdHeadToDB(STRemTaskArg *tTaskArg, CString szDevSN)
+BOOL Convert2DTo3D(DWORD dwTzHandle)
+void DeleteObjInMem(DWORD dwHandle)
+void DeleteRspCETd(DWORD dwID)
+void DeleteIpspCETd(DWORD dwID)
+void DeleteRsp2DTd(DWORD dwID)
+void DeleteIpsp2DTd(DWORD dwID)
+CTestingData* GetTestingData(DWORD dwHandle)
+bool ShowTdListByTz(DWORD dwTzHandle, CListCtrl& tdList)
+bool ShowTd2DListByTz(DWORD dwTzHandle, int iEAmount, float fEDistance, int iAR, CListCtrl& tdList)
+bool ShowTdListByDev(DWORD dwDevHandle, CListCtrl& tdList)
+bool ImportTdSpecAttr(const CStringArray& strAttrArray, int iDataType, DWORD dwChID, int iTSN)
+bool ShowTdListByProject(DWORD dwProHandle, CListCtrl& tdList)
+void GetTaskAttr(DWORD dwHandle, DWORD& dwTdID, int& iStyle)
+CTdManager(_ConnectionPtr& pConnection)
+virtual ~CTdManager()
+CHandleProcessor m_handleProcessor
-CLinkList<CTestingData*> m_tdLinkList
-_ConnectionPtr m_pConnection
-FILE *m_pFile
-CString m_log
-BOOL InitialTd2DTo3DDlg(COpTd2DTo3DDlg *pOpTd2DTo3DDlg)
}
```
**图表来源**
- [TdManager.h](file://h/TdManager.h#L1-L109)
### TaskDataOper 分析
`TaskDataOper`类负责处理测试数据,包括创建任务、查询任务信息、更新任务数据等。它通过`Create1DTask``Create2DTask``Create3DTask`等方法创建不同类型的任务,并通过`QueryTdDataByElec`等方法查询任务数据。
#### 类图
```mermaid
classDiagram
class CTaskDataOper {
+CTaskDataOper()
+virtual ~CTaskDataOper()
+CString CreateTaskCN(int iTestType)
+int Create1DTask(const ST1DTaskParam& stTaskParam, CString strTaskCN)
+int Create2DTask(const ST2DTaskParam& stTaskParam, CString strTaskCN)
+int Create3DTask(const ST3DTaskParam& stTaskParam, CString strTaskCN)
+void QuerySptByAR(int iAR, std : : vector<STQuerySptInfo>& vtQuerySptRes, int iSptType)
+void QuerySptRectByARandSCname(int iAR, CString strSCname, CString& vtQuerySptRes, CString& strPoleStep, CString& strPoleDistance, CString& strLineDirection, BYTE& ucSptType)
+void QueryMediumInfo(int iSptType, std : : vector<STQueryMediumInfo>& vtQueryAR)
+void QueryCmInfo(CString strCName, std : : vector<STQueryCMInfo>& vtQueryCM)
+bool DeleteTask(int iTaskID)
+bool DeleteTaskArray(std : : vector<int> vtTaskID)
+bool DeleteTaskArray(std : : vector<CString> vtTaskID)
+bool Query2DSptInfo(int iSptID, int &iMinLayer, int &iMaxLayer)
+bool QuerySptElecTpMount(int iSptID, int &iEAmount, int &iTpMount)
+bool QueryTdBasicInfo(int iTaskID, STQueryTaskBasicInfo* pTaskBasicInfo, CString strTaskCN=_T(""))
+bool UpdateGrData(int iTaskID, const char* pData, int iSptType, int iAddedVal = 0)
+bool InsertGrData(int iTaskID, WORD wGrNum, const char* pData, int iSptType=1)
+bool InsertCoordinatesData(CString strTaskID, WORD wGrNum, const char* pData, float fMaxWellDepth, int iSptType = 1)
+bool DeleteGrInfo(int iTaskID, WORD wElecID, BYTE ucDelFlag)
+bool DeleteTdDataInfo(BYTE ucTdType, int iTaskID, WORD wTSN, BYTE ucDelFlag)
+bool OpenTdData(int iTaskID, BYTE ucTdType, WORD wStartTSN = 0, WORD wMaxChannel = 1)
+void CloseTdData()
+int QueryNextTdBasicData(std : : vector<STSigSndDataInfo>& vtTestData)
+bool QueryTdSigBasicData(int iTaskID, BYTE ucTdType, WORD wTSN, LPSTSigSndDataInfo pMeasuBasicReq)
+bool UpdateTdBasicData(BYTE ucTdType, BYTE ucTestType, int iTdChannelID, int iTsn, LPSTMeasuBasicDataRes pMeasuBasicData, void *pAttachData)
+bool UpdateTdBasicData(BYTE ucTdType, BYTE ucTestType, int iTdChannelID, int iTsn, LPSTMeasuBasicDataResEx pMeasuBasicData, void *pAttachData)
+bool OnlineDownloadRes2DData(BYTE ucTdType, STTaskDataRes *ptTaskData, DWORD dwChID)
+bool OnlineUploadRes2DTask(DWORD dwChID)
+bool OnlineUploadRes2DArg(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+bool OnlineUploadRes2DRg(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+bool OnlineUploadRes2DData(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+bool UploadElecCoordinatesInfo(DWORD dwTdID, STQueryTaskBasicInfo stQueryTdInfo)
+int GetCurSelTdTestType()
+bool QueryTdDataFromTsn(int iTaskID, BYTE ucTdType, WORD wStartTSN, int iCnt, std : : vector<STTaskDetailBasicData>& vtRes)
+bool QueryTdDataByElec(int iSptID, BYTE ucTdType, int iStartElec, int iEndElec, std : : vector<STTaskDetailBasicData>& vtRes)
+bool QueryElecMaxWellDepthByCN(CString strScriptCN, BYTE ucTdType, STElecCoordinatesInfoHead& stElecInfoHead)
+bool QueryElecCoordinatesDataByCN(CString strScriptCN, BYTE ucTdType, WORD wStartTSN, int iCnt, std : : vector<STElecCoordinatesInfoBody>& vtRes)
+void QueryTdAttrToCtrl(CListCtrl& listAttr, int iTaskID)
+void QueryTdDataToCtrl(CListCtrl& listData, int iTaskID, int iSptType, int iTestType)
+void QueryOnLineTdAttrToCtrl(CListCtrl& listAttr, STTaskListItem taskItem, STRemTaskArg *pstTaskArg)
+bool QueryOnLineTdAttrFromSev(STRemTaskTable stTaskTable, STRemTaskArg *pstTaskArg)
+bool QueryOnLineTdRgFromSev(STRemTaskArg stTaskArg, char *pRg)
+bool QueryOnLineElecInfoFromSev(STRemTaskArg stTaskArg, char* pElecInfo)
+bool QueryOnLineTdDataFromSev(STRemTaskArg stTaskArg, UINT32 uiStartPoint, UINT32 uiEndPoint, char *pData)
+void QueryOnLineTdDataToCtrl(STRemTaskArg stTaskArg, std : : vector<STTaskDetailBasicData>& vtData)
+void InitialTaskTreeCtrl(CTreeCtrl& taskTree, int iSptType)
+void InitOnLineTaskTreeCtrl(CTreeCtrl& taskTree, int iSptType, std : : map<CString, STRemTaskTable> mapTaskList, HWND hWnd)
+void QueryTimerTaskValiad(std : : vector<STTimerTask>& vtTimerTask, const SYSTEMTIME &sysCurTime)
+int GetDefaultTzID()
+bool DeleteTimerTask(const std : : vector<int>& vtTask)
+bool DeleteTimerTask(UINT32 uiTDID)
+void QueryTdBrowseInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, bool bIsQueryTimerTd=false)
+void QueryTdBrowseInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, int iSptType, int iType, bool bIsQueryTimerTd=false)
+bool AddTimerTask(int iTaskID, CString strTaskName, CString strTime, CString strPlcID)
+bool InsertPlcStatusData(const STRemPlcDataInfo* pRemPlcData)
+bool QueryPlcStatusData(std : : vector<STPlcStatusInfo>& vtPlcStatus)
+void DeleteOldPlcStatusData()
+bool QueryTaskPacketAttr(int& iLoopTime, int& iInterval, CString& strPlcID)
+bool QueryTaskPacketInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo)
+bool InsertTaskPacketInfo(const std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, const STTaskPacketAttr& stAttr)
+bool CreateSigTaskByAuto(const STTdBrowseInfo& stBasicTask, CString strTime, STTdBrowseInfo& stNewTask)
+bool CreateTaskPacket(const std : : vector<STTdBrowseInfo>& vtBasicTaskPacket, CString strSysTime, std : : vector<STTdBrowseInfo>& vtNewTaskPacket)
+int QueryARByTdID(DWORD dwTdID)
+void AdjustParam()
+void SetCurrentTimeRange(time_t tStartTime, time_t tEndTime)
+unsigned int m_uiDevID
-CGUCodeCreator m_guCodeCreator
-_RecordsetPtr m_pRecTdData
-BYTE m_iTdType
-int m_iTestType
-WORD m_wMaxChannel
-time_t m_tStartTime
-time_t m_tEndTime
}
```
**图表来源**
- [TaskDataOper.h](file://h/TaskDataOper.h#L1-L448)
### TdRecord 分析
`TdRecord`类负责处理测试数据的记录,包括加载原始数据、显示原始数据曲线等。它通过`LoadCEOrgData``Load2DOrgData``Load3DOrgData`等方法加载不同类型的原始数据,并通过`DisplayRawDataSplines`方法显示原始数据曲线。
#### 类图
```mermaid
classDiagram
class CTdRecord {
+CTdRecord(_ConnectionPtr& pConnection)
+virtual ~CTdRecord()
+DWORD m_dwChID
+int m_iTsn
+int m_iN
+float m_fK
+float m_fI
+float m_fV
+float m_fR0
+float m_fSP
+CString m_Datetime
+CStringArray m_saVRawData
+CStringArray m_saIRawData
+_ConnectionPtr m_pConnection
+float GetMaxAbsV()
+virtual void DisplayRawDataSplines()
+virtual BOOL LoadOrgData()
+virtual float ConvertVOrgData(float fVOrgData)
-float m_fMaxAbsV
-BOOL LoadCEOrgData()
-BOOL Load2DOrgData()
-BOOL Load3DOrgData()
}
```
**图表来源**
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
### TestingData 分析
`TestingData`类负责处理测试数据的高级功能,包括显示测试数据列表、保存数据为不同格式的文件等。它通过`ShowConList``ShowConListByPage`等方法显示测试数据列表,并通过`SaveTdToExcelFile``SaveTdToRes2DFile`等方法保存数据为不同格式的文件。
#### 类图
```mermaid
classDiagram
class CTestingData {
+virtual void UpdataTopography(int f_disType, int f_start, CListCtrl &f_list)
+BOOL ExcuteSql(CString f_sql)
+virtual void GetTimeWindowList(CListCtrl &f_list)
+virtual BOOL ShowTimeWindow(CListCtrl &tdConList, int iTsn)
+void GetORGCStringToArray(CString f_SrcString, CStringArray *f_array)
+void FreeWindowsTime()
+double GetPeriod()
+bool GetTimeWindowInfo(std : : vector<STSigTWInfo>& vtTWInfo)
+void CreateWindowsTime()
+virtual bool CalculateTimeWindows(struct _WinTimeList f_winTimeList, CStringArray *v_orgData, int f_TRwave, int f_Tcycle, int f_Sample, int f_interation, int f_industrial)
+virtual bool CalculateTWInfo(CStringArray *v_orgData, int nFrenquence)
+virtual BOOL DisplayIpCurveGraph()
+CDevice* m_pDevice
+DWORD m_dwID
+CString m_szTdName
+CString m_szTdCN
+CString m_szTLocation
+CString m_szPrCN
+CString m_szTzName
+CString m_szTzCN
+DWORD m_dwTzID
+DWORD m_dwSCID
+CString m_szSCCN
+CString m_szSName
+int m_iSType
+int m_iTType
+int m_iTMode
+int m_iEAmount
+int m_iTPAmount
+int m_iCHAmount
+int m_iN
+int m_iTRWave
+int m_iTRFrequency
+int m_iIFrequency
+int m_iSAFrequency
+int m_iCLayout
+float m_fESpace
+CString m_szEDistance
+int m_iWeather
+int m_iWDIR
+float m_fTemperature
+float m_fHeight
+float m_fHumidity
+float m_fTRPeriod
+CString m_szCDate
+CString m_szCTime
+CString m_szTDate
+CString m_szTTime
+int m_iRCamount
+int m_iRDirection
+int m_iCRtime
+CString m_szPM
+CString m_szOP
+CString m_szQA
+virtual bool ShowGrList(CListCtrl &tdGrList)
+virtual bool ShowConList(CListCtrl &tdConList)
+virtual bool ShowConListByPage(CListCtrl &tdConList, int iSType = 2)
+virtual bool ShowDetailInfo(CListCtrl &tdDetailList)
+virtual BOOL SaveTdToExcelFile(CString f_szFileName)
+virtual BOOL SaveTdToResCEFile(CString f_szFileName)
+virtual BOOL SaveTdToRes2DFile(CString f_szFileName)
+virtual BOOL SaveTdToRes3DFile(CString f_szFileName)
+virtual BOOL SaveTdToCsvFile(CString f_szFileName)
+virtual bool ExportIP2DDataToDat(CString strFile)
+virtual bool ExportIP3DToDatFile(CString strFile)
+virtual BOOL ExportIPDataToTxt(CString strFileName)
+virtual BOOL ExportResDataToTxt(CString strFileName)
+virtual BOOL ExportSpDataToTxt(CString strFileName)
+virtual BOOL Export2dDataToTxtBySort(CString strFileName, int iSortMethod)
+virtual BOOL ExportDataToUrf(CString strFileName)
+void SetCustToCrossHole(bool bFlag)
+virtual BOOL DisplayGraph()
+virtual BOOL DisplayTPSplinesGraph(int iTSN)
+virtual BOOL LoadData(CLinkList<CMedium*>& m_medLinkList)
+inline int CalcuCrossHolePoleID(int iOrgPoleID, int iHoleID, int iHoleFlag, int iMidEamount)
+CTestingData(DWORD dwID, _ConnectionPtr& pConnection)
+virtual ~CTestingData()
+CPtrList m_tdChaList
+_WaveCount *m_waveCount
-_ConnectionPtr m_pConnection
-CHandleProcessor m_handleProcessor
-double GetCycle(int f_type)
-BOOL SaveOrgDataToDB(DWORD dwTdID)
-BOOL SaveGRDataToDB(DWORD dwTdID)
-bool m_bIsCustomToCrossHole
-int GetSample(int f_nindustrial, int f_type)
-double adc_integral(int *adc_buf, int length)
-double adc_average(int *adc_buf, int length)
-CString GetWaveNum(int f_waveType)
-void TransfToDouble(double* f_buff, CStringArray f_szArray)
-double adc_calculate(double offset, double coefficient, double adc)
}
```
**图表来源**
- [TestingData.h](file://h/TestingData.h#L1-L276)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 依赖分析
TdManager模块依赖于多个其他模块和库,包括`HandleProcessor``LinkList``TestingData``CtrlProtocolDef`等。这些依赖关系确保了模块的完整性和功能性。
```mermaid
graph TD
TdManager --> HandleProcessor
TdManager --> LinkList
TdManager --> TestingData
TdManager --> CtrlProtocolDef
TaskDataOper --> GUCodeCreator
TaskDataOper --> CtrlProtocolDef
TdRecord --> TestingData
TestingData --> Device
TestingData --> Medium
```
**图表来源**
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [TaskDataOper.h](file://h/TaskDataOper.h#L1-L448)
- [TdRecord.h](file://h/TdRecord.h#L1-L47)
- [TestingData.h](file://h/TestingData.h#L1-L276)
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 性能考虑
TdManager模块在设计时考虑了性能优化,特别是在数据处理和存储方面。通过使用高效的数据库操作和数据结构,模块能够快速处理大量测试数据。此外,模块还提供了多种数据导出格式,以满足不同用户的需求。
## 故障排除指南
### 数据解析错误
当遇到数据解析错误时,可以检查以下几点:
- 确认数据文件的格式是否正确。
- 检查数据文件是否损坏。
- 确认数据文件的编码是否正确。
### 存储失败
当遇到存储失败时,可以检查以下几点:
- 确认数据库连接是否正常。
- 检查数据库是否有足够的存储空间。
- 确认数据库表结构是否正确。
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
## 结论
TdManager模块是GeomativeStudio软件中不可或缺的一部分,负责测试数据的采集、处理和存储。通过详细的分析,我们了解了模块的职责和实现,以及其在测试数据与项目、脚本的关联机制中的作用。模块的设计考虑了性能优化和错误处理,确保了数据的完整性和一致性。希望本文档能帮助开发者更好地理解和使用TdManager模块。
## 附录
### 代码示例
以下是一个简单的代码示例,展示如何使用`TdManager`类接收和记录测试数据:
```cpp
// 创建TdManager实例
CTdManager tdManager(connection);
// 从设备上载1D数据
CString prCN = _T("Project1");
CString tzCN = _T("Zone1");
CString tdCN = _T("Test1");
CString subTdCN = _T("");
CDevice* pDev = new CDevice();
BOOL result = tdManager.UploadCETdFromDev(prCN, tzCN, tdCN, subTdCN, pDev);
if (result) {
// 数据上载成功
AfxMessageBox(_T("数据上载成功"));
} else {
// 数据上载失败
AfxMessageBox(_T("数据上载失败"));
}
// 清理资源
delete pDev;
```
**章节来源**
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L800)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L1-L800)
- [TdRecord.cpp](file://cpp/ProblemZone/TdRecord.cpp#L1-L325)
- [TestingData.cpp](file://cpp/ProblemZone/TestingData.cpp#L1-L800)
@@ -0,0 +1,511 @@
# DataOperator
<cite>
**本文档引用的文件**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [DataOperator.h](file://h/DataOperator.h)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TaskDataOper.h](file://h/TaskDataOper.h)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TdManager.h](file://h/TdManager.h)
- [TestingData.h](file://h/TestingData.h)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
DataOperator模块是GeomativeStudio软件中负责数据读写、处理与管理的核心组件。该模块主要负责与TdManager协同工作,实现测试数据的加载、保存和格式转换,支持Excel、CSV、TXT、URF等多种导出格式。通过TaskDataOper类,实现了任务数据的操作流程,包括数据序列化、反序列化及内存管理策略。DataOperator模块在实际业务场景中,如从设备读取原始测量数据并持久化到项目文件中的完整流程,展现了其强大的数据处理能力。此外,该模块还深入分析了与ADO数据库交互的实现机制,包括记录集操作和异常处理,确保了数据操作的稳定性和可靠性。
## 项目结构
GeomativeStudio项目结构清晰,主要分为CACHE、DB、Install、LOG、Release、cpp、h、res、tools等目录。其中,cpp目录下的Operator子目录包含了DataOperator和TaskDataOper等核心操作类的实现文件,Managers目录下的TdManager.cpp文件则负责测试数据的管理。h目录下的头文件定义了各个类的接口和数据结构,为模块间的协同工作提供了基础。整个项目结构体现了模块化设计的思想,便于维护和扩展。
```mermaid
graph TD
subgraph "核心模块"
DataOperator[DataOperator.cpp]
TaskDataOper[TaskDataOper.cpp]
TdManager[TdManager.cpp]
end
subgraph "工具类"
Excel[excel.cpp]
OperTxtFile[OperTxtFile.cpp]
OperUrfFile[OperUrfFile.cpp]
end
subgraph "头文件"
DataOperatorH[DataOperator.h]
TaskDataOperH[TaskDataOper.h]
TdManagerH[TdManager.h]
TestingDataH[TestingData.h]
end
DataOperator --> TdManager
DataOperator --> TaskDataOper
TaskDataOper --> TdManager
DataOperator --> Excel
DataOperator --> OperTxtFile
DataOperator --> OperUrfFile
DataOperatorH --> DataOperator
TaskDataOperH --> TaskDataOper
TdManagerH --> TdManager
TestingDataH --> TdManager
```
**图源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
## 核心组件
DataOperator模块的核心组件包括DataOperator类、TaskDataOper类和TdManager类。DataOperator类负责数据的读写和管理,通过与TdManager类的协同工作,实现了测试数据的加载、保存和格式转换。TaskDataOper类则专注于任务数据的操作流程,包括数据的序列化、反序列化及内存管理策略。这些核心组件共同构成了DataOperator模块的基础,确保了数据处理的高效性和可靠性。
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
## 架构概述
DataOperator模块的架构设计充分考虑了模块化和可扩展性。通过定义清晰的接口和数据结构,实现了各组件间的松耦合。DataOperator类作为数据操作的入口,通过调用TaskDataOper类和TdManager类的方法,完成具体的数据处理任务。这种设计不仅提高了代码的可读性和可维护性,还便于后续的功能扩展和性能优化。
```mermaid
classDiagram
class DataOperator {
+_ConnectionPtr m_pConnection
+CStateProcessor m_stateProcessor
+CHandleProcessor m_handleProcessor
+InitialNavDataView(CNavDataView* pNavDataView)
+ShowProjectInfo(DWORD dwProHandle, CView *pAppDataView)
+ShowTzInfo(DWORD dwTzHandle, CView *pAppDataView)
+ShowRsp3DTdInfo(DWORD dwTdHandle, CView *pAppDataView)
+ShowRsp2DTdInfo(DWORD dwTdHandle, CView *pAppDataView)
+ShowRspCETdInfo(DWORD dwTdHandle, CView* pAppDataView)
+LoadRspCERecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+LoadRsp2dRecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+LoadRsp3dRecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+ShowIps2DpTdInfo(DWORD dwTdHandle, CView *pAppDataView)
+ShowIps3DpTdInfo(DWORD dwTdHandle, CView *pAppDataView)
+ShowIpsCEpTdInfo(DWORD dwTdHandle, CView *pAppDataView)
+LoadIpspCERecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+LoadIpsp2dRecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+LoadIpsp3dRecordbyPage(DWORD dwTdHandle, CView *pAppDataView, int iSType)
+CreateProjectInDB(CNavDataView* pNavDataView)
+DeleteProjectInDB(CNavDataView* pNavDataView)
+DeleteTzInDB(CNavDataView* pNavDataView)
+Convert2DTo3D(CNavDataView* pNavDataView)
+Delete3DRecord(CNavDataView *pNavDataView, int iTSN)
+Delete1DRecord(CNavDataView *pNavDataView, int iTSN)
+Delete2DRecord(CNavDataView *pNavDataView, int iTSN)
+DeleteSPCETdInDB(DWORD dwTdID)
+DeleteSP2DTdInDB(DWORD dwTdID)
+DeleteSP3DTdInDB(DWORD dwTdID)
+DeleteRspCETdInDB(DWORD dwTdID)
+DeleteRsp2DTdInDB(DWORD dwTdID)
+DeleteRsp3DTdInDB(DWORD dwTdID)
+DeleteIpspCETdInDB(DWORD dwTdID)
+DeleteIpsp2DTdInDB(DWORD dwTdID)
+DeleteIpsp3DTdInDB(DWORD dwTdID)
+DisplayRsp2DTdGraph(CNavDataView *pNavDataView)
+DisplayRsp3DTdGraph(CNavDataView *pNavDataView)
+DisplayRspCETdGraph(CNavDataView *pNavDataView)
+DisplayTPSplinesGraph(CNavDataView *pNavDataView, int iTSN)
+ExportIpCETdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIp2DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIp3DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIpCETdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIp2DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIp3DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIpSpCETdToDAT(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIP2DToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIP3DToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportIP1DToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSPCETdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP2DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP3DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSPCETdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP2DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP3DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSPCETdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP2DTdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportSP3DTdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp1DTdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp2DTdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp3DTdToTxt(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRspCETdToDAT(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp2DTdToDAT(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp3DTdToDAT(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRspCETdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp2DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp3DTdToExcel(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRspCETdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp2DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp3DTdToCSV(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp2DTdToUrf(CNavDataView *pNavDataView, CString f_szFileName)
+ExportRsp3DTdToUrf(CNavDataView *pNavDataView, CString f_szFileName)
+Delete1DElectrodeRecord(CNavDataView *pNavDataView, const std : : vector<float>* pvtDelElecID)
+Delete2DElectrodeRecord(CNavDataView *pNavDataView, const std : : vector<float>* pvtDelElecID)
+Delete3DElectrodeRecord(CNavDataView *pNavDataView, const std : : vector<float>* pvtDelElecID)
+ExportTdRecFile(CNavDataView *pNavData)
+ExportTdOrgFile(CNavDataView *pNavData)
+SaveRecFileToLocal(CString strRecFile, CString strDstFile,int iTaskType)
+QueryTaskARInDB(DWORD dwTdHandle)
}
class TaskDataOper {
+_RecordsetPtr m_pRecTdData
+bool m_bIsOpenTdData
+BYTE m_iTdType
+int m_iTestType
+WORD m_wMaxChannel
+time_t m_tStartTime
+time_t m_tEndTime
+CGUCodeCreator m_guCodeCreator
+GetDefaultTzID()
+Create1DTask(const ST1DTaskParam& stTaskParam,CString strTaskCN)
+Create2DTask(const ST2DTaskParam& stTaskParam, CString strTaskCN)
+Create3DTask(const ST3DTaskParam& stTaskParam, CString strTaskCN)
+QuerySptByAR(int iAR, std : : vector<STQuerySptInfo>& vtQuerySptRes, int iSptType)
+QuerySptRectByARandSCname(int iAR, CString strSCname, CString& vtQuerySptRes, CString& strPoleStep, CString& strPoleDistance, CString& strLineDirection, BYTE& ucSptType)
+QueryMediumInfo(int iSptType, std : : vector<STQueryMediumInfo>& vtQueryAR)
+QueryCmInfo(CString strCName, std : : vector<STQueryCMInfo>& vtQueryCM)
+DeleteTask(int iTaskID)
+DeleteTaskArray(std : : vector<int> vtTaskID)
+DeleteTaskArray(std : : vector<CString> vtTaskID)
+Query2DSptInfo(int iSptID, int &iMinLayer,int &iMaxLayer)
+QuerySptElecTpMount(int iSptID, int &iEAmount, int &iTpMount)
+QueryTdBasicInfo(int iTaskID, STQueryTaskBasicInfo* pTaskBasicInfo,CString strTaskCN=_T(""))
+UpdateGrData(int iTaskID, const char* pData,int iSptType,int iAddedVal = 0)
+InsertGrData(int iTaskID, WORD wGrNum, const char* pData, int iSptType=1)
+InsertCoordinatesData(CString strTaskID, WORD wGrNum, const char* pData,float fMaxWellDepth, int iSptType = 1)
+DeleteGrInfo(int iTaskID, WORD wElecID, BYTE ucDelFlag)
+DeleteTdDataInfo(BYTE ucTdType, int iTaskID, WORD wTSN, BYTE ucDelFlag)
+OpenTdData(int iTaskID, BYTE ucTdType, WORD wStartTSN = 0, WORD wMaxChannel = 1)
+CloseTdData()
+QueryNextTdBasicData(std : : vector<STSigSndDataInfo>& vtTestData)
+QueryTdSigBasicData(int iTaskID, BYTE ucTdType, WORD wTSN, LPSTSigSndDataInfo pMeasuBasicReq)
+UpdateTdBasicData(BYTE ucTdType, BYTE ucTestType,int iTdChannelID, int iTsn, LPSTMeasuBasicDataRes pMeasuBasicData,void *pAttachData)
+UpdateTdBasicData(BYTE ucTdType, BYTE ucTestType, int iTdChannelID, int iTsn, LPSTMeasuBasicDataResEx pMeasuBasicData, void *pAttachData)
+OnlineDownloadRes2DData(BYTE ucTdType, STTaskDataRes *ptTaskData, DWORD dwChID)
+OnlineUploadRes2DTask(DWORD dwChID)
+OnlineUploadRes2DArg(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+OnlineUploadRes2DRg(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+OnlineUploadRes2DData(DWORD dwChID, STQueryTaskBasicInfo stQueryTdInfo)
+UploadElecCoordinatesInfo(DWORD dwTdID, STQueryTaskBasicInfo stQueryTdInfo)
+QueryTdDataFromTsn(int iTaskID, BYTE ucTdType, WORD wStartTSN, int iCnt, std : : vector<STTaskDetailBasicData>& vtRes)
+QueryTdDataByElec(int iSptID, BYTE ucTdType, int iStartElec, int iEndElec, std : : vector<STTaskDetailBasicData>& vtRes)
+QueryElecMaxWellDepthByCN(CString strScriptCN, BYTE ucTdType, STElecCoordinatesInfoHead& stElecInfoHead)
+QueryElecCoordinatesDataByCN(CString strScriptCN, BYTE ucTdType, WORD wStartTSN, int iCnt, std : : vector<STElecCoordinatesInfoBody>& vtRes)
+QueryTdAttrToCtrl(CListCtrl& listAttr, int iTaskID)
+QueryTdDataToCtrl(CListCtrl& listData, int iTaskID, int iSptType, int iTestType)
+QueryOnLineTdAttrToCtrl(CListCtrl& listAttr, STTaskListItem taskItem, STRemTaskArg *pstTaskArg)
+QueryOnLineTdAttrFromSev(STRemTaskTable stTaskTable, STRemTaskArg *pstTaskArg)
+QueryOnLineTdRgFromSev(STRemTaskArg stTaskArg, char *pRg)
+QueryOnLineElecInfoFromSev(STRemTaskArg stTaskArg, char* pElecInfo)
+QueryOnLineTdDataFromSev(STRemTaskArg stTaskArg, UINT32 uiStartPoint, UINT32 uiEndPoint, char *pData)
+QueryOnLineTdDataToCtrl(STRemTaskArg stTaskArg, std : : vector<STTaskDetailBasicData>& vtData)
+InitialTaskTreeCtrl(CTreeCtrl& taskTree,int iSptType)
+InitOnLineTaskTreeCtrl(CTreeCtrl& taskTree, int iSptType, std : : map<CString, STRemTaskTable> mapTaskList, HWND hWnd)
+QueryTimerTaskValiad(std : : vector<STTimerTask>& vtTimerTask, const SYSTEMTIME &sysCurTime)
+DeleteTimerTask(const std : : vector<int>& vtTask)
+DeleteTimerTask(UINT32 uiTDID)
+QueryTdBrowseInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, bool bIsQueryTimerTd=false)
+QueryTdBrowseInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, int iSptType, int iType ,bool bIsQueryTimerTd=false)
+AddTimerTask(int iTaskID, CString strTaskName, CString strTime,CString strPlcID)
+InsertPlcStatusData(const STRemPlcDataInfo* pRemPlcData)
+QueryPlcStatusData(std : : vector<STPlcStatusInfo>& vtPlcStatus)
+DeleteOldPlcStatusData()
+QueryTaskPacketAttr(int& iLoopTime, int& iInterval, CString& strPlcID)
+QueryTaskPacketInfo(std : : vector<STTdBrowseInfo>& vtTdBrowseInfo)
+InsertTaskPacketInfo(const std : : vector<STTdBrowseInfo>& vtTdBrowseInfo, const STTaskPacketAttr& stAttr)
+CreateSigTaskByAuto(const STTdBrowseInfo& stBasicTask, CString strTime, STTdBrowseInfo& stNewTask)
+CreateTaskPacket(const std : : vector<STTdBrowseInfo>& vtBasicTaskPacket, CString strSysTime, std : : vector<STTdBrowseInfo>& vtNewTaskPacket)
+QueryARByTdID(DWORD dwTdID)
+AdjustParam()
+SetCurrentTimeRange(time_t tStartTime, time_t tEndTime)
+unsigned int m_uiDevID
}
class TdManager {
+CLinkList<CTestingData*> m_tdLinkList
+_ConnectionPtr m_pConnection
+FILE *m_pFile
+CString m_log
+DeleteSPCETd(DWORD dwID)
+DeleteSP3DTd(DWORD dwID)
+DeleteSP2DTd(DWORD dwID)
+DeleteRsp3DTd(DWORD dwID)
+DeleteIpsp3DTd(DWORD dwID)
+CheckTdExist(CString szPrCN, CString szTzCN, CString szTdCN, CDevice* const pDev,DWORD *TdId)
+OnlineCheckTdExist(CString szTdID, CString szDevSN, DWORD *TdId)
+OnlineCheckElecInfoExist(CString strTdID, CString szDevSN)
+ImportTdHeadToDB(DWORD dwTzID, CString szHeadFile, CDevice* const pDev,DWORD* pExTdID = NULL)
+Import2DTdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+ImportCETdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+Import3DTdOrgToDB(DWORD dwTdID, CString szOrgFile, CDevice* const pDev)
+Import2DTdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev, int* pTSN=NULL)
+ImportCETdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev)
+Import3DTdConToDB(DWORD dwTdID, CString szDatFile, CDevice* const pDev, int* pTSN = NULL)
+Import2DGRToDB(DWORD dwTdID, CString szGRFile, CDevice* const pDev)
+Import3DGRToDB(DWORD dwTdID, CString szGRFile)
+ImportCETGRToDB(DWORD dwTdID, CString szGRFile)
+UploadCETdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+Upload2DTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+Upload3DTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+UploadWellTdFromDev(CString szPrCN, CString szTzCN, CString szTdCN, CString strSubTdCN, CDevice* const pDev)
+DeleteTdInDev(CString szPrCN, CString szTzCN, CString szTdCN, CDevice* const pDev)
+InitialTDListByTzForSyn(CListCtrl &tdList, CString szDeSN, CString szTzCN)
+InitialTDListByProForSyn(CListCtrl &tdList, CString szDeSN, CString szPrCN)
+InitialTDListByOnLineForSyn(CListCtrl &tdList, CString szDeSN, UINT32 &uiTotNum)
+InitialDevListByOnLineForSyn(CListCtrl &tdList, CString szDeSN,STSynDevParam *ptLocalDevParam)
+InitialCableListByOnLineForSyn(CListCtrl &tdList, CString szDeSN, STRemCableCallInfo *ptLocalCableInfo)
+OnlineSevTDListSynToDB(STRemTaskArg *tTaskArg, CString szDeSN)
+OnLineImportTdHeadToDB(STRemTaskArg *tTaskArg, CString szDevSN)
+Convert2DTo3D(DWORD dwTzHandle)
+DeleteObjInMem(DWORD dwHandle)
+DeleteRspCETd(DWORD dwID)
+DeleteIpspCETd(DWORD dwID)
+DeleteRsp2DTd(DWORD dwID)
+DeleteIpsp2DTd(DWORD dwID)
+GetTestingData(DWORD dwHandle)
+ShowTdListByTz(DWORD dwTzHandle, CListCtrl& tdList)
+ShowTd2DListByTz(DWORD dwTzHandle, int iEAmount, float fEDistance, int iAR, CListCtrl& tdList)
+ShowTdListByDev(DWORD dwDevHandle, CListCtrl& tdList)
+ImportTdSpecAttr(const CStringArray& strAttrArray, int iDataType, DWORD dwChID,int iTSN)
+ShowTdListByProject(DWORD dwProHandle, CListCtrl& tdList)
+GetTaskAttr(DWORD dwHandle, DWORD& dwTdID, int& iStyle)
}
class CTestingData {
+CDevice* m_pDevice
+DWORD m_dwID
+CString m_szTdName
+CString m_szTdCN
+CString m_szTLocation
+CString m_szPrCN
+CString m_szTzName
+CString m_szTzCN
+DWORD m_dwTzID
+DWORD m_dwSCID
+CString m_szSCCN
+CString m_szSName
+int m_iSType
+int m_iTType
+int m_iTMode
+int m_iEAmount
+int m_iTPAmount
+int m_iCHAmount
+int m_iN
+int m_iTRWave
+int m_iTRFrequency
+int m_iIFrequency
+int m_iSAFrequency
+int m_iCLayout
+float m_fEspace
+float m_fEdistance
+CString m_strRect
+CString m_strValidElec
+CString m_strValildLayer
+int m_iStartElec
+int m_iEndElec
+int m_iStartLayer
+int m_iEndLayer
+int m_iRollCnt
+int m_iSkipCable
+int m_iAR
+int m_iTxWave
+int m_iTxPeriod
+int m_iSAInterval
+int m_iWeather
+int m_iWDIR
+float m_fTemperature
+float m_fHeight
+float m_fHumidity
+CString m_strCdate
+CString m_strCtime
+CString m_strTdate
+CString m_strTtime
+int m_iRdirection
+int m_iCRtime
+CString m_strPM
+CString m_strOP
+CString m_strQA
+int m_iTestGRFlag
+int m_iLineDirection
+int m_iCreateTime
+int m_iSTime
+int m_iETime
+int m_iTTimer
+int m_iTdStatus
+int m_iDesn
+int m_iStacking
+int m_iTestPeriod
+int m_iCableLayout
+int m_iSAInterval
+int m_iEamount
+int m_iTestPeriod
+int m_iCableLayout
+int m_iStartElec
+int m_iEndElec
+int m_iStacking
+int m_iTxWave
+int m_iTxPeriod
+int m_iSAInterval
+int m_iEAmount
+float m_fEspace
+float m_fHoleSpace
+unsigned int m_uiDevID
+unsigned int m_uiTimerTime
+BYTE m_ucOrgFlg
+BYTE m_ucTestGRFlag
+UpdataTopography(int f_disType ,int f_start,CListCtrl &f_list)
+ExcuteSql(CString f_sql)
+GetTimeWindowList(CListCtrl &f_list)
+ShowTimeWindow(CListCtrl &tdConList, int iTsn)
+GetORGCStringToArray(CString f_SrcString, CStringArray *f_array)
+FreeWindowsTime()
+GetPeriod()
+GetTimeWindowInfo(std : : vector<STSigTWInfo>& vtTWInfo)
+CreateWindowsTime()
+CalculateTimeWindows(struct _WinTimeList f_winTimeList, CStringArray *v_orgData, int f_TRwave, int f_Tcycle, int f_Sample, int f_interation, int f_industrial)
+CalculateTWInfo(CStringArray *v_orgData, int nFrenquence)
+DisplayIpCurveGraph()
}
DataOperator --> TdManager
DataOperator --> TaskDataOper
TaskDataOper --> TdManager
DataOperator --> CTestingData
TaskDataOper --> CTestingData
TdManager --> CTestingData
```
**图源**
- [DataOperator.h](file://h/DataOperator.h)
- [TaskDataOper.h](file://h/TaskDataOper.h)
- [TdManager.h](file://h/TdManager.h)
- [TestingData.h](file://h/TestingData.h)
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
## 详细组件分析
### DataOperator类分析
DataOperator类是数据操作的核心,负责与数据库的交互,实现数据的读取、写入和管理。通过调用TdManager类的方法,DataOperator能够获取和更新测试数据,并通过TaskDataOper类处理任务数据的序列化和反序列化。DataOperator还支持多种数据格式的导出,包括Excel、CSV、TXT和URF,满足了不同用户的需求。
#### 数据读写与管理
DataOperator通过ADO数据库接口与数据库进行交互,实现了数据的高效读写。在数据读取过程中,DataOperator首先通过SQL查询获取数据记录集,然后将记录集中的数据转换为应用程序内部的数据结构。在数据写入过程中,DataOperator将应用程序内部的数据结构转换为SQL语句,通过执行SQL语句将数据持久化到数据库中。这一过程确保了数据的一致性和完整性。
```mermaid
flowchart TD
Start([开始]) --> CheckConnection["检查数据库连接"]
CheckConnection --> |连接正常| QueryData["执行SQL查询"]
CheckConnection --> |连接异常| HandleError["处理异常"]
QueryData --> ProcessData["处理查询结果"]
ProcessData --> ConvertData["转换为内部数据结构"]
ConvertData --> End([结束])
HandleError --> End
```
**图源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
#### 与TdManager的协同工作
DataOperator与TdManager的协同工作是实现数据管理的关键。TdManager负责测试数据的加载、保存和格式转换,而DataOperator则负责与数据库的交互。当需要加载测试数据时,DataOperator调用TdManager的相应方法,TdManager从数据库中读取数据并返回给DataOperator。当需要保存测试数据时,DataOperator将数据传递给TdManagerTdManager将数据写入数据库。这种协同工作模式确保了数据操作的高效性和可靠性。
```mermaid
sequenceDiagram
participant DataOperator
participant TdManager
participant Database
DataOperator->>TdManager : 请求加载测试数据
TdManager->>Database : 执行SQL查询
Database-->>TdManager : 返回查询结果
TdManager-->>DataOperator : 返回测试数据
DataOperator->>TdManager : 请求保存测试数据
TdManager->>Database : 执行SQL插入
Database-->>TdManager : 确认保存成功
TdManager-->>DataOperator : 确认保存成功
```
**图源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
### TaskDataOper类分析
TaskDataOper类专注于任务数据的操作流程,包括数据的序列化、反序列化及内存管理策略。通过定义一系列方法,TaskDataOper实现了任务数据的高效处理,确保了数据操作的稳定性和可靠性。
#### 任务数据操作流程
TaskDataOper类通过定义一系列方法,实现了任务数据的序列化、反序列化及内存管理。在数据序列化过程中,TaskDataOper将应用程序内部的数据结构转换为适合存储的格式,如JSON或XML。在数据反序列化过程中,TaskDataOper将存储的格式转换回应用程序内部的数据结构。内存管理策略则确保了数据在内存中的高效使用,避免了内存泄漏和性能下降。
```mermaid
flowchart TD
Start([开始]) --> SerializeData["序列化数据"]
SerializeData --> StoreData["存储数据"]
StoreData --> DeserializeData["反序列化数据"]
DeserializeData --> UseData["使用数据"]
UseData --> ManageMemory["管理内存"]
ManageMemory --> End([结束])
```
**图源**
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
**节源**
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
## 依赖关系分析
DataOperator模块的依赖关系清晰,主要依赖于TdManager和TaskDataOper两个模块。TdManager负责测试数据的管理,而TaskDataOper负责任务数据的操作。DataOperator通过调用这两个模块的方法,实现了数据的读写和管理。此外,DataOperator还依赖于ADO数据库接口,用于与数据库的交互。这种依赖关系确保了模块间的松耦合,提高了代码的可维护性和可扩展性。
```mermaid
graph TD
DataOperator --> TdManager
DataOperator --> TaskDataOper
DataOperator --> ADO[ADO数据库接口]
TdManager --> ADO
TaskDataOper --> ADO
```
**图源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
**节源**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
## 性能考虑
在设计DataOperator模块时,充分考虑了性能优化。通过批量处理和缓存机制,提高了数据操作的效率。批量处理减少了数据库交互的次数,降低了网络延迟和数据库负载。缓存机制则减少了对数据库的频繁访问,提高了数据读取的速度。此外,通过优化SQL查询语句和索引,进一步提升了数据操作的性能。
## 故障排除指南
### 数据丢失
数据丢失可能是由于数据库连接异常或数据写入失败导致的。在遇到数据丢失问题时,首先检查数据库连接是否正常,然后检查数据写入的日志,确认是否有写入失败的记录。如果发现写入失败,可以尝试重新执行写入操作,或检查数据库的存储空间是否充足。
### 格式不兼容
格式不兼容问题通常出现在数据导出时。在导出数据时,确保选择正确的导出格式,并检查导出文件的编码是否与目标系统兼容。如果遇到格式不兼容问题,可以尝试使用不同的导出工具或转换工具,将数据转换为兼容的格式。
## 结论
DataOperator模块是GeomativeStudio软件中不可或缺的一部分,通过与TdManager和TaskDataOper模块的协同工作,实现了高效的数据读写、处理与管理。该模块不仅支持多种数据格式的导出,还通过批量处理和缓存机制,提高了数据操作的性能。在实际应用中,DataOperator模块展现了其强大的数据处理能力和稳定性,为用户提供了可靠的数据管理解决方案。
@@ -0,0 +1,378 @@
# DeviceOperator
<cite>
**本文引用的文件**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [DevOperator.h](file://h/DevOperator.h)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [GD10OperCmd.h](file://h/GD10OperCmd.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [Device.h](file://h/Device.h)
- [DevManager.h](file://h/DevManager.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文围绕 DeviceOperator 模块,系统阐述 GD10 设备的检测、连接、参数配置与状态监控的实现细节。重点覆盖:
- DetcGD10Dev 如何通过 USB 可移动盘符识别 GD10/GD20 主机并建立“逻辑连接”(挂载路径)。
- DevOperator 如何封装底层指令下发与响应解析,协调 DevManager 完成设备初始化、参数修改、升级流程与状态展示。
- GD10OperCmd 命令协议封装,用于在主机侧模拟对 GD 设备内部文件系统的操作。
- 异常处理与调试方法,安全与权限控制,以及性能优化建议(连接池、异步通信)。
## 项目结构
DeviceOperator 涉及的关键文件与职责如下:
- DetcGD10Dev:设备检测与挂载路径管理,提供“已连接”状态与设备路径。
- DevOperator:设备操作入口,负责树形视图初始化、参数修改、升级流程、权限校验与日志记录。
- GD10OperCmd:命令协议封装,负责在主机侧对 GD 设备内部 XML/文件进行增删改查。
- Device:设备抽象,封装串口命令执行、文件收发、参数读取与状态维护。
- DevManager:设备生命周期与远程设备集合管理。
```mermaid
graph TB
subgraph "设备检测层"
DGD["DetcGD10Dev<br/>检测GD10/GD20设备"]
end
subgraph "设备操作层"
DOp["DevOperator<br/>设备操作入口"]
GOC["GD10OperCmd<br/>命令协议封装"]
end
subgraph "设备抽象层"
Dev["CDevice<br/>串口命令/文件收发"]
DM["DevManager<br/>设备管理"]
end
DGD --> DOp
DOp --> Dev
DOp --> DM
GOC --> DGD
GOC --> Dev
```
图表来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1-L200)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L120)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
- [DevManager.h](file://h/DevManager.h#L1-L69)
章节来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1-L200)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L120)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
- [DevManager.h](file://h/DevManager.h#L1-L69)
## 核心组件
- DetcGD10Dev:单例,负责扫描可移动盘符,匹配 GD10/GD20 设备卷标,记录设备路径与连接状态;提供日志输出能力。
- DevOperator:封装设备树初始化、参数修改、升级流程、权限校验与日志记录;与 DevManager 协作完成设备注册/注销与状态展示。
- GD10OperCmd:命令封装,提供工程/测区/脚本/任务的增删改查与参数设置;在主机侧对 GD 内部 XML 文件进行操作。
- CDevice:设备抽象,封装串口命令执行、文件收发、参数读取、状态维护与日志记录;提供“执行命令带轮询”的超时控制。
- DevManager:维护在线/离线设备列表、远程设备集合、设备句柄映射与设备对象生命周期。
章节来源
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L1-L38)
- [DevOperator.h](file://h/DevOperator.h#L1-L59)
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
- [Device.h](file://h/Device.h#L1-L128)
- [DevManager.h](file://h/DevManager.h#L1-L69)
## 架构总览
DeviceOperator 的整体交互流程如下:
- 设备检测:DetcGD10Dev 扫描可移动盘符,匹配 GD 设备卷标,返回挂载路径并更新连接状态。
- 设备操作:DevOperator 依据选择的设备项,调用 DevManager 获取设备对象,执行参数修改、升级、权限校验等操作。
- 命令封装:GD10OperCmd 在主机侧对 GD 内部 XML/文件进行增删改查,确保 GD 设备侧数据一致性。
- 设备抽象:CDevice 封装串口命令执行与文件传输,提供超时控制与日志记录。
```mermaid
sequenceDiagram
participant UI as "界面"
participant DO as "DevOperator"
participant DM as "DevManager"
participant D as "CDevice"
participant GD as "GD10OperCmd"
participant DET as "DetcGD10Dev"
UI->>DO : 选择设备项
DO->>DM : GetDeviceByID/GetDevice
DM-->>DO : 返回设备对象
alt 参数修改
DO->>D : ModifyParameter()
D-->>DO : 成功/失败
else 升级流程
DO->>DO : StartDeviceUpgrade()
DO->>D : 执行升级命令/文件传输
D-->>DO : 结果
end
alt 需要主机侧操作
DO->>GD : 调用命令封装
GD->>DET : 读取设备路径
GD-->>DO : 成功/失败
end
```
图表来源
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L384-L433)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1139-L1262)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L51-L105)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L80)
## 详细组件分析
### DetcGD10DevGD10/GD20 设备检测与连接
- 功能要点
- 单例模式,提供 IsGD10DevConnect 与 GetGD10DevAddr 接口。
- 通过扫描 GetLogicalDriveStrings 返回的可移动盘符,匹配 GD10/GD20 卷标或特定路径特征,确定设备挂载路径。
- 提供 CompareDriverName 通过 GetVolumeInformation 获取卷标名进行比对。
- 日志输出到 detect_gd20_log.txt,便于调试设备检测问题。
- 关键流程
- DetectGD10Dev:根据应用多通道配置选择 GD20 或 GD10 设备名,调用 FindUsbDevice 获取路径并更新连接状态。
- FindUsbDevice:遍历可移动盘符,尝试匹配卷标或特定目录特征,返回第一个匹配路径。
- CompareDriverName:读取卷标名并与期望名称比较,返回布尔结果。
- 异常与调试
- 若打开日志失败,弹出提示;若扫描失败,返回空路径。
- 建议:在 UI 层显示检测进度与失败原因,避免静默失败。
```mermaid
flowchart TD
Start(["开始检测"]) --> Scan["扫描可移动盘符"]
Scan --> Match{"匹配GD10/GD20卷标?"}
Match --> |是| Found["返回设备路径"]
Match --> |否| Next["继续扫描下一个盘符"]
Next --> Scan
Found --> Update["更新连接状态与路径"]
Update --> Log["写入检测日志"]
Log --> End(["结束"])
```
图表来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L61-L175)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L177-L189)
章节来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L1-L38)
### DevOperator:设备操作入口与与 DevManager 协作
- 功能要点
- 初始化导航设备视图,填充在线/离线/新设备树节点,绑定设备句柄与状态图标。
- 设备升级:创建升级线程,调用 StartDeviceUpgrade,处理升级结果与提示。
- 参数修改:调用设备对象 ModifyParameter 并刷新详情列表。
- 注册/注销:调用设备对象 Register/Unregister,更新树节点与状态。
- 权限校验:GD10 密码校验与数据库密码存储,支持二次确认与日志记录。
- 与 DevManager 协作
- 通过 GetDeviceByID/GetDevice 获取设备对象。
- 添加/删除设备对象,更新树节点与状态。
- 异常与调试
- 创建线程失败、解析设备信息失败等场景均弹出提示并返回失败码。
- 建议:在 UI 中显示升级进度条与错误码,便于定位问题。
```mermaid
sequenceDiagram
participant UI as "界面"
participant DO as "DevOperator"
participant DM as "DevManager"
participant D as "CDevice"
UI->>DO : 点击“注册新设备”
DO->>DM : GetDevice(szDevSN)
DM-->>DO : 返回设备对象
DO->>D : Register()
alt 成功
DO->>DM : AddDevice(pDev)
DO->>DO : 刷新树节点与状态
else 失败
DO-->>UI : 返回失败
end
```
图表来源
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1139-L1262)
- [DevManager.h](file://h/DevManager.h#L1-L69)
章节来源
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1-L200)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1139-L1262)
- [DevOperator.h](file://h/DevOperator.h#L1-L59)
- [DevManager.h](file://h/DevManager.h#L1-L69)
### GD10OperCmd:命令协议封装与主机侧操作
- 功能要点
- 工程/测区/脚本/任务的增删改查,参数设置,用户注销等。
- 通过 DetcGD10Dev 获取 GD 设备挂载路径,直接对 SD 卡内 XML/文件进行操作。
- 对 XML 文件的读取、查找、新增/删除元素、保存等操作均有错误日志记录。
- 关键接口
- project_add/project_delete/testzone_add/testzone_delete/script_add/script_delete/meas_delete/set_param/unregister_user/loadDeviceMarkDataFromGD。
- 安全与日志
- 所有关键操作均记录日志,便于审计与回溯。
- 在非 USB 传输模式下会拒绝执行命令,防止误操作。
```mermaid
classDiagram
class CGD10OperCmd {
+GetInstance()
+project_add(mac, projectcn)
+project_delete(mac, projectcn)
+testzone_add(projectcn, testzonecn, type)
+testzone_delete(projectcn, testzonecn)
+script_add(scriptcn, scriptname, mediumid)
+script_delete(scriptcn)
+meas_delete(projectcn, testzonecn, measuringcn)
+set_param(strParam)
+unregister_user(mac)
+loadDeviceMarkDataFromGD()
-AddProjectInLocalHost()
-AddProjectInMacXml()
-DelProjectInLocalHost()
-DelProjectInMacXml()
-DeleteTaskInTz()
-DeleteTaskFile()
}
class CDetcGD10Dev {
+GetInstance()
+IsGD10DevConnect()
+GetGD10DevAddr()
+DetectGD10Dev()
}
CGD10OperCmd --> CDetcGD10Dev : "使用设备路径"
```
图表来源
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L120)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L1-L38)
章节来源
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L200)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L800-L1213)
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L80)
### CDevice:串口命令执行与文件传输
- 功能要点
- ExecuteOrder/ExecuteSignleOrder:向 GD 设备发送命令并等待特定标志,内置轮询与超时控制。
- SendFile/ReceiveFile:文件上传/下载,支持重试与错误处理。
- GetSynInfo/GetGRInfo/ShowGRInfo/ShowACInfo:数据同步与结果显示。
- Register/Unregister/ModifyParameter/ShowCableHeadInfoDlg:设备注册、参数修改与界面交互。
- 超时与重试
- 默认轮询次数与间隔可配置,避免长时间阻塞。
- 文件传输失败自动重试,提升鲁棒性。
- 日志与状态
- 统一的日志输出接口,便于问题定位。
- 设备状态(在线/离线/新)与图标映射。
```mermaid
flowchart TD
Enter(["进入命令执行"]) --> Clear["清空收发缓冲"]
Clear --> Send["发送命令字符串"]
Send --> Poll["轮询等待标志"]
Poll --> Timeout{"超时/成功?"}
Timeout --> |成功| Parse["解析响应"]
Timeout --> |超时| Retry{"是否重试?"}
Retry --> |是| Send
Retry --> |否| Fail["返回失败"]
Parse --> Done(["返回成功"])
```
图表来源
- [Device.h](file://h/Device.h#L1-L128)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
章节来源
- [Device.h](file://h/Device.h#L1-L128)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L200)
### DevManager:设备生命周期与远程设备管理
- 功能要点
- 在线/离线设备列表获取与管理。
- 远程设备集合(STSigRemoteDev)的增删与查询。
- 设备对象的添加/删除与句柄映射。
- 与 DevOperator 协作
- DevOperator 通过 DevManager 获取设备对象并执行操作。
- 更新远程设备集合,驱动 UI 列表刷新。
章节来源
- [DevManager.h](file://h/DevManager.h#L1-L69)
## 依赖关系分析
- DetcGD10Dev 与 GD10OperCmdGD10OperCmd 在执行前依赖 DetcGD10Dev 的连接状态与设备路径。
- DevOperator 与 DevManager/CDeviceDevOperator 通过 DevManager 获取设备对象,再调用 CDevice 的命令与文件操作。
- GD10OperCmd 与 DetcGD10DevGD10OperCmd 通过 DetcGD10Dev 的单例获取设备挂载路径,直接对 SD 卡内文件进行操作。
- CDevice 与 GD10OperCmd:两者均面向 GD 设备,前者侧重命令与文件传输,后者侧重主机侧对 GD 内部数据结构的维护。
```mermaid
graph LR
DGD["DetcGD10Dev"] --> GOC["GD10OperCmd"]
DOp["DevOperator"] --> DM["DevManager"]
DOp --> Dev["CDevice"]
GOC --> DGD
Dev --> DM
```
图表来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L80)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L120)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1-L200)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
- [DevManager.h](file://h/DevManager.h#L1-L69)
## 性能考量
- 连接池与并发
- 建议为串口通信引入连接池,避免频繁打开/关闭串口导致的握手延迟。
- 对于文件传输与升级流程,采用分块传输与进度回调,减少 UI 卡顿。
- 异步通信
- 将长耗时操作(如升级、文件传输)放入后台线程,主线程仅负责 UI 更新与状态提示。
- 使用事件/消息队列传递结果,避免阻塞 UI 线程。
- 超时与重试策略
- 命令轮询与文件传输应设置合理的超时阈值与重试次数,避免无限等待。
- 对网络不稳定或设备繁忙的情况,增加退避重试与降速策略。
- 缓存与预热
- 对设备参数与常用配置进行缓存,减少重复查询。
- 在设备连接后预热串口与文件传输通道,降低首次操作延迟。
## 故障排查指南
- 设备未检测到
- 检查 DetcGD10Dev 的 DetectGD10Dev 是否返回有效路径;确认卷标名与设备类型匹配。
- 查看 detect_gd20_log.txt 与 device_log.txt 的错误日志。
- 升级失败
- 确认 StartDeviceUpgrade 返回码与提示信息;检查升级包完整性与版本匹配。
- 若 GD10OperCmd 报告文件写入失败,检查 SD 卡空间与权限。
- 命令执行超时
- 调整 CDevice 的轮询次数与间隔;检查串口波特率与线缆质量。
- 对于复杂命令,拆分为多个小命令并增加中间态检查。
- 权限校验失败
- 确认 GD10 密码输入正确;检查数据库中密码记录是否更新成功。
- 若多次失败,建议清理缓存并重新输入。
- 文件传输失败
- 检查 SD 卡路径是否存在;确认文件名大小写与扩展名一致。
- 增加重试次数与断点续传机制。
章节来源
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L177-L189)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1-L120)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L1517-L1599)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L120)
## 结论
DeviceOperator 模块通过 DetcGD10Dev 的设备检测、DevOperator 的操作编排、GD10OperCmd 的命令封装与 CDevice 的底层通信,形成了完整的 GD10 设备管理闭环。模块具备良好的日志记录与异常处理能力,建议进一步引入连接池、异步通信与缓存机制,以提升性能与用户体验。
## 附录
- 安全性建议
- 权限验证:GD10 密码校验与数据库存储,避免未授权访问。
- 操作审计:所有关键操作均写入日志,便于追踪与回溯。
- 输入校验:对命令参数与文件路径进行严格校验,防止注入与越权。
- 性能优化清单
- 串口连接池与复用。
- 异步升级与文件传输。
- 超时与重试策略优化。
- UI 进度反馈与中断机制。
@@ -0,0 +1,225 @@
# ExecutionOperator
<cite>
**本文档中引用的文件**
- [ExecOperator.cpp](file://cpp/Operator/ExecOperator.cpp)
- [ExecOperator.h](file://h/ExecOperator.h)
- [ExecManager.cpp](file://cpp/Managers/ExecManager.cpp)
- [ExecManager.h](file://h/ExecManager.h)
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp)
- [DialMeasureData.h](file://h/DialMeasureData.h)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
- [TaskDataOper.h](file://h/TaskDataOper.h)
- [DialOfflineDeviceTaskManager.cpp](file://cpp/Views/DialOfflineDeviceTaskManager.cpp)
- [DialOfflineDeviceTaskManager.h](file://h/DialOfflineDeviceTaskManager.h)
</cite>
## 目录
1. [引言](#引言)
2. [ExecutionOperator职责与实现](#executionoperator职责与实现)
3. [与ExecManager的协作机制](#与execmanager的协作机制)
4. [用户交互事件处理流程](#用户交互事件处理流程)
5. [异常情况恢复策略](#异常情况恢复策略)
6. [性能监控指标与优化建议](#性能监控指标与优化建议)
7. [离线模式执行任务的实现细节](#离线模式执行任务的实现细节)
8. [结论](#结论)
## 引言
ExecutionOperator是Geomative Studio系统中负责测量任务执行过程的核心组件。它协调设备、脚本和数据管理组件,完成测量任务的启动、暂停、恢复和终止。本文档详细阐述了ExecutionOperator的职责与实现,说明其如何与ExecManager协作管理任务状态机、实时数据采集和进度反馈,并结合UI组件(如DialMeasureData)说明用户交互事件的处理流程。此外,文档还提供了任务执行过程中异常情况的恢复策略、性能监控指标与优化建议,以及支持离线模式执行任务的实现细节。
## ExecutionOperator职责与实现
ExecutionOperator的主要职责是协调设备、脚本和数据管理组件,完成测量任务的启动、暂停、恢复和终止。它通过ExecManager与底层设备进行通信,确保测量任务的顺利执行。
ExecutionOperator的实现主要集中在`ExecOperator.cpp``ExecOperator.h`文件中。`CExecOperator`类的构造函数接受一个数据库连接指针,用于与数据库进行交互。`ExecRSPTest`方法是执行电阻率测试的入口点,它创建并初始化`COpExec2DRSPTestSetDlg`对话框,然后根据用户选择的测试类型调用ExecManager的相应方法。
```mermaid
classDiagram
class CExecOperator {
+CExecOperator(_ConnectionPtr& pConnection)
+~CExecOperator()
+ExecRSPTest()
+InitialExec2DRSPTestDlg(COpExec2DRSPTestSetDlg* pOpExec2DRSPTestSetDlg)
}
class COpExec2DRSPTestSetDlg {
+Create()
+ShowWindow()
+RunModalLoop()
+DestroyWindow()
}
CExecOperator --> COpExec2DRSPTestSetDlg : "创建并初始化"
```
**Diagram sources**
- [ExecOperator.cpp](file://cpp/Operator/ExecOperator.cpp#L26-L72)
- [ExecOperator.h](file://h/ExecOperator.h#L23-L26)
**Section sources**
- [ExecOperator.cpp](file://cpp/Operator/ExecOperator.cpp#L1-L72)
- [ExecOperator.h](file://h/ExecOperator.h#L1-L37)
## 与ExecManager的协作机制
ExecutionOperator与ExecManager紧密协作,共同完成测量任务的管理。ExecManager负责具体的任务执行逻辑,包括初始化测量、发送测量命令和处理测量结果。
`CExecManager`类提供了`Exec2DRSPTest``ExecCERSPTest`方法,分别用于执行2D和1D电阻率测试。这些方法首先创建相应的测量数据对象(`CRsp2DTd``CRspCETd`),然后调用`Save2DRSPSetInfo``SaveCERSPSetInfo`方法将测量设置信息保存到对象中。接着,将测量头信息保存到文件,并通过设备的`SendFile`方法将文件发送到主机。最后,调用`InitialMeasure`方法初始化测量,并创建相应的测量对话框(`COpExec2DRSPTestDlg``COpExecCERSPTestDlg`)来显示测量进度。
```mermaid
sequenceDiagram
participant ExecutionOperator
participant ExecManager
participant Device
participant MeasurementDialog
ExecutionOperator->>ExecManager : Exec2DRSPTest()
ExecManager->>ExecManager : 创建CRsp2DTd对象
ExecManager->>ExecManager : 保存测量设置信息
ExecManager->>ExecManager : 保存头信息到文件
ExecManager->>Device : SendFile()
Device-->>ExecManager : 文件发送成功
ExecManager->>ExecManager : InitialMeasure()
ExecManager->>MeasurementDialog : 创建并显示对话框
MeasurementDialog-->>ExecManager : 测量完成
ExecManager-->>ExecutionOperator : 返回结果
```
**Diagram sources**
- [ExecManager.cpp](file://cpp/Managers/ExecManager.cpp#L483-L548)
- [ExecManager.h](file://h/ExecManager.h#L26-L27)
**Section sources**
- [ExecManager.cpp](file://cpp/Managers/ExecManager.cpp#L483-L548)
- [ExecManager.h](file://h/ExecManager.h#L26-L27)
## 用户交互事件处理流程
用户交互事件的处理主要由`DialMeasureData`类负责。该类处理用户在测量界面上的各种操作,如启动测量、暂停测量、从当前位置继续测量等。
`CDialMeasureData`类的`OnMeasureAll`方法处理“启动测量”按钮的点击事件。它首先检查是否已经下载了测量配置,如果没有,则调用`OnMeasureDownloadMeasuCfg`方法下载配置。然后,更新任务的测试日期和时间,并根据测量类型打开相应的数据表。最后,设置按钮状态,准备开始测量。
```mermaid
flowchart TD
Start([开始测量]) --> CheckDownload{"已下载配置?"}
CheckDownload --> |否| Download[下载测量配置]
Download --> UpdateTime[更新测试日期和时间]
CheckDownload --> |是| UpdateTime
UpdateTime --> OpenTable{"测量类型"}
OpenTable --> |数据| OpenData[打开数据表]
OpenTable --> |接地电阻| ClearGR[清除接地电阻信息]
OpenData --> SetStatus[设置按钮状态]
ClearGR --> SetStatus
SetStatus --> End([准备测量])
```
**Diagram sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L378-L497)
- [DialMeasureData.h](file://h/DialMeasureData.h#L61-L64)
**Section sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L378-L497)
- [DialMeasureData.h](file://h/DialMeasureData.h#L61-L64)
## 异常情况恢复策略
在测量过程中,可能会遇到各种异常情况,如设备断开、数据校验失败等。系统提供了一系列恢复策略来应对这些异常。
当设备断开时,系统会尝试重新连接设备。如果重新连接失败,会显示错误消息并终止测量。对于数据校验失败的情况,系统会记录错误日志,并尝试重新发送数据包。如果多次尝试后仍然失败,会终止测量并提示用户检查设备连接。
```mermaid
stateDiagram-v2
[*] --> Idle
Idle --> Measuring : "启动测量"
Measuring --> DeviceDisconnected : "设备断开"
Measuring --> DataValidationFailed : "数据校验失败"
DeviceDisconnected --> Reconnect : "尝试重新连接"
Reconnect --> Measuring : "连接成功"
Reconnect --> Error : "连接失败"
DataValidationFailed --> Retry : "重新发送"
Retry --> Measuring : "发送成功"
Retry --> Error : "重试次数超限"
Error --> Idle : "终止测量"
Measuring --> Completed : "测量完成"
Completed --> Idle : "结束"
```
**Diagram sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L928-L1100)
- [DialMeasureData.h](file://h/DialMeasureData.h#L80-L81)
**Section sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L928-L1100)
- [DialMeasureData.h](file://h/DialMeasureData.h#L80-L81)
## 性能监控指标与优化建议
系统提供了多种性能监控指标,包括采样频率、响应延迟等。这些指标可以帮助用户评估测量过程的性能,并进行相应的优化。
采样频率是指每秒采集的数据点数,它直接影响测量的精度和速度。响应延迟是指从发送测量命令到接收到测量结果的时间间隔,它反映了系统的实时性。
为了优化性能,建议采取以下措施:
1. 使用高速通信接口,如USB或以太网,以减少通信延迟。
2. 优化数据处理算法,减少CPU占用率。
3. 使用高效的数据库查询语句,减少数据访问时间。
4. 定期维护设备,确保设备处于最佳工作状态。
```mermaid
erDiagram
MEASUREMENT ||--o{ PERFORMANCE_METRICS : "包含"
PERFORMANCE_METRICS {
float sampling_frequency
float response_latency
float cpu_usage
float memory_usage
}
OPTIMIZATION_STRATEGIES ||--o{ PERFORMANCE_METRICS : "优化"
OPTIMIZATION_STRATEGIES {
string high_speed_interface
string optimized_algorithm
string efficient_query
string regular_maintenance
}
```
**Diagram sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L948-L954)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L4314-L4332)
**Section sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L948-L954)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L4314-L4332)
## 离线模式执行任务的实现细节
系统支持离线模式执行任务,允许用户在没有网络连接的情况下进行测量。离线模式的实现主要依赖于`DialOfflineDeviceTaskManager`类。
在离线模式下,测量数据首先保存在本地数据库中。当网络连接恢复后,用户可以手动上传数据到服务器。`CDialOfflineDeviceTaskManager`类提供了创建、删除、上传和管理离线任务的功能。
```mermaid
classDiagram
class CDialOfflineDeviceTaskManager {
+OnButtonCreateTask()
+OnButtonDelTask()
+OnButtonMeasureTask()
+OnBtnDataDownload()
+OnBtnCreateTimerTask()
+OnBtnManageTimerTask()
}
class CTaskDataOper {
+Create1DTask()
+Create2DTask()
+Create3DTask()
+DeleteTaskArray()
+InsertPlcStatusData()
}
CDialOfflineDeviceTaskManager --> CTaskDataOper : "使用"
```
**Diagram sources**
- [DialOfflineDeviceTaskManager.cpp](file://cpp/Views/DialOfflineDeviceTaskManager.cpp#L323-L325)
- [TaskDataOper.h](file://h/TaskDataOper.h#L344-L346)
**Section sources**
- [DialOfflineDeviceTaskManager.cpp](file://cpp/Views/DialOfflineDeviceTaskManager.cpp#L323-L325)
- [TaskDataOper.h](file://h/TaskDataOper.h#L344-L346)
## 结论
ExecutionOperator是Geomative Studio系统中测量任务执行的核心组件。它通过与ExecManager的紧密协作,实现了测量任务的启动、暂停、恢复和终止。系统提供了完善的用户交互事件处理流程、异常情况恢复策略、性能监控指标与优化建议,以及支持离线模式执行任务的实现细节。这些功能共同确保了测量任务的高效、可靠执行。
@@ -0,0 +1,168 @@
# Operator模块
<cite>
**Referenced Files in This Document**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [DataOperator.h](file://h/DataOperator.h)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [DevOperator.h](file://h/DevOperator.h)
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptOperator.h](file://h/SptOperator.h)
- [ExecOperator.cpp](file://cpp/Operator/ExecOperator.cpp)
- [ExecOperator.h](file://h/ExecOperator.h)
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp)
- [SynOperator.h](file://h/SynOperator.h)
- [InputPasswordDial.cpp](file://cpp/Operator/InputPasswordDial.cpp)
- [InputPasswordDial.h](file://h/InputPasswordDial.h)
- [DialCustomSptInput.cpp](file://cpp/Operator/DialCustomSptInput.cpp)
- [DialCustomSptInput.h](file://h/DialCustomSptInput.h)
- [UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp)
- [UpdateDataBase.h](file://h/UpdateDataBase.h)
</cite>
## 目录
1. [DataOperator数据操作器](#dataoperator数据操作器)
2. [DetcGD10Dev设备检测器](#detcgd10dev设备检测器)
3. [DevOperator设备操作器](#devoperator设备操作器)
4. [SptOperator脚本操作器](#sptoperator脚本操作器)
5. [ExecOperator执行操作器](#execoperator执行操作器)
6. [SynOperator同步操作器](#synoperator同步操作器)
7. [对话框操作器](#对话框操作器)
8. [UpdateDataBase数据库升级器](#updatedatabase数据库升级器)
9. [操作器与管理器调用关系](#操作器与管理器调用关系)
## DataOperator数据操作器
DataOperator类负责处理应用程序中的数据读写操作,主要功能包括初始化导航数据视图、显示各类测试数据信息以及在数据库中创建和删除项目。该操作器通过ADO数据库连接与后端数据库进行交互,使用`_ConnectionPtr`对象管理数据库连接。
`InitialNavDataView`方法负责初始化导航数据视图,通过查询数据库中的项目(project)、测区(tz)和测试任务(td)表,构建树形结构的数据视图。该方法首先获取项目信息,然后为每个项目获取其下属的测区,最后为每个测区获取其包含的测试任务。在构建树形结构时,使用`CHandleProcessor`生成唯一句柄,并通过`CStateProcessor`设置节点状态图像。
数据展示功能通过一系列`ShowXxxInfo`方法实现,如`ShowProjectInfo``ShowTzInfo``ShowRsp3DTdInfo`等。这些方法通过调用管理器对象(如`theApp.m_pProManager``theApp.m_pTdManager`)获取相应的数据对象,并调用其`ShowDetailInfo``ShowConList`等方法将数据显示在相应的视图控件中。
数据库操作方面,`CreateProjectInDB`方法在数据库中创建新项目,通过事务处理确保数据一致性。该方法首先调用`ProManager``CreateProjectInDB`方法在数据库中创建项目记录,然后更新导航视图以反映新创建的项目。类似地,`DeleteProjectInDB`方法用于删除项目及其相关数据。
**Section sources**
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp#L241-L486)
- [DataOperator.h](file://h/DataOperator.h#L96-L171)
## DetcGD10Dev设备检测器
DetcGD10Dev类负责检测GD10设备的连接状态,采用单例模式实现,确保在整个应用程序中只有一个实例存在。该类通过USB设备枚举机制检测特定名称的可移动驱动器来识别GD10设备。
`DetectGD10Dev`方法是设备检测的核心,它调用`FindUsbDevice`方法搜索系统中的USB设备。搜索过程首先获取系统中所有逻辑驱动器,然后遍历这些驱动器,识别类型为`DRIVE_REMOVABLE`(可移动驱动器)的设备。对于每个可移动驱动器,通过`CompareDriverName`方法验证其卷标名称是否匹配预期的设备名称("GD10"或"GD20")。
`CompareDriverName`方法不仅检查卷标名称,还提供了备用检测机制。当卷标名称不匹配时,会检查驱动器根目录下是否存在"\SD\equipment"目录,这种设计提高了设备识别的可靠性,避免了因卷标名称修改导致的识别失败。
设备检测结果通过`m_bGD10DevIsCon`布尔变量和`m_strDevAddr`字符串变量存储,分别表示设备是否连接和设备的驱动器路径。检测过程中的日志信息通过`PrintLog`方法写入"log\detect_gd20_log.txt"文件,便于问题排查和调试。
**Section sources**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L61-L68)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L22-L24)
## DevOperator设备操作器
DevOperator类负责执行与设备相关的各种操作,包括初始化设备导航视图、显示设备信息、设备升级和参数修改等。该操作器管理在线和离线设备的显示,通过树形结构组织设备信息。
`InitialNavDevView`方法构建设备导航视图的树形结构,分为在线设备和离线设备两大分支。在线设备进一步分为已注册设备和新发现设备。该方法通过`DevManager`获取设备列表,并根据设备状态(在线、离线、新设备)将其添加到相应的树节点中。设备信息的显示使用`m_handleProcessor`生成的句柄进行标识。
设备信息展示功能通过`ShowFLDeviceInfo``ShowOLDeviceInfo`方法实现,分别用于显示离线和在线设备的详细信息。这些方法调用`DevManager`获取设备对象,并调用其`ShowFLDetailInfo``ShowOLDetailInfo`等方法将设备参数、接地电阻信息等数据显示在相应的视图控件中。
设备管理功能包括`DevieUpgrade`(设备升级)、`ModifyDeviceParameter`(修改设备参数)和`ShowCableHeadDlg`(显示电缆头对话框)等。设备升级操作通过创建独立线程执行,避免阻塞用户界面。`RefreshGRRec``RefreshAllGRRec`方法用于刷新设备的接地电阻记录,通过与设备通信获取最新的接地电阻测量数据。
**Section sources**
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L60-L331)
- [DevOperator.h](file://h/DevOperator.h#L23-L47)
## SptOperator脚本操作器
SptOperator类负责处理脚本相关的操作,包括初始化脚本导航视图、显示脚本信息以及在数据库中创建和管理脚本。该操作器支持一维(CE)、二维(2D)和三维(3D)三种类型的脚本。
`InitialNavExecView`方法初始化脚本导航视图,依次调用`InitialNavExecCEDlg``InitialNavExec2DDlg``InitialNavExec3DDlg`方法初始化不同类型脚本的对话框。这些初始化方法通过查询数据库中的`scon`表获取已存在的脚本列表,并将其显示在相应的列表控件中。
脚本信息展示通过`ShowCEScriptConInfo``Show2DScriptConInfo``Show3DScriptConInfo`方法实现。这些方法首先通过`SptManager`获取脚本对象,然后调用其`ShowSptDetailInfo``ShowChannelList``ShowSptConInfo`方法将脚本的详细信息、通道列表和测量序列数据显示在相应的视图控件中。
脚本管理功能包括`Create2DSConInDB``Create3DSConInDB``CreateCESConInDB`等方法,用于在数据库中创建新的脚本。创建成功后,通过`AddSConItemToSConList`方法将新脚本添加到相应的列表控件中。`Export2DSConInDB``Export3DSConInDB`方法支持将脚本导出为文本文件。
**Section sources**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp#L43-L791)
- [SptOperator.h](file://h/SptOperator.h#L33-L45)
## ExecOperator执行操作器
ExecOperator类负责执行测量任务,主要功能是启动电阻率测试。`ExecRSPTest`方法是执行测量任务的核心,它创建并显示`COpExec2DRSPTestSetDlg`对话框,让用户配置测试参数。
`InitialExec2DRSPTestSetDlg`方法初始化测试设置对话框,填充各种参数选项。该方法查询数据库中的`cm`表获取参数的可选值,如电极排列方式(`Clayout`)、发射频率(`Trfrequency`)、采样频率(`SAfrequency50`)等。这些参数根据当前语言设置进行本地化显示。
当用户完成参数配置并确认后,`ExecRSPTest`方法根据用户选择的标签页(一维或二维)调用`ExecManager`的相应方法执行测试。对于一维测试,调用`ExecCERSPTest`;对于二维测试,调用`Exec2DRSPTest`。测试执行完成后,对话框被销毁。
**Section sources**
- [ExecOperator.cpp](file://cpp/Operator/ExecOperator.cpp#L37-L213)
- [ExecOperator.h](file://h/ExecOperator.h#L26-L27)
## SynOperator同步操作器
SynOperator类负责处理同步操作,主要功能是启动同步对话框。`Syn`方法创建并显示`COpSynDlg`对话框,允许用户选择要同步的设备。
`InitialSynDlg`方法初始化同步对话框,通过`DevManager`获取在线设备列表,并将设备序列号添加到设备选择下拉框中。该方法只显示状态为"在线"而非"新设备"的设备,确保用户只能选择已注册的设备进行同步。
同步操作本身在`COpSynDlg`对话框中实现,`SynOperator`主要负责对话框的初始化和显示。当用户完成同步设置并确认后,同步过程开始;如果用户取消操作,则对话框被销毁。
**Section sources**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp#L32-L81)
- [SynOperator.h](file://h/SynOperator.h#L17-L18)
## 对话框操作器
### InputPasswordDial密码输入对话框
InputPasswordDial类实现密码输入对话框,用于用户身份验证。该对话框支持两种验证模式:直接验证和GD10验证,通过`m_bIsDirectVerify`标志区分。
`OnOK`方法处理用户确认操作,首先验证输入的密码是否为空,然后根据验证模式检查密码是否正确。在直接验证模式下,将输入密码与预设的Geomative密码进行比较;在GD10验证模式下,跳过密码检查。密码验证通过后,调用基类的`OnOK`方法关闭对话框。
`OnInitDialog`方法初始化对话框,根据验证模式设置窗口标题和提示信息。如果处于直接验证模式但未设置密码,则显示错误消息并拒绝初始化。`OnCancel`方法处理用户取消操作,显示确认对话框询问用户是否确实要放弃输入密码。
**Section sources**
- [InputPasswordDial.cpp](file://cpp/Operator/InputPasswordDial.cpp#L47-L108)
- [InputPasswordDial.h](file://h/InputPasswordDial.h#L38-L40)
### DialCustomSptInput自定义脚本输入对话框
DialCustomSptInput类实现自定义脚本输入对话框,允许用户手动输入或从Excel文件导入脚本参数。该对话框支持两种K值计算模式:手动输入和自动计算。
`OnButtonAddList`方法处理添加脚本行操作,首先验证输入的有效性,然后计算K值(如果处于自动计算模式),最后将参数添加到列表控件中。`CheckInputIsValid`方法验证所有输入字段,确保A、B、M、N、层数和迭代数为正整数或-1,K值为有效浮点数。
`CalculateK`方法在自动计算模式下根据A、B、M、N电极位置计算几何因子K。计算公式基于电极间距离的倒数差,考虑了无穷远电极(用-1表示)的特殊情况。`OnButtonImport`方法支持从Excel文件导入脚本数据,使用COM接口与Excel应用程序交互,读取指定工作表中的数据。
**Section sources**
- [DialCustomSptInput.cpp](file://cpp/Operator/DialCustomSptInput.cpp#L78-L757)
- [DialCustomSptInput.h](file://h/DialCustomSptInput.h#L48-L56)
## UpdateDataBase数据库升级器
UpdateDataBase类负责处理数据库升级,通过解析XML配置文件执行数据库结构的变更。该类在应用程序启动时检查当前数据库版本,并根据需要执行升级操作。
`ParserUpdateDBXml`方法解析"updates\database_modify.xml"文件,查找与当前软件版本匹配的升级记录。该方法首先验证当前版本与XML文件中声明的版本是否一致,然后查找前一版本的升级信息。`GetDBUpdatesInfo`方法解析具体的数据库变更指令,包括创建表、删除表和修改表结构。
数据库操作通过`AddDBTable``DeleteDBTable``ModifyDBTable`方法实现。`AddDBTable`方法根据`STTableInfo`结构中的信息生成CREATE TABLE SQL语句,包括列定义、主键、索引和外键约束。`ModifyDBTable`方法处理表结构修改,支持添加列、删除列和修改列属性。
`UpdateDBInfo`方法是数据库升级的入口点,按顺序执行所有收集的数据库变更操作。升级完成后,调用`WriteVersionToDB`方法更新数据库中的版本信息,确保下次启动时不会重复执行已应用的升级。
**Section sources**
- [UpdateDataBase.cpp](file://cpp/Operator/UpdateDataBase.cpp#L432-L873)
- [UpdateDataBase.h](file://h/UpdateDataBase.h#L76-L97)
## 操作器与管理器调用关系
操作器与管理器之间存在紧密的调用关系,操作器作为管理器功能的前端接口,将用户操作转化为对管理器的具体调用。这种设计模式实现了关注点分离,操作器负责用户界面交互,管理器负责核心业务逻辑。
以SptManager调用SptOperator为例,当用户在脚本管理界面创建新脚本时,SptManager的`Create2DSConInDB`方法被调用。该方法执行核心的脚本创建逻辑,包括在数据库中插入记录和在内存中创建脚本对象。创建成功后,控制权返回给SptOperator,由其调用`AddSConItemToSConList`方法更新用户界面,将新脚本显示在列表中。
类似地,DevOperator在设备升级操作中调用DevManager的功能。`DevieUpgrade`方法首先获取选中的设备句柄,然后通过`DevManager``GetDeviceByID`方法获取设备对象。升级操作在独立线程中执行,调用`StartDeviceUpgrade`函数,该函数最终会调用DevManager中的设备通信和固件更新逻辑。
这种调用关系体现了典型的MVCModel-View-Controller)模式,其中管理器扮演Model角色,负责数据和业务逻辑;操作器扮演Controller角色,处理用户输入并协调Model和View;视图类(如各种CView派生类)则负责数据显示。通过这种分层架构,系统实现了良好的模块化和可维护性。
**Section sources**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp#L497-L522)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp#L385-L433)
@@ -0,0 +1,253 @@
# ScriptOperator
<cite>
**本文档中引用的文件**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [Script.cpp](file://cpp/ProblemZone/Script.cpp)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
- [SptManager.h](file://h/SptManager.h)
- [SptOperator.h](file://h/SptOperator.h)
- [Script.h](file://h/Script.h)
- [Script2D.h](file://h/Script2D.h)
- [Script3D.h](file://h/Script3D.h)
- [ScriptCE.h](file://h/ScriptCE.h)
- [Medium.h](file://h/Medium.h)
- [MediumBasicWenAndSch.h](file://h/MediumBasicWenAndSch.h)
- [MediumStrongWenAndSch.h](file://h/MediumStrongWenAndSch.h)
- [MediumCrossHoleGeomative.h](file://h/MediumCrossHoleGeomative.h)
- [MediumCustom2D.h](file://h/MediumCustom2D.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
ScriptOperator是Geomative Studio软件中负责脚本生成与管理的核心组件,主要处理2D、3D及跨孔测量脚本的创建、编辑和导出功能。该组件与SptManager协同工作,根据用户配置生成相应的测量脚本,包括电极排列计算、测量序列生成和脚本验证逻辑。ScriptOperator支持多种测量模式,如温纳法、施伦贝尔格法等,并提供脚本格式的内部结构说明和性能优化策略。
## 项目结构
Geomative Studio项目包含多个目录,其中与ScriptOperator相关的文件主要位于`cpp/Operator``cpp/Managers`目录下。`cpp/Operator`目录包含SptOperator.cpp,负责脚本操作的具体实现;`cpp/Managers`目录包含SptManager.cpp,负责脚本的管理和协调。此外,`cpp/ProblemZone`目录包含各种脚本类的实现文件,如Script2D.cpp、Script3D.cpp等。
```mermaid
graph TD
subgraph "核心组件"
SptOperator["SptOperator.cpp"]
SptManager["SptManager.cpp"]
end
subgraph "脚本实现"
Script2D["Script2D.cpp"]
Script3D["Script3D.cpp"]
ScriptCE["ScriptCE.cpp"]
end
subgraph "介质类"
Medium["Medium.h"]
MediumBasic["MediumBasicWenAndSch.h"]
MediumStrong["MediumStrongWenAndSch.h"]
MediumCrossHole["MediumCrossHoleGeomative.h"]
MediumCustom["MediumCustom2D.h"]
end
SptOperator --> SptManager
SptManager --> Script2D
SptManager --> Script3D
SptManager --> ScriptCE
Script2D --> MediumBasic
Script2D --> MediumStrong
Script3D --> MediumCrossHole
ScriptCE --> MediumCustom
```
**图源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
- [Medium.h](file://h/Medium.h)
- [MediumBasicWenAndSch.h](file://h/MediumBasicWenAndSch.h)
- [MediumStrongWenAndSch.h](file://h/MediumStrongWenAndSch.h)
- [MediumCrossHoleGeomative.h](file://h/MediumCrossHoleGeomative.h)
- [MediumCustom2D.h](file://h/MediumCustom2D.h)
**节源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 核心组件
ScriptOperator的核心功能包括创建、删除和导出2D、3D及跨孔测量脚本。通过与SptManager的交互,ScriptOperator能够根据用户配置生成相应的脚本,并处理脚本生成过程中的常见错误,如电极冲突和参数越界。SptManager负责管理脚本的生命周期,包括脚本的创建、编辑和导出。
**节源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 架构概述
ScriptOperator与SptManager之间的调用关系如下图所示。SptOperator负责用户界面的交互,接收用户输入并调用SptManager的方法来创建、删除和导出脚本。SptManager则负责具体的脚本生成逻辑,包括电极排列计算、测量序列生成和脚本验证。
```mermaid
sequenceDiagram
participant 用户 as "用户"
participant SptOperator as "SptOperator"
participant SptManager as "SptManager"
participant 脚本类 as "脚本类"
用户->>SptOperator : 创建新脚本
SptOperator->>SptManager : 调用Create2DSConInDB
SptManager->>脚本类 : 生成脚本
脚本类-->>SptManager : 返回脚本ID
SptManager-->>SptOperator : 返回执行结果
SptOperator-->>用户 : 显示结果
用户->>SptOperator : 编辑现有脚本
SptOperator->>SptManager : 调用EditScript
SptManager->>脚本类 : 修改脚本
脚本类-->>SptManager : 返回修改结果
SptManager-->>SptOperator : 返回执行结果
SptOperator-->>用户 : 显示结果
用户->>SptOperator : 导出脚本
SptOperator->>SptManager : 调用Export2DSConInDB
SptManager->>脚本类 : 导出脚本
脚本类-->>SptManager : 返回导出结果
SptManager-->>SptOperator : 返回执行结果
SptOperator-->>用户 : 显示结果
```
**图源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
**节源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 详细组件分析
### ScriptOperator分析
ScriptOperator的主要职责是处理用户界面的交互,接收用户输入并调用SptManager的方法来执行具体的操作。例如,当用户创建新脚本时,SptOperator会调用SptManager的Create2DSConInDB方法来生成脚本。
```mermaid
classDiagram
class CSptOperator {
+InitialNavExecView(CNavSptView* pNavExecView)
+ShowCEScriptConInfo(DWORD dwSptHandle, CView* pAppExecView)
+Show2DScriptConInfo(DWORD dwSptHandle, CView* pAppExecView)
+Show3DScriptConInfo(DWORD dwSptHandle, CView* pAppExecView)
+Create2DSConInDB(CNavSptView* pNavExecView)
+Delete2DSConInDB(CNavSptView* pNavExecView)
+Export2DSConInDB(CNavSptView* pNavExecView)
+CreateCESConInDB(CNavSptView* pNavExecView)
+DeleteCESConInDB(CNavSptView* pNavExecView)
+Create3DSConInDB(CNavSptView* pNavExecView)
+Delete3DSConInDB(CNavSptView* pNavExecView)
}
class CSptManager {
+Create2DSConInDB(DWORD& dwSConID)
+Delete2DSConInDB(DWORD dwSConID)
+Export2DSConInDB(DWORD dwSConID, CString strSptFilePath)
+CreateCESConInDB(DWORD& dwSConID)
+DeleteCESConInDB(DWORD dwSConID)
+Create3DSConInDB(DWORD dwSConID[128])
+Delete3DSConInDB(DWORD dwSConID)
}
CSptOperator --> CSptManager : "调用"
```
**图源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
**节源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
### 脚本生成逻辑分析
脚本生成过程中,SptManager会根据用户选择的测量模式(如温纳法、施伦贝尔格法等)生成相应的电极排列和测量序列。不同的测量模式有不同的实现类,如MediumBasicWenAndSch、MediumStrongWenAndSch等。
```mermaid
flowchart TD
Start([开始]) --> 选择测量模式
选择测量模式 --> 生成电极排列
生成电极排列 --> 生成测量序列
生成测量序列 --> 验证脚本
验证脚本 --> 保存脚本
保存脚本 --> 结束([结束])
验证脚本 --> |电极冲突| 处理错误
验证脚本 --> |参数越界| 处理错误
处理错误 --> 重新生成
重新生成 --> 生成电极排列
```
**图源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [MediumBasicWenAndSch.h](file://h/MediumBasicWenAndSch.h)
- [MediumStrongWenAndSch.h](file://h/MediumStrongWenAndSch.h)
- [MediumCrossHoleGeomative.h](file://h/MediumCrossHoleGeomative.h)
- [MediumCustom2D.h](file://h/MediumCustom2D.h)
**节源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [MediumBasicWenAndSch.h](file://h/MediumBasicWenAndSch.h)
- [MediumStrongWenAndSch.h](file://h/MediumStrongWenAndSch.h)
- [MediumCrossHoleGeomative.h](file://h/MediumCrossHoleGeomative.h)
- [MediumCustom2D.h](file://h/MediumCustom2D.h)
## 依赖关系分析
ScriptOperator依赖于SptManager来执行具体的脚本生成逻辑,而SptManager又依赖于各种脚本类和介质类来生成具体的测量脚本。这种分层架构使得各个组件之间的职责清晰,便于维护和扩展。
```mermaid
graph TD
SptOperator --> SptManager
SptManager --> Script2D
SptManager --> Script3D
SptManager --> ScriptCE
Script2D --> MediumBasic
Script2D --> MediumStrong
Script3D --> MediumCrossHole
ScriptCE --> MediumCustom
```
**图源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [Script2D.cpp](file://cpp/ProblemZone/Script2D.cpp)
- [Script3D.cpp](file://cpp/ProblemZone/Script3D.cpp)
- [ScriptCE.cpp](file://cpp/ProblemZone/ScriptCE.cpp)
- [MediumBasicWenAndSch.h](file://h/MediumBasicWenAndSch.h)
- [MediumStrongWenAndSch.h](file://h/MediumStrongWenAndSch.h)
- [MediumCrossHoleGeomative.h](file://h/MediumCrossHoleGeomative.h)
- [MediumCustom2D.h](file://h/MediumCustom2D.h)
**节源**
- [SptOperator.cpp](file://cpp/Operator/SptOperator.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 性能考虑
为了提高脚本生成的性能,SptManager采用了缓存机制来存储常用的计算结果。例如,电极排列和测量序列的计算结果会被缓存,以避免重复计算。此外,SptManager还支持多线程操作,可以在后台线程中执行耗时的计算任务,从而提高用户体验。
**节源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 故障排除指南
在脚本生成过程中,可能会遇到电极冲突和参数越界等常见错误。SptManager提供了相应的错误处理机制,可以在生成脚本时检测并处理这些错误。例如,当检测到电极冲突时,SptManager会提示用户重新配置电极排列。
**节源**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 结论
ScriptOperator是Geomative Studio中负责脚本生成与管理的核心组件,通过与SptManager的协同工作,能够根据用户配置生成2D、3D及跨孔测量脚本。该组件支持多种测量模式,并提供了脚本格式的内部结构说明和性能优化策略。通过合理的架构设计和错误处理机制,ScriptOperator能够高效地生成和管理测量脚本,满足用户的需求。
@@ -0,0 +1,299 @@
# SynchronizationOperator
<cite>
**本文档中引用的文件**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp)
- [SynOperator.h](file://h/SynOperator.h)
- [opsyndlg.cpp](file://cpp/Views/opsyndlg.cpp)
- [opsyndlg.h](file://h/opsyndlg.h)
- [opsyntddlg.cpp](file://cpp/Views/opsyntddlg.cpp)
- [opsyntddlg.h](file://h/opsyntddlg.h)
- [opsynsptdlg.cpp](file://cpp/Views/opsynsptdlg.cpp)
- [opsynsptdlg.h](file://h/opsynsptdlg.h)
- [OperMediumPt.cpp](file://cpp/Operator/OperMediumPt.cpp)
- [OperMediumPt.h](file://h/OperMediumPt.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
SynchronizationOperatorSynOperator)是GeomativeStudio软件中的一个核心组件,负责协调多个设备或模块之间的同步操作。该组件主要处理设备同步、数据同步和状态同步,确保测量过程的同步性。通过与同步对话框(opsyndlg)的交互,用户可以配置同步参数并接收状态反馈。本文档将深入探讨SynOperator的实现机制,包括其如何协调时间基准和操作序列,以及与OperMediumPt类结合进行介质点同步计算的算法逻辑。同时,文档还将提供同步失败、时钟漂移等常见问题的诊断方法和解决方案,并评估同步精度和网络延迟对同步性能的影响。
## 项目结构
GeomativeStudio项目的结构清晰地组织了各个功能模块。核心的同步功能主要位于`cpp/Operator`目录下的`SynOperator.cpp``SynOperator.h`文件中。同步对话框的实现则分布在`cpp/Views`目录下的`opsyndlg.cpp``opsyntddlg.cpp``opsynsptdlg.cpp`文件中。此外,设备管理和项目管理的相关逻辑分别在`cpp/Managers`目录下的`DevManager.cpp``ProManager.cpp`文件中实现。脚本管理器的功能则由`SptManager.cpp`文件提供。这些文件共同构成了SynchronizationOperator的技术基础。
```mermaid
graph TD
subgraph "核心组件"
SynOperator[SynOperator.cpp]
OperMediumPt[OperMediumPt.cpp]
end
subgraph "视图组件"
opsyndlg[opsyndlg.cpp]
opsyntddlg[opsyntddlg.cpp]
opsynsptdlg[opsynsptdlg.cpp]
end
subgraph "管理器"
DevManager[DevManager.cpp]
ProManager[ProManager.cpp]
SptManager[SptManager.cpp]
end
SynOperator --> opsyndlg
opsyndlg --> opsyntddlg
opsyndlg --> opsynsptdlg
SynOperator --> OperMediumPt
opsyndlg --> DevManager
opsyndlg --> ProManager
opsyntddlg --> ProManager
opsynsptdlg --> SptManager
```
**图表来源**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp)
- [opsyndlg.cpp](file://cpp/Views/opsyndlg.cpp)
- [opsyntddlg.cpp](file://cpp/Views/opsyntddlg.cpp)
- [opsynsptdlg.cpp](file://cpp/Views/opsynsptdlg.cpp)
- [OperMediumPt.cpp](file://cpp/Operator/OperMediumPt.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
**章节来源**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp)
- [opsyndlg.cpp](file://cpp/Views/opsyndlg.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
## 核心组件
SynchronizationOperator的核心功能包括设备同步、数据同步和状态同步。它通过调用`Syn()`方法启动同步过程,该方法创建并初始化同步对话框`COpSynDlg`,然后显示该对话框以供用户进行配置。`InitialSynDlg`方法用于初始化同步对话框,从设备管理器中获取已连接的设备列表,并将其添加到对话框的设备选择下拉列表中。整个同步过程依赖于与设备管理器、项目管理器和脚本管理器的紧密协作,确保所有相关数据和配置都能正确同步。
**章节来源**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp#L32-L55)
- [SynOperator.h](file://h/SynOperator.h#L17-L18)
## 架构概述
SynchronizationOperator的架构设计旨在实现高效且可靠的同步操作。其主要组件包括`CSynOperator`类,负责协调同步流程;`COpSynDlg`类,提供用户界面以配置同步参数;以及`COpSynTDDlg``COpSynSptDlg`类,分别处理测试数据和脚本的同步。这些组件通过事件驱动的方式进行通信,确保用户操作能够及时反映到同步过程中。此外,`CSynOperator`还利用`CStateProcessor``CHandleProcessor`来处理状态变化和句柄生成,进一步增强了系统的稳定性和可维护性。
```mermaid
classDiagram
class CSynOperator {
+Syn()
+InitialSynDlg(COpSynDlg* pOpSynDlg)
-_ConnectionPtr m_pConnection
-CStateProcessor m_stateProcessor
-CHandleProcessor m_handleProcessor
}
class COpSynDlg {
+OnInitDialog()
+OnSelchangeOpSynTab()
+OnSelchangeTdDesn()
-COpSynTDDlg m_opSynTDDlg
-COpSynSptDlg m_opSynSptDlg
-CDevice* m_pDev
-_ConnectionPtr m_pConnection
}
class COpSynTDDlg {
+OnInitDialog()
+OnSelchangedTreeSynDms()
+OnItemchangedListSynTd()
+OnTdSel()
+OnTdRemove()
+OnCreate()
+OnDmsDel()
+OnTdDel()
+OnTdUpload()
-CListCtrl m_tdList
-CListCtrl m_selTdList
-CTreeCtrl m_dmsTree
-CDevice* m_pDev
}
class COpSynSptDlg {
+OnInitDialog()
+OnSptDownload()
+OnSptUpload()
+OnSptDel()
-CListCtrl m_locSptList
-CListCtrl m_hostSptList
-CDevice* m_pDev
-int m_iSelSCID
}
CSynOperator --> COpSynDlg : "创建并初始化"
COpSynDlg --> COpSynTDDlg : "包含"
COpSynDlg --> COpSynSptDlg : "包含"
COpSynTDDlg --> ProManager : "调用"
COpSynSptDlg --> SptManager : "调用"
```
**图表来源**
- [SynOperator.h](file://h/SynOperator.h#L13-L24)
- [opsyndlg.h](file://h/opsyndlg.h#L19-L62)
- [opsyntddlg.h](file://h/opsyntddlg.h#L15-L61)
- [opsynsptdlg.h](file://h/opsynsptdlg.h#L13-L58)
## 详细组件分析
### SynOperator分析
`CSynOperator`类是同步操作的核心,其主要职责是启动和管理同步流程。`Syn()`方法通过创建`COpSynDlg`实例并调用其`RunModalLoop`方法来启动非模态窗口的模态循环,从而允许用户在不阻塞主程序的情况下进行同步配置。`InitialSynDlg`方法负责初始化同步对话框,从设备管理器中获取当前连接的设备列表,并将这些设备的信息填充到对话框的设备选择下拉列表中。这一过程确保了用户能够看到所有可用的设备,并选择需要同步的目标设备。
#### 对象导向组件:
```mermaid
classDiagram
class CSynOperator {
+Syn()
+InitialSynDlg(COpSynDlg* pOpSynDlg)
-_ConnectionPtr m_pConnection
-CStateProcessor m_stateProcessor
-CHandleProcessor m_handleProcessor
}
class COpSynDlg {
+OnInitDialog()
+OnSelchangeOpSynTab()
+OnSelchangeTdDesn()
-COpSynTDDlg m_opSynTDDlg
-COpSynSptDlg m_opSynSptDlg
-CDevice* m_pDev
-_ConnectionPtr m_pConnection
}
CSynOperator --> COpSynDlg : "创建并初始化"
```
**图表来源**
- [SynOperator.h](file://h/SynOperator.h#L13-L24)
- [opsyndlg.h](file://h/opsyndlg.h#L19-L62)
**章节来源**
- [SynOperator.cpp](file://cpp/Operator/SynOperator.cpp#L21-L55)
- [SynOperator.h](file://h/SynOperator.h#L13-L24)
### OperMediumPt分析
`COperMediumPt`类负责处理介质点的同步计算,特别是在不同类型的测量装置中进行深度排序。`InitiSortInfo`方法根据指定的方法初始化排序信息,而`AddSortPtInfo`方法则用于添加具体的排序点信息。`GetUniSptXPos`方法计算统一测点的位置,这是通过分析四个电极的位置(A、B、M、N)来完成的。对于不同类型的装置(如四级、三级、二级装置),该方法采用不同的算法来计算测点位置,确保结果的准确性。`GetFirstSortID``GetNextSortID`方法则用于遍历排序后的测点列表,提供有序的访问接口。
#### 对象导向组件:
```mermaid
classDiagram
class COperMediumPt {
+COperMediumPt(int iAR)
+~COperMediumPt()
+InitiSortInfo(int iMethod)
+AddSortPtInfo(int iA, int iB, int iM, int iN, int iLayer, int iIndex)
+GetUniSptXPos(int iA, int iB, int iM, int iN)
+GetFirstSortID(int iMethod)
+GetNextSortID()
-int m_iAR
-std : : map<STDepthSortKey, int> m_mapDepthSort
-std : : map<STDepthSortKey, int> : : iterator m_iterSortDepth
}
class STDepthSortKey {
+float fXPos
+int iLayer
+operator < (const STDepthSortKey& Obj) const
}
COperMediumPt --> STDepthSortKey : "使用"
```
**图表来源**
- [OperMediumPt.h](file://h/OperMediumPt.h#L39-L58)
- [OperMediumPt.cpp](file://cpp/Operator/OperMediumPt.cpp#L18-L137)
**章节来源**
- [OperMediumPt.cpp](file://cpp/Operator/OperMediumPt.cpp#L18-L137)
- [OperMediumPt.h](file://h/OperMediumPt.h#L39-L58)
### 同步对话框交互流程
同步对话框(`COpSynDlg`)提供了用户与SynchronizationOperator交互的界面。当用户选择同步操作时,`CSynOperator`会创建`COpSynDlg`实例并显示它。`OnInitDialog`方法初始化对话框,设置标签文本和控件布局。`OnSelchangeOpSynTab`方法处理标签页切换事件,根据用户选择的不同标签页(测试数据或脚本)显示相应的子对话框。`OnSelchangeTdDesn`方法在用户选择不同的设备时更新测试数据和脚本列表,确保用户能够看到与所选设备相关的所有信息。
#### API/服务组件:
```mermaid
sequenceDiagram
participant User as "用户"
participant SynOperator as "CSynOperator"
participant OpSynDlg as "COpSynDlg"
participant OpSynTDDlg as "COpSynTDDlg"
participant OpSynSptDlg as "COpSynSptDlg"
participant ProManager as "CProManager"
participant SptManager as "CSptManager"
User->>SynOperator : 调用Syn()
SynOperator->>OpSynDlg : 创建并初始化
OpSynDlg->>OpSynDlg : OnInitDialog()
OpSynDlg->>OpSynTDDlg : 显示
OpSynDlg->>OpSynSptDlg : 隐藏
User->>OpSynDlg : 切换标签页
OpSynDlg->>OpSynDlg : OnSelchangeOpSynTab()
OpSynDlg->>OpSynTDDlg : 隐藏
OpSynDlg->>OpSynSptDlg : 显示
User->>OpSynDlg : 选择设备
OpSynDlg->>OpSynDlg : OnSelchangeTdDesn()
OpSynDlg->>ProManager : 获取测试数据
ProManager-->>OpSynDlg : 返回测试数据
OpSynDlg->>SptManager : 获取脚本
SptManager-->>OpSynDlg : 返回脚本
```
**图表来源**
- [opsyndlg.cpp](file://cpp/Views/opsyndlg.cpp#L30-L237)
- [opsyntddlg.cpp](file://cpp/Views/opsyntddlg.cpp#L43-L200)
- [opsynsptdlg.cpp](file://cpp/Views/opsynsptdlg.cpp#L38-L200)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L53-L93)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L30-L53)
**章节来源**
- [opsyndlg.cpp](file://cpp/Views/opsyndlg.cpp#L30-L237)
- [opsyntddlg.cpp](file://cpp/Views/opsyntddlg.cpp#L43-L200)
- [opsynsptdlg.cpp](file://cpp/Views/opsynsptdlg.cpp#L38-L200)
## 依赖分析
SynchronizationOperator的实现依赖于多个关键组件,包括设备管理器(`CDevManager`)、项目管理器(`CProManager`)和脚本管理器(`CSptManager`)。`CDevManager`负责管理所有连接的设备,提供设备列表和状态信息。`CProManager`管理项目和测试区域的数据,确保同步过程中能够正确识别和处理相关的测试数据。`CSptManager`则负责脚本的创建、删除和同步,确保设备上的脚本与本地数据库保持一致。这些组件之间的依赖关系通过接口调用和数据传递来实现,形成了一个紧密协作的系统。
```mermaid
graph TD
SynOperator[CSynOperator] --> OpSynDlg[COpSynDlg]
OpSynDlg --> OpSynTDDlg[COpSynTDDlg]
OpSynDlg --> OpSynSptDlg[COpSynSptDlg]
OpSynTDDlg --> ProManager[CProManager]
OpSynSptDlg --> SptManager[CSptManager]
OpSynDlg --> DevManager[CDevManager]
ProManager --> Database[(数据库)]
SptManager --> Database
DevManager --> Database
```
**图表来源**
- [SynOperator.h](file://h/SynOperator.h#L13-L24)
- [opsyndlg.h](file://h/opsyndlg.h#L19-L62)
- [opsyntddlg.h](file://h/opsyntddlg.h#L15-L61)
- [opsynsptdlg.h](file://h/opsynsptdlg.h#L13-L58)
- [DevManager.h](file://h/DevManager.h#L22-L54)
- [ProManager.h](file://h/ProManager.h#L32-L64)
- [SptManager.h](file://h/SptManager.h#L27-L85)
**章节来源**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L24-L46)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L32-L51)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L74-L83)
## 性能考虑
SynchronizationOperator在设计时充分考虑了性能因素。首先,通过使用非模态窗口的模态循环,避免了阻塞主程序的执行,提高了用户体验。其次,同步过程中采用了批量操作,减少了与数据库和设备的交互次数,从而降低了网络延迟的影响。此外,`COperMediumPt`类中的算法优化确保了介质点位置计算的高效性,即使在处理大量数据时也能保持良好的性能表现。为了进一步优化同步稳定性,建议在高网络延迟环境下使用更长的超时设置,并定期检查设备状态以确保同步过程的顺利进行。
## 故障排除指南
在使用SynchronizationOperator时,可能会遇到同步失败或时钟漂移等问题。对于同步失败,首先应检查设备连接状态和网络状况,确保设备能够正常通信。如果问题仍然存在,可以尝试重新启动设备或软件,有时这能解决临时的通信故障。对于时钟漂移问题,建议定期校准设备时钟,并在同步配置中启用时钟同步功能。此外,查看日志文件可以帮助定位具体的问题原因,例如通过分析`Device.cpp`中的日志输出,可以了解同步过程中具体的错误信息。
**章节来源**
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L1779-L1908)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L595-L637)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L2696-L2739)
## 结论
SynchronizationOperator是GeomativeStudio软件中实现设备、数据和状态同步的关键组件。通过深入分析其架构和实现机制,我们可以看到它如何有效地协调多个设备或模块的时间基准和操作序列,确保测量过程的同步性。结合`OperMediumPt`类的介质点同步计算算法,SynchronizationOperator不仅提供了强大的同步功能,还保证了计算的准确性和效率。通过与同步对话框的交互,用户可以方便地配置同步参数并监控同步状态。针对常见的同步问题,文档提供了详细的诊断方法和解决方案,帮助用户快速解决问题。总体而言,SynchronizationOperator的设计充分考虑了性能和稳定性,为用户提供了一个可靠且高效的同步解决方案。
@@ -0,0 +1,305 @@
# Device数据模型
<cite>
**本文档引用的文件**
- [Device.h](file://h/Device.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [DevManager.h](file://h/DevManager.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
- [SComPort.h](file://h/SComPort.h)
- [config.ini](file://Install/Geomative Studio/config.ini)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
本项目GeomativeStudio是一个用于地质勘探设备管理的应用程序,其核心功能围绕设备管理展开。系统通过Device类来封装和管理各种设备(如GD10)的属性和行为,包括设备型号、固件版本、通信端口、设备状态及硬件参数配置等。Device类通过DevManager进行统一管理,并与底层驱动(如stmcdc.inf)和通信协议(CtrlProtocolDef.h)交互。设备检测逻辑在DetcGD10Dev.cpp中实现,负责识别设备并进行状态轮询。设备参数的持久化存储与config.ini文件联动,确保配置信息的持久性。在设备热插拔和通信异常时,系统具备相应的错误处理策略,保证了系统的稳定性和可靠性。
## 项目结构
项目结构清晰地组织了各个模块,便于维护和扩展。核心代码位于`cpp`目录下,分为多个子目录,如`Managers``Operator``ProblemZone`等,分别处理不同的业务逻辑。头文件位于`h`目录下,定义了类和结构体。安装文件和配置文件位于`Install`目录下,其中`config.ini`文件用于存储用户配置信息。日志文件位于`LOG`目录下,记录系统运行时的信息。数据库相关文件位于`DB`目录下,管理设备和任务数据。
```mermaid
graph TD
subgraph "核心代码"
A[cpp] --> B[Managers]
A --> C[Operator]
A --> D[ProblemZone]
end
subgraph "头文件"
E[h] --> F[Device.h]
E --> G[DevManager.h]
E --> H[CtrlProtocolDef.h]
end
subgraph "资源文件"
I[Install] --> J[config.ini]
I --> K[stmcdc.inf]
end
subgraph "日志"
L[LOG] --> M[detect_gd20_log.txt]
end
A --> E
A --> I
A --> L
```
**Diagram sources**
- [Device.h](file://h/Device.h)
- [DevManager.h](file://h/DevManager.h)
- [config.ini](file://Install/Geomative Studio/config.ini)
**Section sources**
- [Device.h](file://h/Device.h)
- [DevManager.h](file://h/DevManager.h)
- [config.ini](file://Install/Geomative Studio/config.ini)
## 核心组件
Device类是设备管理的核心,封装了设备的各种属性和方法。它通过DevManager进行统一管理,实现了设备的注册、注销、参数修改等功能。Device类还负责与底层驱动和通信协议交互,确保设备的正常运行。
**Section sources**
- [Device.h](file://h/Device.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
## 架构概述
系统架构采用分层设计,上层为UI层,中间为业务逻辑层,底层为设备驱动层。DevManager作为业务逻辑层的核心,负责管理所有设备实例。Device类通过SComPort类与设备进行通信,使用Zmodem协议进行文件传输。CtrlProtocolDef.h定义了通信协议,确保数据的正确传输。
```mermaid
graph TD
A[UI层] --> B[业务逻辑层]
B --> C[设备驱动层]
B --> D[DevManager]
D --> E[Device]
E --> F[SComPort]
F --> G[设备]
H[CtrlProtocolDef.h] --> F
```
**Diagram sources**
- [DevManager.h](file://h/DevManager.h)
- [Device.h](file://h/Device.h)
- [SComPort.h](file://h/SComPort.h)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
## 详细组件分析
### Device类分析
Device类封装了设备的各种属性,如设备型号(GD10)、固件版本、通信端口(串口/USB)、设备状态(在线/离线/故障)及硬件参数配置。通过GetDevInfo方法,可以从设备获取这些信息并更新到数据库中。
#### 类图
```mermaid
classDiagram
class CDevice {
+DWORD m_dwID
+UINT m_uState
+UINT m_uType
+BYTE m_ucDevType
+CSComPort m_sComPort
+CString m_szDevSN
+CString m_szDevName
+CString m_szModelNO
+CString m_szMDate
+CString m_szHWV
+CString m_szSWV
+CString m_szMBatch
+CString m_szMacAddress
+_ConnectionPtr m_pConnection
+FILE* m_pLogFile
+CDevice(DWORD dwID, _ConnectionPtr& pConnection)
+~CDevice()
+void SetState(UINT uState)
+void SetID(DWORD dwID)
+BOOL Reset()
+BOOL GetDevInfo()
+BOOL GetGRInfo()
+bool ShowFLDetailInfo(CListCtrl& devDetailList)
+bool ShowOLDetailInfo(CListCtrl& devDetailList)
+bool ShowGRInfo(CListCtrl& devGRList)
+bool ShowACInfo(CListCtrl& devDetailList)
+BOOL ModifyParameter()
+BOOL ShowCableHeadInfoDlg()
+BOOL IsTheNumofPoleChanged()
+BOOL TestGRForPerPole(int iSN, CStringArray& strResArray)
+BOOL TestGRForAllPole()
+void PrintLog(CString& strLog)
+int IsExistOtherUserData()
+int CheckGD10Password(CString strGD10Password)
+BOOL EndTransfer()
+UINT32 GetXmlMaxTestPoint(int iSubIndex, CString strPrCN, CString strTzCN, CString strXmlFile)
}
class CSComPort {
+HANDLE m_hCom
+HWND m_hOwnerWnd
+long m_lCommID
+CString m_szComName
+DCB m_dcbBlock
+FILE* m_Log
+CSComPort()
+CSComPort(HWND hOwnerWnd, long lCommID)
+~CSComPort()
+BOOL OpenComm(CString szComName)
+void CloseComm()
+BOOL SendDataDirectly(char* pDataBuff, int iDataSize)
+BOOL ReceiveDataDirectly(char* pDataBuff, int* iDataSize)
+BOOL ZmodemReceiveDataDirectly(char* pDataBuff, int* iDataSize)
+BOOL ZmodemSendDataDirectly(char* pDataBuff, int iDataSize)
+void ClearCommReceiveBuff()
+void ClearCommSendBuff()
+BOOL ExecuteOrder(CString strOrder, CString strSign, CString* pStrResult, int nRepeatCnt=3)
+BOOL ExecuteSignleOrder(CString f_szOrder, CString f_szSign, CString* f_szResult)
+BOOL ExecuteNoResOrder(CString f_szOrder)
}
CDevice --> CSComPort : "uses"
```
**Diagram sources**
- [Device.h](file://h/Device.h)
- [SComPort.h](file://h/SComPort.h)
**Section sources**
- [Device.h](file://h/Device.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
### DevManager类分析
DevManager类负责管理所有设备实例,提供设备的增删改查功能。它通过m_devLinkList链表存储设备实例,并通过m_handleProcessor生成设备句柄。
#### 类图
```mermaid
classDiagram
class CDevManager {
+CLinkList<CDevice*> m_devLinkList
+_ConnectionPtr m_pConnection
+std : : set<STSigRemoteDev> m_setRemoteDev
+CHandleProcessor m_handleProcessor
+CDevManager(_ConnectionPtr& pConnection)
+~CDevManager()
+CDevice* GetDeviceByID(DWORD dwDevID)
+CDevice* GetDevice(DWORD dwHandle)
+CDevice* GetDevice(CString szDevSN)
+void InitialDevLinkList()
+BOOL AddDevice(CDevice* const pDev)
+void DeleteObjInMem(DWORD dwHandle)
+void DeleteObjInMem(CString szDevSN)
+void AddObjInMem(CString szDevSN)
+void AddOfflineObjInMem(CString szDevSN)
+BOOL DeleteDevice(DWORD dwHandle)
+BOOL DeleteDevice(CString strDev)
+BOOL GetOLDevList(CPtrArray* pOLDevList)
+BOOL GetFLDevList(CPtrArray* pFLDevList)
+void UpdateDevInfo(STSynDevParam stDevParam, BYTE bRemoteDeveTyp)
+BOOL SetDeviceHandle(CDevice* const pDev)
+CDevice* GetRegisterDevice(CString szDevSN, bool& bIsRegister)
+void DeleteRemoteDevice(STSigRemoteDev stDevice)
+void AddRemoteDevice(STSigRemoteDev stDevice)
+std : : set<STSigRemoteDev> GetRemoteDeviceInfo()
}
class CDevice {
+DWORD m_dwID
+UINT m_uState
+UINT m_uType
+BYTE m_ucDevType
+CSComPort m_sComPort
+CString m_szDevSN
+CString m_szDevName
+CString m_szModelNO
+CString m_szMDate
+CString m_szHWV
+CString m_szSWV
+CString m_szMBatch
+CString m_szMacAddress
+_ConnectionPtr m_pConnection
+FILE* m_pLogFile
}
CDevManager --> CDevice : "manages"
```
**Diagram sources**
- [DevManager.h](file://h/DevManager.h)
- [Device.h](file://h/Device.h)
**Section sources**
- [DevManager.h](file://h/DevManager.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
### DetcGD10Dev类分析
DetcGD10Dev类负责检测GD10设备,通过USB接口查找设备并建立连接。它使用FindUsbDevice方法遍历所有可移动驱动器,通过卷标名或特定目录结构来识别设备。
#### 序列图
```mermaid
sequenceDiagram
participant User as "用户"
participant DetcGD10Dev as "CDetcGD10Dev"
participant System as "系统"
User->>DetcGD10Dev : DetectGD10Dev()
DetcGD10Dev->>System : GetLogicalDriveStrings()
System-->>DetcGD10Dev : 驱动器列表
loop 每个驱动器
DetcGD10Dev->>System : GetDriveType()
alt 可移动驱动器
DetcGD10Dev->>System : GetVolumeInformation()
System-->>DetcGD10Dev : 卷标名
alt 卷标名匹配
DetcGD10Dev-->>User : 找到设备
break
else 目录结构匹配
DetcGD10Dev->>System : _stat()
System-->>DetcGD10Dev : 目录存在
DetcGD10Dev-->>User : 找到设备
break
end
end
end
alt 未找到设备
DetcGD10Dev-->>User : 未找到设备
end
```
**Diagram sources**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
**Section sources**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
## 依赖分析
系统依赖于多个外部组件,包括数据库连接、串口通信、文件操作等。DevManager依赖于Device类,Device类依赖于SComPort类进行通信,SComPort类依赖于Windows API进行串口操作。此外,系统还依赖于Zmodem协议进行文件传输。
```mermaid
graph TD
A[DevManager] --> B[Device]
B --> C[SComPort]
C --> D[Windows API]
B --> E[Zmodem]
A --> F[数据库]
G[DetcGD10Dev] --> H[Windows API]
```
**Diagram sources**
- [DevManager.h](file://h/DevManager.h)
- [Device.h](file://h/Device.h)
- [SComPort.h](file://h/SComPort.h)
**Section sources**
- [DevManager.h](file://h/DevManager.h)
- [Device.h](file://h/Device.h)
- [SComPort.h](file://h/SComPort.h)
## 性能考虑
系统在设计时考虑了性能优化,如使用链表存储设备实例,减少内存分配开销;使用异步通信避免阻塞主线程;使用缓存减少数据库查询次数。此外,系统还实现了错误重试机制,提高通信的可靠性。
## 故障排除指南
当设备无法连接时,首先检查USB线缆是否连接正常,设备是否上电。然后检查设备的卷标名是否正确,或是否存在特定目录结构。如果问题仍然存在,可以查看日志文件detect_gd20_log.txt,获取详细的错误信息。对于通信异常,可以尝试重启设备或重新插拔USB线缆。
**Section sources**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [Install/Geomative Studio/LOG/detect_gd20_log.txt](file://Install/Geomative Studio/LOG/detect_gd20_log.txt)
## 结论
通过深入分析Device类及其相关组件,我们了解了GeomativeStudio项目中设备管理的核心机制。Device类通过封装设备属性和方法,实现了对设备的全面管理。DevManager类提供了统一的管理接口,确保了设备实例的一致性和完整性。DetcGD10Dev类实现了设备检测功能,保证了设备的自动识别和连接。整个系统设计合理,层次分明,具有良好的可维护性和扩展性。
@@ -0,0 +1,249 @@
# Medium测量模型
<cite>
**本文档引用的文件**
- [Medium.cpp](file://cpp\ProblemZone\Medium.cpp)
- [MediumA.cpp](file://cpp\ProblemZone\MediumA.cpp)
- [MediumB.cpp](file://cpp\ProblemZone\MediumB.cpp)
- [MediumC.cpp](file://cpp\ProblemZone\MediumC.cpp)
- [MediumD.cpp](file://cpp\ProblemZone\MediumD.cpp)
- [MediumCrossHoleGeomative.cpp](file://cpp\ProblemZone\MediumCrossHoleGeomative.cpp)
- [MediumCustom2D.cpp](file://cpp\ProblemZone\MediumCustom2D.cpp)
- [MediumZ.cpp](file://cpp\ProblemZone\MediumZ.cpp)
- [MediumY.cpp](file://cpp\ProblemZone\MediumY.cpp)
- [MediumX.cpp](file://cpp\ProblemZone\MediumX.cpp)
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp\crossHole\CCrossHoleConfig2DMainDlg.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [核心Medium类架构](#核心medium类架构)
3. [标准电极排列模型](#标准电极排列模型)
- [温纳α装置 (MediumA)](#温纳α装置-mediuma)
- [温纳β装置 (MediumB)](#温纳β装置-mediumb)
- [温纳γ装置 (MediumC)](#温纳γ装置-mediumc)
- [施伦贝谢装置 (MediumD)](#施伦贝谢装置-mediumd)
4. [特殊测量模型](#特殊测量模型)
- [跨孔测量模型 (MediumCrossHoleGeomative)](#跨孔测量模型-mediumcrossholegeomative)
- [自定义2D模型 (MediumCustom2D)](#自定义2d模型-mediumcustom2d)
5. [高级与遗留模型](#高级与遗留模型)
- [通用脚本模型 (MediumX, MediumY, MediumZ)](#通用脚本模型-mediumx-mediumy-mediumz)
6. [用户界面与配置](#用户界面与配置)
7. [模型选择决策树](#模型选择决策树)
8. [结论](#结论)
## 引言
Medium系列类是Geomative Studio软件中实现地球物理电阻率测量模型的核心组件。这些类通过继承自基类`CMedium`,为各种电极排列方式和测量方法提供了具体的实现。本文档系统地介绍了从`MediumA``MediumZ`的系列类,详细说明了它们所对应的电极排列方式,如`MediumA`代表温纳α装置,`MediumB`代表温纳β装置,`MediumCrossHoleGeomative`支持跨孔测量等。文档将深入分析各模型在电极间距计算、供电-测量电极组合逻辑、数据采集序列生成等方面的实现差异,并结合配置界面说明用户如何选择和参数化特定的测量模型。
## 核心Medium类架构
`CMedium`是所有测量模型的抽象基类,定义了测量脚本生成、测点定位和数据管理的通用接口。所有具体的测量模型,如`CMediumA``CMediumB`等,都继承自此类,并重写其核心方法以实现特定的测量逻辑。
```mermaid
classDiagram
class CMedium {
+int m_iAR
+int m_iStartPole
+int m_iEndPole
+float m_fEOffsetR
+float m_fLOffsetR
+map<int, int> m_mapUniversalLayer
+CalculateTdPtLoc()
+SetValidPoleInfo()
+GeneralUniSptInfo()
+ReSortPoint()
+AddSptToUniLayer()
+QuerySptLayerFromUni()
+GetUniSptXPos()
}
class CMediumA {
+GenerateSptRecElecVal()
+CalculateSptKVal()
+CalculateSptLevel()
+CalculateSptPtLoc()
}
class CMediumB {
+GenerateSptRecElecVal()
+CalculateSptKVal()
+CalculateSptLevel()
+CalculateSptPtLoc()
}
class CMediumC {
+GenerateSptRecElecVal()
+CalculateSptKVal()
+CalculateSptLevel()
+CalculateSptPtLoc()
}
class CMediumD {
+int m_iParamIma
+int m_iParamImn
+SetParamVal()
+GenerateSptRecElecVal()
+CalculateSptKVal()
+CalculateSptLevel()
+CalculateSptPtLoc()
}
class CMediumCrossHoleGeomative {
+float m_fSeprate
+GenerateSptRecElecVal()
+CalculateCESptKVal()
+CalculateSptPtLoc()
+GenSptRecLevel()
}
class CMediumCustom2D {
+GenerateSptRecElecVal()
}
CMedium <|-- CMediumA
CMedium <|-- CMediumB
CMedium <|-- CMediumC
CMedium <|-- CMediumD
CMedium <|-- CMediumCrossHoleGeomative
CMedium <|-- CMediumCustom2D
```
**图源**
- [Medium.cpp](file://cpp\ProblemZone\Medium.cpp#L11-L657)
- [MediumA.cpp](file://cpp\ProblemZone\MediumA.cpp#L19-L239)
- [MediumB.cpp](file://cpp\ProblemZone\MediumB.cpp#L19-L237)
- [MediumC.cpp](file://cpp\ProblemZone\MediumC.cpp#L19-L240)
- [MediumD.cpp](file://cpp\ProblemZone\MediumD.cpp#L19-L362)
- [MediumCrossHoleGeomative.cpp](file://cpp\ProblemZone\MediumCrossHoleGeomative.cpp#L19-L148)
- [MediumCustom2D.cpp](file://cpp\ProblemZone\MediumCustom2D.cpp#L19-L228)
**节源**
- [Medium.cpp](file://cpp\ProblemZone\Medium.cpp#L11-L657)
## 标准电极排列模型
标准电极排列模型是地球物理勘探中最常用的几种方法,它们在`MediumA``MediumD`类中实现。
### 温纳α装置 (MediumA)
`CMediumA`类实现了温纳α(Wenner Alpha)装置,这是一种对称四极排列,其中供电电极(A, B)和测量电极(M, N)等间距排列,且M、N位于A、B的中点。
`GenerateSptRecElecVal`方法中,该类通过一个双重循环生成测点序列。外层循环遍历测量电极M的位置,内层循环则通过移动供电电极A来生成同一层内的所有测点。其电极间距计算遵循公式:`AB = 3 * MN`,且MN间距随层数增加而增大。
```mermaid
flowchart TD
Start["开始生成温纳α测点"] --> Init["初始化参数 iEAmount, iMaxSpace"]
Init --> OuterLoop["外层循环: iMMVal = 2 to iEAmount-2"]
OuterLoop --> CalcSpace["计算当前间距 iSpace"]
CalcSpace --> InnerLoop["内层循环: 移动A电极"]
InnerLoop --> CreatePoint["创建CSptRecord: A, B, M, N"]
CreatePoint --> SetK["计算K值: k = 2π * (N-M)"]
SetK --> SetLevel["计算层数: Level = N - M"]
SetLevel --> AddToArray["将测点加入pSptRecArray"]
AddToArray --> CheckInner["内层循环结束?"]
CheckInner --> |否| InnerLoop
CheckInner --> |是| CheckOuter["外层循环结束?"]
CheckOuter --> |否| OuterLoop
CheckOuter --> |是| End["返回测点总数和最大层数"]
```
**图源**
- [MediumA.cpp](file://cpp\ProblemZone\MediumA.cpp#L30-L167)
**节源**
- [MediumA.cpp](file://cpp\ProblemZone\MediumA.cpp#L30-L167)
### 温纳β装置 (MediumB)
`CMediumB`类实现了温纳β(Wenner Beta)装置。与α装置不同,β装置的供电电极B和测量电极M是相邻的。
`GenerateSptRecElecVal`方法的逻辑与`MediumA`非常相似,主要区别在于电极的赋值顺序和K值的计算。在β装置中,K值的计算公式为 `k = 6π * (N-M)`,这反映了其不同的几何因子。
**节源**
- [MediumB.cpp](file://cpp\ProblemZone\MediumB.cpp#L30-L167)
### 温纳γ装置 (MediumC)
`CMediumC`类实现了温纳γ(Wenner Gamma)装置。在这种排列中,两个测量电极M和N位于一个供电电极A的同一侧。
该类的`GenerateSptRecElecVal`方法同样采用双重循环结构,但其电极组合逻辑和K值计算(`k = 3π * (M-A)`)与α和β装置有显著不同,体现了γ装置的独特性。
**节源**
- [MediumC.cpp](file://cpp\ProblemZone\MediumC.cpp#L30-L168)
### 施伦贝谢装置 (MediumD)
`CMediumD`类实现了施伦贝谢(Schlumberger)装置,这是一种非对称排列,其中测量电极MN间距固定,而供电电极AB间距随测量深度增加而扩大。
该模型的实现更为复杂,因为它引入了可配置的参数`m_iParamIma``m_iParamImn`,分别代表AM和MN的最大间距倍数。`SetParamVal`方法允许从外部设置这些参数。`GenerateSptRecElecVal`方法中的三重循环结构(`for (a = 1; a <= m_iParamIma; a++)`)用于生成不同MN间距的测点,这与标准温纳装置的单一间距不同。
**节源**
- [MediumD.cpp](file://cpp\ProblemZone\MediumD.cpp#L32-L279)
## 特殊测量模型
除了标准的地面测量模型,系统还支持更复杂的特殊测量场景。
### 跨孔测量模型 (MediumCrossHoleGeomative)
`CMediumCrossHoleGeomative`类专门用于跨孔(Cross-Hole)电阻率成像。这种模型涉及两个或多个钻孔中的电极,极大地增加了测量的复杂性。
该类的`GenerateSptRecElecVal`方法实现了一种特定的跨孔测量方案:电极按特定顺序排列(1-12从下到上,13-24从上到下),并遵循“C1P1 = C2P2”的规则。其核心逻辑是一个`while(TRUE)`循环,通过逐层增加测量深度(`iLayer++`)并控制C1/P1和C2/P2的移动来生成所有测点。K值的计算使用了更复杂的公式 `k = π / (1/AM - 1/sqrt(AM² + AB²))`,考虑了电极间的垂直和水平距离。
**节源**
- [MediumCrossHoleGeomative.cpp](file://cpp\ProblemZone\MediumCrossHoleGeomative.cpp#L47-L111)
### 自定义2D模型 (MediumCustom2D)
`CMediumCustom2D`类为用户提供了最大的灵活性,允许用户手动输入任意的电极组合(A, B, M, N)、K值、叠加次数(N)和层数。
该类的`GenerateSptRecElecVal`方法会弹出一个对话框`CDialCustomSptInput`,让用户输入所有测点的详细信息。程序会逐行读取用户输入,并创建`CSptRecord`对象。此外,它还支持多通道模式,会根据A、B、M的值对测点进行排序,以避免重复。
**节源**
- [MediumCustom2D.cpp](file://cpp\ProblemZone\MediumCustom2D.cpp#L28-L228)
## 高级与遗留模型
系统中还包含一些更高级或为兼容性而保留的模型。
### 通用脚本模型 (MediumX, MediumY, MediumZ)
`MediumX``MediumY``MediumZ`这三个类代表了基于外部脚本引擎的通用测量模型。它们不直接在C++代码中生成测点,而是通过调用底层的`scr_*`函数(如`scr_create`, `scr_generate`)来操作一个脚本引擎。
这些类的`GenerateSptRecElecVal`方法非常简单,主要是遍历由脚本引擎生成的测点列表(`scr_get_points`),并将每个点的A、B、M、N、K值复制到`CSptRecord`对象中。`MediumZ``MediumY``MediumX`分别对应“WennerAlfa”、“Schlumberger”和“Dipole-Dipole”等不同的脚本名称,这表明它们是通过配置不同的脚本来实现不同测量方法的。
**节源**
- [MediumZ.cpp](file://cpp\ProblemZone\MediumZ.cpp#L28-L61)
- [MediumY.cpp](file://cpp\ProblemZone\MediumY.cpp#L29-L62)
- [MediumX.cpp](file://cpp\ProblemZone\MediumX.cpp#L27-L60)
## 用户界面与配置
用户通过`CCrossHoleConfig2DMainDlg`等配置对话框来选择和参数化测量模型。例如,在跨孔配置界面中,用户可以在“参数”选项卡中选择测量类型(`GetMediumType`),并输入脚本名称、时间间隔等信息。
当用户点击“创建”按钮时,`OnBnClickedBtnCreate`方法会被调用。该方法首先获取用户选择的测量类型和参数,然后调用`TwoBoreholeGenerateScript`等函数来生成具体的测量脚本。最终,`SaveTestPointToDB`方法会将生成的测点信息(A, B, M, N, K值等)写入数据库的`script2d`表中,完成整个配置流程。
**节源**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp\crossHole\CCrossHoleConfig2DMainDlg.cpp#L734-L780)
## 模型选择决策树
为了帮助用户根据地质条件和勘探目标选择合适的Medium类型,以下提供一个决策树:
```mermaid
flowchart TD
Start["开始选择测量模型"] --> Q1["勘探目标是二维剖面吗?"]
Q1 --> |是| Q2["需要标准对称排列吗?"]
Q1 --> |否| Q3["是跨孔测量吗?"]
Q2 --> |是| Q4["需要最大探测深度?"]
Q2 --> |否| Q5["需要高横向分辨率?"]
Q3 --> |是| Recommend["推荐: MediumCrossHoleGeomative"]
Q3 --> |否| Q6["需要完全自定义电极组合?"]
Q4 --> |是| RecommendA["推荐: MediumA (温纳α)"]
Q4 --> |否| Q7["需要快速测量?"]
Q5 --> |是| RecommendD["推荐: MediumD (施伦贝谢)"]
Q5 --> |否| RecommendB["推荐: MediumB (温纳β)"]
Q6 --> |是| RecommendCustom["推荐: MediumCustom2D"]
Q6 --> |否| RecommendLegacy["考虑: MediumX/Y/Z (通用脚本)"]
Q7 --> |是| RecommendC["推荐: MediumC (温纳γ)"]
Q7 --> |否| RecommendA
Recommend --> End
RecommendA --> End
RecommendB --> End
RecommendC --> End
RecommendD --> End
RecommendCustom --> End
RecommendLegacy --> End
End["完成选择"]
```
**图源**
- [MediumA.cpp](file://cpp\ProblemZone\MediumA.cpp)
- [MediumB.cpp](file://cpp\ProblemZone\MediumB.cpp)
- [MediumC.cpp](file://cpp\ProblemZone\MediumC.cpp)
- [MediumD.cpp](file://cpp\ProblemZone\MediumD.cpp)
- [MediumCrossHoleGeomative.cpp](file://cpp\ProblemZone\MediumCrossHoleGeomative.cpp)
- [MediumCustom2D.cpp](file://cpp\ProblemZone\MediumCustom2D.cpp)
## 结论
Medium系列类构成了Geomative Studio软件的核心测量引擎,通过面向对象的设计,为多种地球物理测量方法提供了清晰、可扩展的实现。从标准的温纳和施伦贝谢装置到复杂的跨孔测量和完全自定义的方案,这些类满足了不同勘探场景的需求。模型的参数(如电极间距、K值计算)直接影响最终的反演结果,因此用户应根据具体的地质条件和勘探目标谨慎选择合适的模型。未来的工作可以进一步优化这些模型的性能,并增加更多先进的测量方法。
@@ -0,0 +1,237 @@
# ProblemZone模块
<cite>
**本文档中引用的文件**
- [Project.cpp](file://cpp/ProblemZone/Project.cpp)
- [Project.h](file://h/Project.h)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [Device.h](file://h/Device.h)
- [Script.cpp](file://cpp/ProblemZone/Script.cpp)
- [Script.h](file://h/Script.h)
- [Medium.cpp](file://cpp/ProblemZone/Medium.cpp)
- [Medium.h](file://h/Medium.h)
- [Electrode.cpp](file://cpp/ProblemZone/Electrode.cpp)
- [Electrode.h](file://h/Electrode.h)
- [SP2DTd.h](file://h/SP2DTd.h)
- [SP2DTdRecord.h](file://h/SP2DTdRecord.h)
- [SP3DTd.h](file://h/SP3DTd.h)
- [SP3DTdRecord.h](file://h/SP3DTdRecord.h)
- [SPCETd.h](file://h/SPCETd.h)
- [SPCETdRecord.h](file://h/SPCETdRecord.h)
- [Rsp2DTd.h](file://h/Rsp2DTd.h)
- [Rsp2DTdRecord.h](file://h/Rsp2DTdRecord.h)
- [Rsp3DTd.h](file://h/Rsp3DTd.h)
- [Rsp3DTdRecord.h](file://h/Rsp3DTdRecord.h)
- [RspCETd.h](file://h/RspCETd.h)
- [RspCETdRecord.h](file://h/RspCETdRecord.h)
- [Ipsp2DTd.h](file://h/Ipsp2DTd.h)
- [Ipsp2DTdRecord.h](file://h/Ipsp2DTdRecord.h)
- [Ipsp3DTd.h](file://h/Ipsp3DTd.h)
- [Ipsp3DTdRecord.h](file://h/Ipsp3DTdRecord.h)
- [IpspCETd.h](file://h/IpspCETd.h)
- [IpspCETdRecord.h](file://h/IpspCETdRecord.h)
- [Channel.h](file://h/Channel.h)
- [DevLinkRecord.h](file://h/DevLinkRecord.h)
- [Res3DDatFileRecord.h](file://h/Res3DDatFileRecord.h)
- [SptRecord.h](file://h/SptRecord.h)
- [TdRecord.h](file://h/TdRecord.h)
</cite>
## 目录
1. [项目数据结构与管理](#项目数据结构与管理)
2. [设备属性与状态管理](#设备属性与状态管理)
3. [脚本配置与生成逻辑](#脚本配置与生成逻辑)
4. [地球物理测量模型实现](#地球物理测量模型实现)
5. [激电测量数据处理类](#激电测量数据处理类)
6. [数据记录类结构设计](#数据记录类结构设计)
7. [数据模型关联关系](#数据模型关联关系)
## 项目数据结构与管理
`CProject`类是系统中项目管理的核心数据模型,负责封装项目的基本信息和管理功能。该类通过数据库连接获取项目数据,包含项目编号、名称、描述、位置、日期、持续时间、项目负责人、客户、项目经理、质量保证标准等属性。`CProject`类提供了显示项目详细信息的功能,通过`ShowDetailInfo`方法将项目信息填充到列表控件中,便于用户查看。项目数据从数据库的`project`表中读取,使用SQL查询语句获取指定ID的项目记录,并将各字段值存储在相应的成员变量中。
**本节来源**
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L15-L81)
- [Project.h](file://h/Project.h#L14-L38)
## 设备属性与状态管理
`CDevice`类负责管理地球物理测量设备的属性和状态。该类包含设备ID、状态、类型、设备序列号、设备名称、型号、生产日期、硬件版本、软件版本、批次等属性。`CDevice`类提供了多种功能方法,包括设置设备状态、重置设备、显示设备详细信息(离线和在线)、显示接地电阻信息、显示视极化率信息等。设备信息从数据库的`device`表和`desetting`表中获取,通过`ShowFLDetailInfo``ShowOLDetailInfo`方法分别显示离线和在线设备的详细信息。类中还包含与设备通信相关的成员变量,如串口对象`m_sComPort`和日志文件指针`m_pLogFile`,用于设备通信和日志记录。
**本节来源**
- [Device.cpp](file://cpp/ProblemZone/Device.cpp#L42-L800)
- [Device.h](file://h/Device.h#L33-L125)
## 脚本配置与生成逻辑
`CScript`类是脚本管理的基础类,定义了脚本的核心属性和接口。该类包含脚本ID、电极数量、脚本类型和装置类型等属性。`CScript`类作为抽象基类,定义了显示脚本内容信息、显示脚本详情信息和显示通道列表的纯虚函数,由派生类具体实现。装置类型`m_iAR`是区分不同测量模式的关键参数,不同的装置类型对应不同的电极配置和测量方法。脚本类通过数据库连接获取相关数据,为上层应用提供脚本配置和生成的基础功能。
**本节来源**
- [Script.cpp](file://cpp/ProblemZone/Script.cpp#L18-L30)
- [Script.h](file://h/Script.h#L15-L38)
## 地球物理测量模型实现
`CMedium`类是地球物理测量模型的基类,为各种具体的测量装置类型提供通用的功能和接口。该类定义了生成脚本记录电极值、计算脚本测点位置、计算脚本记录层数、计算每层脚本记录位置、获取最大层数等纯虚函数,由具体的装置类型类(如`MediumA``MediumB`等)实现。`CMedium`类还提供了通用的测点位置计算、测点重排序、通用脚本信息生成等功能。类中包含装置类型`m_iAR`、电极偏移率`m_fEOffsetR`、层偏移率`m_fLOffsetR`、起始电极`m_iStartPole`和结束电极`m_iEndPole`等成员变量,用于描述测量模型的参数。`CMedium`类通过`std::map`容器`m_mapUniversalLayer`管理通用层信息,支持不同装置类型的层数计算和管理。
**本节来源**
- [Medium.cpp](file://cpp/ProblemZone/Medium.cpp#L12-L657)
- [Medium.h](file://h/Medium.h#L73-L115)
## 激电测量数据处理类
SP、Rsp、Ipsp系列类分别代表不同类型的激电测量数据处理功能。这些类按照2D、3D和CECross-Sectional)三种测量维度组织,每种维度都有对应的头文件和记录文件。SP系列处理自然电位测量数据,Rsp系列处理电阻率测量数据,Ipsp系列处理激发极化测量数据。每个系列都包含一个主处理类(如`SP2DTd`)和一个记录类(如`SP2DTdRecord`),分别负责数据处理逻辑和数据存储。这些类通过继承和实现`CMedium`类定义的接口,针对特定的测量方法和数据格式进行具体实现,构成了激电测量数据处理的核心框架。
```mermaid
classDiagram
class SP2DTd {
+GenerateSptRecElecVal()
+CalculateSptPtLoc()
+GenSptRecLevel()
+GenSptRecPosInLevel()
+GetMaxLevelByEAmount()
}
class SP2DTdRecord {
+m_iLevel
+m_iPosInLevel
+m_recPtArea
+m_fSptXPos
}
class Rsp2DTd {
+GenerateSptRecElecVal()
+CalculateSptPtLoc()
+GenSptRecLevel()
+GenSptRecPosInLevel()
+GetMaxLevelByEAmount()
}
class Rsp2DTdRecord {
+m_iLevel
+m_iPosInLevel
+m_recPtArea
+m_fSptXPos
}
class Ipsp2DTd {
+GenerateSptRecElecVal()
+CalculateSptPtLoc()
+GenSptRecLevel()
+GenSptRecPosInLevel()
+GetMaxLevelByEAmount()
}
class Ipsp2DTdRecord {
+m_iLevel
+m_iPosInLevel
+m_recPtArea
+m_fSptXPos
}
SP2DTd <|-- SP3DTd
SP2DTd <|-- SPCETd
Rsp2DTd <|-- Rsp3DTd
Rsp2DTd <|-- RspCETd
Ipsp2DTd <|-- Ipsp3DTd
Ipsp2DTd <|-- IpspCETd
SP2DTd --|> SP2DTdRecord : "creates"
Rsp2DTd --|> Rsp2DTdRecord : "creates"
Ipsp2DTd --|> Ipsp2DTdRecord : "creates"
```
**图表来源**
- [SP2DTd.h](file://h/SP2DTd.h)
- [SP2DTdRecord.h](file://h/SP2DTdRecord.h)
- [Rsp2DTd.h](file://h/Rsp2DTd.h)
- [Rsp2DTdRecord.h](file://h/Rsp2DTdRecord.h)
- [Ipsp2DTd.h](file://h/Ipsp2DTd.h)
- [Ipsp2DTdRecord.h](file://h/Ipsp2DTdRecord.h)
## 数据记录类结构设计
数据记录类包括`Channel``Electrode``TdRecord``SptRecord`等,用于存储测量过程中的具体数据。`Channel`类代表测量通道,`Electrode`类代表电极,包含电极编码、状态、测量日期、测量时间和测量值等属性。`TdRecord`是测量数据记录的基类,`SptRecord`是脚本记录类,存储脚本测点的详细信息,如测点序号、电极配置(C1, C2, P1, P2)、层数、层内位置等。`DevLinkRecord`类用于设备链接记录,`Res3DDatFileRecord`类用于3D电阻率数据文件记录。这些记录类通过指针数组`CPtrArray`进行管理,支持动态的数据存储和访问,构成了系统数据持久化的基础。
```mermaid
classDiagram
class Channel {
+m_iChannelID
+m_szChannelName
+m_bIsActive
}
class Electrode {
+m_szECode
+m_bCState
+m_szMDate
+m_szMTime
+m_fOMValue
}
class TdRecord {
+m_iRecordID
+m_szRecordTime
+m_fValue
}
class SptRecord {
+m_iTsn
+m_iC1
+m_iC2
+m_iP1
+m_iP2
+m_iLevel
+m_iPosInLevel
+m_fSptXPos
}
class DevLinkRecord {
+m_iLinkID
+m_dwDeviceID
+m_szLinkTime
}
class Res3DDatFileRecord {
+m_szFileName
+m_szFilePath
+m_iDataCount
}
Channel "1" -- "0..*" Electrode : "has"
TdRecord "1" -- "1" SptRecord : "corresponds to"
DevLinkRecord "1" -- "1" Device : "links"
Res3DDatFileRecord "1" -- "0..*" TdRecord : "contains"
```
**图表来源**
- [Channel.h](file://h/Channel.h)
- [Electrode.h](file://h/Electrode.h#L12-L23)
- [TdRecord.h](file://h/TdRecord.h)
- [SptRecord.h](file://h/SptRecord.h)
- [DevLinkRecord.h](file://h/DevLinkRecord.h)
- [Res3DDatFileRecord.h](file://h/Res3DDatFileRecord.h)
## 数据模型关联关系
系统中的数据模型通过层次化的关联关系组织。`Project`类作为顶级容器,包含多个`TestingZone`(测区)。每个测区包含多个`Script`(脚本),每个脚本定义了一组测量任务。脚本通过`CMedium`类的派生类(如`MediumA``MediumB`等)实现具体的测量模型,生成`SptRecord`(脚本记录)。每个脚本记录关联特定的电极配置,由`Electrode`类表示。测量执行时,生成`TdRecord`(测量数据记录),与脚本记录对应。设备`Device`通过`Channel`与电极连接,形成完整的测量链路。这种层次化的数据模型设计,清晰地表达了从项目规划到数据采集的完整流程,支持复杂的地球物理测量任务管理。
```mermaid
graph TD
Project --> TestingZone
TestingZone --> Script
Script --> MediumA
Script --> MediumB
Script --> MediumC
MediumA --> SptRecord
MediumB --> SptRecord
MediumC --> SptRecord
SptRecord --> Electrode
Electrode --> Channel
Channel --> Device
SptRecord --> TdRecord
Device --> DevLinkRecord
TdRecord --> Res3DDatFileRecord
style Project fill:#f9f,stroke:#333
style Device fill:#bbf,stroke:#333
style Res3DDatFileRecord fill:#f96,stroke:#333
```
**图表来源**
- [Project.h](file://h/Project.h#L16-L38)
- [Device.h](file://h/Device.h#L33-L125)
- [Script.h](file://h/Script.h#L15-L38)
- [Medium.h](file://h/Medium.h#L73-L115)
- [SptRecord.h](file://h/SptRecord.h)
- [Electrode.h](file://h/Electrode.h#L12-L23)
- [Channel.h](file://h/Channel.h)
- [TdRecord.h](file://h/TdRecord.h)
- [DevLinkRecord.h](file://h/DevLinkRecord.h)
- [Res3DDatFileRecord.h](file://h/Res3DDatFileRecord.h)
@@ -0,0 +1,306 @@
# Project数据模型
<cite>
**本文档中引用的文件**
- [Project.h](file://h/Project.h)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp)
- [ProManager.h](file://h/ProManager.h)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [TestingZone.h](file://h/TestingZone.h)
- [TestingZone.cpp](file://cpp/ProblemZone/TestingZone.cpp)
- [DataMngStruct.h](file://h/DataMngStruct.h)
- [Script.h](file://h/Script.h)
- [TdRecord.h](file://h/TdRecord.h)
- [Device.h](file://h/Device.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
Project类是GeomativeStudio系统中的核心数据模型,作为项目容器管理着测区、任务配置、脚本集合和测量数据记录等关键信息。该类通过与ProManager模块协作,实现了项目生命周期的完整管理,包括创建、加载、保存和关闭操作。Project类从数据库加载元数据,并通过XML缓存文件实现项目持久化,确保数据在不同会话间的连续性。作为系统中项目管理的中心实体,Project类在多项目环境中通过单例模式提供统一访问接口,协调与Device、Script、TdRecord等其他模型的交互关系。
## 项目结构
GeomativeStudio项目采用分层架构设计,将核心数据模型、管理器、操作员和视图组件分离。Project类位于ProblemZone目录中,作为CDataMngStruct的派生类实现项目数据管理功能。系统通过ProManager类统一管理项目生命周期,而项目相关的持久化操作通过XML文件在CACHE目录中实现。项目结构清晰地划分了数据访问、业务逻辑和用户界面层,确保了系统的可维护性和扩展性。
```mermaid
graph TB
subgraph "数据层"
DB[(数据库)]
CACHE[(缓存文件)]
end
subgraph "业务逻辑层"
ProManager[ProManager]
Project[Project]
TestingZone[TestingZone]
end
subgraph "数据访问层"
DataMngStruct[CDataMngStruct]
end
subgraph "用户界面层"
Views[视图组件]
end
DB --> Project
CACHE --> Project
Project --> ProManager
ProManager --> Views
DataMngStruct --> Project
DataMngStruct --> TestingZone
```
**图源**
- [Project.h](file://h/Project.h#L1-L41)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [DataMngStruct.h](file://h/DataMngStruct.h#L1-L23)
**本节来源**
- [Project.h](file://h/Project.h#L1-L41)
- [ProManager.h](file://h/ProManager.h#L1-L77)
## 核心组件
Project类作为系统核心数据模型,负责管理项目元数据和聚合相关实体。该类从数据库加载项目信息,包括名称、创建时间、坐标系统等属性,并通过成员函数实现与ProManager的协作。Project实例通过句柄机制在内存中管理,确保多项目环境下的高效访问。类的设计体现了数据封装原则,将数据库访问细节与业务逻辑分离,提供了清晰的接口供上层组件调用。
**本节来源**
- [Project.h](file://h/Project.h#L1-L41)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L1-L81)
## 架构概述
Project类在GeomativeStudio系统架构中扮演着中心角色,作为项目容器协调多个子系统的交互。系统采用管理器模式,通过ProManager统一管理Project实例的生命周期。Project类与TestingZone、Script、TdRecord等模型形成聚合关系,构建了完整的项目数据结构。持久化机制通过XML文件实现,确保项目数据在设备间的同步和本地缓存。整个架构设计支持多项目并发操作,通过句柄处理器和状态处理器确保线程安全和数据一致性。
```mermaid
classDiagram
class CDataMngStruct {
+ShowDetailInfo(CListCtrl& dmsDetailList) bool
+CDataMngStruct()
+~CDataMngStruct()
}
class CProject {
+ShowDetailInfo(CListCtrl& proDetailList) bool
+CProject(DWORD dwID, _ConnectionPtr& pConnection)
+~CProject()
+DWORD m_dwID
-_ConnectionPtr m_pConnection
-CString m_szCN
-CString m_szPRname
-CString m_szDesc
-CString m_szLocation
-CString m_szPRdate
-CString m_szDuration
-CString m_szPS
-CString m_szCS
-CString m_szPM
-CString m_szQAS
-CString m_szStandard
}
class CTestingZone {
+ShowDetailInfo(CListCtrl& tzDetailList) bool
+CTestingZone(DWORD dwID, _ConnectionPtr& pConnection)
+~CTestingZone()
+DWORD m_dwID
-_ConnectionPtr m_pConnection
-CString m_szTZname
-CString m_szCDate
-CString m_szDesc
-CString m_szLocation
-CString m_szCN
-CString m_szTZtype
}
class CScript {
+CScript(DWORD dwID, _ConnectionPtr& pConnection)
+~CScript()
+DWORD m_dwID
+CHandleProcessor m_handleProcessor
+int m_iEAmount
+int m_iSType
+int m_iAR
+AdjustRecListColumn(int iAR, CListCtrl& sptConListInfo) void
+ShowSptConInfo(CListCtrl& sptConList) bool
+ShowSptDetailInfo(CListCtrl& sptDetailList) bool
+ShowChannelList(CListCtrl& sptChannelList) bool
-_ConnectionPtr m_pConnection
}
class CDevice {
+m_szDevSN CString
+m_dwID DWORD
+SendFile(CString szHostFilePath, CString szFilePath, CString szFileName) BOOL
}
class CTdRecord {
+m_dwTzID DWORD
+m_dwSCID DWORD
+m_pDevice CDevice*
}
CDataMngStruct <|-- CProject
CDataMngStruct <|-- CTestingZone
CProject "1" *-- "0..*" CTestingZone : 包含
CProject "1" *-- "0..*" CScript : 包含
CProject "1" *-- "0..*" CTdRecord : 包含
CTestingZone "1" *-- "0..*" CTdRecord : 包含
CTdRecord --> CDevice : 关联
```
**图源**
- [Project.h](file://h/Project.h#L1-L41)
- [TestingZone.h](file://h/TestingZone.h#L1-L33)
- [Script.h](file://h/Script.h#L1-L39)
- [Device.h](file://h/Device.h#L1-L50)
- [TdRecord.h](file://h/TdRecord.h#L1-L45)
## 详细组件分析
### Project类分析
Project类作为系统核心数据模型,实现了项目元数据的存储和管理功能。该类从数据库加载项目信息,包括项目编号(CN)、项目名称(PRname)、描述(PRdesc)、位置(location)、创建日期(PRdate)、持续时间(duration)、投影系统(PS)、坐标系统(CS)、项目经理(PM)、质量保证(QAS)和标准(standard)等属性。通过继承CDataMngStruct基类,Project类获得了统一的数据管理接口。
#### 类图
```mermaid
classDiagram
class CProject {
+ShowDetailInfo(CListCtrl& proDetailList) bool
+CProject(DWORD dwID, _ConnectionPtr& pConnection)
+~CProject()
+DWORD m_dwID
-_ConnectionPtr m_pConnection
-CString m_szCN
-CString m_szPRname
-CString m_szDesc
-CString m_szLocation
-CString m_szPRdate
-CString m_szDuration
-CString m_szPS
-CString m_szCS
-CString m_szPM
-CString m_szQAS
-CString m_szStandard
}
class CDataMngStruct {
+ShowDetailInfo(CListCtrl& dmsDetailList) bool
+CDataMngStruct()
+~CDataMngStruct()
}
CDataMngStruct <|-- CProject
```
**图源**
- [Project.h](file://h/Project.h#L1-L41)
- [DataMngStruct.h](file://h/DataMngStruct.h#L1-L23)
#### 项目生命周期管理
Project类的生命周期由ProManager模块管理,通过一系列协作方法实现完整的项目操作流程:
```mermaid
sequenceDiagram
participant UI as 用户界面
participant ProManager as ProManager
participant Project as Project
participant DB as 数据库
participant Cache as 缓存文件
UI->>ProManager : 创建项目请求
ProManager->>ProManager : 显示创建对话框
ProManager->>DB : 检查项目名称是否存在
DB-->>ProManager : 返回检查结果
alt 项目名称已存在
ProManager-->>UI : 显示错误消息
else 项目名称可用
ProManager->>DB : 插入项目记录
DB-->>ProManager : 返回项目ID
ProManager->>Project : 创建Project实例
Project->>DB : 加载项目数据
DB-->>Project : 返回项目信息
ProManager->>Cache : 生成project.xml
Cache-->>ProManager : 确认文件创建
ProManager->>UI : 返回成功状态
end
UI->>ProManager : 加载项目
ProManager->>DB : 查询项目列表
DB-->>ProManager : 返回项目数据
ProManager->>Project : 获取Project实例
Project-->>UI : 显示项目详情
```
**图源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L317)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L19-L50)
**本节来源**
- [Project.h](file://h/Project.h#L1-L41)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L1-L81)
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
### ProManager协作关系
ProManager作为项目管理器,与Project类形成紧密的协作关系。通过GetDMS方法,ProManager根据句柄获取相应的数据管理结构实例,实现了Project对象的单例访问模式。这种设计确保了在多项目环境中,每个项目实例在内存中只存在一个副本,提高了系统性能和数据一致性。
#### 单例访问模式
```mermaid
flowchart TD
Start([获取DMS实例]) --> CheckCache["检查缓存中是否存在"]
CheckCache --> |存在| ReturnInstance["返回缓存实例"]
CheckCache --> |不存在| CreateInstance["创建新实例"]
CreateInstance --> LoadFromDB["从数据库加载数据"]
LoadFromDB --> AddToCache["添加到缓存链表"]
AddToCache --> ReturnInstance
ReturnInstance --> End([返回DMS实例])
```
**图源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L155-L182)
**本节来源**
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L155-L182)
## 依赖关系分析
Project类与系统中多个组件存在依赖关系,形成了复杂的交互网络。通过分析这些依赖关系,可以更好地理解系统的整体架构和数据流。
```mermaid
graph TD
ProManager --> Project : 创建/管理
Project --> TestingZone : 聚合
Project --> Script : 聚合
Project --> TdRecord : 聚合
Project --> DB : 数据持久化
Project --> Cache : XML缓存
TestingZone --> TdRecord : 聚合
TdRecord --> Device : 关联
Script --> TdRecord : 配置
ProManager --> DB : 数据访问
ProManager --> Device : 设备同步
style Project fill:#f9f,stroke:#333
style ProManager fill:#bbf,stroke:#333
```
**图源**
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [Project.h](file://h/Project.h#L1-L41)
- [TestingZone.h](file://h/TestingZone.h#L1-L33)
**本节来源**
- [ProManager.h](file://h/ProManager.h#L1-L77)
- [Project.h](file://h/Project.h#L1-L41)
- [TestingZone.h](file://h/TestingZone.h#L1-L33)
## 性能考虑
Project类的设计考虑了性能优化,通过缓存机制减少数据库访问频率。ProManager维护的m_dmsLinkList链表缓存了已创建的Project实例,避免了重复创建和数据加载。在多项目环境下,这种缓存策略显著提高了系统响应速度。同时,XML缓存文件的使用减少了设备同步时的数据传输量,优化了I/O性能。线程安全通过句柄处理器和状态处理器实现,确保在并发访问时的数据一致性。
## 故障排除指南
在使用Project类时可能遇到的常见问题及解决方案:
1. **项目创建失败**:检查项目名称是否已存在,确保数据库连接正常
2. **数据加载异常**:验证数据库表结构是否匹配,检查XML缓存文件完整性
3. **设备同步问题**:确认设备连接状态,检查文件传输权限
4. **内存泄漏**:确保通过ProManager正确管理Project实例生命周期
5. **线程安全问题**:使用ProManager提供的同步机制,避免直接操作Project实例
**本节来源**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L249-L483)
- [Project.cpp](file://cpp/ProblemZone/Project.cpp#L19-L50)
## 结论
Project类作为GeomativeStudio系统的核心数据模型,成功实现了项目容器的功能,有效管理了项目元数据和相关实体。通过与ProManager模块的协作,该类提供了完整的项目生命周期管理功能,包括创建、加载、保存和关闭操作。基于XML的持久化机制确保了数据的可靠存储和设备同步。类的设计体现了良好的面向对象原则,通过继承、聚合和单例模式构建了清晰的架构。未来可进一步优化缓存策略和线程安全机制,以支持更大规模的项目管理和更高的并发性能。
@@ -0,0 +1,202 @@
# Script数据模型
<cite>
**本文档引用的文件**
- [Script.h](file://h/Script.h)
- [SptRecord.h](file://h/SptRecord.h)
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp)
- [OpCreate3DSptDlg.cpp](file://cpp/Views/OpCreate3DSptDlg.cpp)
- [GD10OperCmd.h](file://h/GD10OperCmd.h)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [Script.cpp](file://cpp/ProblemZone/Script.cpp)
- [SptRecord.cpp](file://cpp/ProblemZone/SptRecord.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [Script类结构与功能](#script类结构与功能)
3. [SptRecord类数据结构](#sptrecord类数据结构)
4. [脚本生成与用户交互流程](#脚本生成与用户交互流程)
5. [脚本管理与增删改查操作](#脚本管理与增删改查操作)
6. [脚本与Medium模型的关联机制](#脚本与medium模型的关联机制)
7. [脚本文件(.urf)导出格式](#脚本文件urf导出格式)
8. [从脚本配置到设备指令的数据流](#从脚本配置到设备指令的数据流)
9. [结论](#结论)
## 引言
Script类及其相关SptRecord类是Geomative Studio系统中测量脚本管理的核心组件。这些类负责定义2D、3D及跨孔测量的任务序列,包括电极排列方式、测量模式(如温纳、施伦贝尔)、供电参数和采集频率等关键配置。本文档将深入分析Script类的实现机制,解释SptRecord作为脚本执行记录的数据结构,并结合UI组件说明脚本生成与编辑的用户交互流程。
**Section sources**
- [Script.h](file://h/Script.h#L1-L39)
- [SptRecord.h](file://h/SptRecord.h#L1-L57)
## Script类结构与功能
Script类是测量脚本的基类,定义了所有测量任务的通用属性和方法。该类通过继承机制支持不同类型的测量任务,包括2D、3D和跨孔测量。Script类的核心属性包括电极数量(m_iEAmount)、测量类型(m_iSType)和装置类型(m_iAR),这些属性共同定义了测量任务的基本特征。
Script类通过虚函数ShowSptConInfo、ShowSptDetailInfo和ShowChannelList提供了显示脚本信息的接口,允许子类根据具体测量类型定制信息展示方式。这种设计模式实现了代码的可扩展性和灵活性,使得系统能够轻松支持新的测量模式。
**Section sources**
- [Script.h](file://h/Script.h#L1-L39)
- [Script.cpp](file://cpp/ProblemZone/Script.cpp#L1-L30)
## SptRecord类数据结构
SptRecord类作为脚本执行记录的数据结构,包含了测量过程中的关键参数和状态信息。该类不仅存储了测量配置参数,如电极位置(m_iC1, m_iC2, m_iP1, m_iP2)、几何因子(m_fK)和迭代次数(m_iN),还包含了执行状态和异常标记。
SptRecord类的成员变量设计体现了对测量过程全面监控的需求。m_bIsSel标志位用于标记测点是否被选中,m_colorREF用于可视化显示,m_recPtArea、m_fPtCenterX、m_fPtCenterY和m_fPtRadius共同定义了测点的区域范围,支持空间查询和交互操作。
**Section sources**
- [SptRecord.h](file://h/SptRecord.h#L1-L57)
- [SptRecord.cpp](file://cpp/ProblemZone/SptRecord.cpp#L1-L54)
## 脚本生成与用户交互流程
脚本生成与编辑的用户交互主要通过OpCreate3DSptDlg类实现。该对话框提供了直观的界面,允许用户配置3D测量任务的关键参数,包括测量区域、电极间距、步长和装置类型。用户通过界面输入的参数被转换为Script3D对象的配置,最终生成具体的测量任务序列。
OpCreate3DSptDlg类的实现展示了复杂的用户交互逻辑,包括参数验证、装置类型选择和区域定义。当用户选择中梯装置时,界面会动态显示C1和C2电极的输入控件,确保用户能够正确配置测量参数。这种动态界面设计提高了用户体验和数据输入的准确性。
```mermaid
sequenceDiagram
participant 用户 as 用户
participant UI as OpCreate3DSptDlg
participant Script as Script3D
participant Medium as CMedium3D
用户->>UI : 输入测量参数
UI->>UI : 验证参数有效性
UI->>Script : 创建Script3D实例
Script->>Medium : 设置测量区域和参数
Medium->>Medium : 生成测量序列
Medium->>Script : 返回测量序列
Script->>UI : 存储测量序列
UI->>用户 : 显示生成结果
```
**Diagram sources**
- [OpCreate3DSptDlg.cpp](file://cpp/Views/OpCreate3DSptDlg.cpp#L1-L700)
- [Script.h](file://h/Script.h#L1-L39)
## 脚本管理与增删改查操作
脚本管理功能由SptManager类实现,该类提供了对测量脚本的增删改查操作。SptManager使用链接列表(m_sptLinkList)管理所有脚本实例,通过句柄机制实现脚本的快速查找和访问。这种设计模式确保了脚本管理的高效性和内存使用的合理性。
SptManager类的Create2DSConInDB和Delete2DSConInDB方法实现了脚本在数据库中的持久化存储和删除。这些方法通过ADO数据库操作,将脚本配置信息写入scon、channel和script2d等数据表,实现了配置数据的可靠存储和版本管理。
**Section sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L1-L800)
## 脚本与Medium模型的关联机制
Script类与Medium模型的关联是通过装置类型(m_iAR)和句柄处理器(m_handleProcessor)实现的。每种测量模式对应特定的Medium子类,如CMedium3D用于3D测量,CMediumCrossHoleGeomative用于跨孔测量。SptManager在创建脚本时,根据装置类型生成相应的Medium实例,并将其关联到脚本的通道中。
这种关联机制实现了测量逻辑与数据模型的解耦,使得系统能够灵活支持多种测量模式。当脚本执行时,相关的Medium实例负责生成具体的测量序列,而Script类则负责管理整体的测量流程和状态。
```mermaid
classDiagram
class Script {
+DWORD m_dwID
+int m_iEAmount
+int m_iSType
+int m_iAR
+CHandleProcessor m_handleProcessor
+_ConnectionPtr m_pConnection
}
class CScript2D {
+CList m_chaList
}
class CScript3D {
+CList m_chaList
}
class CScriptCE {
+CList m_chaList
}
class CChannel {
+int m_iChNumber
+CMedium* m_pMedium
+CArray m_sptRecArray
+int m_iPtAmount
}
class CMedium {
+int m_iAR
}
class CMedium3D {
+void create()
+bool generate()
+void GenerateSptRecElecVal3D()
}
class CMediumCrossHoleGeomative {
+void create()
+bool generate()
}
Script <|-- CScript2D
Script <|-- CScript3D
Script <|-- CScriptCE
CScript2D --> CChannel
CScript3D --> CChannel
CScriptCE --> CChannel
CChannel --> CMedium
CMedium <|-- CMedium3D
CMedium <|-- CMediumCrossHoleGeomative
```
**Diagram sources**
- [SptManager.cpp](file://cpp/Managers/SptManager.cpp#L1-L800)
- [Script.h](file://h/Script.h#L1-L39)
## 脚本文件(.urf)导出格式
脚本文件的导出格式采用XML结构,包含了测量任务的完整配置信息。导出的XML文件包含脚本名称、类型、描述、定义者、日期、测量区域、电极数量、通道信息和具体的测量序列。这种结构化的格式便于数据的交换和长期存储。
在跨孔测量等特殊情况下,系统还会生成额外的坐标文件,记录电极的三维位置信息。这些坐标文件通过C_H_Script标签在主脚本文件中引用,实现了测量配置与空间数据的分离管理。
```mermaid
flowchart TD
Start([开始导出]) --> CheckType["检查测量类型"]
CheckType --> |3D测量| GenerateXML["生成XML结构"]
CheckType --> |跨孔测量| GenerateCoord["生成坐标文件"]
GenerateXML --> AddHeader["添加XML头部"]
AddHeader --> AddBasicInfo["添加基本信息"]
AddBasicInfo --> AddChannelInfo["添加通道信息"]
AddChannelInfo --> AddLayout["添加测量序列"]
AddLayout --> SaveFile["保存文件"]
GenerateCoord --> SaveCoord["保存坐标文件"]
SaveCoord --> SaveMain["保存主脚本文件"]
SaveFile --> End([完成])
SaveMain --> End
```
**Diagram sources**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L800)
- [OpCreate3DSptDlg.cpp](file://cpp/Views/OpCreate3DSptDlg.cpp#L529-L633)
## 从脚本配置到设备指令的数据流
从脚本配置到设备指令的生成过程是一个复杂的数据转换流程。首先,用户通过UI界面配置测量参数,这些参数被封装为Script对象。然后,SptManager将Script对象的配置信息转换为数据库记录,实现持久化存储。
当测量任务执行时,系统从数据库读取脚本配置,通过GD10OperCmd类生成相应的设备指令。这些指令通过USB或蓝牙传输到GD10设备,控制其执行具体的测量操作。整个数据流确保了测量配置的准确传递和执行。
```mermaid
sequenceDiagram
participant UI as 用户界面
participant Script as Script类
participant DB as 数据库
participant GD10Cmd as GD10OperCmd
participant Device as GD10设备
UI->>Script : 输入测量配置
Script->>DB : 存储脚本配置
DB->>Script : 读取脚本配置
Script->>GD10Cmd : 请求生成指令
GD10Cmd->>GD10Cmd : 生成设备指令
GD10Cmd->>Device : 发送指令
Device->>Device : 执行测量
Device->>GD10Cmd : 返回结果
GD10Cmd->>Script : 处理结果
Script->>UI : 显示结果
```
**Diagram sources**
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L800)
## 结论
Script类及其相关SptRecord类构成了Geomative Studio测量脚本管理的核心。通过精心设计的类结构和数据模型,系统实现了对2D、3D及跨孔测量任务的全面支持。从用户交互、数据存储到设备控制的完整数据流,确保了测量过程的准确性和可靠性。这种模块化的设计也为系统的扩展和维护提供了良好的基础。
@@ -0,0 +1,286 @@
# 测量数据模型
<cite>
**本文档引用的文件**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp)
- [TestingData.h](file://h\TestingData.h)
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp)
- [TdRecord.h](file://h\TdRecord.h)
- [Channel.cpp](file://cpp\ProblemZone\Channel.cpp)
- [Channel.h](file://h\Channel.h)
- [Electrode.cpp](file://cpp\ProblemZone\Electrode.cpp)
- [Electrode.h](file://h\Electrode.h)
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp)
- [SP2DTd.h](file://h\SP2DTd.h)
- [SP2DTdRecord.h](file://h\SP2DTdRecord.h)
</cite>
## 目录
1. [引言](#引言)
2. [核心数据模型体系](#核心数据模型体系)
3. [TestingData类分析](#testingdata类分析)
4. [TdRecord类分析](#tdrecord类分析)
5. [通道与电极数据管理](#通道与电极数据管理)
6. [地球物理参数计算模型](#地球物理参数计算模型)
7. [视图组件与数据可视化](#视图组件与数据可视化)
8. [数据采集与处理流程](#数据采集与处理流程)
9. [数据校验与异常处理](#数据校验与异常处理)
## 引言
GeomativeStudio测量数据模型体系是一个完整的地球物理数据采集与处理框架,旨在管理从设备采集到的原始测量数据,并将其转换为可用于分析和可视化的地球物理参数。该体系以TestingData类为核心,通过聚合TdRecord、Channel和Electrode等组件,构建了一个层次化的数据容器结构。本文档将深入解析这一数据模型体系,重点阐述其核心组件的职责、相互关系以及数据处理流程。
## 核心数据模型体系
GeomativeStudio的测量数据模型采用面向对象的设计模式,构建了一个层次化的数据容器体系。该体系以TestingData类作为顶层数据容器,负责管理一次完整的测量任务的所有数据。TestingData类通过聚合关系管理多个Channel对象,每个Channel代表一个数据采集通道,负责管理该通道下的所有测量记录(TdRecord)。同时,TestingData类还管理着电极配置(Electrode)信息,定义了测量过程中的电极几何布局。
该数据模型体系支持多种地球物理测量方法,包括视电阻率(SP)、电阻率反演(Rsp)和激电二次场(Ipsp)等。每种测量方法都有对应的派生类,如SP2DTd、Rsp2DTd和Ipsp2DTd等,这些类继承自TestingData类并实现了特定的计算逻辑。这种设计模式使得系统能够灵活地支持不同的测量技术和数据处理算法。
**Section sources**
- [TestingData.h](file://h\TestingData.h#L158-L273)
- [Channel.h](file://h\Channel.h#L19-L34)
- [Electrode.h](file://h\Electrode.h#L12-L24)
## TestingData类分析
TestingData类是整个测量数据模型体系的核心,作为顶层数据容器负责管理一次测量任务的所有相关数据。该类在构造时接收一个数据库连接指针和任务ID,用于与数据库进行交互。类中定义了大量的成员变量,用于存储测量任务的元数据,包括任务名称、位置、测量类型、电极数量、测量模式等。
该类通过m_tdChaList成员变量管理多个Channel对象,形成了一对多的聚合关系。每个Channel对象代表一个独立的数据采集通道,可以同步采集多通道数据。TestingData类还提供了丰富的数据操作方法,包括数据加载(LoadData)、数据保存(SaveTdToExcelFile、SaveTdToRes2DFile等)和数据显示(ShowConList、ShowGrList等)。
特别值得注意的是,TestingData类中定义了复杂的时窗计算相关结构体,如_WinTime、_WinTimeList和_WaveCount,这些结构体用于存储和处理激电测量中的时域信息。类中的CalculateTWInfo和CalculateTimeWindows方法实现了激电数据的时窗积分和参数计算,这是激电测量数据处理的核心算法。
```mermaid
classDiagram
class CTestingData {
+DWORD m_dwID
+CString m_szTdName
+CString m_szTdCN
+int m_iSType
+int m_iTType
+int m_iEAmount
+float m_fESpace
+CString m_szEDistance
+CPtrList m_tdChaList
+_WaveCount* m_waveCount
+CTestingData(DWORD, _ConnectionPtr&)
+virtual ~CTestingData()
+bool ShowConListByPage(CListCtrl&, int)
+BOOL SaveOrgDataToDB(DWORD)
+bool CalculateTWInfo(CStringArray*, int)
+bool CalculateTimeWindows(_WinTimeList, CStringArray*, int, int, int, int, int)
}
class CChannel {
+int m_iChNumber
+int m_iMaxLevel
+int m_iPtAmount
+int m_iEAmount
+CMedium* m_pMedium
+CPtrArray m_sptRecArray
+CChannel()
+virtual ~CChannel()
+void ClearSptRecList()
}
class CElectrode {
+CString m_szECode
+BOOL m_bCState
+CString m_szMDate
+CString m_szMTime
+float m_fOMValue
+CElectrode()
+virtual ~CElectrode()
}
CTestingData --> CChannel : "聚合"
CTestingData --> CElectrode : "聚合"
```
**Diagram sources**
- [TestingData.h](file://h\TestingData.h#L158-L273)
- [Channel.h](file://h\Channel.h#L19-L34)
- [Electrode.h](file://h\Electrode.h#L12-L24)
**Section sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L29-L85)
- [TestingData.h](file://h\TestingData.h#L158-L273)
## TdRecord类分析
TdRecord类是测量数据模型中的基本数据单元,代表一次具体的测量记录。该类作为抽象基类,定义了测量记录的基本结构和通用操作。每个TdRecord对象包含测量的基本参数,如通道ID(m_dwChID)、测点编号(m_iTsn)、电极配置参数(m_iN)、几何因子(m_fK)、电流(m_fI)、电压(m_fV)、视电阻率(m_fSP)等。
该类的核心功能是管理原始测量数据,通过m_saVRawData和m_saIRawData两个CStringArray成员变量存储电压和电流的原始波形数据。类中定义了LoadOrgData纯虚函数,由派生类实现具体的原始数据加载逻辑。同时,该类提供了ConvertVOrgData方法,用于将原始电压数据转换为实际物理值。
TdRecord类还实现了原始数据的可视化功能,通过DisplayRawDataSplines方法可以显示电压原始波形曲线。该方法首先调用LoadOrgData加载原始数据,然后创建CDispTdRecSplinesGrapDlg对话框来显示波形。类中还维护了m_fMaxAbsV变量,用于存储电压原始数据的最大绝对值,这在波形显示时用于确定Y轴的缩放比例。
```mermaid
classDiagram
class CTdRecord {
+DWORD m_dwChID
+int m_iTsn
+int m_iN
+float m_fK
+float m_fI
+float m_fV
+float m_fR0
+float m_fSP
+CString m_Datetime
+CStringArray m_saVRawData
+CStringArray m_saIRawData
+_ConnectionPtr m_pConnection
+CTdRecord(_ConnectionPtr&)
+virtual ~CTdRecord()
+float GetMaxAbsV()
+virtual void DisplayRawDataSplines()
+virtual BOOL LoadOrgData()
+virtual float ConvertVOrgData(float)
}
CTdRecord <|-- SP2DTdRecord : "继承"
CTdRecord <|-- Rsp2DTdRecord : "继承"
CTdRecord <|-- Ipsp2DTdRecord : "继承"
```
**Diagram sources**
- [TdRecord.h](file://h\TdRecord.h#L12-L46)
- [SP2DTdRecord.h](file://h\SP2DTdRecord.h)
**Section sources**
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp#L21-L325)
- [TdRecord.h](file://h\TdRecord.h#L12-L46)
## 通道与电极数据管理
通道(Channel)和电极(Electrode)是测量数据模型中的两个关键组件,分别负责管理数据采集通道和电极配置信息。Channel类作为数据采集通道的抽象,每个Channel对象代表一个独立的采集通道,可以同步采集多通道数据。类中通过m_sptRecArray成员变量管理多个SptRecord对象,这些对象代表了该通道下的所有测量脚本记录。
Channel类还与测量装置(Medium)相关联,通过m_pMedium指针引用具体的测量装置配置。不同的测量装置类型(如温纳装置、施伦贝谢装置等)决定了电极的排列方式和测量方法。类中定义了m_iEAmount成员变量来记录该通道使用的电极数量,m_iMaxLevel记录了测量的最大层级。
Electrode类则负责管理单个电极的配置信息,包括电极编码(m_szECode)、连接状态(m_bCState)、测量日期时间(m_szMDate, m_szMTime)和测量值(m_fOMValue)等。这些信息对于确保测量数据的准确性和可追溯性至关重要。在多通道同步采集场景中,多个Channel对象可以共享相同的电极配置,从而实现高效的多通道数据采集。
```mermaid
classDiagram
class CChannel {
+int m_iChNumber
+int m_iMaxLevel
+int m_iPtAmount
+int m_iEAmount
+CMedium* m_pMedium
+CPtrArray m_sptRecArray
+CChannel()
+virtual ~CChannel()
+void ClearSptRecList()
}
class CElectrode {
+CString m_szECode
+BOOL m_bCState
+CString m_szMDate
+CString m_szMTime
+float m_fOMValue
+CElectrode()
+virtual ~CElectrode()
}
class CMedium {
+int m_iType
+int m_iN
+float m_fK
+CMedium()
+virtual ~CMedium()
}
CChannel --> CMedium : "关联"
CChannel --> CElectrode : "使用"
```
**Diagram sources**
- [Channel.h](file://h\Channel.h#L19-L34)
- [Electrode.h](file://h\Electrode.h#L12-L24)
**Section sources**
- [Channel.cpp](file://cpp\ProblemZone\Channel.cpp#L11-L48)
- [Channel.h](file://h\Channel.h#L19-L34)
- [Electrode.cpp](file://cpp\ProblemZone\Electrode.cpp#L18-L27)
- [Electrode.h](file://h\Electrode.h#L12-L24)
## 地球物理参数计算模型
GeomativeStudio支持多种地球物理参数的计算,主要包括视电阻率(SP)、电阻率反演(Rsp)和激电二次场(Ipsp)等。这些参数的计算模型在相应的派生类中实现,如SP2DTd、Rsp2DTd和Ipsp2DTd等。这些类继承自TestingData类,并重写了特定的计算方法。
对于视电阻率(SP)计算,系统根据测量装置类型和电极配置,使用相应的几何因子(K)和测量得到的电压(V)、电流(I)值,通过公式ρa = K × (V/I)计算得到视电阻率。这一计算过程在SP2DTd类的实现中完成,该类还负责将计算结果保存到数据库中,并提供数据导出功能。
激电二次场(Ipsp)的计算更为复杂,涉及时域数据的处理。系统通过CalculateTWInfo方法对原始电压波形进行多项式拟合,然后在预定义的时窗内进行积分计算,得到二次场面积。再结合充电末期电压(Vp),通过公式M = 积分面积/Vp计算得到充电率(M)参数。这一过程充分利用了_WaveCount结构体来管理多时窗的计算结果。
```mermaid
flowchart TD
Start([开始]) --> LoadOrgData["加载原始数据"]
LoadOrgData --> Preprocess["数据预处理"]
Preprocess --> FitCurve["电压波形多项式拟合"]
FitCurve --> DefineTW["定义时窗"]
DefineTW --> Integrate["时窗内积分计算"]
Integrate --> CalcVp["计算充电末期电压Vp"]
CalcVp --> CalcM["计算充电率M = 积分面积/Vp"]
CalcM --> CalcETA["计算极化率η = 100×V2/Vp"]
CalcETA --> Output["输出Ipsp参数"]
Output --> End([结束])
```
**Diagram sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L463-L566)
- [TestingData.h](file://h\TestingData.h#L171-L172)
**Section sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L463-L566)
- [SP2DTd.h](file://h\SP2DTd.h#L17-L58)
## 视图组件与数据可视化
视图组件负责将测量数据转换为可视化图形,其中AppDataSP2DTdView是视电阻率2D测量数据的视图组件。该组件采用MFC的文档/视图架构,通过CSplitterWnd实现多窗格界面布局。在Create方法中,它创建了一个水平分割器(m_firstSplitter)和一个垂直分割器(m_secondSplitter),将视图窗口划分为三个区域。
左侧区域显示测量数据详情列表(CAppDataTdDetailListView),右侧上部区域显示数据内容列表(CAppDataSP2DTdConListView),右侧下部区域显示接地电阻列表(CAppDataTdGrListView)。这种布局使得用户可以同时查看测量数据的不同方面,提高了数据浏览的效率。
视图组件通过OnSize方法处理窗口大小调整事件,确保分割器能够正确地重新布局。当窗口大小改变时,它会调用RecalcLayout方法重新计算分割器的布局,保持界面的整洁和可用性。这种设计模式使得视图组件能够适应不同大小的显示区域,提供了良好的用户体验。
```mermaid
flowchart TD
A([AppDataSP2DTdView]) --> B["m_firstSplitter\n水平分割器"]
B --> C["左侧窗格\nCAppDataTdDetailListView"]
B --> D["右侧窗格"]
D --> E["m_secondSplitter\n垂直分割器"]
E --> F["上部窗格\nCAppDataSP2DTdConListView"]
E --> G["下部窗格\nCAppDataTdGrListView"]
```
**Diagram sources**
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp#L71-L107)
**Section sources**
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp#L25-L123)
## 数据采集与处理流程
测量数据的采集与处理流程从设备数据包接收开始,经过解析、存储到数据库,最终完成数据校验和异常值处理。流程首先通过设备对象的ReceiveFile方法从设备获取ORG格式的原始数据文件。这些文件包含了电压和电流的原始波形数据,以文本格式存储。
获取到ORG文件后,系统通过CMarkup类解析XML格式的数据,提取出每个测点的电压和电流原始数据。然后,通过SQL语句将这些原始数据更新到数据库的相应记录中。这一过程在TestingData类的SaveOrgDataToDB方法中实现,确保了原始数据的完整性和可追溯性。
数据处理阶段,系统从数据库中读取原始数据,通过SplitterString函数将其按分号分割成数组,然后对每个数据点进行转换和处理。电压数据会经过adc_calculate方法进行校准,将ADC值转换为实际的电压值。处理后的数据被存储在TdRecord对象中,供后续的参数计算和可视化使用。
```mermaid
sequenceDiagram
participant 设备 as "测量设备"
participant 应用程序 as "应用程序"
participant 数据库 as "数据库"
设备->>应用程序 : 发送ORG数据包
应用程序->>应用程序 : 接收并保存为本地文件
应用程序->>应用程序 : 使用CMarkup解析XML
应用程序->>数据库 : 执行SQL更新原始数据
数据库-->>应用程序 : 确认更新完成
应用程序->>应用程序 : 从数据库读取原始数据
应用程序->>应用程序 : 分割并转换数据
应用程序->>应用程序 : 存储处理后的数据
应用程序-->>用户 : 显示处理结果
```
**Diagram sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L326-L420)
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp#L34-L211)
**Section sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L326-L420)
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp#L34-L211)
## 数据校验与异常处理
系统实现了多层次的数据校验与异常处理机制,确保测量数据的准确性和系统的稳定性。在数据采集阶段,系统会检查接收到的ORG文件是否完整,如果接收失败会向用户显示错误信息。在数据解析阶段,系统会对原始数据进行有效性检查,如检查电压和电流数据的长度是否匹配。
在参数计算阶段,系统特别关注关键参数的合理性。例如,在计算充电率(M)时,系统会检查充电末期电压(Vp)是否为零,如果为零会弹出错误提示,因为这会导致除零错误。这种预防性检查避免了计算过程中的数值异常,保证了计算结果的可靠性。
异常处理主要通过C++的异常机制和MFC的消息框实现。对于数据库操作异常,系统捕获_com_error异常并显示详细的错误描述。对于用户可感知的错误,如Vp为零的情况,系统会根据当前语言设置显示中文或英文的错误消息,提高了系统的国际化支持能力。
**Section sources**
- [TestingData.cpp](file://cpp\ProblemZone\TestingData.cpp#L505-L512)
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp#L115-L118)
@@ -0,0 +1,218 @@
# 激电二次场数据模型
<cite>
**本文档引用文件**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp)
- [Ipsp3DTd.cpp](file://cpp/ProblemZone/Ipsp3DTd.cpp)
- [IpspCETd.cpp](file://cpp/ProblemZone/IpspCETd.cpp)
- [Ipsp2DTd.h](file://h/Ipsp2DTd.h)
- [Ipsp3DTd.h](file://h/Ipsp3DTd.h)
- [IpspCETd.h](file://h/IpspCETd.h)
- [Ipsp2DTdRecord.h](file://h/Ipsp2DTdRecord.h)
- [Ipsp3DTdRecord.h](file://h/Ipsp3DTdRecord.h)
- [IpspCETdRecord.h](file://h/IpspCETdRecord.h)
- [TdChannel.h](file://h/TdChannel.h)
- [Channel.h](file://h/Channel.h)
- [AppDataIpspCETdView.cpp](file://cpp/Views/AppDataIpspCETdView.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [Ipsp系列类架构](#ipsp系列类架构)
3. [核心数据结构与物理参数](#核心数据结构与物理参数)
4. [供电周期与采样时序处理](#供电周期与采样时序处理)
5. [时序对齐机制](#时序对齐机制)
6. [可视化组件与剖面生成](#可视化组件与剖面生成)
7. [数据处理流程](#数据处理流程)
8. [数据质量控制](#数据质量控制)
## 引言
Ipsp系列类(Ipsp2DTd、Ipsp3DTd、IpspCETd)是Geomative Studio软件中用于处理时间域激电法(TDIP)二次场数据的核心组件。这些类定义了从数据采集、存储、处理到可视化的完整数据模型,支持二维、三维及一维中心激电(CEIP)等多种测量模式。该数据模型不仅管理原始电压信号,还负责计算关键的地球物理参数,如视极化率、半衰时和衰减系数,为地质解释提供基础数据。
## Ipsp系列类架构
Ipsp系列类继承自`CTestingData`基类,形成一个层次化的数据处理体系。每个类针对特定的测量模式进行优化,但共享统一的数据管理接口。
```mermaid
classDiagram
class CTestingData {
+DWORD m_dwID
+_ConnectionPtr& m_pConnection
+virtual BOOL LoadData(CLinkList<CMedium*>& m_medLinkList)
+virtual BOOL SaveData()
}
class CIpsp2DTd {
+BOOL ShowConList(CListCtrl &tdConList)
+BOOL ShowGrList(CListCtrl &tdGrList)
+int GetFitEquationInfo(int nTSN, double nPeriod)
}
class CIpsp3DTd {
+BOOL ShowConList(CListCtrl &tdConList)
+BOOL ShowGrList(CListCtrl &tdGrList)
+int GetFitEquationInfo(int nTSN, double nPeriod)
}
class CIpspCETd {
+BOOL ShowConList(CListCtrl &tdConList)
+BOOL ShowGrList(CListCtrl &tdGrList)
+int GetFitEquationInfo(int nTSN, double nPeriod)
+BOOL SaveCERSPSetInfo(COpExec2DRSPTestSetDlg* pOpExec2DRSPTestSetDlg, CLinkList<CMedium*>& m_medLinkList)
}
CTestingData <|-- CIpsp2DTd
CTestingData <|-- CIpsp3DTd
CTestingData <|-- CIpspCETd
```
**图源**
- [Ipsp2DTd.h](file://h/Ipsp2DTd.h)
- [Ipsp3DTd.h](file://h/Ipsp3DTd.h)
- [IpspCETd.h](file://h/IpspCETd.h)
**节源**
- [Ipsp2DTd.h](file://h/Ipsp2DTd.h)
- [Ipsp3DTd.h](file://h/Ipsp3DTd.h)
- [IpspCETd.h](file://h/IpspCETd.h)
## 核心数据结构与物理参数
Ipsp系列类通过`CIpspXDTdRecord`记录类管理每个测点的详细数据。这些记录类继承自`CTdRecord`,并根据测量模式定义了特定的数据结构。
### 数据结构定义
| 类型 | 字段 | 描述 | 物理意义 |
| :--- | :--- | :--- | :--- |
| **CIpsp2DTdRecord** | m_iC1, m_iC2 | 供电电极编号 | 电流电极A、B的位置 |
| | m_iP1, m_iP2 | 测量电极编号 | 电位电极M、N的位置 |
| | m_iLevel, m_iPosInLevel | 测点层级与位置 | 在二维剖面中的空间索引 |
| **CIpsp3DTdRecord** | m_iC1, m_iC2 | 供电电极编号 | 电流电极A、B的位置 |
| | m_iP1, m_iP2 | 测量电极编号 | 电位电极M、N的位置 |
| | m_iLevel, m_iPosInLevel | 测点层级与位置 | 在三维网格中的空间索引 |
| **CIpspCETdRecord** | m_fA, m_fB | 电极距 | AB/2和MN/2的距离 |
| | m_fX, m_fY | 坐标 | 测点在地表的平面位置 |
| | m_fDepth | 深度 | 当前测量的深度层 |
### 关键物理参数计算
#### 二次场电压与视极化率
二次场电压(V)和视极化率(SP)是直接从数据库中读取的核心参数。视极化率(SP)是二次场电压与一次场电压的比值,通常以毫秒(ms)或百分比(%)表示。
#### 半衰时(Half-Life Time
半衰时是激电响应衰减到其初始值一半所需的时间,是反映极化体弛豫特性的重要参数。其计算通过`GetFitEquationInfo`方法实现,该方法利用多项式拟合和二分法求解。
```mermaid
flowchart TD
Start([开始]) --> LoadData["加载原始数据"]
LoadData --> CalcPeriod["计算供电周期"]
CalcPeriod --> ExtractData["提取放电阶段数据"]
ExtractData --> FitCurve["多项式拟合衰减曲线"]
FitCurve --> SolveHL["二分法求解半衰时"]
SolveHL --> AverageHL["计算多次放电的平均半衰时"]
AverageHL --> Return["返回半衰时结果"]
```
**图源**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp#L446-L509)
- [Ipsp3DTd.cpp](file://cpp/ProblemZone/Ipsp3DTd.cpp)
- [IpspCETd.cpp](file://cpp/ProblemZone/IpspCETd.cpp)
**节源**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp#L446-L509)
#### 衰减系数
衰减系数描述了激电响应的衰减速率。虽然代码中未直接命名“衰减系数”,但通过多项式拟合得到的系数数组(`dCoefficient`)本质上包含了衰减的数学模型,可用于计算衰减率。
## 供电周期与采样时序处理
Ipsp系列类通过`GetCycle`函数将数据库中的`TRfrequency`值转换为实际的供电周期(秒)。该周期是计算半衰时等参数的关键输入。
```cpp
// 伪代码:供电周期处理
double dPeriod = GetCycle(nTmp); // 将频率索引转换为周期
int nDataNum = atoi(m_saVRawData.GetAt(3)); // 获取采样点数
double dSplit = dPeriod / (nDataNum - 1); // 计算时间间隔
```
采样时序数据存储在`m_saVRawData`字符串数组中,其中包含了完整的电压时间序列。数据处理时,根据供电周期将时间序列分割成多个时窗,分别对每个时窗进行分析。
**节源**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp#L453-L454)
## 时序对齐机制
Ipsp系列类通过`CTdChannel`类与`Channel`类实现时序对齐。`CTdChannel``IpspXDTd`类的数据通道,它管理一个`m_tdRecArray`数组,该数组按测点编号(TSN)顺序存储`CIpspXDTdRecord`记录。
```mermaid
classDiagram
class CIpspCETd {
+CLinkList<CTdChannel*> m_tdChaList
}
class CTdChannel {
+DWORD m_dwID
+int m_iChNum
+CMedium* m_pMedium
+CPtrArray m_tdRecArray
+CTdRecord* GetTdRecord(int iTSN)
}
class CIpspCETdRecord {
+int m_iTSN
+CStringArray m_saVRawData
}
CIpspCETd --> CTdChannel : "包含"
CTdChannel --> CIpspCETdRecord : "包含"
```
**图源**
- [IpspCETd.h](file://h/IpspCETd.h)
- [TdChannel.h](file://h/TdChannel.h)
- [IpspCETdRecord.h](file://h/IpspCETdRecord.h)
**节源**
- [IpspCETd.h](file://h/IpspCETd.h)
- [TdChannel.h](file://h/TdChannel.h)
## 可视化组件与剖面生成
`AppDataIpspCETdView`类负责IpspCETd数据的可视化展示。它使用`CSplitterWnd`将视图分割为三个面板:详细信息、数据内容列表和接地电阻列表。
```mermaid
graph TB
AppDataIpspCETdView["CAppDataIpspCETdView"] --> m_firstSplitter["m_firstSplitter (1x2)"]
m_firstSplitter --> m_pDetailListView["m_pDetailListView (详情)"]
m_firstSplitter --> m_secondSplitter["m_secondSplitter (2x1)"]
m_secondSplitter --> m_pContentListView["m_pContentListView (数据内容)"]
m_secondSplitter --> m_pGrListView["m_pGrListView (接地电阻)"]
```
**图源**
- [AppDataIpspCETdView.cpp](file://cpp/Views/AppDataIpspCETdView.cpp)
**节源**
- [AppDataIpspCETdView.cpp](file://cpp/Views/AppDataIpspCETdView.cpp)
激电剖面的生成逻辑如下:
1. **数据加载**`CIpspCETd::LoadData`方法从数据库加载所有通道和测点数据。
2. **数据组织**:数据按通道和测点编号有序存储在`m_tdChaList``m_tdRecArray`中。
3. **参数计算**:调用`GetFitEquationInfo`等方法计算半衰时、视极化率等参数。
4. **视图更新**`ShowConList`方法将计算结果填充到`CListCtrl`控件中,形成可交互的剖面图。
## 数据处理流程
完整的数据处理流程涵盖了从原始信号到最终解释参数的转换。
```mermaid
flowchart TD
RawData["原始电压信号"] --> Filter["信号滤波"]
Filter --> BackgroundSub["背景噪声扣除"]
BackgroundSub --> ParameterExtract["参数提取"]
ParameterExtract --> HalfLife["半衰时计算"]
ParameterExtract --> DecayCoeff["衰减系数计算"]
ParameterExtract --> ApparentCharge["视极化率计算"]
HalfLife --> QC["数据质量控制"]
DecayCoeff --> QC
ApparentCharge --> QC
QC --> Output["输出结果"]
```
**节源**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp#L446-L509)
## 数据质量控制
数据质量控制策略主要体现在以下几个方面:
1. **异常值处理**:在`ShowDetailInfo``ShowGrList`方法中,对数据库中的空值(`VT_NULL`)和特殊值(如"-9999")进行检查和处理,避免无效数据显示。
2. **数据完整性验证**:在`ShowConList`等方法中,通过检查`GetRecordCount()`确保数据库查询结果有效,若无数据则返回错误。
3. **错误捕获**:使用`try-catch`块捕获数据库操作中的`_com_error`异常,确保程序的健壮性。
4. **数据对齐**:通过`TSN`(测点编号)确保不同通道的数据在时间和空间上正确对齐。
**节源**
- [Ipsp2DTd.cpp](file://cpp/ProblemZone/Ipsp2DTd.cpp#L372-L395)
- [IpspCETd.cpp](file://cpp/ProblemZone/IpspCETd.cpp#L604-L610)
@@ -0,0 +1,208 @@
# 电阻率反演数据模型
<cite>
**本文档引用的文件**
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp)
- [Rsp3DTd.cpp](file://cpp\Views\Rsp3DTd.cpp)
- [RspCETd.cpp](file://cpp\ProblemZone\RspCETd.cpp)
- [AppDataRsp3DTdView.cpp](file://cpp\Views\AppDataRsp3DTdView.cpp)
- [Rsp3DTdRecord.cpp](file://cpp\Views\Rsp3DTdRecord.cpp)
- [Medium3D.h](file://h\Medium3D.h)
- [MediumCrossHoleGeomative.h](file://h\MediumCrossHoleGeomative.h)
</cite>
## 目录
1. [引言](#引言)
2. [核心数据结构与输入输出](#核心数据结构与输入输出)
3. [反演算法流程与中间数据存储](#反演算法流程与中间数据存储)
4. [反演结果精度评估指标](#反演结果精度评估指标)
5. [反演剖面图生成流程](#反演剖面图生成流程)
6. [SP原始数据依赖关系](#sp原始数据依赖关系)
7. [不同地质模型适配策略](#不同地质模型适配策略)
8. [反演流程数据流图](#反演流程数据流图)
9. [典型配置参数及其影响](#典型配置参数及其影响)
## 引言
本文档旨在全面解析Geomative Studio软件中Rsp系列类(`Rsp2DTd``Rsp3DTd``RspCETd`)实现的电阻率反演数据模型。该模型是地球物理勘探中用于解释野外采集的电阻率数据的核心算法,通过将观测到的视电阻率数据与理论正演模型进行对比,迭代更新地下电性结构模型,最终获得与实际地质情况相符的电阻率分布剖面。文档将重点阐述反演算法的数据结构、迭代过程、精度评估、可视化生成以及与不同地质模型的适配机制。
## 核心数据结构与输入输出
### 输入数据结构
反演模型的输入数据主要来源于野外采集的原始测量数据,其核心结构由`CTestingData`基类及其派生类(如`CRsp2DTd``CRsp3DTd`)定义。输入数据主要包括:
- **测量任务元数据**:存储在`td`数据库表中,包含任务名称(`TDname`)、位置(`Tlocation`)、设备信息(`DESN`)、脚本信息(`SCCN``Sname`)、测量模式(`Tmode`)、电极数量(`Eamount`)、电极间距(`Edistance`)等。
- **测量配置参数**:包括电流频率(`TRfrequency`)、工业频率(`Ifrequency`)、采样率(`SAfrequency`)、装置类型(`Clayout`)、气象信息(`weather``temperature`)等。
- **原始测量记录**:存储在`td2dcon``td3dcon``td1dcon`等数据库表中,每条记录包含电极配置(`C1`, `C2`, `P1`, `P2`)、几何因子(`K`)、测量电流(`I`)、测量电压(`V`)、计算得到的视电阻率(`R0`)和视极化率(`SP`)等。
### 输出数据结构
反演过程的输出是经过迭代优化后的地下电阻率模型,其数据结构体现在:
- **反演结果数据**:最终的电阻率模型以网格化数据的形式存储,每个网格单元包含其位置坐标和反演得到的电阻率值。这些数据通常通过`SaveHeadInfoToFile`方法以XML格式导出,文件中包含`measure`根节点下的`medium_set``pole_count``pole_distance`等关键信息。
- **中间迭代数据**:在迭代过程中,每次迭代的模型参数和拟合误差会被临时存储在内存中,以便进行收敛性判断和结果回溯。
**Section sources**
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp#L56-L57)
- [Rsp3DTd.cpp](file://cpp\Views\Rsp3DTd.cpp#L43-L44)
- [RspCETd.cpp](file://cpp\ProblemZone\RspCETd.cpp#L34-L35)
## 反演算法流程与中间数据存储
### 迭代过程
电阻率反演是一个非线性优化问题,通常采用最小二乘法或其变种(如阻尼最小二乘法)进行求解。其核心迭代流程如下:
1. **初始化**:根据用户输入的初始模型(通常为均匀半空间或简单分层模型)和测量数据,构建初始的地下电性结构模型。
2. **正演计算**:基于当前的模型,计算所有测量点的理论视电阻率值。
3. **误差计算**:将理论计算值与实际观测值进行对比,计算残差(Residual)和目标函数(如均方根误差RMSE)。
4. **模型更新**:根据残差和雅可比矩阵(Jacobian Matrix,描述模型参数变化对观测值的影响),计算模型参数的更新量,并更新地下模型。
5. **收敛判断**:检查目标函数是否小于预设阈值,或迭代次数是否达到上限。若未收敛,则返回步骤2继续迭代。
### 中间数据存储机制
在迭代过程中,关键的中间数据通过以下方式存储:
- **内存链表**`CTestingData`类使用`CLinkList<CTdChannel*> m_tdChaList`来管理所有通道(Channel)的数据。每个`CTdChannel`对象又通过`CPtrArray m_tdRecArray`来存储该通道下所有测量点(`CRsp3DTdRecord`等)的详细记录。
- **数据库持久化**:原始测量数据和部分中间结果(如每次迭代的模型快照)会持久化存储在Access数据库中,相关的表包括`td`(任务头信息)、`tdchannel`(通道信息)、`td3dcon`3D测量记录)等。
- **XML文件缓存**`SaveHeadInfoToFile`方法将当前的测量配置和模型信息序列化为XML文件,用于临时缓存和后续处理。
```mermaid
flowchart TD
A[初始化模型] --> B[正演计算]
B --> C[计算理论视电阻率]
C --> D[与观测值对比]
D --> E[计算残差和目标函数]
E --> F{是否收敛?}
F --> |否| G[计算模型更新量]
G --> H[更新地下模型]
H --> B
F --> |是| I[输出最终模型]
```
**Diagram sources **
- [Rsp3DTd.cpp](file://cpp\Views\Rsp3DTd.cpp#L43-L51)
- [Rsp3DTdRecord.cpp](file://cpp\Views\Rsp3DTdRecord.cpp#L18-L38)
## 反演结果精度评估指标
反演结果的精度主要通过以下指标进行评估:
- **均方根误差 (RMSE)**:这是最核心的评估指标,衡量反演模型预测值与实际观测值之间的平均差异。RMSE值越小,说明模型拟合度越高。其计算公式为:`RMSE = sqrt(Σ(observed_i - predicted_i)² / N)`,其中N为数据点总数。
- **拟合优度 (Goodness of Fit, GOF)**:通常以百分比形式表示,反映观测数据被模型解释的程度。GOF越高,模型越好。
- **模型光滑度 (Model Smoothness)**:在反演目标函数中通常会加入一个正则化项,用于控制模型的复杂度,防止出现过度拟合的“斑点”模型。一个合理的模型应在拟合度和光滑度之间取得平衡。
- **残差分布图**:通过绘制残差的空间分布图,可以直观地判断模型在哪些区域拟合不佳,从而指导模型的进一步调整。
**Section sources**
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp#L576-L798)
- [Rsp3DTd.cpp](file://cpp\Views\Rsp3DTd.cpp#L586-L798)
## 反演剖面图生成流程
反演剖面图的生成流程主要由`AppDataRsp3DTdView`类负责,其逻辑如下:
### 渲染逻辑
1. **视图创建**`AppDataRsp3DTdView::Create`方法在创建时,会初始化一个分割窗口(Splitter Window),将主视图划分为三个子视图。
2. **子视图布局**
- **左侧视图**:创建`CAppDataTdDetailListView`,用于显示任务的详细信息列表。
- **右侧上半部分**:创建`CAppDataRsp3DTdConListView`,用于显示测量数据的内容列表(即`ShowConList`方法填充的数据)。
- **右侧下半部分**:创建`CAppDataTdGrListView`,用于显示电极状态和质量控制(GR)信息列表。
3. **数据绑定与更新**:每个子视图在`OnInitialUpdate`时,会从其关联的文档(Document)中获取数据,并调用相应的`ShowDetailInfo``ShowConList``ShowGrList`方法来填充列表控件(`CListCtrl`)。
4. **动态调整**:当窗口大小改变时,`OnSize`方法会重新计算分割窗口的布局,确保各子视图按比例正确显示。
```mermaid
graph TB
subgraph "AppDataRsp3DTdView"
A[主视图] --> B[分割窗口1]
B --> C[左侧: 详情列表]
B --> D[右侧: 分割窗口2]
D --> E[上半: 数据内容列表]
D --> F[下半: GR信息列表]
end
C --> G[ShowDetailInfo]
E --> H[ShowConList]
F --> I[ShowGrList]
```
**Diagram sources **
- [AppDataRsp3DTdView.cpp](file://cpp\Views\AppDataRsp3DTdView.cpp#L69-L97)
## SP原始数据依赖关系
电阻率反演模型与SP(激发极化)原始数据存在紧密的依赖关系:
- **数据来源**SP原始数据是反演算法的直接输入。`CRspCETd``CRsp2DTd`等类通过`LoadData`方法从数据库(如`td1dcon``td2dcon`表)中加载包含视极化率(`SP`)字段的测量记录。
- **数据处理**:在`ShowConList`方法中,代码会从数据库读取`SP`字段的值,并将其格式化后显示在用户界面的列表控件中。这表明SP数据是整个数据处理流程中的一个关键环节。
- **联合反演**:虽然当前代码主要展示了电阻率反演,但SP数据的存在暗示了系统可能支持电阻率与激发极化数据的联合反演,以获得更全面的地下物性信息。
**Section sources**
- [RspCETd.cpp](file://cpp\ProblemZone\RspCETd.cpp#L501-L575)
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp#L297-L377)
## 不同地质模型适配策略
系统通过`Medium`类的继承体系来适配不同的地质模型,核心策略是**多态性**和**接口抽象**。
### 适配机制
- **基类定义接口**`CMedium`基类(或其子类如`CMedium3D`)定义了一系列纯虚函数(如`GenerateSptRecElecVal``CalculateSptPtLoc``GenSptRecLevel`),这些函数构成了一个抽象接口,规定了所有地质模型必须实现的行为。
- **具体模型实现**`Medium3D.cpp``MediumCrossHoleGeomative.cpp`等文件实现了具体的地质模型。例如,`CMedium3D`类会实现3D网格化模型的生成算法,而`CMediumCrossHoleGeomative`类则会实现跨孔(Cross-Hole)探测的特定几何计算。
- **运行时绑定**:在`CRsp2DTd::Save2DRSPSetInfo`等方法中,通过`m_handleProcessor.GenerateHandle`生成特定模型的句柄,并从`m_medLinkList`链表中查找对应的`CMedium`对象。由于`m_pMedium`是指向基类的指针,在调用`CalculateDepth`等方法时,会根据实际对象的类型自动调用其对应的实现,从而实现了对不同地质模型的无缝适配。
```mermaid
classDiagram
class CMedium {
<<abstract>>
+virtual void CalculateDepth(float fA, float fFactor)
+virtual void CalculateTdPtLoc(...)
+virtual void Destroy()
}
class CMedium3D {
+void SetPoleDistance(DOUBLE f_dis_x, DOUBLE f_dis_y)
+void SetPoleStep(int f_step_x, int f_step_y)
+void SetFlags(BOOL Flags)
+void create()
+void SetRect(int x0, int y0, int x1, int y1)
+BOOL setPoleStart(int startpole)
+BOOL generate()
}
class CMediumCrossHoleGeomative {
+void SetHoleDepth(float depth)
+void SetHoleSpacing(float spacing)
+void CalculateCrossHoleGeometry()
}
CMedium <|-- CMedium3D : "继承"
CMedium <|-- CMediumCrossHoleGeomative : "继承"
```
**Diagram sources **
- [Medium3D.h](file://h\Medium3D.h#L30-L77)
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp#L577-L582)
## 反演流程数据流图
以下数据流图概括了从数据输入到结果输出的完整反演流程。
```mermaid
flowchart LR
A[SP原始数据] --> B[数据库存储]
B --> C[加载数据 LoadData]
C --> D[初始化模型]
D --> E[正演计算 Forward Modeling]
E --> F[误差计算 Error Calculation]
F --> G{收敛?}
G --> |否| H[模型更新 Model Update]
H --> E
G --> |是| I[生成剖面图 Render View]
I --> J[用户界面显示]
K[配置参数] --> C
K --> D
```
**Diagram sources **
- [RspCETd.cpp](file://cpp\ProblemZone\RspCETd.cpp#L142-L195)
- [AppDataRsp3DTdView.cpp](file://cpp\Views\AppDataRsp3DTdView.cpp#L69-L97)
## 典型配置参数及其影响
| 配置参数 | 参数说明 | 对反演结果的影响 |
| :--- | :--- | :--- |
| `Eamount` | 电极总数 | 电极数越多,横向和纵向分辨率越高,但测量时间也越长。 |
| `Edistance` | 电极间距 | 间距越大,探测深度越深,但横向分辨率降低。 |
| `Clayout` | 装置类型 | 不同的装置(如温纳、施伦贝谢、偶极-偶极)具有不同的探测深度和灵敏度分布,直接影响反演结果的形态。 |
| `TRfrequency` | 发射频率 | 影响激发极化效应的测量,对SP数据反演至关重要。 |
| `Ifrequency` | 工业频率 | 用于选择滤波器,抑制50Hz或60Hz的工频干扰。 |
| `SAfrequency` | 采样率 | 采样率越高,数据质量越好,但数据量也越大。 |
**Section sources**
- [Rsp2DTd.cpp](file://cpp\ProblemZone\Rsp2DTd.cpp#L467-L565)
- [RspCETd.cpp](file://cpp\ProblemZone\RspCETd.cpp#L242-L337)
@@ -0,0 +1,346 @@
# 视电阻率数据模型
<cite>
**本文档引用文件**
- [SP2DTd.cpp](file://cpp\ProblemZone\SP2DTd.cpp)
- [SP2DTd.h](file://h\SP2DTd.h)
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp)
- [SP3DTd.cpp](file://cpp\ProblemZone\SP3DTd.cpp)
- [SP3DTd.h](file://h\SP3DTd.h)
- [SP3DTdRecord.cpp](file://cpp\ProblemZone\SP3DTdRecord.cpp)
- [SPCETd.cpp](file://cpp\ProblemZone\SPCETd.cpp)
- [SPCETd.h](file://h\SPCETd.h)
- [SPCETdRecord.cpp](file://cpp\ProblemZone\SPCETdRecord.cpp)
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp)
- [TestingData.h](file://h\TestingData.h)
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp)
- [TdRecord.h](file://h\TdRecord.h)
</cite>
## 目录
1. [引言](#引言)
2. [SP系列数据模型概述](#sp系列数据模型概述)
3. [二维视电阻率数据模型 (SP2DTd)](#二维视电阻率数据模型-sp2dtd)
4. [三维视电阻率数据模型 (SP3DTd)](#三维视电阻率数据模型-sp3dtd)
5. [跨孔视电阻率数据模型 (SPCETd)](#跨孔视电阻率数据模型-spcetd)
6. [视电阻率计算原理](#视电阻率计算原理)
7. [数据可视化流程](#数据可视化流程)
8. [数据采集与处理时序流程](#数据采集与处理时序流程)
9. [字段定义与单位说明](#字段定义与单位说明)
10. [典型应用场景](#典型应用场景)
11. [异常数据识别方法](#异常数据识别方法)
12. [核心计算逻辑代码片段](#核心计算逻辑代码片段)
## 引言
本文档旨在深入解析GeomativeStudio软件中SP系列类(SP2DTd、SP3DTd、SPCETd)所代表的视电阻率数据模型。这些模型是地球物理勘探中电阻率成像技术的核心,用于处理和分析从野外采集的原始电压和电流数据,进而计算出地下介质的视电阻率值。文档将详细阐述这些模型在二维、三维及跨孔测量中的数据结构设计、物理意义、计算方法以及可视化逻辑,并提供完整的数据处理流程。
## SP系列数据模型概述
SP系列数据模型是GeomativeStudio软件中用于处理视电阻率(Self-Potential, SP)数据的核心类体系。该系列包含三个主要类:`CSP2DTd``CSP3DTd``CSPCETd`,分别对应二维、三维和跨孔(一维)测量模式。这些类均继承自`CTestingData`基类,共享通用的数据结构和处理逻辑,同时根据各自的测量几何和物理特性,定义了特定的数据字段和方法。
**Section sources**
- [SP2DTd.h](file://h\SP2DTd.h#L17-L59)
- [SP3DTd.h](file://h\SP3DTd.h#L17-L53)
- [SPCETd.h](file://h\SPCETd.h#L18-L53)
- [TestingData.h](file://h\TestingData.h)
## 二维视电阻率数据模型 (SP2DTd)
`CSP2DTd`类是二维视电阻率数据模型的具体实现,专为沿一条测线进行的二维电阻率测量设计。其数据结构主要包含测点信息、电极配置和测量结果。
### 数据结构设计
该模型的核心数据存储在`td2dcon`数据库表中,主要字段包括:
- **C1, C2**: 供电电极编号。
- **P1, P2**: 测量电极编号。
- **N**: 叠加次数。
- **K**: 装置系数,由电极间距决定。
- **I**: 测量得到的电流值(单位:mA)。
- **V**: 测量得到的电压值(单位:mV)。
- **R0**: 接地电阻(单位:Ω)。
- **SP**: 计算得到的视电阻率值(单位:Ω·m)。
### 物理意义
二维模型假设地下电性结构在垂直于测线的方向上是无限延伸且不变的。通过沿测线移动电极阵列,可以获取一系列测点的视电阻率数据,这些数据经过反演处理后,可以生成反映地下电性分布的二维剖面图。
```mermaid
classDiagram
class CSP2DTd {
+DWORD m_dwID
+CString m_szTdName
+int m_iEAmount
+int m_iCHAmount
+float m_fESpace
+CString m_szEDistance
+CTypedPtrList< CObList, CTdChannel* > m_tdChaList
+BOOL SaveData()
+BOOL LoadData(CLinkList<CMedium*>&)
+BOOL ShowConList(CListCtrl&)
+BOOL SaveHeadInfoToFile(CString&, CString&)
}
class CTdChannel {
+DWORD m_dwID
+int m_iChNum
+int m_iEAmount
+CMedium* m_pMedium
}
class CSP2DTdRecord {
+int m_iTsn
+int m_iC1
+int m_iC2
+int m_iP1
+int m_iP2
+int m_iN
+float m_fK
+float m_fI
+float m_fV
+float m_fR0
+float m_fSP
+BOOL SaveData(DWORD)
+BOOL LoadOrgData()
}
CSP2DTd --> CTdChannel : "包含"
CTdChannel --> CSP2DTdRecord : "包含"
```
**Diagram sources **
- [SP2DTd.h](file://h\SP2DTd.h#L17-L59)
- [SP2DTdRecord.h](file://h\SP2DTdRecord.h#L12-L35)
- [SP2DTd.cpp](file://cpp\ProblemZone\SP2DTd.cpp#L40-L86)
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp#L18-L38)
**Section sources**
- [SP2DTd.cpp](file://cpp\ProblemZone\SP2DTd.cpp#L279-L341)
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp#L53-L80)
## 三维视电阻率数据模型 (SP3DTd)
`CSP3DTd`类用于处理三维视电阻率数据,适用于在二维测网内进行的测量。其数据结构与二维模型类似,但需要记录更复杂的电极空间位置。
### 数据结构设计
三维模型的核心数据存储在`td3dcon`数据库表中,其字段与`td2dcon`表基本相同(C1, C2, P1, P2, I, V, R0, SP等)。关键区别在于,三维测量的电极位置信息通常在脚本(Script)或装置(Medium)定义中管理,而`CSP3DTd`类通过`m_tdChaList`列表管理多个通道的数据,以支持更复杂的测量阵列。
### 物理意义
三维模型旨在获取地下电性结构的立体分布。通过在多个测线上进行测量,可以构建一个三维数据体。该模型能够更真实地反映地下异常体的形态和空间展布,但数据采集和处理的复杂度也显著增加。
```mermaid
classDiagram
class CSP3DTd {
+DWORD m_dwID
+CString m_szTdName
+int m_iEAmount
+int m_iCHAmount
+float m_fESpace
+CString m_szEDistance
+CTypedPtrList< CObList, CTdChannel* > m_tdChaList
+BOOL SaveData()
+BOOL LoadData(CLinkList<CMedium*>&)
+BOOL ShowConList(CListCtrl&)
+BOOL SaveHeadInfoToFile(CString&, CString&)
}
class CTdChannel {
+DWORD m_dwID
+int m_iChNum
+int m_iEAmount
+CMedium* m_pMedium
}
class CSP3DTdRecord {
+int m_iTsn
+int m_iC1
+int m_iC2
+int m_iP1
+int m_iP2
+int m_iN
+float m_fK
+float m_fI
+float m_fV
+float m_fR0
+float m_fSP
+BOOL SaveData(DWORD)
+BOOL LoadOrgData()
}
CSP3DTd --> CTdChannel : "包含"
CTdChannel --> CSP3DTdRecord : "包含"
```
**Diagram sources **
- [SP3DTd.h](file://h\SP3DTd.h#L17-L53)
- [SP3DTdRecord.h](file://h\SP3DTdRecord.h#L12-L35)
- [SP3DTd.cpp](file://cpp\ProblemZone\SP3DTd.cpp#L34-L80)
- [SP3DTdRecord.cpp](file://cpp\ProblemZone\SP3DTdRecord.cpp#L18-L38)
**Section sources**
- [SP3DTd.cpp](file://cpp\ProblemZone\SP3DTd.cpp#L272-L334)
- [SP3DTdRecord.cpp](file://cpp\ProblemZone\SP3DTdRecord.cpp#L53-L80)
## 跨孔视电阻率数据模型 (SPCETd)
`CSPCETd`类用于处理跨孔(或称一维)视电阻率数据,常用于垂直电测深(VES)或井间测量。
### 数据结构设计
该模型的核心数据存储在`td1dcon`数据库表中,其字段与二维/三维模型有显著不同:
- **a, b**: 供电电极间距(单位:m)。
- **x, y**: 测量电极间距(单位:m)。
- **N**: 叠加次数。
- **K**: 装置系数,由a、b、x、y计算得出。
- **I, V, R0, SP**: 与二维/三维模型相同。
### 物理意义
跨孔模型主要用于研究地下介质随深度的变化。通过固定测量电极并逐步增大供电电极间距,可以探测到不同深度的地下电性。该模型生成的数据通常用于绘制视电阻率曲线(如S-曲线、Q-曲线),以推断地层的分层结构。
```mermaid
classDiagram
class CSPCETd {
+DWORD m_dwID
+CString m_szTdName
+int m_iEAmount
+int m_iCHAmount
+float m_fESpace
+CString m_szEDistance
+CTypedPtrList< CObList, CTdChannel* > m_tdChaList
+BOOL SaveData()
+BOOL LoadData(CLinkList<CMedium*>&)
+BOOL ShowConList(CListCtrl&)
+BOOL SaveHeadInfoToFile(CString&, CString&)
}
class CTdChannel {
+DWORD m_dwID
+int m_iChNum
+int m_iEAmount
+CMedium* m_pMedium
}
class CSPCETdRecord {
+int m_iTsn
+float m_fA
+float m_fB
+float m_fX
+float m_fY
+int m_iN
+float m_fK
+float m_fI
+float m_fV
+float m_fR0
+float m_fSP
+BOOL SaveData(DWORD)
+BOOL LoadOrgData()
}
CSPCETd --> CTdChannel : "包含"
CTdChannel --> CSPCETdRecord : "包含"
```
**Diagram sources **
- [SPCETd.h](file://h\SPCETd.h#L18-L53)
- [SPCETdRecord.h](file://h\SPCETdRecord.h#L14-L37)
- [SPCETd.cpp](file://cpp\ProblemZone\SPCETd.cpp#L32-L80)
- [SPCETdRecord.cpp](file://cpp\ProblemZone\SPCETdRecord.cpp#L18-L38)
**Section sources**
- [SPCETd.cpp](file://cpp\ProblemZone\SPCETd.cpp#L498-L562)
- [SPCETdRecord.cpp](file://cpp\ProblemZone\SPCETdRecord.cpp#L44-L77)
## 视电阻率计算原理
视电阻率(Apparent Resistivity, ρa)的计算基于欧姆定律和特定的电极装置系数(K)。其基本公式为:
ρa = K * (V / I)
其中:
- **ρa**: 视电阻率,单位为欧姆·米(Ω·m)。
- **K**: 装置系数,单位为米(m),由所采用的电极排列方式(如温纳装置、施伦贝谢装置等)和电极间距决定。
- **V**: 测量得到的电压值,单位为伏特(V)。
- **I**: 测量得到的电流值,单位为安培(A)。
在代码实现中,`CSP2DTdRecord``CSP3DTdRecord``CSPCETdRecord`类的`SaveData`方法负责将计算出的`m_fSP`(即ρa)值保存到相应的数据库表(`td2dcon``td3dcon``td1dcon`)中。
**Section sources**
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp#L68-L80)
- [SP3DTdRecord.cpp](file://cpp\ProblemZone\SP3DTdRecord.cpp#L68-L80)
- [SPCETdRecord.cpp](file://cpp\ProblemZone\SPCETdRecord.cpp#L61-L77)
## 数据可视化流程
`AppDataSP2DTdView.cpp`文件定义了二维视电阻率数据的可视化界面。其核心是创建一个分栏视图,将数据详情、测量内容和接地电阻列表组织在一起。
### 可视化逻辑
1. **视图创建 (`Create`)**: 该方法使用`CSplitterWnd`创建一个静态分栏器,将主视图分为左右两部分。
2. **左侧面板**: 左侧创建一个`CAppDataTdDetailListView`视图,用于显示测试任务的详细信息(如任务名称、位置、设备型号等)。
3. **右侧面板**: 右侧再创建一个上下分栏的`CSplitterWnd`
- **上方面板**: 创建一个`CAppDataSP2DTdConListView`视图,用于显示所有测点的测量数据(即`td2dcon`表中的内容)。
- **下方面板**: 创建一个`CAppDataTdGrListView`视图,用于显示各电极的接地电阻测量结果。
4. **数据映射**: `CAppDataSP2DTdConListView`视图通过调用`CSP2DTd::ShowConList`方法,从数据库中查询数据并填充到列表控件中,实现了数据到曲线图的映射。
```mermaid
flowchart TD
A[AppDataSP2DTdView::Create] --> B[创建左右分栏]
B --> C[左侧面板: CAppDataTdDetailListView]
B --> D[右侧面板: 上下分栏]
D --> E[上方面板: CAppDataSP2DTdConListView]
D --> F[下方面板: CAppDataTdGrListView]
E --> G[调用 CSP2DTd::ShowConList]
G --> H[查询 td2dcon 表]
H --> I[填充列表控件]
F --> J[显示接地电阻]
```
**Diagram sources **
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp#L71-L103)
**Section sources**
- [AppDataSP2DTdView.cpp](file://cpp\Views\AppDataSP2DTdView.cpp#L71-L103)
- [SP2DTd.cpp](file://cpp\ProblemZone\SP2DTd.cpp#L279-L341)
## 数据采集与处理时序流程
从设备接收原始数据包到最终存储为视电阻率数据,整个流程遵循严格的时序。
### 处理路径
1. **数据包接收**: 设备通过串口或网络将原始数据包发送给软件。
2. **数据解析**: 软件解析数据包,提取出原始的电压(Vrawdata)和电流(Irawdata)序列。这些数据被存储在`td2dcon``td3dcon``td1dcon`表的相应字段中。
3. **数据加载**: `CTdRecord`类的`LoadOrgData`方法(如`Load2DOrgData`)被调用,从数据库中读取原始数据序列。
4. **数据处理**: 原始数据经过滤波、去噪等预处理,并计算出最终的电压值V和电流值I。
5. **视电阻率计算**: 根据公式 ρa = K * (V / I) 计算视电阻率值SP。
6. **数据存储**: 计算结果(I, V, R0, SP)通过`CSPxDTdRecord::SaveData`方法写回数据库。
```mermaid
sequenceDiagram
participant 设备 as "测量设备"
participant 软件 as "GeomativeStudio软件"
participant 数据库 as "数据库"
设备->>软件 : 发送原始数据包(Vrawdata, Irawdata)
软件->>数据库 : 解析并存储原始数据
软件->>数据库 : 查询原始数据(LoadOrgData)
软件->>软件 : 预处理并计算V, I
软件->>软件 : 计算视电阻率SP = K * (V/I)
软件->>数据库 : 存储最终结果(SaveData)
```
**Diagram sources **
- [TdRecord.cpp](file://cpp\ProblemZone\TdRecord.cpp#L34-L320)
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp#L53-L80)
## 字段定义与单位说明
| 字段名 | 中文名称 | 数据类型 | 单位 | 说明 |
| :--- | :--- | :--- | :--- | :--- |
| `C1`, `C2` | 供电电极 | int | 无 | 电极编号 |
| `P1`, `P2` | 测量电极 | int | 无 | 电极编号 |
| `a`, `b` | 供电电极间距 | float | 米 (m) | 仅SPCETd模型 |
| `x`, `y` | 测量电极间距 | float | 米 (m) | 仅SPCETd模型 |
| `I` | 电流 | float | 毫安 (mA) | 测量得到的电流值 |
| `V` | 电压 | float | 毫伏 (mV) | 测量得到的电压值 |
| `R0` | 接地电阻 | float | 欧姆 (Ω) | 电极接地电阻 |
| `SP` | 视电阻率 | float | 欧姆·米 (Ω·m) | 计算得到的视电阻率值 |
| `K` | 装置系数 | float | 米 (m) | 由电极排列方式决定 |
| `N` | 叠加次数 | int | 无 | 信号叠加次数 |
| `Espace` | 电极步长 | float | 米 (m) | 移动电极的步长 |
| `Edistance` | 电极距离 | float | 米 (m) | 电极间的距离 |
## 典型应用场景
- **SP2DTd**: 用于二维地质剖面调查,如滑坡体探测、地下水污染羽流成像、岩溶发育区调查等。
- **SP3DTd**: 用于三维地质体精细成像,如矿体圈定、地下空洞三维定位、复杂地质构造研究等。
- **SPCETd**: 用于地层垂向分层研究,如工程地质勘察中的土层划分、地下水含水层深度探测、基岩面起伏调查等。
## 异常数据识别方法
1. **接地电阻异常**: 通过`gr`表检查各电极的接地电阻`OMvalue`。若电阻值过高(如大于10kΩ)或为-9999(表示未测量),则表明电极接触不良。
2. **电压/电流值异常**: 检查`V``I`值是否为零或接近零。若`I`为零,则供电回路可能断开;若`V`为零而`I`正常,则测量回路可能断开。
3. **视电阻率值异常**: 检查`SP`值是否为负数或极大值。负值通常由电极极性接反或强电磁干扰引起;极大值可能由数据采集错误或极端地质条件导致。
4. **数据完整性检查**: 确保每个测点(TSN)都有对应的`C1, C2, P1, P2`配置和有效的`V, I`读数。
## 核心计算逻辑代码片段
视电阻率的计算和存储逻辑主要在`CSPxDTdRecord`类的`SaveData`方法中实现。该方法将计算结果插入到相应的数据库表中。
**Section sources**
- [SP2DTdRecord.cpp](file://cpp\ProblemZone\SP2DTdRecord.cpp#L53-L80)
- [SP3DTdRecord.cpp](file://cpp\ProblemZone\SP3DTdRecord.cpp#L53-L80)
- [SPCETdRecord.cpp](file://cpp\ProblemZone\SPCETdRecord.cpp#L44-L77)
@@ -0,0 +1,118 @@
# Views模块
<cite>
**Referenced Files in This Document**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [taskmngframe.cpp](file://cpp/Views/taskmngframe.cpp)
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp)
- [DialNew2DTask.cpp](file://cpp/Views/DialNew2DTask.cpp)
- [DialCfgTaskPacket.cpp](file://cpp/Views/DialCfgTaskPacket.cpp)
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp)
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp)
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [主框架窗口设计](#主框架窗口设计)
3. [设备管理框架](#设备管理框架)
4. [任务管理框架](#任务管理框架)
5. [数据管理框架](#数据管理框架)
6. [对话框用户交互逻辑](#对话框用户交互逻辑)
7. [跨孔测量UI组件](#跨孔测量ui组件)
8. [测井功能UI](#测井功能ui)
9. [自定义控件实现](#自定义控件实现)
10. [UI消息映射机制](#ui消息映射机制)
## 简介
Views模块是GeomativeStudio项目的核心用户界面组件集合,负责实现所有UI相关的功能。该模块包含主框架窗口、设备管理、任务管理、数据管理等多个框架窗口,以及各种对话框和自定义控件。这些组件共同构成了应用程序的用户交互界面,通过MFC框架实现丰富的图形用户界面功能。
## 主框架窗口设计
主框架窗口由MainFrm.cpp实现,作为应用程序的主容器,管理所有子窗口和UI元素。该窗口继承自CMDIFrameWnd,支持多文档界面(MDI)架构。在OnCreate方法中,初始化了工具栏和状态栏,并创建了启动画面。主框架窗口通过成员变量m_pDataMngFrm、m_pDevMngFrm和m_pSptMngFrm分别管理数据管理、设备管理和任务管理框架窗口的实例。
消息映射系统处理各种用户操作,如菜单命令IDM_MNG_DATA_WIN、IDM_MNG_EXEC_WIN和IDM_MNG_DEV_WIN,分别用于显示数据管理、执行管理和设备管理窗口。当用户选择这些菜单项时,框架会检查相应窗口是否已存在,如果不存在则创建新的子窗口,否则激活已存在的窗口。
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L1-L800)
## 设备管理框架
设备管理框架由devmngframe.cpp实现,继承自CMDIChildWnd,作为MDI子窗口运行。该框架采用分割窗口设计,左侧为导航视图(CNavDevView),右侧为内容视图,根据设备状态动态切换显示内容。
框架通过m_splitter对象创建静态分割窗口,左侧显示设备树形结构,右侧根据设备状态显示不同的视图:在线设备显示CAppDevOLView,离线设备显示CAppDevView,无选择状态显示CBlankView。这种设计实现了灵活的UI布局,能够根据设备连接状态动态调整界面内容。
消息映射系统处理设备管理相关的各种操作,如IDM_OP_DE_REG_O_UPG(设备升级)、IDM_OP_DE_REG_O_MP(修改设备参数)和IDM_REM_GD10_TASK_MANAGER(远程任务管理)。ShowAppView方法根据设备状态决定显示哪个视图,并调用相应的操作类方法更新界面内容。
**Section sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L1-L800)
## 任务管理框架
任务管理框架由taskmngframe.cpp实现,同样继承自CMDIChildWnd。该框架的实现相对简单,目前仅包含基本的框架结构和消息映射,没有具体的UI元素或业务逻辑实现。
从代码结构看,该框架预留了扩展空间,但当前版本中未实现具体的功能。这表明任务管理功能可能在其他模块中实现,或者该框架将在后续版本中进一步开发。
**Section sources**
- [taskmngframe.cpp](file://cpp/Views/taskmngframe.cpp#L1-L36)
## 数据管理框架
数据管理框架由datamngframe.cpp实现,继承自CMDIChildWnd,采用与设备管理框架类似的分割窗口设计。左侧为CNavDataView导航视图,右侧为内容视图,根据数据类型动态切换。
框架支持多种数据类型的管理,包括2D电阻率数据(CAppDataRsp2DTdView)、2D激电数据(CAppDataIpsp2DTdView)、3D电阻率数据(CAppDataRsp3DTdView)等。ShowAppView和ShowContentListByPageView方法根据数据类型和状态决定显示哪个视图,并调用CDataOperator操作类的方法加载和显示数据。
消息映射系统处理数据管理的各种操作,如IDM_OP_TD_2DRSP_DB_D(删除2D电阻率数据)、IDM_OP_TD_2DRSP_DB_EXCEL(导出2D电阻率数据到Excel)和IDM_OP_TD_2DRSP_DB_GRAP(显示2D电阻率数据图表)。这些操作通过CDataOperator类与后台数据管理器进行交互,实现数据的增删改查功能。
**Section sources**
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp#L1-L800)
## 对话框用户交互逻辑
Views模块包含多个对话框实现,用于处理特定的用户交互场景。DialNew2DTask.cpp实现创建新2D任务的对话框,提供任务名称、测试地点、测试类型、装置类型等参数的输入界面。
对话框通过组合框控件实现选项选择,如m_cmbTestType用于选择测试方法(电阻率、激电、自电),m_cmbArray用于选择装置类型。OnInitDialog方法初始化界面元素,设置默认值和本地化文本。OnSelchangeCombo2dTestType等事件处理方法根据用户选择动态更新相关控件的状态。
DialCfgTaskPacket.cpp实现配置任务包的对话框,允许用户将多个任务组合成一个任务包,并设置循环次数、时间间隔等参数。对话框使用列表控件m_listTaskPacket显示已添加的任务,提供添加和删除功能。用户可以通过界面设置PLC ID、循环次数和时间间隔,然后保存到数据库。
**Section sources**
- [DialNew2DTask.cpp](file://cpp/Views/DialNew2DTask.cpp#L1-L665)
- [DialCfgTaskPacket.cpp](file://cpp/Views/DialCfgTaskPacket.cpp#L1-L301)
## 跨孔测量UI组件
跨孔测量功能由crossHole目录下的组件实现,主要包括CCrossHoleConfig2DMainDlg.cpp。该对话框提供跨孔测量的配置界面,包含电极坐标、井下、地面和参数四个选项卡页面。
通过m_tabChange选项卡控件实现多页面切换,每个页面对应不同的配置内容。井下和地面页面使用COption2DBoreholeDlg和COption2DSurfaceDlg等子对话框实现具体功能。CCrosshole2dDrawingBoardDlg用于显示和编辑电极坐标,C2DSimulationDlg用于模拟测量过程。
TwoBoreholeGenerateScript方法实现跨孔测量脚本的生成算法,根据两个孔的电极位置计算测量点的ABMN参数和几何因子K值。SaveTestPointToDB方法将生成的脚本保存到数据库,包括脚本基本信息、通道信息和测点信息。
**Section sources**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L1-L800)
## 测井功能UI
测井功能由logging目录下的组件实现,主要包括CDialogLoggingWnd.cpp。该对话框实现测井数据的显示界面,采用自定义绘制方式呈现测井曲线和岩性柱状图。
OnInitDialog方法初始化界面布局,设置各静态文本控件的位置。DrawFirstColumnStatic到DrawSixColumnStatic系列方法负责绘制界面的六个列区域,包括钻孔编号、探管型号、测量时间等基本信息。OnPaint方法实现自定义绘制,绘制测井曲线、深度刻度线和岩性柱状图。
UpdateLowHighValue方法更新各测量参数的最小值和最大值显示,用于指示数据范围。对话框支持加载测井数据文件,根据数据动态调整绘图区域大小,并在界面上绘制SP(自电)、长电位电阻率、短电位电阻率和梯度电阻率等测井曲线。
**Section sources**
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp#L1-L800)
## 自定义控件实现
ctrl目录包含自定义控件的实现,主要是CDragListCtrl.cpp。该控件继承自CListCtrl,扩展了拖拽功能,允许用户通过鼠标拖拽重新排序列表项。
控件通过重写OnLvnBegindrag、OnMouseMove和OnLButtonUp等消息处理方法实现拖拽逻辑。在OnLvnBegindrag中创建拖拽图像列表,并开始拖拽操作。OnMouseMove方法更新拖拽图像的位置,OnLButtonUp方法结束拖拽操作并清理资源。
CDragList辅助类提供拖拽操作的底层支持,BeginDrag、Dragging和EndDrag方法分别处理拖拽的开始、过程和结束阶段。这种设计实现了流畅的拖拽用户体验,允许用户直观地重新排列列表项。
**Section sources**
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp#L1-L260)
## UI消息映射机制
Views模块采用MFC的消息映射机制处理用户交互。每个UI类通过BEGIN_MESSAGE_MAP和END_MESSAGE_MAP宏定义消息映射表,将Windows消息或命令ID映射到相应的处理方法。
例如,主框架窗口的消息映射表将ON_COMMAND(IDM_MNG_DATA_WIN, OnMngDataWin)映射到OnMngDataWin方法,当用户点击"数据管理"菜单时触发该方法。设备管理框架通过ON_MESSAGE(WM_SCHEDULE, OnSchedule)处理自定义的WM_SCHEDULE消息,用于更新界面状态。
消息处理方法通常先进行参数验证和状态检查,然后调用相应的业务逻辑类完成具体操作。例如,OnDevieUpgrade方法先确认用户是否要进行升级,然后调用m_pDevOperator->DevieUpgrade方法执行升级操作。这种设计实现了UI层与业务逻辑层的分离,提高了代码的可维护性。
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L1-L800)
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp#L1-L800)
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp#L1-L800)
@@ -0,0 +1,248 @@
# 专用功能界面
<cite>
**本文档引用的文件**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp)
- [CCrossHoleConfig3DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig3DMainDlg.cpp)
- [COption2DGeometryDlg.cpp](file://cpp/crossHole/COption2DGeometryDlg.cpp)
- [COption3DGeometryDlg.cpp](file://cpp/crossHole/COption3DGeometryDlg.cpp)
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp)
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp)
- [CDialogLoggingLithologicWnd.cpp](file://cpp/logging/CDialogLoggingLithologicWnd.cpp)
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp)
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp)
- [CCrosshole2dDrawingBoardDlg.cpp](file://cpp/crossHole/CCrosshole2dDrawingBoardDlg.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [跨孔测量配置对话框设计](#跨孔测量配置对话框设计)
3. [几何参数设置逻辑](#几何参数设置逻辑)
4. [测井功能用户界面实现](#测井功能用户界面实现)
5. [自定义控件绘制与交互机制](#自定义控件绘制与交互机制)
6. [专用UI与后台算法协同工作](#专用ui与后台算法协同工作)
7. [自定义控件消息处理与重绘逻辑](#自定义控件消息处理与重绘逻辑)
8. [复杂参数配置验证与默认值管理](#复杂参数配置验证与默认值管理)
9. [跨孔测量与测井功能操作流程](#跨孔测量与测井功能操作流程)
10. [自定义控件扩展最佳实践](#自定义控件扩展最佳实践)
## 引言
本项目Geomative Studio是一款专业的地质勘探软件,专注于跨孔测量和测井功能的实现。系统通过专用的UI组件为用户提供直观、高效的操作界面,支持2D和3D跨孔测量配置、测井参数设置、数据可视化等功能。核心功能模块包括跨孔测量配置对话框、几何参数设置、测井功能界面以及自定义控件等。这些组件通过精心设计的架构和交互逻辑,实现了从用户输入到数据处理再到结果展示的完整工作流。系统采用MFC框架开发,利用对话框、列表控件等标准UI元素,并结合自定义绘制和消息处理机制,提供了高度可定制的用户界面体验。
**Section sources**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L1-L50)
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp#L1-L50)
## 跨孔测量配置对话框设计
跨孔测量配置对话框是系统的核心功能之一,提供2D和3D两种模式的测量配置。`CCrossHoleConfig2DMainDlg``CCrossHoleConfig3DMainDlg`两个类分别实现了2D和3D模式的对话框功能。对话框采用标签页(Tab)设计,包含"电极坐标"、"井下"、"地面"和"参数"四个主要页面。用户可以通过标签页切换不同的配置视图。对话框初始化时,会创建并管理多个子对话框,如`CCrosshole2dDrawingBoardDlg`用于2D绘图,`C2DSimulationDlg`用于模拟显示,以及`COption2DGeometryDlg``COption2DBoreholeDlg`等用于具体参数设置。这种设计实现了功能的模块化和界面的层次化,提高了代码的可维护性和用户体验。
```mermaid
classDiagram
class CCrossHoleConfig2DMainDlg {
+m_tabChange CTabCtrl
+m_pConnection _ConnectionPtr
+m_vecAllBoreholeABMNInfo vector<STDatabaseABMNInfo>
+m_mapDatabaseABMNInfo map<int, map<int, map<int, STDatabaseABMNInfo>>>
+GetInstance() CCrossHoleConfig2DMainDlg*
+OnInitDialog() BOOL
+OnBnClickedBtnCreate() void
+OnBnClickedBtnLoadGeometry() void
+SaveTestPointToDB() BOOL
}
class CCrossHoleConfig3DMainDlg {
+m_tabChange CTabCtrl
+m_pConnection _ConnectionPtr
+m_vecAllBoreholeABMNInfo vector<STDatabaseABMNInfo>
+m_mapDatabaseABMNInfo map<int, map<int, map<int, STDatabaseABMNInfo>>>
+GetInstance() CCrossHoleConfig3DMainDlg*
+OnInitDialog() BOOL
+OnBnClickedBtnCreate() void
+OnBnClickedBtnLoadGeometry() void
+SaveTestPointToDB() BOOL
}
class COption2DGeometryDlg {
+m_geometryList CListCtrl
+m_vecNoSortAllPoints vector<STBoreHolePoints>
+m_mapBoreholeCoordinates map<STWellPoints, vector<STBoreHolePoints>>
+m_vecSurfaceCoordinates vector<STBoreHolePoints>
+GetInstance() COption2DGeometryDlg*
+AddCoordinatesPoints() void
+DeleteCoordinatesPoint() void
+ShowCoordinatesPoints() void
}
class COption3DGeometryDlg {
+m_geometryList CListCtrl
+m_vecNoSortAllPoints vector<STBoreHolePoints>
+m_mapBoreholeCoordinates map<STWellPoints, vector<STBoreHolePoints>>
+m_mapSurfaceXCoordinates map<float, vector<STBoreHolePoints>>
+m_mapSurfaceYCoordinates map<float, vector<STBoreHolePoints>>
+GetInstance() COption3DGeometryDlg*
+AddCoordinatesPoints() void
+DeleteCoordinatesPoint() void
+ShowCoordinatesPoints() void
}
CCrossHoleConfig2DMainDlg --> COption2DGeometryDlg : "包含"
CCrossHoleConfig3DMainDlg --> COption3DGeometryDlg : "包含"
CCrossHoleConfig2DMainDlg --> CCrosshole2dDrawingBoardDlg : "包含"
CCrossHoleConfig3DMainDlg --> CCrosshole3dDrawingBoardDlg : "包含"
```
**Diagram sources **
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L15-L20)
- [CCrossHoleConfig3DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig3DMainDlg.cpp#L15-L20)
- [COption2DGeometryDlg.cpp](file://cpp/crossHole/COption2DGeometryDlg.cpp#L19-L20)
- [COption3DGeometryDlg.cpp](file://cpp/crossHole/COption3DGeometryDlg.cpp#L19-L20)
**Section sources**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L11-L800)
- [CCrossHoleConfig3DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig3DMainDlg.cpp#L11-L800)
## 几何参数设置逻辑
几何参数设置是跨孔测量配置的核心环节,主要由`COption2DGeometryDlg``COption3DGeometryDlg`两个类实现。这两个类负责管理电极的坐标信息,包括井下电极和地表电极。`COption2DGeometryDlg`使用`CListCtrl`控件`m_geometryList`以列表形式展示所有电极的ID、地址、X、Y、Z坐标。用户可以通过"加载坐标"按钮从`.geomative`文件中导入坐标数据,系统会解析CSV格式的文件内容,将电极信息存储在`m_vecNoSortAllPoints`向量中,并同步更新列表显示。对于3D模式,`COption3DGeometryDlg`还支持X方向和Y方向的电缆测线,使用`m_mapSurfaceXCoordinates``m_mapSurfaceYCoordinates`两个映射来分别管理不同方向的测线数据。当用户添加或删除坐标点时,系统会自动调用`AddCoordinatesPoints``DeleteCoordinatesPoint`方法更新内部数据结构,并通知绘图板进行重绘。
```mermaid
flowchart TD
Start([开始]) --> LoadGeometry["加载坐标文件"]
LoadGeometry --> ParseFile["解析.geomative文件"]
ParseFile --> ExtractPoints["提取电极坐标点"]
ExtractPoints --> ClassifyPoints{"分类坐标点"}
ClassifyPoints --> |地表| AddToSurface["添加到地表集合"]
ClassifyPoints --> |井下| AddToBorehole["添加到井下集合"]
AddToSurface --> UpdateList["更新列表显示"]
AddToBorehole --> UpdateList
UpdateList --> NotifyDrawingBoard["通知绘图板更新"]
NotifyDrawingBoard --> End([结束])
```
**Diagram sources **
- [COption2DGeometryDlg.cpp](file://cpp/crossHole/COption2DGeometryDlg.cpp#L78-L103)
- [COption3DGeometryDlg.cpp](file://cpp/crossHole/COption3DGeometryDlg.cpp#L78-L108)
**Section sources**
- [COption2DGeometryDlg.cpp](file://cpp/crossHole/COption2DGeometryDlg.cpp#L1-L309)
- [COption3DGeometryDlg.cpp](file://cpp/crossHole/COption3DGeometryDlg.cpp#L1-L396)
## 测井功能用户界面实现
测井功能的用户界面由`CDialogLoggingWnd``CDialogLoggingParameterSetting``CDialogLoggingLithologicWnd`三个主要对话框构成。`CDialogLoggingWnd`是主窗口,负责展示测井数据的完整报告,包括公司名称、钻孔编号、井深、测井方向等基本信息,以及自电、长电位、短电位和梯度电阻率的曲线图。`CDialogLoggingParameterSetting`用于设置测井参数,如任务名、测试地点、测井类型、采样间隔、初始深度、结束深度等。`CDialogLoggingLithologicWnd`则用于编辑和管理岩性剖面,用户可以添加、删除和修改不同深度范围的岩性信息。这些对话框通过消息映射(`BEGIN_MESSAGE_MAP`)处理用户的交互操作,如按钮点击、下拉框选择等,并通过`CMarkup`类解析和加载XML格式的参数文件和DAT格式的数据文件。
```mermaid
sequenceDiagram
participant User as "用户"
participant ParamDlg as "CDialogLoggingParameterSetting"
participant MainWnd as "CDialogLoggingWnd"
participant DataOper as "CLoggingDataOper"
User->>ParamDlg : 打开参数设置对话框
ParamDlg->>DataOper : 查询任务列表
DataOper-->>ParamDlg : 返回任务信息
ParamDlg->>ParamDlg : 填充下拉框
User->>ParamDlg : 选择任务并修改参数
ParamDlg->>DataOper : 更新任务信息到数据库
DataOper-->>ParamDlg : 返回操作结果
ParamDlg->>MainWnd : 传递测井参数
User->>MainWnd : 打开测井文件
MainWnd->>MainWnd : 加载XML参数文件
MainWnd->>MainWnd : 加载DAT数据文件
MainWnd->>MainWnd : 解析并绘制曲线图
MainWnd-->>User : 显示测井报告
```
**Diagram sources **
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp#L34-L37)
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp#L45-L51)
- [CDialogLoggingLithologicWnd.cpp](file://cpp/logging/CDialogLoggingLithologicWnd.cpp#L50-L59)
**Section sources**
- [CDialogLoggingWnd.cpp](file://cpp/logging/CDialogLoggingWnd.cpp#L1-L1222)
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp#L1-L356)
- [CDialogLoggingLithologicWnd.cpp](file://cpp/logging/CDialogLoggingLithologicWnd.cpp#L1-L304)
## 自定义控件绘制与交互机制
系统实现了两个重要的自定义控件:`CDragListCtrl``CListTextColorCtrl``CDragListCtrl`继承自`CListCtrl`,支持列表项的拖拽操作。它通过重写`OnLvnBegindrag``OnMouseMove``OnLButtonUp`消息处理函数,实现了拖拽的开始、移动和结束逻辑。当用户开始拖拽时,系统创建一个`CImageList`作为拖拽图像,并调用`DragEnter`方法显示拖拽效果。在鼠标移动过程中,`DragMove`方法会实时更新拖拽图像的位置。`CListTextColorCtrl`则是一个支持自定义文本颜色和背景颜色的列表控件。它通过将控件设置为`LVS_OWNERDRAWFIXED`风格,并重写`DrawItem`方法,实现了对每个列表项的自定义绘制。控件内部维护了`m_colTextColor``m_ItemTextColor`等链表,用于存储列和单元格的文本颜色,从而实现灵活的样式控制。
```mermaid
classDiagram
class CDragListCtrl {
+m_nSelItem int
+m_pDragImageList CImageList*
+m_bDragging BOOL
+OnLvnBegindrag() void
+OnMouseMove() void
+OnLButtonUp() void
}
class CListTextColorCtrl {
+m_nRowHeight int
+m_fontHeight int
+m_fontWith int
+m_color COLORREF
+m_ptrListCol CPtrList
+m_ptrListItem CPtrList
+m_colTextColor CPtrList
+m_ItemTextColor CPtrList
+DrawItem() void
+MeasureItem() void
+SetColTextColor() void
+SetItemTextColor() void
}
CDragListCtrl --|> CListCtrl : "继承"
CListTextColorCtrl --|> CListCtrl : "继承"
```
**Diagram sources **
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp#L9-L17)
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp#L16-L21)
**Section sources**
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp#L1-L260)
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp#L1-L399)
## 专用UI与后台算法协同工作
专用UI组件与后台算法模块通过数据结构和函数调用紧密协同工作。以跨孔测量配置为例,用户在`COption2DGeometryDlg`中设置的电极坐标,会通过`AddCoordinatesPoints`方法传递给`CCrossHoleConfig2DMainDlg`。当用户点击"创建"按钮时,`CCrossHoleConfig2DMainDlg`会调用`TwoBoreholeGenerateScript`等算法函数,根据电极的相对位置和间距,计算出测点的ABMN参数和几何因子K值。这些计算结果被存储在`m_vecAllBoreholeABMNInfo`向量中,最终通过`SaveTestPointToDB`方法写入数据库。对于测井功能,`CDialogLoggingParameterSetting`收集的参数被封装成`STLoggingParamSettingReq`结构体,传递给数据采集模块。采集到的数据则由`CDialogLoggingWnd`负责解析和可视化,实现了从用户配置到数据采集再到结果展示的完整闭环。
**Section sources**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L734-L736)
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp#L353-L356)
## 自定义控件消息处理与重绘逻辑
自定义控件的消息处理和重绘逻辑是其核心功能的实现基础。`CListTextColorCtrl`通过`PreSubclassWindow`方法将控件风格修改为`LVS_OWNERDRAWFIXED`,这表示控件将自行负责绘制其内容。当需要重绘时,系统会调用`DrawItem`方法。该方法首先获取要绘制的列表项信息,然后根据项的状态(选中或未选中)设置不同的背景色。接着,它会遍历所有列,计算每列的显示位置,并从`m_colTextColor``m_ItemTextColor`链表中查找对应的文本颜色,最后使用`DrawText`函数将文本绘制到指定位置。为了支持行高设置,控件重写了`MeasureItem`方法,根据`m_nRowHeight`成员变量的值来确定每一行的高度。这种机制使得控件能够灵活地适应不同的显示需求。
**Section sources**
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp#L62-L69)
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp#L71-L234)
## 复杂参数配置验证与默认值管理
系统对复杂参数配置进行了严格的验证和默认值管理。在`CDialogLoggingParameterSetting`中,当用户点击"确定"按钮时,`OnBnClickedOk`方法会执行一系列验证检查。例如,它会检查任务名、采样间隔、初始深度等必填字段是否为空,以及测井类型是否已选择。如果验证失败,会弹出相应的错误提示。对于数值型参数,系统会使用`atof`函数将其从字符串转换为浮点数,并进行范围检查。此外,系统还实现了默认值管理,例如在`OnInitDialog`方法中,会将"迭代次数"的默认值设置为0,并将"发射波形"的默认选项设置为"0+0-"。对于跨孔测量配置,系统会根据电极的Z坐标自动计算L值的取值范围,并确保生成的测点符合物理规律。
**Section sources**
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp#L174-L351)
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L302-L349)
## 跨孔测量与测井功能操作流程
对于初学者,跨孔测量和测井功能的操作流程如下:
1. **跨孔测量配置**
* 打开"跨孔测量配置"对话框。
* 切换到"井下"或"地面"标签页,通过"加载坐标"按钮导入电极坐标。
* 切换到"参数"标签页,输入脚本名称、时间间隔等信息。
* 点击"创建"按钮生成测点脚本。
* 点击"开始模拟"按钮进行模拟运行。
2. **测井功能使用**
* 在主界面选择"测井"功能。
* 点击"参数设置"按钮,配置任务名、测井类型、深度范围等参数。
* 开始数据采集。
* 采集完成后,点击"打开文件"按钮加载测井数据。
* 在主窗口查看自电、电阻率等曲线图,并通过"岩性编辑"功能添加岩性剖面。
**Section sources**
- [CCrossHoleConfig2DMainDlg.cpp](file://cpp/crossHole/CCrossHoleConfig2DMainDlg.cpp#L734-L736)
- [CDialogLoggingParameterSetting.cpp](file://cpp/logging/CDialogLoggingParameterSetting.cpp#L174-L351)
## 自定义控件扩展最佳实践
对于高级开发者,扩展自定义控件的最佳实践包括:
1. **继承与重写**:从标准MFC控件(如`CListCtrl`)继承,并重写关键的虚函数,如`DrawItem``PreSubclassWindow`等。
2. **消息映射**:使用`BEGIN_MESSAGE_MAP`宏将Windows消息(如`WM_PAINT``WM_LBUTTONDOWN`)映射到自定义的消息处理函数。
3. **状态管理**:在控件类中定义私有成员变量来管理控件的内部状态,如选中的项、拖拽状态、自定义颜色等。
4. **资源管理**:注意GDI对象(如`CPen``CBrush``CFont`)的创建和销毁,避免资源泄漏。通常在`OnPaint`等函数中创建,并在函数结束前通过`SelectObject`恢复旧对象。
5. **性能优化**:对于复杂的绘制操作,考虑使用双缓冲技术(Double Buffering)来减少屏幕闪烁。可以通过在内存DC上先绘制,再一次性拷贝到屏幕DC上来实现。
**Section sources**
- [CListTextColorCtrl.cpp](file://cpp/ctrl/CListTextColorCtrl.cpp#L1-L399)
- [CDragListCtrl.cpp](file://cpp/ctrl/CDragListCtrl.cpp#L1-L260)
@@ -0,0 +1,410 @@
# 主界面
<cite>
**本文档中引用的文件**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp)
- [ChildView.cpp](file://cpp/Views/ChildView.cpp)
- [Splash.cpp](file://cpp/Views/Splash.cpp)
- [MainFrm.h](file://h/MainFrm.h)
- [ChildFrm.h](file://h/ChildFrm.h)
- [ChildView.h](file://h/ChildView.h)
- [Splash.h](file://h/Splash.h)
- [GeoMative.rc2](file://res/GeoMative.rc2)
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
</cite>
## 目录
1. [主界面架构概述](#主界面架构概述)
2. [主窗口初始化流程](#主窗口初始化流程)
3. [菜单栏、工具栏与状态栏构建](#菜单栏工具栏与状态栏构建)
4. [多文档界面(MDI)组织方式](#多文档界面mdi组织方式)
5. [子窗口创建与管理逻辑](#子窗口创建与管理逻辑)
6. [启动画面显示控制](#启动画面显示控制)
7. [UI元素资源定义与消息映射](#ui元素资源定义与消息映射)
8. [用户操作路由机制](#用户操作路由机制)
9. [主界面与其他管理模块的数据交互](#主界面与其他管理模块的数据交互)
10. [MFC框架下的窗口生命周期管理](#mfc框架下的窗口生命周期管理)
11. [性能优化建议](#性能优化建议)
## 主界面架构概述
Geomative Studio主界面采用MFCMicrosoft Foundation Classes)框架实现,基于多文档界面(MDI)架构。系统通过`CMainFrame`类作为主框架窗口,继承自`CMDIFrameWnd`,负责管理整个应用程序的主窗口、菜单栏、工具栏和状态栏。主界面通过消息映射机制处理用户交互,并与设备管理、项目管理和数据管理器等模块进行数据交互。
主界面架构采用分层设计,包括:
- **主框架层**`CMainFrame`负责整体布局和核心功能调度
- **子框架层**`CChildFrame`用于管理多文档界面中的子窗口
- **视图层**`CChildView`提供客户端区域的显示功能
- **辅助组件层**:如启动画面`CSplashWnd`等提供辅助功能
该架构支持模块化扩展,通过消息驱动机制实现各组件间的松耦合通信。
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L56-L94)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L18-L27)
- [ChildView.cpp](file://cpp/Views/ChildView.cpp#L26-L30)
## 主窗口初始化流程
主窗口的初始化流程始于`CGeoMativeApp::InitInstance()`方法,在`GeoMative.cpp`中创建`CMainFrame`实例并加载框架。初始化过程包含以下关键步骤:
1. **主框架创建**:通过`new CMainFrame`实例化主窗口对象
2. **框架加载**:调用`LoadFrame(IDR_MAINFRAME)`加载资源定义的框架结构
3. **窗口显示**:设置为最大化显示并通过`ShowWindow``UpdateWindow`呈现
4. **数据库连接**:初始化ADO连接,打开GeoMativeDB.accdb数据库
5. **系统初始化**:创建系统时间获取线程,初始化诊断信息等
`CMainFrame::OnCreate`方法中,进一步完成工具栏、状态栏的创建和初始化,并显示启动画面。整个初始化流程确保了应用程序在启动时能够正确配置所有必要的UI组件和系统资源。
```mermaid
sequenceDiagram
participant App as CGeoMativeApp
participant MainFrame as CMainFrame
participant Splash as CSplashWnd
App->>MainFrame : new CMainFrame()
App->>MainFrame : LoadFrame(IDR_MAINFRAME)
App->>MainFrame : ShowWindow(SW_SHOWMAXIMIZED)
App->>MainFrame : UpdateWindow()
MainFrame->>MainFrame : OnCreate()
MainFrame->>Splash : CSplashWnd : : ShowSplashScreen()
MainFrame->>MainFrame : 创建工具栏和状态栏
MainFrame->>MainFrame : 初始化设备检测
```
**Diagram sources**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L208-L227)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L164-L223)
- [Splash.cpp](file://cpp/Views/Splash.cpp#L45-L55)
**Section sources**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L208-L399)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L111-L135)
- [Splash.cpp](file://cpp/Views/Splash.cpp#L19-L55)
## 菜单栏、工具栏与状态栏构建
主界面的菜单栏、工具栏和状态栏通过MFC的标准机制构建。在`MainFrm.cpp`中,`BEGIN_MESSAGE_MAP`宏定义了这些控件的创建和管理。
**工具栏**通过`CToolBar::CreateEx`方法创建,使用`TBSTYLE_FLAT|TBSTYLE_TOOLTIPS`样式实现扁平化外观和工具提示功能。工具栏加载`IDR_MAINFRAME`资源中的工具栏定义,并通过`EnableDocking(CBRS_ALIGN_ANY)`启用任意方向停靠。
**状态栏**通过`CStatusBar::Create`方法创建,使用预定义的指示器数组`indicators`来显示状态信息。该数组包含分隔符、大写锁定、数字锁定和滚动锁定等标准状态指示器。
```mermaid
classDiagram
class CMainFrame {
+CStatusBar m_wndStatusBar
+CToolBar m_wndToolBar
+LoadFrame()
+OnCreate()
+PreCreateWindow()
}
class CToolBar {
+CreateEx()
+LoadToolBar()
+EnableDocking()
+DockControlBar()
}
class CStatusBar {
+Create()
+SetIndicators()
}
CMainFrame --> CToolBar : 包含
CMainFrame --> CStatusBar : 包含
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L169-L189)
- [MainFrm.h](file://h/MainFrm.h#L56-L57)
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L164-L190)
- [MainFrm.h](file://h/MainFrm.h#L56-L57)
## 多文档界面(MDI)组织方式
Geomative Studio采用标准的MFC多文档界面(MDI)架构,通过`CMDIFrameWnd``CMDIChildWnd`类实现。主框架`CMainFrame`继承自`CMDIFrameWnd`,负责管理多个子窗口。
MDI架构的核心组件包括:
- **主框架窗口**`CMainFrame`,作为所有子窗口的容器
- **子框架窗口**`CChildFrame`,继承自`CMDIChildWnd`,每个子窗口的容器
- **视图窗口**`CChildView`,继承自`CWnd`,提供实际内容显示区域
子窗口的创建通过`CreateNewChild`方法实现,该方法使用运行时类信息创建指定类型的子框架。例如,在`OnMngDataWin`方法中,通过`CreateNewChild(RUNTIME_CLASS(CDataMngFrame), IDR_MAINFRAME)`创建数据管理框架。
```mermaid
graph TD
A[CMainFrame] --> B[CChildFrame]
A --> C[CChildFrame]
A --> D[CChildFrame]
B --> E[CChildView]
C --> F[CChildView]
D --> G[CChildView]
style A fill:#f9f,stroke:#333
style B fill:#bbf,stroke:#333
style C fill:#bbf,stroke:#333
style D fill:#bbf,stroke:#333
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L624-L667)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L17-L27)
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L624-L667)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L17-L118)
- [ChildFrm.h](file://h/ChildFrm.h#L14-L54)
## 子窗口创建与管理逻辑
子窗口的创建与管理由`CChildFrame``CChildView`类共同实现。`CChildFrame`作为子框架窗口,负责管理子窗口的生命周期和布局,而`CChildView`则负责客户端区域的内容显示。
`CChildFrame::OnCreate`方法中,通过`m_wndView.Create()`创建`CChildView`实例,并将其作为子窗口的视图组件。`OnCmdMsg`方法实现了命令消息的传递机制,确保视图能够优先处理命令消息。
子窗口的关闭通过`OnFileClose`方法实现,该方法发送`WM_CLOSE`消息来关闭窗口。`OnSetFocus`方法确保当子窗口获得焦点时,其内部的视图组件也能获得焦点。
```mermaid
flowchart TD
Start([子窗口创建]) --> PreCreateWindow["CChildFrame::PreCreateWindow"]
PreCreateWindow --> OnCreate["CChildFrame::OnCreate"]
OnCreate --> CreateView["创建CChildView"]
CreateView --> SetStyle["设置窗口样式"]
SetStyle --> Complete([子窗口创建完成])
CloseStart([子窗口关闭]) --> OnFileClose["CChildFrame::OnFileClose"]
OnFileClose --> SendClose["发送WM_CLOSE消息"]
SendClose --> CompleteClose([窗口关闭])
```
**Diagram sources**
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L85-L98)
- [ChildView.cpp](file://cpp/Views/ChildView.cpp#L36-L47)
**Section sources**
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L33-L118)
- [ChildView.cpp](file://cpp/Views/ChildView.cpp#L17-L58)
- [ChildView.h](file://h/ChildView.h#L15-L52)
## 启动画面显示控制
启动画面由`CSplashWnd`类实现,遵循MFC向导添加的典型启动画面模式。该类继承自`CWnd`,通过静态方法`ShowSplashScreen`控制启动画面的显示。
启动画面的显示控制流程如下:
1. 调用`CSplashWnd::ShowSplashScreen(this)`显示启动画面
2. `Create`方法加载`IDB_SPLASH`位图资源并创建弹出式窗口
3. `OnCreate`方法调用`CenterWindow`居中显示,并设置2秒定时器
4. `OnPaint`方法使用`BitBlt`绘制启动画面位图
5. `OnTimer`方法触发`HideSplashScreen`隐藏并销毁窗口
启动画面具有用户交互感知能力,通过`PreTranslateAppMessage`方法检测键盘或鼠标消息,一旦用户交互即立即隐藏启动画面,提升用户体验。
```mermaid
stateDiagram-v2
[*] --> Hidden
Hidden --> Creating : ShowSplashScreen()
Creating --> Displayed : Create()
Displayed --> Hiding : OnTimer() or 用户交互
Hiding --> Hidden : DestroyWindow()
note right of Displayed
定时器设置为2秒
或等待用户交互
end note
```
**Diagram sources**
- [Splash.cpp](file://cpp/Views/Splash.cpp#L45-L55)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L193-L194)
**Section sources**
- [Splash.cpp](file://cpp/Views/Splash.cpp#L1-L142)
- [Splash.h](file://h/Splash.h#L12-L56)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L193-L194)
## UI元素资源定义与消息映射
UI元素的资源定义主要通过`.rc`资源文件实现,尽管`GeoMative.rc2`文件当前为空,但系统使用`IDR_MAINFRAME`等资源ID引用主框架资源。菜单、工具栏和对话框等UI元素在资源编辑器中定义,并通过资源ID在代码中引用。
消息映射机制是MFC的核心特性,通过`BEGIN_MESSAGE_MAP``END_MESSAGE_MAP`宏定义消息处理函数。在`MainFrm.cpp`中,消息映射表将WM_CREATE、菜单命令ID和自定义消息映射到相应的处理方法。
例如,菜单命令`IDM_MNG_DATA_WIN`映射到`OnMngDataWin`方法,自定义消息`WM_SCHEDULE`映射到`OnSchedule`方法。这种声明式的消息映射机制使得UI事件处理清晰且易于维护。
```mermaid
erDiagram
RESOURCE : "资源定义" {
string 资源类型
string 资源ID
string 资源数据
}
MESSAGE_MAP : "消息映射" {
string 消息ID
string 处理函数
string 消息类型
}
HANDLER : "处理函数" {
string 函数名
string 参数
string 返回值
}
RESOURCE ||--o{ MESSAGE_MAP : "通过ID关联"
MESSAGE_MAP }o--|| HANDLER : "映射到"
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L58-L94)
- [GeoMative.rc2](file://res/GeoMative.rc2#L1-L14)
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L58-L94)
- [GeoMative.rc2](file://res/GeoMative.rc2#L1-L14)
## 用户操作路由机制
用户操作路由机制基于MFC的消息映射系统实现。`ON_COMMAND`宏将菜单项和工具栏按钮的命令ID映射到特定的处理函数。当用户点击菜单项或工具栏按钮时,MFC框架自动调用相应的处理方法。
例如,`ON_COMMAND(IDM_MNG_DATA_WIN, OnMngDataWin)`将数据管理窗口命令映射到`OnMngDataWin`方法。该方法检查`m_pDataMngFrm`是否已创建,若未创建则通过`CreateNewChild`创建新的子框架,否则激活现有窗口。
自定义消息通过`ON_MESSAGE`宏处理,如`ON_MESSAGE(WM_SCHEDULE, OnSchedule)`处理调度消息。这种机制支持应用程序内部组件间的通信,实现了松耦合的设计。
```mermaid
sequenceDiagram
participant User as 用户
participant Frame as CMainFrame
participant Handler as 处理函数
User->>Frame : 点击菜单项IDM_MNG_DATA_WIN
Frame->>Frame : 查找消息映射表
Frame->>Handler : 调用OnMngDataWin()
Handler->>Handler : 检查m_pDataMngFrm
alt 窗口未创建
Handler->>Handler : CreateNewChild()
Handler->>Handler : ShowWindow(SW_SHOWMAXIMIZED)
else 窗口已存在
Handler->>Handler : ActivateFrame()
end
Handler-->>Frame : 返回
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L60-L63)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L624-L636)
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L60-L63)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L624-L667)
## 主界面与其他管理模块的数据交互
主界面通过成员变量和消息机制与设备管理、项目管理和数据管理器等模块进行数据交互。`CMainFrame`类定义了指向这些管理框架的指针,如`m_pDevMngFrm``m_pDataMngFrm``m_pSptMngFrm`
数据交互主要通过以下方式实现:
- **直接方法调用**:通过管理框架指针调用其公开方法
- **消息传递**:使用`SendMessage`发送`WM_REFRESH`等自定义消息
- **共享数据对象**:通过应用程序对象`theApp`访问全局管理器
例如,在设备连接时,主框架通过`m_pDevMngFrm->SendMessage(WM_REFRESH_NAV_DEVICE)`通知设备管理框架刷新设备列表。文件导入导出操作通过`theApp.m_pIOManager`执行,并在完成后发送刷新消息。
```mermaid
classDiagram
class CMainFrame {
+CDevMngFrame* m_pDevMngFrm
+CDataMngFrame* m_pDataMngFrm
+CSptMngFrame* m_pSptMngFrm
+OnRefresh()
+OnFileExp()
+OnFileImp()
}
class CDevMngFrame {
+OnRefreshNavDevice()
}
class CDataMngFrame {
+OnRefresh()
}
class CIOManager {
+Export()
+Import()
}
CMainFrame --> CDevMngFrame : 持有引用
CMainFrame --> CDataMngFrame : 持有引用
CMainFrame --> CIOManager : 通过theApp访问
CMainFrame ..> CDevMngFrame : 发送WM_REFRESH_NAV_DEVICE
CMainFrame ..> CDataMngFrame : 发送WM_REFRESH
```
**Diagram sources**
- [MainFrm.h](file://h/MainFrm.h#L40-L42)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L554-L555)
**Section sources**
- [MainFrm.h](file://h/MainFrm.h#L40-L42)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L554-L555)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L69-L72)
## MFC框架下的窗口生命周期管理
MFC框架通过一系列虚拟方法和消息处理机制管理窗口生命周期。窗口的创建、销毁和消息处理遵循特定的顺序和模式。
**创建过程**
1. 构造函数:`CMainFrame()`进行成员变量初始化
2. `PreCreateWindow`:修改窗口样式和类属性
3. `OnCreate`:创建子控件(工具栏、状态栏)
4. `LoadFrame`:加载框架资源并显示窗口
**销毁过程**
1. `WM_CLOSE`消息:触发窗口关闭
2. 析构函数:`~CMainFrame()`清理资源
3. `PostNcDestroy`:删除C++对象(在`CSplashWnd`中实现)
子窗口通过`OnCmdMsg`方法实现命令消息的传递,确保视图能够优先处理命令。这种分层的消息处理机制是MFC框架的核心特性之一。
```mermaid
flowchart TB
subgraph 创建流程
A[构造函数] --> B[PreCreateWindow]
B --> C[OnCreate]
C --> D[LoadFrame]
D --> E[ShowWindow]
end
subgraph 销毁流程
F[WM_CLOSE] --> G[析构函数]
G --> H[PostNcDestroy]
H --> I[对象删除]
end
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L111-L127)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L33-L43)
- [Splash.cpp](file://cpp/Views/Splash.cpp#L100-L104)
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L111-L127)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp#L33-L43)
- [Splash.cpp](file://cpp/Views/Splash.cpp#L100-L104)
## 性能优化建议
针对Geomative Studio主界面的性能优化,建议从以下几个方面着手:
**减少重绘开销**
- 使用双缓冲技术减少闪烁
- 仅重绘无效区域而非整个窗口
- 延迟更新,批量处理UI变更
**减少消息处理延迟**
- 避免在消息处理函数中执行耗时操作
- 将长时间运行的任务移至工作线程
- 使用定时器分批处理大量数据更新
**内存管理优化**
- 及时释放不再使用的子窗口
- 使用智能指针管理动态创建的对象
- 缓存频繁访问的资源
**启动性能优化**
- 延迟加载非关键模块
- 优化数据库连接和初始化流程
- 减少启动时的同步操作
通过实施这些优化措施,可以显著提升主界面的响应速度和用户体验。
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
- [ChildFrm.cpp](file://cpp/Views/ChildFrm.cpp)
- [Splash.cpp](file://cpp/Views/Splash.cpp)
@@ -0,0 +1,269 @@
# 数据管理界面
<cite>
**本文档引用的文件**
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp)
- [DialListMeasuData.cpp](file://cpp/Views/DialListMeasuData.cpp)
- [DialListMeasuGR.cpp](file://cpp/Views/DialListMeasuGR.cpp)
- [DialListRealTimeMeasuData.cpp](file://cpp/Views/DialListRealTimeMeasuData.cpp)
- [DialListRealTimeMeasuGR.cpp](file://cpp/Views/DialListRealTimeMeasuGR.cpp)
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp)
- [DialMeasureDetailInfo.cpp](file://cpp/Views/DialMeasureDetailInfo.cpp)
- [DialRealTimeMeasureData.cpp](file://cpp/Views/DialRealTimeMeasureData.cpp)
- [TdManager.h](file://h/TdManager.h)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp)
- [DataOperator.h](file://h/DataOperator.h)
- [TaskDataOper.h](file://h/TaskDataOper.h)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp)
</cite>
## 目录
1. [数据管理框架](#数据管理框架)
2. [测量数据列表展示](#测量数据列表展示)
3. [实时数据刷新机制](#实时数据刷新机制)
4. [测量详情查看功能](#测量详情查看功能)
5. [实时监控界面行为](#实时监控界面行为)
6. [数据查询、过滤与导出](#数据查询过滤与导出)
7. [大数据量列表优化](#大数据量列表优化)
8. [操作指南](#操作指南)
9. [高级开发者指南](#高级开发者指南)
## 数据管理框架
数据管理框架的核心是`CDataMngFrame`类,它负责组织和管理数据管理界面的整体结构。该框架采用MDI(多文档界面)设计,通过分割窗口(Splitter Window)将界面分为左右两个视图:左侧为导航数据视图(`CNavDataView`),右侧为应用数据视图(`CAppDataView`)。
框架通过消息映射(`BEGIN_MESSAGE_MAP`)处理各种数据管理操作,如创建、删除、导出和刷新数据。当用户在导航树中选择一个数据项时,框架会根据数据类型(如2D电阻率、3D激电等)动态创建相应的应用视图,并加载和显示数据。
```mermaid
classDiagram
class CDataMngFrame {
+m_pNavDataView : CView*
+m_pAppDataView : CView*
+m_pDataOperator : CDataOperator*
+OnCreateClient() bool
+OnSchedule() LRESULT
+ShowAppView() bool
+ShowContentListByPageView() bool
}
class CDataOperator {
+ShowRsp2DTdInfo() void
+ShowIps2DpTdInfo() void
+ShowSP2DTdInfo() void
+LoadRsp2dRecordbyPage() void
+LoadIpsp2dRecordbyPage() void
+LoadSP2dRecordbyPage() void
}
CDataMngFrame --> CDataOperator : "使用"
CDataMngFrame --> CView : "包含 m_pNavDataView"
CDataMngFrame --> CView : "包含 m_pAppDataView"
```
**Diagram sources**
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp#L49-L602)
**Section sources**
- [datamngframe.cpp](file://cpp/Views/datamngframe.cpp#L1-L800)
## 测量数据列表展示
测量数据列表的展示逻辑主要由`CDialListMeasuData``CDialListMeasuGR`两个类实现。`CDialListMeasuData`负责显示测量数据,而`CDialListMeasuGR`负责显示接地电阻信息。
`CDialListMeasuData``OnInitDialog`方法中初始化列表控件,设置列标题(如ID、A、B、M、N、Stack、K、V(mV)、I(mA)、R0(Ohm*m)等),并从数据库查询数据填充列表。它支持分页显示,通过“上一页”和“下一页”按钮进行导航。
```mermaid
flowchart TD
A[初始化对话框] --> B[设置列表控件样式]
B --> C[插入列标题]
C --> D[查询数据库获取数据]
D --> E{数据是否为空?}
E --> |否| F[调用OnShowTdData显示数据]
E --> |是| G[返回]
F --> H[遍历数据向量]
H --> I[格式化并插入列表项]
I --> J[设置列表项文本]
J --> K{是否遍历完成?}
K --> |否| H
K --> |是| L[结束]
```
**Diagram sources**
- [DialListMeasuData.cpp](file://cpp/Views/DialListMeasuData.cpp#L52-L120)
**Section sources**
- [DialListMeasuData.cpp](file://cpp/Views/DialListMeasuData.cpp#L1-L333)
- [DialListMeasuGR.cpp](file://cpp/Views/DialListMeasuGR.cpp#L1-L113)
## 实时数据刷新机制
实时数据的刷新机制由`CDialListRealTimeMeasureData``CDialListRealTimeMeasuGR`类实现。与静态数据列表不同,实时数据需要在测量过程中动态更新。
`CDialListRealTimeMeasureData`提供了三种数据更新方法:
1. `OnShowTdData`: 一次性清空并重新填充整个列表。
2. `OnShowTdDataAppend`: 在列表末尾追加新数据。
3. `OnShowTdDataUpdate`: 智能更新,先查找是否存在该数据,存在则更新,不存在则追加。
这种设计确保了在高频率数据更新场景下的流畅性和准确性。
```mermaid
sequenceDiagram
participant Device as "测量设备"
participant App as "应用程序"
participant List as "实时数据列表"
Device->>App : 发送测量数据包
App->>App : 解析数据包
App->>List : 调用OnShowTdDataUpdate
List->>List : 查找ID匹配的列表项
alt 找到匹配项
List->>List : 更新现有项的数据
else 未找到匹配项
List->>List : 在末尾追加新项
end
List->>List : 确保新项可见(EnsureVisible)
List->>List : 更新界面
```
**Diagram sources**
- [DialListRealTimeMeasuData.cpp](file://cpp/Views/DialListRealTimeMeasuData.cpp#L171-L332)
- [DialListRealTimeMeasuData.cpp](file://cpp/Views/DialListRealTimeMeasuData.cpp#L251-L332)
**Section sources**
- [DialListRealTimeMeasuData.cpp](file://cpp/Views/DialListRealTimeMeasuData.cpp#L1-L486)
- [DialListRealTimeMeasuGR.cpp](file://cpp/Views/DialListRealTimeMeasuGR.cpp#L1-L244)
## 测量详情查看功能
测量详情的查看功能由`CDialMeasureDetailInfo`类实现。该类提供了一个包含“数据”和“接地电阻”两个标签页的对话框,允许用户在一个界面中查看任务的详细信息。
其核心逻辑在`OnInitDialog`方法中:
1. 根据传入的任务参数(`STRemTaskArg`),创建并初始化`CDialListMeasuTask``CDialListMeasuGR`两个子控件。
2. 分别调用`ShowPage(1)``GetGRRequest(m_stTaskArg)`从云端获取任务数据和接地电阻信息。
3. 通过标签页的切换事件(`OnSelchangeTabChg`)来显示或隐藏相应的子控件。
```mermaid
classDiagram
class CDialMeasureDetailInfo {
+m_dialListMeasuTask : CDialListMeasuTask
+m_dialListMeasuGR : CDialListMeasuGR
+OnInitDialog() bool
+OnSelchangeTabChg() void
}
class CDialListMeasuTask {
+ShowPage() void
}
class CDialListMeasuGR {
+GetGRRequest() void
}
CDialMeasureDetailInfo --> CDialListMeasuTask
CDialMeasureDetailInfo --> CDialListMeasuGR
```
**Diagram sources**
- [DialMeasureDetailInfo.cpp](file://cpp/Views/DialMeasureDetailInfo.cpp#L56-L104)
**Section sources**
- [DialMeasureData.cpp](file://cpp/Views/DialMeasureData.cpp#L1-L2262)
- [DialMeasureDetailInfo.cpp](file://cpp/Views/DialMeasureDetailInfo.cpp#L1-L186)
## 实时监控界面行为
实时监控界面由`CDialRealTimeMeasureData`类控制。该类不仅管理数据列表,还处理用户与测量过程的交互,如开始测量、暂停测量、单点测量等。
其行为逻辑如下:
- **按钮状态管理**: 通过`SetBtnStatus`方法根据当前测量状态(如`EN_MEASU_BTN_MEASU_ALL`)启用或禁用按钮,防止用户进行无效操作。
- **测量命令发送**: 当用户点击“开始测量”时,`OnMeasureAll`方法会构造一个`STTrusteeTaskTable`结构体,并通过`PostNetRequest`发送到设备,启动测量任务。
- **数据接收与处理**: 重写`ProcRcvMeasuData`等方法来处理从设备接收的实时数据,并更新UI。
**Section sources**
- [DialRealTimeMeasureData.cpp](file://cpp/Views/DialRealTimeMeasureData.cpp#L1-L1879)
## 数据查询、过滤与导出
数据查询、过滤和导出功能主要由`CTdManager``CDataOperator`两个核心类实现。
`CTdManager`作为数据管理器,提供了与数据库交互的底层接口:
- **查询**: `ShowTdListByTz``ShowTdListByProject`等方法用于从数据库查询任务列表。
- **删除**: `DeleteRsp2DTd``DeleteIpsp2DTd`等方法用于删除不同类型的数据。
- **导入/导出**: `Import2DTdConToDB``ExportRsp2DTdToDAT`等方法用于数据的导入和导出。
`CDataOperator`则作为`CTdManager`的上层封装,为UI提供更便捷的调用接口。例如,`ExportRsp2DTdToExcel`方法会调用`CTdManager`的底层功能,将2D电阻率数据导出为Excel文件。
```mermaid
classDiagram
class CDataOperator {
+ExportRsp2DTdToExcel() UINT
+ExportRsp2DTdToDAT() UINT
+ExportRsp2DTdToCSV() UINT
+ShowRsp2DTdInfo() void
+LoadRsp2dRecordbyPage() void
}
class CTdManager {
+ShowTdListByTz() bool
+DeleteRsp2DTd() void
+Import2DTdConToDB() bool
+ExportRsp2DTdToDAT() UINT
}
CDataOperator --> CTdManager : "使用"
```
**Diagram sources**
- [DataOperator.h](file://h/DataOperator.h#L29-L87)
- [TdManager.h](file://h/TdManager.h#L33-L87)
**Section sources**
- [TdManager.h](file://h/TdManager.h#L1-L109)
- [TdManager.cpp](file://cpp/Managers/TdManager.cpp#L1-L6839)
- [DataOperator.h](file://h/DataOperator.h#L1-L174)
## 大数据量列表优化
在处理大数据量时,直接加载所有数据会导致界面卡顿。项目通过以下两种技术进行优化:
1. **分页加载**: `CDataOperator`类中的`LoadRsp2dRecordbyPage`等方法实现了分页查询。它接收一个起始ID和页大小,只从数据库加载指定范围的数据,显著减少了内存占用和加载时间。
2. **虚拟列表**: 虽然代码中未直接体现,但`CListCtrl`控件支持虚拟列表(Virtual List)模式。在这种模式下,控件不会存储所有数据,而是通过`LVN_GETDISPINFO`消息在需要显示某一行时,由程序动态提供该行的数据。这是一种处理海量数据的高效方式。
**Section sources**
- [DataOperator.h](file://h/DataOperator.h#L76-L83)
- [TaskDataOper.h](file://h/TaskDataOper.h#L378-L382)
- [TaskDataOper.cpp](file://cpp/Operator/TaskDataOper.cpp#L418-L513)
## 操作指南
### 初学者:数据查看与导出
1. **查看数据**:
* 打开“数据管理”界面。
* 在左侧导航树中选择一个任务。
* 右侧会自动显示该任务的测量数据列表。
* 使用“上一页”和“下一页”按钮浏览不同页面的数据。
2. **导出数据**:
* 在导航树中右键点击要导出的任务。
* 选择“导出”菜单下的格式,如“导出为Excel”或“导出为DAT文件”。
* 在弹出的对话框中选择保存路径,点击“保存”即可。
## 高级开发者指南
### 高频率数据更新场景下的UI线程安全处理
在实时监控等高频率数据更新场景下,数据接收通常在后台线程中进行,而UI更新必须在主线程(UI线程)中完成。为确保线程安全,项目采用了以下方案:
1. **消息传递机制**: 后台线程不直接操作UI控件,而是通过`PostMessage``PostNetRequest`向UI线程发送自定义消息(如`WM_MSG_UPDATE_TASK_LIST`)。
2. **UI线程处理**: UI线程的消息循环接收到消息后,调用相应的消息处理函数(如`OnShowTdDataUpdate`)来安全地更新列表控件。
这种“生产者-消费者”模式有效地隔离了数据处理和UI更新,避免了多线程访问共享资源导致的竞态条件和崩溃。
```mermaid
sequenceDiagram
participant WorkerThread as "工作线程"
participant UIThread as "UI线程"
participant ListCtrl as "列表控件"
WorkerThread->>WorkerThread : 接收测量数据
WorkerThread->>UIThread : PostMessage(WM_SHOW_TD_DATA_UPDATE, 数据指针)
UIThread->>UIThread : 消息循环接收到消息
UIThread->>UIThread : 调用OnShowTdDataUpdate
UIThread->>ListCtrl : 安全地更新列表项
ListCtrl->>UIThread : 更新完成
```
**Section sources**
- [DialListRealTimeMeasuData.cpp](file://cpp/Views/DialListRealTimeMeasuData.cpp#L344-L343)
- [DialRealTimeMeasureData.cpp](file://cpp/Views/DialRealTimeMeasureData.cpp#L593-L594)
@@ -0,0 +1,266 @@
# 设备管理界面
<cite>
**本文档引用的文件**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DailLogin.cpp](file://cpp/Views/DailLogin.cpp)
- [DialFindComShow.cpp](file://cpp/Views/DialFindComShow.cpp)
- [DialShowDevParam.cpp](file://cpp/Views/DialShowDevParam.cpp)
- [DialPlcStatusShow.cpp](file://cpp/Views/DialPlcStatusShow.cpp)
- [DialDevStatusTimerShow.cpp](file://cpp/Views/DialDevStatusTimerShow.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
- [NetRequestDialog.cpp](file://cpp/socket/NetRequestDialog.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
本文档深入分析GeomativeStudio项目中设备管理相关的UI组件实现。重点介绍devmngframe.cpp中设备管理框架的布局结构与消息响应机制,DailLogin.cpp中的登录验证流程,DialFindComShow.cpp中的串口检测对话框逻辑,DialShowDevParam.cpp中的设备参数展示机制。解释DialPlcStatusShow.cpp和DialDevStatusTimerShow.cpp中PLC状态与定时状态的实时更新策略。结合MFC消息映射机制,说明用户操作如何触发CDevManager的接口调用。为初学者提供UI与设备通信协同工作的概念性解释,为高级开发者提供多线程环境下UI刷新的最佳实践。
## 项目结构
项目采用典型的MFC应用程序结构,设备管理相关的UI组件主要位于cpp/Views目录下。核心的设备管理框架由devmngframe.cpp实现,负责整体布局和消息分发。各个功能模块如登录、串口检测、设备参数展示等通过独立的对话框类实现。业务逻辑与数据管理由cpp/Managers和cpp/Operator目录下的类负责,形成清晰的分层架构。
**Section sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DailLogin.cpp](file://cpp/Views/DailLogin.cpp)
## 核心组件
设备管理界面的核心组件包括设备管理框架、登录验证、串口检测、设备参数展示和状态监控等模块。这些组件通过MFC的消息映射机制协同工作,实现用户与设备的交互。CDevManager类作为设备管理的核心,负责设备的生命周期管理和状态维护。
**Section sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
## 架构概述
系统采用MFC文档/视图架构,设备管理功能通过CMDIChildWnd派生的CDevMngFrame类实现。该框架使用CSplitterWnd进行界面分割,左侧为导航树视图,右侧为内容显示区域。通过消息映射机制处理用户操作,调用CDevOperator和CDevManager类完成具体业务逻辑。
```mermaid
graph TB
subgraph "UI层"
A[CDevMngFrame] --> B[CNavDevView]
A --> C[CAppDevView]
A --> D[CAppDevOLView]
end
subgraph "业务逻辑层"
E[CDevOperator] --> F[CDevManager]
E --> G[CDevice]
end
subgraph "数据层"
F --> H[数据库]
G --> I[串口通信]
end
A --> E
B --> E
C --> E
D --> E
```
**Diagram sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
## 详细组件分析
### 设备管理框架分析
CDevMngFrame类实现了设备管理的主框架,负责界面布局和消息处理。通过CSplitterWnd将界面分为导航区域和内容显示区域,支持动态切换视图以显示不同状态的设备信息。
#### 布局结构
```mermaid
classDiagram
class CDevMngFrame {
+CSplitterWnd m_splitter
+CView* m_pAppDevView
+CView* m_pNavDevView
+OnCreateClient()
+ShowAppView()
}
class CNavDevView {
+CTreeCtrl m_devTree
}
class CAppDevView {
+CListCtrl m_devDetailList
}
class CAppDevOLView {
+CListCtrl m_devOLDetailList
+CListCtrl m_devOLGRList
+CListCtrl m_devOLACList
}
CDevMngFrame --> CNavDevView : "包含"
CDevMngFrame --> CAppDevView : "包含"
CDevMngFrame --> CAppDevOLView : "包含"
```
**Diagram sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [navdevview.h](file://h/navdevview.h)
- [appdevview.h](file://h/appdevview.h)
#### 消息响应机制
```mermaid
sequenceDiagram
participant 用户
participant CDevMngFrame
participant CDevOperator
participant CDevManager
用户->>CDevMngFrame : 点击"修改设备参数"
CDevMngFrame->>CDevMngFrame : OnModifyDeviceParameter()
CDevMngFrame->>CDevOperator : ModifyDeviceParameter()
CDevOperator->>CDevManager : GetDeviceByID()
CDevManager-->>CDevOperator : 返回CDevice对象
CDevOperator->>CDevice : ModifyParameter()
CDevice-->>CDevOperator : 显示参数对话框
CDevOperator->>CDevice : ShowOLDetailInfo()
CDevice-->>CDevOperator : 更新UI
CDevOperator-->>CDevMngFrame : 返回结果
CDevMngFrame-->>用户 : 显示更新后的界面
```
**Diagram sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
### 登录验证流程分析
DailLogin.cpp实现了系统的登录验证功能,包括用户身份验证、密码保存和自动登录等特性。
#### 登录验证流程
```mermaid
flowchart TD
Start([开始]) --> ValidateInput["验证输入参数"]
ValidateInput --> InputValid{"输入有效?"}
InputValid --> |否| ReturnError["显示错误信息"]
InputValid --> |是| CheckUserID["验证用户ID"]
CheckUserID --> UserIDValid{"ID正确?"}
UserIDValid --> |否| ReturnError
UserIDValid --> |是| CheckPassword["验证密码"]
CheckPassword --> PasswordValid{"密码正确?"}
PasswordValid --> |否| ReturnError
PasswordValid --> |是| SaveSettings["保存设置"]
SaveSettings --> CheckSave["是否保存密码?"]
CheckSave --> |是| SavePassword["写入config.ini"]
CheckSave --> |否| SkipSave
SkipSave --> CheckAutoLogin["是否自动登录?"]
CheckAutoLogin --> |是| SaveAutoLogin["写入config.ini"]
CheckAutoLogin --> |否| SkipAutoLogin
SkipAutoLogin --> Complete["登录成功"]
ReturnError --> End([结束])
Complete --> End
```
**Diagram sources**
- [DailLogin.cpp](file://cpp/Views/DailLogin.cpp)
### 串口检测对话框逻辑
DialFindComShow.cpp实现了串口检测结果的显示功能,用于向用户展示串口连接状态。
#### 串口检测逻辑
```mermaid
classDiagram
class CDialFindComShow {
+CString m_strShowCom
+OnInitDialog()
}
CDialFindComShow --> CDialog : "继承"
```
**Diagram sources**
- [DialFindComShow.cpp](file://cpp/Views/DialFindComShow.cpp)
### 设备参数展示机制
DialShowDevParam.cpp实现了设备参数的展示和修改功能,支持参数的本地编辑和云端同步。
#### 参数展示与同步
```mermaid
sequenceDiagram
participant CDevMngFrame
participant CDialShowDevParam
participant Network
CDevMngFrame->>CDialShowDevParam : OnShowDevParam()
CDialShowDevParam->>CDialShowDevParam : 显示参数对话框
CDialShowDevParam->>CDialShowDevParam : 用户修改参数
CDialShowDevParam->>CDialShowDevParam : 验证输入
CDialShowDevParam->>Network : 发送修改请求
Network-->>CDialShowDevParam : 接收响应
CDialShowDevParam-->>CDevMngFrame : 返回结果
```
**Diagram sources**
- [DialShowDevParam.cpp](file://cpp/Views/DialShowDevParam.cpp)
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
### PLC状态与定时状态实时更新
DialPlcStatusShow.cpp和DialDevStatusTimerShow.cpp实现了PLC状态和定时状态的实时监控功能。
#### 状态更新策略
```mermaid
classDiagram
class CDialPlcStatusShow {
+STRemPlcData* m_pRemPlcData
+OnInitDialog()
}
class CDialDevStatusTimerShow {
+CListCtrl m_listData
+ShowPlcStatusData()
}
class CTaskDataOper {
+QueryPlcStatusData()
}
CDialDevStatusTimerShow --> CTaskDataOper : "使用"
CDialPlcStatusShow --> CDialDevStatusTimerShow : "关联"
```
**Diagram sources**
- [DialPlcStatusShow.cpp](file://cpp/Views/DialPlcStatusShow.cpp)
- [DialDevStatusTimerShow.cpp](file://cpp/Views/DialDevStatusTimerShow.cpp)
- [TaskDataOper.h](file://h/TaskDataOper.h)
**Section sources**
- [DialPlcStatusShow.cpp](file://cpp/Views/DialPlcStatusShow.cpp)
- [DialDevStatusTimerShow.cpp](file://cpp/Views/DialDevStatusTimerShow.cpp)
## 依赖关系分析
系统各组件之间存在清晰的依赖关系,UI组件依赖于业务逻辑组件,业务逻辑组件又依赖于数据访问组件。这种分层架构有助于代码的维护和扩展。
```mermaid
graph TD
A[devmngframe.cpp] --> B[DevOperator.cpp]
B --> C[DevManager.cpp]
C --> D[Device.cpp]
D --> E[SComPort.cpp]
A --> F[DialShowDevParam.cpp]
F --> G[NetRequestDialog.cpp]
A --> H[DialPlcStatusShow.cpp]
H --> I[DialDevStatusTimerShow.cpp]
I --> J[TaskDataOper.cpp]
```
**Diagram sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [DevOperator.cpp](file://cpp/Operator/DevOperator.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
## 性能考虑
在多线程环境下进行UI刷新时,应避免在主线程中执行耗时操作。建议使用工作线程处理设备通信,通过PostMessage或SendMessage将结果发送回UI线程进行界面更新。对于频繁的状态更新,可以采用定时器机制,避免过度刷新导致的性能问题。
## 故障排除指南
常见问题包括设备连接失败、参数同步异常和界面刷新延迟等。检查网络连接状态、验证设备ID和类型、确认串口配置正确性是解决问题的关键步骤。使用日志文件可以帮助定位具体的问题根源。
**Section sources**
- [devmngframe.cpp](file://cpp/Views/devmngframe.cpp)
- [Device.cpp](file://cpp/ProblemZone/Device.cpp)
## 结论
本文档详细分析了GeomativeStudio项目中设备管理相关的UI组件实现。通过MFC框架实现了功能完整的设备管理界面,各组件之间通过清晰的消息传递机制协同工作。系统架构合理,代码组织规范,为类似应用的开发提供了有价值的参考。
@@ -0,0 +1,358 @@
# 项目管理界面
<cite>
**本文档引用的文件**
- [taskmngframe.cpp](file://cpp/Views/taskmngframe.cpp)
- [DialNew1DTask.cpp](file://cpp/Views/DialNew1DTask.cpp)
- [DialNew2DTask.cpp](file://cpp/Views/DialNew2DTask.cpp)
- [DialNew3DTask.cpp](file://cpp/Views/DialNew3DTask.cpp)
- [DialCfgTaskPacket.cpp](file://cpp/Views/DialCfgTaskPacket.cpp)
- [DialCfgTerrain.cpp](file://cpp/Views/DialCfgTerrain.cpp)
- [DialTaskTree.cpp](file://cpp/Views/DialTaskTree.cpp)
- [DialTaskManager.cpp](file://cpp/Views/DialTaskManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
</cite>
## 目录
1. [任务管理框架与导航逻辑](#任务管理框架与导航逻辑)
2. [测量任务创建流程](#测量任务创建流程)
3. [任务包配置机制](#任务包配置机制)
4. [地形参数设置逻辑](#地形参数设置逻辑)
5. [任务树形视图构建与交互](#任务树形视图构建与交互)
6. [项目数据持久化与加载](#项目数据持久化与加载)
7. [任务参数验证与数据传递](#任务参数验证与数据传递)
8. [任务配置工作流](#任务配置工作流)
9. [UI响应性能优化建议](#ui响应性能优化建议)
## 任务管理框架与导航逻辑
任务管理框架由`taskmngframe.cpp`实现,作为MDI子窗口继承自`CMDIChildWnd`。该框架负责组织和管理所有与任务相关的UI组件,提供统一的导航界面。框架通过消息映射处理窗口激活、关闭等事件,确保在用户切换不同任务管理视图时能够正确更新状态和界面元素。
**Section sources**
- [taskmngframe.cpp](file://cpp/Views/taskmngframe.cpp#L1-L36)
## 测量任务创建流程
### 1D测量任务创建
`DialNew1DTask.cpp`实现了1D测量任务的创建对话框。该对话框初始化时会根据当前UI语言设置控件文本,并从`CTaskDataOper`操作类查询可用的介质类型和脚本信息。用户选择测试类型(电阻率、激电、自电)后,系统会动态更新发射波形和发射周期的选项。在用户点击"创建"按钮后,系统会执行完整的参数验证,包括任务名、测试方式、装置类型、脚本、迭代次数等,确保所有必填项都已正确填写且符合取值范围。
```mermaid
flowchart TD
Start([开始创建1D任务]) --> Init["初始化对话框\n设置语言、加载介质和脚本"]
Init --> SelectType["用户选择测试类型\n(电阻率/激电/自电)"]
SelectType --> UpdateOptions["更新发射波形和周期选项"]
UpdateOptions --> UserInput["用户输入任务参数\n任务名、位置、脚本等"]
UserInput --> Validate["点击创建时验证参数"]
Validate --> CheckName{"任务名为空?"}
CheckName --> |是| ShowError1["显示错误提示"]
CheckName --> |否| CheckType{"测试方式未选?"}
CheckType --> |是| ShowError2["显示错误提示"]
CheckType --> |否| CheckArray{"装置类型未选?"}
CheckArray --> |是| ShowError3["显示错误提示"]
CheckArray --> |否| CheckScript{"脚本未选?"}
CheckScript --> |是| ShowError4["显示错误提示"]
CheckScript --> |否| CheckStacking{"迭代次数有效?"}
CheckStacking --> |否| ShowError5["显示错误提示"]
CheckStacking --> |是| CreateTask["调用CTaskDataOper::Create1DTask\n创建任务"]
CreateTask --> Success{"创建成功?"}
Success --> |是| CloseDialog["关闭对话框"]
Success --> |否| Return["返回等待用户修正"]
ShowError1 --> Return
ShowError2 --> Return
ShowError3 --> Return
ShowError4 --> Return
ShowError5 --> Return
```
**Diagram sources**
- [DialNew1DTask.cpp](file://cpp/Views/DialNew1DTask.cpp#L1-L381)
### 2D测量任务创建
`DialNew2DTask.cpp`实现了2D测量任务的创建对话框。与1D任务相比,2D任务需要更多的参数配置,包括电缆布线方式、起始/结束电极、电极间距、孔间距(对于交叉孔装置)等。对话框在初始化时会根据装置类型动态显示或隐藏相关控件,例如交叉孔装置会显示孔间距输入框。当用户选择不同的测试类型时,系统会过滤可用的电缆布线方式,确保选择的配置是有效的。
```mermaid
flowchart TD
Start([开始创建2D任务]) --> Init["初始化对话框\n加载介质、脚本、电缆布局"]
Init --> SelectType["用户选择测试类型"]
SelectType --> FilterLayout["根据测试类型过滤\n电缆布线方式"]
FilterLayout --> UserInput["用户输入任务参数\n包括电极间距、起始/结束电极等"]
UserInput --> Validate["点击创建时验证参数"]
Validate --> CheckCommon["验证通用参数\n(任务名、测试方式等)"]
CheckCommon --> CheckSpace{"电极间距为零?"}
CheckSpace --> |是| ShowError1["显示错误提示"]
CheckSpace --> |否| CheckHole{"交叉孔装置且孔间距为零?"}
CheckHole --> |是| ShowError2["显示错误提示"]
CheckHole --> |否| CheckRange{"起始/结束电极越界?"}
CheckRange --> |是| ShowError3["显示错误提示"]
CheckRange --> |否| CreateTask["调用CTaskDataOper::Create2DTask\n创建任务"]
CreateTask --> Success{"创建成功?"}
Success --> |是| CloseDialog["关闭对话框"]
Success --> |否| Return["返回等待用户修正"]
ShowError1 --> Return
ShowError2 --> Return
ShowError3 --> Return
```
**Diagram sources**
- [DialNew2DTask.cpp](file://cpp/Views/DialNew2DTask.cpp#L1-L665)
### 3D测量任务创建
`DialNew3DTask.cpp`实现了3D测量任务的创建对话框。3D任务的配置最为复杂,需要设置网格大小、X/Y方向电极步长和距离等参数。当用户选择脚本时,系统会自动查询并显示网格信息(X和Y方向的点数),帮助用户理解测量范围。对话框还提供了发射周期的"自动"选项,为用户提供更灵活的配置选择。
```mermaid
flowchart TD
Start([开始创建3D任务]) --> Init["初始化对话框\n加载介质、脚本、电缆布局"]
Init --> SelectScript["用户选择脚本"]
SelectScript --> QueryGrid["查询并显示网格信息\n(X方向点数, Y方向点数)"]
QueryGrid --> UserInput["用户输入任务参数\n包括步长、距离等"]
UserInput --> Validate["点击创建时验证参数"]
Validate --> CheckCommon["验证通用参数\n(任务名、测试方式等)"]
CheckCommon --> CheckStep{"步长为空?"}
CheckStep --> |是| ShowError["显示错误提示"]
CheckStep --> |否| CreateTask["调用CTaskDataOper::Create3DTask\n创建任务"]
CreateTask --> Success{"创建成功?"}
Success --> |是| CloseDialog["关闭对话框"]
Success --> |否| Return["返回等待用户修正"]
ShowError --> Return
```
**Diagram sources**
- [DialNew3DTask.cpp](file://cpp/Views/DialNew3DTask.cpp#L1-L570)
## 任务包配置机制
`DialCfgTaskPacket.cpp`实现了任务包的配置对话框,允许用户将多个任务组织成一个任务包进行循环执行。对话框包含一个任务选择下拉框和一个任务列表,用户可以将选中的任务添加到列表中,也可以从列表中删除任务。系统会检查重复添加的任务,避免同一个任务被多次执行。
在用户点击"保存"时,系统会对输入的参数进行严格验证:
- 循环次数必须在1-60000之间
- 时间间隔必须在1-60000之间
- PLC ID不能为空
验证通过后,系统会将任务列表和属性信息保存到数据库中,供后续执行使用。
```mermaid
classDiagram
class CDialCfgTaskPacket {
+m_cmbTaskName : CComboBox
+m_listTaskPacket : CListCtrl
+m_cmbTaskName : CComboBox
+OnButtonAdd() void
+OnButtonDel() void
+OnOK() void
+IsRepeatTask(int) bool
+InitialTaskList() void
}
class CTaskDataOper {
+QueryTdBrowseInfo() void
+QueryTaskPacketAttr() bool
+InsertTaskPacketInfo() bool
}
CDialCfgTaskPacket --> CTaskDataOper : "使用"
```
**Diagram sources**
- [DialCfgTaskPacket.cpp](file://cpp/Views/DialCfgTaskPacket.cpp#L1-L301)
## 地形参数设置逻辑
`DialCfgTerrain.cpp`实现了地形参数的配置对话框,允许用户导入DAT文件并编辑地形数据点。对话框提供了添加、修改、删除地形数据点的功能,并支持从文件导入地形数据。用户可以设置地形数据标志(水平距离或垂直距离)和第一个电极对应的地形数据点编号。
在保存地形数据时,系统会执行严格的验证:
- 必须已导入DAT文件
- 必须选择地形数据标志
- 必须输入至少一个地形数据点
- 第一个电极对应的地形数据点编号必须在有效范围内
验证通过后,系统会将地形数据写入原DAT文件的末尾,遵循特定的文件格式要求。
```mermaid
flowchart TD
Start([开始配置地形]) --> ImportFile["用户导入DAT文件"]
ImportFile --> EditData["编辑地形数据点\n添加、修改、删除"]
EditData --> SetParams["设置地形参数\n标志、第一个电极编号"]
SetParams --> Save["点击保存"]
Save --> Validate["验证参数"]
Validate --> CheckFile{"已导入文件?"}
CheckFile --> |否| ShowError1["显示错误提示"]
CheckFile --> |是| CheckFlag{"选择数据标志?"}
CheckFlag --> |否| ShowError2["显示错误提示"]
CheckFlag --> |是| CheckData{"输入地形数据?"}
CheckData --> |否| ShowError3["显示错误提示"]
CheckData --> |是| CheckIndex{"第一个电极编号有效?"}
CheckIndex --> |否| ShowError4["显示错误提示"]
CheckIndex --> |是| WriteFile["将地形数据写入DAT文件"]
WriteFile --> Success{"写入成功?"}
Success --> |是| ShowSuccess["显示成功提示"]
Success --> |否| ShowFail["显示失败提示"]
ShowError1 --> Return["返回等待用户修正"]
ShowError2 --> Return
ShowError3 --> Return
ShowError4 --> Return
```
**Diagram sources**
- [DialCfgTerrain.cpp](file://cpp/Views/DialCfgTerrain.cpp#L1-L843)
## 任务树形视图构建与交互
### 任务树形视图构建
`DialTaskTree.cpp`实现了任务树形视图,使用`CTreeCtrl`控件显示任务列表。视图支持复选框功能,允许用户选择多个任务进行批量操作。视图初始化时会根据任务类型(2D或3D)从服务器获取任务数据,并构建树形结构。
```mermaid
classDiagram
class CDialTaskTree {
+m_treeTask : CTreeCtrl
+m_iSptType : int
+m_pParentWnd : CWnd*
+ShowTaskTree(int, time_t, time_t) void
+OnClickTreeTaskList() void
+OnNMRClickTreeTaskList() void
+OnAddNewItemToTaskList() bool
+OnClearTaskList() void
}
class CTaskDataOper {
+InitOnLineTaskTreeCtrl() void
+SetCurrentTimeRange() void
}
CDialTaskTree --> CTaskDataOper : "使用"
```
**Diagram sources**
- [DialTaskTree.cpp](file://cpp/Views/DialTaskTree.cpp#L1-L686)
### 任务树形视图交互
任务树形视图支持丰富的交互功能:
- 点击复选框:选择或取消选择任务,同时更新父节点的选中计数
- 右键点击:显示上下文菜单,提供创建任务、删除任务、取消测试、刷新列表等操作
- 点击任务节点:通知父窗口当前选中的任务
上下文菜单的选项会根据当前UI语言动态调整文本内容,确保用户界面的国际化支持。
```mermaid
sequenceDiagram
participant User as "用户"
participant Tree as "任务树"
participant Parent as "父窗口"
User->>Tree : 右键点击任务节点
Tree->>Tree : 显示上下文菜单
User->>Tree : 选择"删除任务"
Tree->>Parent : 发送WM_MSG_DELETE_TASK消息
Parent->>Parent : 处理删除任务逻辑
Parent->>Tree : 刷新任务列表
Tree->>User : 显示操作结果
```
**Diagram sources**
- [DialTaskTree.cpp](file://cpp/Views/DialTaskTree.cpp#L1-L686)
## 项目数据持久化与加载
`ProManager.cpp`实现了项目管理器,负责项目数据的持久化与加载。项目管理器通过`CProManager`类与数据库交互,实现项目和测区的创建、查询和同步功能。
项目创建流程包括:
1. 在数据库中创建项目记录
2. 在设备上创建项目文件夹和配置文件
3. 同步项目数据到设备
```mermaid
sequenceDiagram
participant UI as "用户界面"
participant ProMgr as "CProManager"
participant DB as "数据库"
participant Dev as "设备"
UI->>ProMgr : 创建项目
ProMgr->>ProMgr : 显示创建对话框
ProMgr->>ProMgr : 验证项目名称唯一性
ProMgr->>DB : 插入项目记录
DB-->>ProMgr : 返回项目ID
ProMgr->>Dev : 在设备上创建项目文件夹
Dev-->>ProMgr : 文件夹创建成功
ProMgr->>Dev : 发送项目配置文件
Dev-->>ProMgr : 文件发送成功
ProMgr->>DB : 记录同步状态
DB-->>ProMgr : 同步记录创建成功
ProMgr-->>UI : 显示创建成功
```
**Diagram sources**
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L2054)
## 任务参数验证与数据传递
### 任务参数验证
系统在创建任务时执行严格的参数验证,确保所有输入都符合业务规则:
- **必填项验证**:任务名、测试方式、装置类型、脚本等必须填写
- **取值范围验证**:迭代次数(1-255)、采样间隔(0-30000)、电极间距(>0)等
- **逻辑关系验证**:交叉孔装置的孔间距必须大于电极间距
- **唯一性验证**:任务包中不能包含重复任务
### 跨对话框数据传递
系统通过多种机制实现跨对话框的数据传递:
- **构造函数传递**:在创建对话框时通过构造函数传递必要的操作对象(如`CTaskDataOper`
- **成员变量共享**:父窗口与子窗口通过成员变量共享数据
- **消息机制**:使用Windows消息在不同窗口间传递数据和状态
例如,在`DialTaskManager.cpp`中,创建任务时会根据任务类型显示相应的创建对话框,并将创建好的任务参数传递给上传逻辑:
```cpp
// 伪代码示例
if (0 == m_iSelTaskType) {
iRet = m_dialNew2DTask.DoModal();
if (IDOK == iRet) {
// 从对话框获取创建好的任务参数
strTaskCN = m_dialNew2DTask.m_strTaskCN;
// 构造任务参数结构体
strcpy(m_TaskArg.ucTaskID, strTmp);
strcpy(m_TaskArg.ucTDName, m_dialNew2DTask.m_strTaskName);
// ... 其他参数
// 上传任务数据
CDownloadDataProgressDlg uploadDlg(&m_TaskArg, &m_taskOper,this);
uploadDlg.DoModal();
}
}
```
**Section sources**
- [DialNew1DTask.cpp](file://cpp/Views/DialNew1DTask.cpp#L1-L381)
- [DialNew2DTask.cpp](file://cpp/Views/DialNew2DTask.cpp#L1-L665)
- [DialNew3DTask.cpp](file://cpp/Views/DialNew3DTask.cpp#L1-L570)
- [DialTaskManager.cpp](file://cpp/Views/DialTaskManager.cpp#L1-L2242)
## 任务配置工作流
对于初学者,任务配置的工作流可以概括为以下步骤:
1. **创建项目**:通过项目管理器创建新的项目,填写项目基本信息
2. **创建测区**:在项目中创建测区,定义测量区域
3. **创建任务**
- 选择任务类型(1D、2D或3D)
- 选择测试方式(电阻率、激电或自电)
- 选择装置类型和测量脚本
- 设置任务参数(迭代次数、发射周期等)
- 点击"创建"完成任务创建
4. **配置地形**(可选):为任务配置地形数据,导入或手动输入地形点
5. **组织任务包**(可选):将多个任务组织成任务包,设置循环执行参数
6. **执行任务**:在任务管理器中选择任务并开始测量
每个步骤都有相应的对话框引导用户完成配置,系统会在关键操作前进行参数验证,确保配置的正确性。
## UI响应性能优化建议
针对大型项目下的UI响应性能,建议采取以下优化措施:
1. **分页加载任务数据**:避免一次性加载所有任务,采用分页或按时间范围加载的方式,减少内存占用和UI阻塞
2. **异步数据加载**:将耗时的数据查询操作放在后台线程执行,避免阻塞UI线程
3. **虚拟化列表控件**:对于包含大量任务的列表,使用虚拟化技术,只渲染可见区域的项目
4. **缓存常用数据**:将频繁访问的数据(如介质类型、脚本列表)缓存到内存中,减少数据库查询次数
5. **优化数据库查询**:为常用查询字段建立索引,优化SQL查询语句,减少查询时间
6. **延迟加载**:对于非关键信息,在用户需要时才进行加载,提高初始加载速度
7. **批量操作优化**:对于批量删除等操作,使用事务处理,减少数据库交互次数
这些优化措施可以显著提升大型项目下的UI响应速度,改善用户体验。
@@ -0,0 +1,243 @@
# 代码结构
<cite>
**本文档引用的文件**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp)
- [Global.cpp](file://cpp/Main/Global.cpp)
- [Constant.h](file://h/Constant.h)
- [GeoMative.h](file://h/GeoMative.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp)
- [DataOperator.cpp](file://cpp/Operator/DataOperator.cpp)
- [Project.h](file://h/Project.h)
- [Device.h](file://h/Device.h)
- [Script.h](file://h/Script.h)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
- [MainFrm.h](file://h/MainFrm.h)
</cite>
## 目录
1. [项目结构概述](#项目结构概述)
2. [核心模块职责划分](#核心模块职责划分)
3. [应用程序对象与管理器协调](#应用程序对象与管理器协调)
4. [常量与枚举的广泛应用](#常量与枚举的广泛应用)
5. [模块间调用关系示例](#模块间调用关系示例)
6. [系统架构图](#系统架构图)
## 项目结构概述
GeomativeStudio项目的代码结构清晰地划分为多个目录,每个目录承担特定的职责。`cpp``h`目录下分别存放C++源文件和头文件,其子目录体现了系统的分层架构。`Main`目录包含应用程序入口和全局对象;`Managers`目录包含设备、项目、脚本、测试数据和I/O管理器等核心业务逻辑组件;`Operator`目录包含具体操作实现;`ProblemZone`目录包含项目、设备、脚本点等核心数据模型;`Views`目录包含所有UI视图和对话框实现。
**Section sources**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L1-L800)
- [Global.cpp](file://cpp/Main/Global.cpp#L1-L800)
## 核心模块职责划分
### Main目录
`Main`目录是应用程序的入口点,主要包含`GeoMative.cpp``Global.cpp`两个核心文件。`GeoMative.cpp`定义了`CGeoMativeApp`类,该类继承自`CWinApp`,负责应用程序的初始化、运行和退出。它在`InitInstance`方法中创建并初始化所有核心管理器对象,并建立与数据库的连接。`Global.cpp`则定义了全局变量和函数,如`theApp`全局应用程序对象实例、`aDevLinkTable`设备链接表以及各种工具函数。
### Managers目录
`Managers`目录包含了系统的核心业务逻辑组件,即各种管理器(Manager)。这些管理器负责处理特定领域的业务逻辑和数据持久化。
- **DevManager**:设备管理器,负责管理所有设备的生命周期,包括设备的注册、连接、状态更新和信息获取。它通过`m_devLinkList`链表维护所有设备对象,并提供`GetDevice``GetDeviceByID`等方法来检索设备。
- **ProManager**:项目管理器,负责管理项目(Project)和测区(TestingZone)的层次结构。它通过`m_dmsLinkList`链表管理数据管理结构(DMS),并提供`ShowProList``ShowTzList`等方法来展示项目和测区列表。
- **SptManager**:脚本管理器,负责管理脚本(Script)的创建、加载和同步。
- **TdManager**:测试数据管理器,负责管理测试数据(Testing Data)的存储和检索。
- **ExecManager**:执行管理器,负责管理执行任务和流程。
- **IOManager**I/O管理器,负责处理输入输出操作。
**Section sources**
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L1-L200)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L1-L200)
### Operator目录
`Operator`目录包含了具体操作的实现类。这些类通常封装了复杂的业务逻辑或与外部系统的交互。例如,`DataOperator.cpp`实现了数据操作相关的功能,如文件导入导出、数据同步等。`DevOperator.cpp``SptOperator.cpp`则分别处理设备和脚本的具体操作。
### ProblemZone目录
`ProblemZone`目录包含了系统的核心数据模型,这些模型代表了业务领域中的实体。
- **Project**:项目类,继承自`CDataMngStruct`,代表一个工程项目,包含项目名称、描述、位置等属性。
- **Device**:设备类,代表一个物理设备,包含设备序列号、型号、软硬件版本、MAC地址等属性,并提供与设备通信的方法。
- **Script**:脚本类,代表一个测量脚本,包含电极数量、测量类型等属性,并提供显示脚本内容和详情的方法。
- **TestingZone**:测区类,代表项目下的一个具体测量区域。
**Section sources**
- [Project.h](file://h/Project.h#L1-L41)
- [Device.h](file://h/Device.h#L1-L128)
- [Script.h](file://h/Script.h#L1-L39)
### Views目录
`Views`目录包含了所有用户界面(UI)的实现,包括主框架窗口、各种管理窗口和对话框。
- **MainFrm**:主框架窗口,是应用程序的主界面,负责管理MDI子窗口的创建和布局。
- **devmngframe**:设备管理框架,用于显示和管理设备。
- **datamngframe**:数据管理框架,用于显示和管理项目、测区和测试数据。
- **sptmngframe**:脚本管理框架,用于显示和管理脚本。
- **各种对话框**:如`DailLogin`(登录对话框)、`OpCreateProjectDlg`(创建项目对话框)等,用于用户交互。
**Section sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L1-L200)
- [MainFrm.h](file://h/MainFrm.h#L1-L120)
## 应用程序对象与管理器协调
`CGeoMativeApp`应用程序对象是整个系统的协调中心。它在`InitInstance`方法中创建并初始化所有核心管理器对象,并将它们作为成员变量存储。这些管理器通过`m_pConnection`共享同一个数据库连接,确保了数据的一致性。
```mermaid
classDiagram
class CGeoMativeApp {
+CDevManager* m_pDevManager
+CTdManager* m_pTdManager
+CProManager* m_pProManager
+CExecManager* m_pExecManager
+CSptManager* m_pSptManager
+CIOManager* m_pIOManager
+_ConnectionPtr m_pConnection
+InitInstance() bool
+ExitInstance() int
}
class CDevManager {
+CLinkList<CDevice*> m_devLinkList
+_ConnectionPtr m_pConnection
+GetDevice(DWORD) CDevice*
+InitialDevLinkList() void
}
class CProManager {
+CLinkList<CDataMngStruct*> m_dmsLinkList
+_ConnectionPtr m_pConnection
+GetDMS(DWORD) CDataMngStruct*
+ShowProList(DWORD, CListCtrl&) bool
}
class CDevice {
+DWORD m_dwID
+UINT m_uState
+CString m_szDevSN
+CString m_szMacAddress
+_ConnectionPtr m_pConnection
+GetDevInfo() bool
+Reset() bool
}
class CProject {
+DWORD m_dwID
+CString m_szPRname
+CString m_szDesc
+_ConnectionPtr m_pConnection
+ShowDetailInfo(CListCtrl&) bool
}
CGeoMativeApp --> CDevManager : "拥有"
CGeoMativeApp --> CProManager : "拥有"
CGeoMativeApp --> CDevice : "通过m_pDevManager访问"
CGeoMativeApp --> CProject : "通过m_pProManager访问"
CDevManager --> CDevice : "管理"
CProManager --> CProject : "管理"
CDevManager --> _ConnectionPtr : "使用"
CProManager --> _ConnectionPtr : "使用"
CGeoMativeApp --> _ConnectionPtr : "拥有"
```
**Diagram sources**
- [GeoMative.h](file://h/GeoMative.h#L115-L182)
- [DevManager.h](file://h/DevManager.h#L16-L68)
- [ProManager.h](file://h/ProManager.h#L30-L76)
- [Device.h](file://h/Device.h#L33-L127)
- [Project.h](file://h/Project.h#L16-L40)
## 常量与枚举的广泛应用
`Constant.h`头文件定义了系统中广泛使用的常量和枚举,为代码提供了清晰的语义和类型安全。
- **语言常量**`LANG_ZHCN``LANG_ENUS`用于标识当前用户界面语言。
- **状态常量**`PZ_STATE_OFFLINE``PZ_STATE_ONLINE`等用于表示设备、项目等实体的状态。
- **样式常量**`PZ_STYLE_PRO``PZ_STYLE_TZ``PZ_STYLE_DEV`等用于区分不同类型的实体。
- **枚举**`EN_TRANSFER_FILE_METHOD`枚举定义了文件传输方式(COM、USB、云端、WiFi),`EN_CHANNEL_INFO`枚举定义了通道信息(单通道、多通道)。
这些常量和枚举在系统各处被引用,例如在`CGeoMativeApp``InitInstance`方法中,根据`g_iTransFileMode`(其值来自`EN_TRANSFER_FILE_METHOD`)决定初始化不同的网络连接逻辑。
**Section sources**
- [Constant.h](file://h/Constant.h#L1-L266)
## 模块间调用关系示例
用户操作通过UI层触发Manager层的业务逻辑处理,这是一个典型的模块间调用流程。以用户在主界面点击“设备管理”菜单为例:
1. **UI层 (Views)**`MainFrm.cpp`中的`OnMngDevWin`消息处理函数被调用。
2. **协调层 (Main)**`OnMngDevWin`函数负责创建或激活`CDevMngFrame`设备管理窗口。
3. **业务逻辑层 (Managers)**`CDevMngFrame`在初始化时,会调用`CGeoMativeApp`的全局实例`theApp`中的`m_pDevManager`管理器的`ShowTzList``GetDevice`等方法来获取设备数据。
4. **数据模型层 (ProblemZone)**`CDevManager`通过其`m_devLinkList`链表或直接查询数据库,获取`CDevice`对象的数据。
5. **返回UI层**:获取的数据被返回给`CDevMngFrame`,并在其UI控件(如`CListCtrl`)中显示。
```mermaid
sequenceDiagram
participant User as 用户
participant MainFrm as MainFrm.cpp
participant DevMngFrame as CDevMngFrame
participant DevManager as CDevManager
participant Device as CDevice
User->>MainFrm : 点击“设备管理”
MainFrm->>DevMngFrame : 创建/激活设备管理窗口
DevMngFrame->>DevManager : 调用ShowTzList(dwProHandle, tzList)
DevManager->>DevManager : 查询数据库获取测区列表
DevManager->>Device : 通过m_devLinkList.Find()获取设备
Device-->>DevManager : 返回CDevice对象
DevManager-->>DevMngFrame : 返回测区和设备数据
DevMngFrame-->>MainFrm : 在UI上显示数据
MainFrm-->>User : 显示设备管理界面
```
**Diagram sources**
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L58-L94)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp#L48-L80)
- [ProManager.cpp](file://cpp/Managers/ProManager.cpp#L53-L92)
## 系统架构图
```mermaid
graph TD
subgraph "UI层 (Views)"
A[MainFrm]
B[CDevMngFrame]
C[CDataMngFrame]
D[对话框]
end
subgraph "协调层 (Main)"
E[CGeoMativeApp]
end
subgraph "业务逻辑层 (Managers)"
F[CDevManager]
G[CProManager]
H[CSptManager]
I[CTdManager]
end
subgraph "数据模型层 (ProblemZone)"
J[CDevice]
K[CProject]
L[CScript]
M[CTestingZone]
end
subgraph "数据访问层"
N[数据库]
end
A --> E
B --> E
C --> E
D --> E
E --> F
E --> G
E --> H
E --> I
F --> J
G --> K
G --> M
H --> L
F --> N
G --> N
H --> N
I --> N
J --> N
K --> N
L --> N
M --> N
```
**Diagram sources**
- [GeoMative.cpp](file://cpp/Main/GeoMative.cpp#L56-L67)
- [DevManager.h](file://h/DevManager.h#L63-L65)
- [ProManager.h](file://h/ProManager.h#L72-L73)
- [Project.h](file://h/Project.h#L24)
- [Device.h](file://h/Device.h#L103)
@@ -0,0 +1,339 @@
# 开发工具与实用程序
<cite>
**本文档引用的文件**
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp)
- [SComPort.h](file://h\SComPort.h)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp)
- [TcpClient.h](file://h\TcpClient.h)
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp)
- [Zmodem.h](file://h\Zmodem.h)
- [Crc16.cpp](file://cpp\Tools\Crc16.cpp)
- [Crc16.h](file://h\Crc16.h)
- [Crc32.cpp](file://cpp\Tools\Crc32.cpp)
- [Crc32.h](file://h\Crc32.h)
- [FileOperTools.cpp](file://cpp\Tools\FileOperTools.cpp)
- [FileOperTools.h](file://h\FileOperTools.h)
- [GD10OperCmd.cpp](file://cpp\Tools\GD10OperCmd.cpp)
- [GD10OperCmd.h](file://h\GD10OperCmd.h)
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp)
- [NetWorkOper.h](file://h\NetWorkOper.h)
- [GUCodeCreator.cpp](file://cpp\Tools\GUCodeCreator.cpp)
- [GUCodeCreator.h](file://h\GUCodeCreator.h)
- [excel.cpp](file://cpp\Tools\excel.cpp)
- [excel.h](file://h\excel.h)
- [checkupdate.cpp](file://cpp\Tools\checkupdate.cpp)
- [checkupdate.h](file://h\checkupdate.h)
</cite>
## 目录
1. [通信相关工具](#通信相关工具)
2. [数据处理工具](#数据处理工具)
3. [设备交互工具](#设备交互工具)
4. [辅助工具](#辅助工具)
5. [调用示例与最佳实践](#调用示例与最佳实践)
## 通信相关工具
### SComPort.cpp - 串口通信
`SComPort.cpp` 文件实现了 `CSComPort` 类,用于处理串口通信。该类提供了串口的打开、关闭、数据发送和接收等基本功能。主要功能包括:
- **串口管理**:通过 `OpenComm``CloseComm` 方法管理串口的打开和关闭。
- **数据传输**:提供 `SendDataDirectly``ReceiveDataDirectly` 方法进行数据的直接发送和接收。
- **事件处理**:支持通过 `SetOwnerWnd``SetCommID` 设置所有者窗口和通信ID,以便在串口事件发生时进行通知。
**Section sources**
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L1-L1199)
- [SComPort.h](file://h\SComPort.h#L1-L74)
### TcpClient.cpp - TCP网络通信
`TcpClient.cpp` 文件实现了 `CTcpClient` 类,用于处理TCP网络通信。该类提供了TCP连接的初始化、连接、断开连接、数据发送和接收等功能。主要功能包括:
- **连接管理**:通过 `InitailTcp``ConnectToServer` 方法初始化和建立TCP连接。
- **数据传输**:提供 `SendData``RecvData` 方法进行数据的发送和接收。
- **连接状态检查**:通过 `GetConnectStatus` 方法检查连接状态,确保通信的可靠性。
**Section sources**
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
- [TcpClient.h](file://h\TcpClient.h#L1-L76)
### Zmodem.cpp - Zmodem协议文件传输
`Zmodem.cpp` 文件实现了 `Zmodem` 类,用于实现Zmodem协议的文件传输。该类提供了文件的发送和接收功能,支持断点续传。主要功能包括:
- **文件发送**:通过 `Send` 方法发送文件,支持批量文件传输。
- **文件接收**:通过 `Receive` 方法接收文件,支持断点续传。
- **协议处理**:内部实现了Zmodem协议的各种帧类型处理,如 `ZRQINIT``ZRINIT``ZFILE` 等。
**Section sources**
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L1-L1940)
- [Zmodem.h](file://h\Zmodem.h#L1-L143)
## 数据处理工具
### Crc16.cpp 和 Crc32.cpp - 数据校验
`Crc16.cpp``Crc32.cpp` 文件分别实现了 `Crc16``Crc32` 类,用于提供数据校验功能。这些类通过计算CRC(循环冗余校验)值来确保数据的完整性。主要功能包括:
- **CRC16**`Crc16` 类提供16位CRC校验,适用于较小的数据块。
- **CRC32**`Crc32` 类提供32位CRC校验,适用于较大的数据块。
**Section sources**
- [Crc16.cpp](file://cpp\Tools\Crc16.cpp#L1-L54)
- [Crc16.h](file://h\Crc16.h#L1-L37)
- [Crc32.cpp](file://cpp\Tools\Crc32.cpp#L1-L51)
- [Crc32.h](file://h\Crc32.h#L1-L37)
### FileOperTools.cpp - 文件操作封装
`FileOperTools.cpp` 文件实现了 `CFileOperTools` 类,提供了一系列文件操作的封装方法。主要功能包括:
- **文件复制**:通过 `CopyFolder` 方法复制整个文件夹。
- **文件删除**:通过 `DeleteFileDirect``DeleteDirectory` 方法删除文件和目录。
- **日志记录**:通过 `WriteComLog` 方法记录通信日志,支持多线程安全。
**Section sources**
- [FileOperTools.cpp](file://cpp\Tools\FileOperTools.cpp#L1-L420)
- [FileOperTools.h](file://h\FileOperTools.h#L1-L37)
## 设备交互工具
### GD10OperCmd.cpp - GD10设备通信指令
`GD10OperCmd.cpp` 文件实现了 `CGD10OperCmd` 类,定义了与GD10设备的通信指令。主要功能包括:
- **工程管理**:通过 `project_add``project_delete` 方法添加和删除工程。
- **测区管理**:通过 `testzone_add``testzone_delete` 方法添加和删除测区。
- **脚本管理**:通过 `script_add``script_delete` 方法添加和删除脚本。
**Section sources**
- [GD10OperCmd.cpp](file://cpp\Tools\GD10OperCmd.cpp#L1-L1213)
- [GD10OperCmd.h](file://h\GD10OperCmd.h#L1-L37)
### NetWorkOper.cpp - 网络操作
`NetWorkOper.cpp` 文件实现了 `CNetWorkOper` 类,处理网络操作。主要功能包括:
- **连接管理**:通过 `StartConnect` 方法建立与服务器的连接。
- **数据传输**:通过 `TransferOper` 方法进行数据的转发。
- **实时数据接收**:通过 `ThreadFunction` 方法在后台线程中接收实时数据。
**Section sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [NetWorkOper.h](file://h\NetWorkOper.h#L1-L37)
## 辅助工具
### GUCodeCreator.cpp - GU码生成
`GUCodeCreator.cpp` 文件实现了 `CGUCodeCreator` 类,用于生成GU码。主要功能包括:
- **MAC地址获取**:通过 `GetMacAddress` 方法获取本机的MAC地址。
- **时间戳生成**:通过 `GetTotalSecond` 方法获取当前时间的总秒数。
- **GU码生成**:通过 `GenerateGUCode` 方法生成GU码,结合MAC地址和时间戳。
**Section sources**
- [GUCodeCreator.cpp](file://cpp\Tools\GUCodeCreator.cpp#L1-L395)
- [GUCodeCreator.h](file://h\GUCodeCreator.h#L1-L37)
### excel.cpp - Excel文件导出
`excel.cpp` 文件实现了与Excel文件的交互,主要用于数据导出。主要功能包括:
- **Excel操作**:通过 `_Application` 类提供的方法进行Excel文件的创建、打开、保存等操作。
- **数据写入**:通过 `Range``Cells` 方法将数据写入Excel文件。
**Section sources**
- [excel.cpp](file://cpp\Tools\excel.cpp#L1-L7130)
- [excel.h](file://h\excel.h#L1-L37)
### checkupdate.cpp - 版本检查
`checkupdate.cpp` 文件实现了 `Ccheckupdate` 类,用于版本检查和更新。主要功能包括:
- **版本检查**:通过 `checkGeoMaive` 方法检查当前版本是否需要更新。
- **文件下载**:通过 `RequestFileContent` 方法从服务器下载文件内容。
- **更新操作**:通过 `UpdateAppToDev``UpdateGD10Dev` 方法执行更新操作。
**Section sources**
- [checkupdate.cpp](file://cpp\Tools\checkupdate.cpp#L1-L1523)
- [checkupdate.h](file://h\checkupdate.h#L1-L37)
## 调用示例与最佳实践
### 通信相关工具调用示例
#### 串口通信
```cpp
CSComPort comPort;
if (comPort.OpenComm("COM1")) {
char sendData[] = "Hello, World!";
comPort.SendDataDirectly(sendData, strlen(sendData));
char receiveData[1024];
int receiveSize;
if (comPort.ReceiveDataDirectly(receiveData, &receiveSize)) {
// 处理接收到的数据
}
comPort.CloseComm();
}
```
#### TCP网络通信
```cpp
CTcpClient tcpClient;
if (tcpClient.InitailTcp()) {
if (tcpClient.ConnectToServer("192.168.1.1", 8080)) {
char sendData[] = "Hello, Server!";
tcpClient.SendData(sendData, strlen(sendData));
char receiveData[1024];
int actualLen;
if (tcpClient.RecvData(receiveData, 1024, actualLen)) {
// 处理接收到的数据
}
tcpClient.CloseConnect();
}
}
```
#### Zmodem协议文件传输
```cpp
CSComPort comPort;
Zmodem zmodem(&comPort);
if (comPort.OpenComm("COM1")) {
char *files[] = {"file1.txt", "file2.txt", NULL};
if (zmodem.Send(files)) {
// 文件发送成功
}
if (zmodem.Receive("C:\\ReceivedFiles\\")) {
// 文件接收成功
}
comPort.CloseComm();
}
```
### 数据处理工具调用示例
#### CRC16和CRC32校验
```cpp
Crc16 crc16(0);
Crc32 crc32(0xFFFFFFFFL);
char data[] = "Hello, World!";
for (int i = 0; i < strlen(data); i++) {
crc16.update(data[i]);
crc32.update(data[i]);
}
unsigned short crc16Value = crc16.value();
unsigned long crc32Value = crc32.value();
```
#### 文件操作
```cpp
CFileOperTools fileTools;
if (fileTools.CopyFolder("C:\\SourceFolder", "D:\\DestinationFolder")) {
// 文件夹复制成功
}
if (fileTools.DeleteDirectory("D:\\OldFolder")) {
// 文件夹删除成功
}
CString logMessage = "This is a log message.";
fileTools.WriteComLog(logMessage);
```
### 设备交互工具调用示例
#### GD10设备通信
```cpp
CGD10OperCmd gd10Cmd;
if (gd10Cmd.project_add("00:11:22:33:44:55", "MyProject")) {
// 工程添加成功
}
if (gd10Cmd.testzone_add("MyProject", "MyTestZone", "Type1")) {
// 测区添加成功
}
if (gd10Cmd.script_add("VES123", "Script1", "0")) {
// 脚本添加成功
}
```
#### 网络操作
```cpp
CNetWorkOper netOper;
if (netOper.Initialize()) {
if (netOper.StartConnect("192.168.1.1", 8080)) {
if (netOper.StartWork()) {
// 网络操作开始
}
}
}
```
### 辅助工具调用示例
#### GU码生成
```cpp
CGUCodeCreator guCodeCreator;
CString guCode = guCodeCreator.GenerateGUCode();
```
#### Excel文件导出
```cpp
_Application excelApp;
excelApp.CreateDispatch("Excel.Application");
excelApp.SetVisible(TRUE);
_Workbook workbook = excelApp.GetWorkbooks().Add();
_Worksheet worksheet = workbook.GetWorksheets().GetItem(1);
Range range = worksheet.GetRange("A1");
range.SetValue2("Hello, Excel!");
workbook.SaveAs("C:\\ExportedFile.xlsx");
workbook.Close();
excelApp.Quit();
```
#### 版本检查
```cpp
Ccheckupdate checkUpdate;
if (checkUpdate.checkGeoMaive()) {
// 需要更新
if (checkUpdate.DownLoadFileList()) {
if (checkUpdate.UpdateGD10Dev()) {
// 更新成功
}
}
}
```
**Section sources**
- [SComPort.cpp](file://cpp\Tools\SComPort.cpp#L1-L1199)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
- [Zmodem.cpp](file://cpp\Tools\Zmodem.cpp#L1-L1940)
- [Crc16.cpp](file://cpp\Tools\Crc16.cpp#L1-L54)
- [Crc32.cpp](file://cpp\Tools\Crc32.cpp#L1-L51)
- [FileOperTools.cpp](file://cpp\Tools\FileOperTools.cpp#L1-L420)
- [GD10OperCmd.cpp](file://cpp\Tools\GD10OperCmd.cpp#L1-L1213)
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [GUCodeCreator.cpp](file://cpp\Tools\GUCodeCreator.cpp#L1-L395)
- [excel.cpp](file://cpp\Tools\excel.cpp#L1-L7130)
- [checkupdate.cpp](file://cpp\Tools\checkupdate.cpp#L1-L1523)
@@ -0,0 +1,179 @@
# 数据处理工具
<cite>
**Referenced Files in This Document**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc16.h](file://h/Crc16.h)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [Crc32.h](file://h/Crc32.h)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [FileOperTools.h](file://h/FileOperTools.h)
- [Markup.cpp](file://cpp/Tools/Markup.cpp)
- [Markup.h](file://h/Markup.h)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperTxtFile.h](file://h/OperTxtFile.h)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
- [OperUrfFile.h](file://h/OperUrfFile.h)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp)
- [Res3DDatFile.h](file://h/Res3DDatFile.h)
</cite>
## 目录
1. [引言](#引言)
2. [数据完整性校验工具](#数据完整性校验工具)
3. [文件操作工具](#文件操作工具)
4. [XML配置文件处理](#xml配置文件处理)
5. [特定格式数据文件处理](#特定格式数据文件处理)
6. [三维测量结果数据处理](#三维测量结果数据处理)
7. [应用场景与使用示例](#应用场景与使用示例)
8. [内存管理与性能优化](#内存管理与性能优化)
9. [错误恢复策略](#错误恢复策略)
10. [结论](#结论)
## 引言
GeomativeStudio中的数据处理工具集为地质勘探数据的完整性校验、文件操作、配置管理及数据存储提供了全面的解决方案。这些工具在项目配置保存、测量数据导出和缓存管理等关键场景中发挥着重要作用。本文档将深入解析Crc16.cpp和Crc32.cpp实现的数据完整性校验机制,FileOperTools.cpp封装的文件操作接口,Markup.cpp对XML配置文件的解析与生成逻辑,以及OperTxtFile.cpp、OperUrfFile.cpp和Res3DDatFile.cpp对特定格式数据文件的读写实现。
## 数据完整性校验工具
### CRC16校验实现
Crc16类实现了基于查表法的CRC16校验算法,通过预计算的256项查找表优化性能。该类采用静态成员变量`table`存储预计算的校验值,并使用`initialized`标志确保表只初始化一次。构造函数接收初始值,通过位运算和多项式0x1021进行校验计算。`update`方法对每个输入字节进行处理,利用查表法快速更新校验值,避免了重复的多项式除法运算。
**Section sources**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L28-L54)
- [Crc16.h](file://h/Crc16.h#L12-L37)
### CRC32校验实现
Crc32类实现了CRC32校验算法,同样采用查表优化技术。与CRC16类似,它使用静态`table`数组存储预计算值,并通过`initialized`标志控制初始化。构造函数使用多项式0xEDB88320L进行表的生成,该多项式是CRC32标准算法的核心。`update`方法通过查表和位移操作高效更新校验值,确保数据完整性验证的高性能。
**Section sources**
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L28-L51)
- [Crc32.h](file://h/Crc32.h#L12-L37)
## 文件操作工具
### 文件操作接口封装
CFileOperTools类封装了常用的文件操作功能,提供了一个单例模式的全局访问点。该类实现了目录复制、文件存在性检查、文件删除、日志写入等核心功能。`CopyFolder`方法递归复制目录及其内容,`DeleteDirectory`方法递归删除目录树,确保了文件系统操作的完整性和可靠性。
**Section sources**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L58-L124)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L261-L323)
- [FileOperTools.h](file://h/FileOperTools.h#L12-L42)
### 文件锁定与日志机制
该工具类实现了线程安全的日志写入机制,使用临界区(CRITICAL_SECTION)保护日志文件的并发访问。`WriteComLog`方法在写入日志前获取临界区锁,确保多线程环境下的数据一致性。同时,类提供了`CloseComLog`方法正确释放资源,防止内存泄漏。`DealGeneralLogFunc`方法创建后台线程定期清理过期日志文件,实现了自动化的日志管理。
**Section sources**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L21-L37)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L150-L197)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L345-L398)
## XML配置文件处理
### XML解析与生成逻辑
CMarkup类提供了完整的XML文档处理能力,支持文档加载、元素查找、属性读取、内容修改等操作。该类采用内部状态机解析XML文档,通过`FindElem`方法定位元素,`GetAttrib``GetData`方法提取属性和内容。`AddElem``SetData`方法允许动态修改文档结构,支持创建和更新XML配置文件。
**Section sources**
- [Markup.cpp](file://cpp/Tools/Markup.cpp#L265-L291)
- [Markup.h](file://h/Markup.h#L276-L305)
### 编码转换与字符处理
CMarkup类内置了强大的编码转换功能,支持UTF-8、UTF-16、UTF-32等多种编码格式的相互转换。通过`TextEncoding`结构体和相关方法,实现了跨平台的字符集处理。`x_GetEncodingCodePage`函数使用哈希表快速查找编码对应的代码页,`PerformConversion`方法执行实际的编码转换,确保XML文档在不同环境下的正确读写。
**Section sources**
- [Markup.cpp](file://cpp/Tools/Markup.cpp#L183-L764)
- [Markup.h](file://h/Markup.h#L387-L394)
## 特定格式数据文件处理
### 文本文件操作
COperTxtFile类专门处理文本文件的读写操作,提供了参数化宽度的文本写入功能。`SetParamWidth`方法设置输出字段宽度,`WriteFileContent`方法将字符串数组按指定宽度格式化写入文件,不足部分用空格填充。该类还实现了错误检查和日志记录,确保文件操作的可靠性和可追溯性。
**Section sources**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L39-L57)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L104-L158)
- [OperTxtFile.h](file://h/OperTxtFile.h#L12-L32)
### URF文件处理
COperUrfFile类负责URFUniversal Resistivity data File)格式文件的生成,支持多种电极阵列配置。该类提供了`WriteElecByAR`系列方法,根据不同的装置类型(如温纳-施伦贝格尔、跨孔等)生成相应的电极坐标信息。`WriteUrfHeadInfo`方法创建文件头,包含格式说明和单位信息,确保文件的标准化和可读性。
**Section sources**
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L64-L77)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L506-L538)
- [OperUrfFile.h](file://h/OperUrfFile.h#L12-L52)
## 三维测量结果数据处理
### 三维数据文件结构
CRes3DDatFile类处理三维测量结果数据的结构化存储,采用特定的二进制文件格式。该类定义了文件头、记录和文件尾三部分结构,通过`WriteHead``WriteRecord``WriteTail`方法分别写入。文件头包含标题、网格尺寸、间距和介质类型等元数据,记录部分存储具体的测量点数据。
**Section sources**
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L189-L257)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L260-L322)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L325-L351)
- [Res3DDatFile.h](file://h/Res3DDatFile.h#L28-L31)
### 数据记录管理
该类使用CPtrArray管理测量记录,通过`AddRecord`方法添加新的测量点,`DelRecord``ClearAllRecord`方法删除记录。`Generate`方法协调整个文件生成过程,先创建文件,然后依次写入头部、记录和尾部信息。`SetMedium`方法将用户友好的介质代码转换为内部表示,确保数据的一致性和正确性。
**Section sources**
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L100-L165)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L353-L386)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L168-L187)
## 应用场景与使用示例
### 项目配置保存
在项目配置保存场景中,Markup类用于序列化应用程序设置到XML文件。通过`SetDoc`方法加载现有配置或创建新文档,使用`AddElem``AddAttrib`方法添加配置项,最后调用`Save`方法持久化到磁盘。这种机制确保了配置数据的结构化存储和跨会话持久性。
**Section sources**
- [Markup.cpp](file://cpp/Tools/Markup.cpp#L344-L346)
- [Markup.cpp](file://cpp/Tools/Markup.cpp#L346-L348)
### 测量数据导出
测量数据导出功能结合使用OperTxtFile和OperUrfFile类,将采集的测量结果转换为标准格式。对于文本报告,使用OperTxtFile按固定宽度格式化输出;对于专业分析,使用OperUrfFile生成URF格式文件,包含电极配置和测量数据。这种多格式支持满足了不同用户和软件的需求。
**Section sources**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L104-L158)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L506-L538)
### 缓存管理
缓存管理利用FileOperTools类的目录操作功能,实现缓存文件的自动清理。`DealGeneralLogThread`方法扫描日志目录,识别并删除两天前的旧文件,防止磁盘空间耗尽。`CopyFolder``DeleteDirectory`方法支持缓存的备份和重置,确保系统稳定运行。
**Section sources**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L346-L398)
## 内存管理与性能优化
### 查表法优化
CRC校验算法采用查表法进行性能优化,将复杂的多项式除法运算转换为简单的查表操作。Crc16和Crc32类在首次使用时预计算256个可能的余数,存储在静态数组中。后续计算只需一次查表和几次位运算,大大提高了处理速度,特别适合处理大量数据的场景。
**Section sources**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L38-L50)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L38-L47)
### 资源管理
所有文件操作类都实现了正确的资源管理,确保文件句柄和内存的及时释放。析构函数中调用`CloseFile``CloseComLog`关闭文件,`ClearAllRecord`方法释放动态分配的记录对象。这种RAII(资源获取即初始化)模式防止了资源泄漏,提高了系统的稳定性和可靠性。
**Section sources**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L27-L35)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp#L26-L29)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp#L35-L36)
## 错误恢复策略
### 异常处理与日志记录
数据处理工具集实现了全面的错误检测和恢复机制。每个关键操作都包含输入验证和边界检查,如`OpenFileforWrite`检查文件名是否为空。操作失败时,通过`WriteComLog`记录详细的错误信息,包括错误码和上下文,便于问题诊断和恢复。
**Section sources**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp#L69-L97)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L150-L197)
### 事务性操作
对于复杂的文件操作,如目录复制和删除,采用事务性设计原则。操作过程中持续检查状态,一旦发现错误立即停止并记录,避免部分完成的状态。`CopyFolder`方法在复制每个文件前检查源和目标路径,`DeleteDirectory`方法在删除每个子项前验证其存在性,确保操作的原子性和一致性。
**Section sources**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L58-L124)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L261-L323)
## 结论
GeomativeStudio的数据处理工具集通过精心设计的类和方法,提供了高效、可靠的数据完整性校验、文件操作和数据存储功能。CRC校验工具采用查表法优化性能,文件操作工具确保线程安全和资源正确管理,XML处理工具支持复杂的配置管理,特定格式文件处理工具满足专业数据交换需求。这些工具协同工作,为地质勘探应用提供了坚实的数据处理基础,确保了数据的完整性、一致性和可追溯性。
@@ -0,0 +1,204 @@
# 数据校验工具
<cite>
**本文档引用的文件**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc16.h](file://h/Crc16.h)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [Crc32.h](file://h/Crc32.h)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
本文档深入解析GeomativeStudio中实现的数据完整性校验机制,重点分析CRC16和CRC32算法的具体实现方式。文档详细说明了标准多项式选择、查表法优化策略、字节序处理及内存对齐优化等关键技术,并阐述这些校验工具在设备通信协议数据包、配置文件传输和测量数据存储中的应用场景。
## 项目结构
GeomativeStudio项目中的CRC校验工具位于cpp/Tools目录下,包含Crc16.cpp和Crc32.cpp两个核心实现文件,以及对应的头文件Crc16.h和Crc32.h。这些工具被集成在Zmodem文件传输协议中,用于确保数据传输的完整性。
```mermaid
graph TD
A[CRC校验工具] --> B[Crc16.cpp]
A --> C[Crc32.cpp]
A --> D[Crc16.h]
A --> E[Crc32.h]
B --> F[Zmodem.cpp]
C --> F
F --> G[文件传输]
F --> H[数据通信]
```
**图示来源**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1-L1940)
**章节来源**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
## 核心组件
CRC16和CRC32类实现了数据完整性校验的核心功能。Crc16类使用16位循环冗余校验,而Crc32类使用32位循环冗余校验。两个类都采用了查表法优化策略,通过预计算的CRC表来提高计算效率。
**章节来源**
- [Crc16.h](file://h/Crc16.h#L1-L37)
- [Crc32.h](file://h/Crc32.h#L1-L37)
## 架构概述
CRC校验工具的架构设计采用了静态查表法,通过预计算的CRC表来加速校验计算过程。初始化时生成CRC表,后续计算直接查表获取结果,大大提高了计算效率。该架构被设计为轻量级工具类,可被多个模块复用。
```mermaid
classDiagram
class Crc16 {
+static unsigned short table[256]
+static int initialized
-unsigned short crc
+Crc16(unsigned short init_value)
+void update(int c)
+unsigned short value()
}
class Crc32 {
+static unsigned long table[256]
+static int initialized
-unsigned long crc
+Crc32(unsigned long init_value)
+void update(int c)
+unsigned long value()
}
Crc16 <|-- Crc32 : 继承自
```
**图示来源**
- [Crc16.h](file://h/Crc16.h#L12-L27)
- [Crc32.h](file://h/Crc32.h#L12-L28)
## 详细组件分析
### CRC16组件分析
CRC16类实现了16位循环冗余校验算法,采用CCITT标准多项式x^16 + x^12 + x^5 + 10x1021)。该实现使用查表法优化,通过预计算256个字节的CRC值来加速计算过程。
#### CRC16类图
```mermaid
classDiagram
class Crc16 {
+static unsigned short table[256]
+static int initialized
-unsigned short crc
+Crc16(unsigned short init_value)
+void update(int c)
+unsigned short value()
}
```
**图示来源**
- [Crc16.h](file://h/Crc16.h#L12-L27)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L28-L54)
### CRC32组件分析
CRC32类实现了32位循环冗余校验算法,采用IEEE 802.3标准多项式x^32 + x^26 + x^23 + x^22 + x^16 + x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x + 10xEDB88320)。与CRC16类似,也采用了查表法优化策略。
#### CRC32类图
```mermaid
classDiagram
class Crc32 {
+static unsigned long table[256]
+static int initialized
-unsigned long crc
+Crc32(unsigned long init_value)
+void update(int c)
+unsigned long value()
}
```
**图示来源**
- [Crc32.h](file://h/Crc32.h#L12-L28)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L28-L51)
### 实际应用分析
CRC校验工具在Zmodem文件传输协议中得到了广泛应用,用于确保数据传输的完整性。在文件传输过程中,发送方计算数据的CRC值并随数据一起发送,接收方重新计算CRC值进行比对,以检测传输过程中的任何数据损坏。
#### Zmodem协议中的CRC使用流程
```mermaid
sequenceDiagram
participant 发送方
participant 接收方
发送方->>发送方 : 初始化CRC32(0xFFFFFFFFL)
发送方->>发送方 : 计算数据CRC值
发送方->>接收方 : 发送数据和CRC校验码
接收方->>接收方 : 初始化CRC32(0xFFFFFFFFL)
接收方->>接收方 : 计算接收到数据的CRC值
接收方->>接收方 : 比对CRC值
alt CRC匹配
接收方->>发送方 : 发送确认
else CRC不匹配
接收方->>发送方 : 请求重传
end
```
**图示来源**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L797-L820)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1643-L1670)
**章节来源**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L790-L890)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1640-L1700)
## 依赖分析
CRC校验工具作为独立的工具类,被Zmodem文件传输模块所依赖。这种设计实现了关注点分离,使得CRC校验功能可以被多个模块复用,同时保持了代码的可维护性和可测试性。
```mermaid
graph TD
A[Zmodem.cpp] --> B[Crc16.cpp]
A --> C[Crc32.cpp]
B --> D[Crc16.h]
C --> E[Crc32.h]
```
**图示来源**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L5-L6)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L5-L6)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L5-L6)
## 性能考虑
CRC校验工具的性能优化主要体现在以下几个方面:
1. 查表法:通过预计算CRC表,将复杂的多项式除法运算转换为简单的查表操作
2. 静态初始化:CRC表只在首次使用时生成,避免重复计算
3. 内联函数:update方法被定义为内联函数,减少函数调用开销
4. 位运算优化:使用位移和异或操作替代乘除法运算
这些优化策略使得CRC计算具有很高的效率,适合在高频率数据传输场景中使用。
## 故障排除指南
在使用CRC校验工具时,可能遇到以下常见问题及解决方案:
1. **CRC计算结果不一致**
- 检查初始化值是否一致
- 确认多项式标准是否匹配
- 验证字节序处理是否正确
2. **性能瓶颈**
- 确认CRC表已正确初始化
- 检查是否重复创建CRC对象
- 考虑批量处理数据以减少对象创建开销
3. **内存问题**
- CRC表占用256*sizeof(unsigned short)或256*sizeof(unsigned long)内存
- 确保有足够的内存空间
**章节来源**
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L33-L52)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L33-L49)
## 结论
GeomativeStudio中的CRC校验工具实现了高效可靠的数据完整性校验机制。通过采用查表法优化和合理的架构设计,这些工具在保证计算准确性的同时,提供了优异的性能表现。在设备通信协议数据包、配置文件传输和测量数据存储等场景中,CRC校验有效防止了数据损坏,确保了系统的可靠运行。建议在高并发或大文件处理场景中复用CRC对象,以进一步优化性能。
@@ -0,0 +1,121 @@
# 数据格式处理
<cite>
**本文档引用的文件**
- [Markup.cpp](file://cpp/Tools/Markup.cpp)
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp)
- [Res3DDatFileRecord.cpp](file://cpp/Tools/Res3DDatFileRecord.cpp)
</cite>
## 目录
1. [引言](#引言)
2. [XML配置文件解析与生成](#xml配置文件解析与生成)
3. [文本测量数据读写](#文本测量数据读写)
4. [URF专有数据格式解析](#urf专有数据格式解析)
5. [三维测量结果数据存储](#三维测量结果数据存储)
6. [应用实例](#应用实例)
7. [性能优化与错误容忍](#性能优化与错误容忍)
8. [结论](#结论)
## 引言
Geomative Studio项目涉及多种数据格式的处理,包括用于配置的XML文件、用于测量数据交换的文本文件、专有的URF数据格式以及用于存储三维测量结果的二进制DAT文件。本文件详细阐述了`Markup.cpp``OperTxtFile.cpp``OperUrfFile.cpp``Res3DDatFile.cpp`四个核心模块的实现逻辑,分析其在项目缓存、任务脚本导出和结果文件生成中的应用,并讨论其性能优化与错误处理策略。
## XML配置文件解析与生成
`Markup.cpp`模块提供了一个功能完整的XML文档解析与生成器,其核心是`CMarkup`类。该模块实现了对XML配置文件的节点遍历、属性读取、嵌套结构处理和内存管理。
### 节点遍历与属性读取
该模块通过维护一个`ElemPos`(元素位置)结构数组来高效地表示XML文档的树形结构。每个`ElemPos`结构记录了节点在文档字符串中的起始位置、长度、标签长度、层级深度以及与其他节点的父子、兄弟关系。这种设计避免了在解析时进行昂贵的字符串分割操作,而是通过索引直接定位节点内容。
对于属性读取,模块提供了`SetAttrib``GetAttrib`等方法。当需要设置属性时,它会计算属性在标签字符串中的精确位置,并使用`x_StrInsertReplace`函数进行原地插入或替换,从而最小化内存拷贝开销。
### 嵌套结构处理
模块通过`iElemParent``iElemChild``iElemNext`等指针在`ElemPos`结构中构建了完整的树形关系。这使得`FindElem``IntoElem``OutOfElem`等方法能够高效地在嵌套的XML结构中进行导航。例如,`IntoElem`会将当前上下文移动到当前元素的第一个子元素,而`OutOfElem`则会返回到父元素。
### 内存管理策略
内存管理是该模块的核心优势。它采用了一种“分段数组”(Segmented Array)的策略来管理`ElemPos`结构。`ElemPosTree`类将`ElemPos`对象存储在多个连续的内存块(段)中,而不是一个巨大的连续数组。这避免了在文档变大时重新分配和复制整个数组的性能瓶颈。`GrowElemPosTree`方法在需要时动态增加新的内存段,确保了在处理大型XML文件时的内存效率和性能稳定性。
**节段来源**
- [Markup.cpp](file://cpp/Tools/Markup.cpp#L917-L998)
## 文本测量数据读写
`OperTxtFile.cpp`模块负责处理以文本格式存储的测量数据,支持多种编码和分隔符,主要用于任务脚本和配置的导出。
### 读写实现
该模块通过标准C库的`fopen``fwrite``fclose`函数进行文件操作。`OpenFileforWrite`方法以写入模式打开文件,`WriteFileContent`方法则负责将`CStringArray`中的内容写入文件。
### 编码与分隔符支持
虽然代码中未显式处理编码转换,但通过使用`CString`类,模块天然支持Unicode(UTF-16)和多字节字符集(MBCS)。文件的最终编码取决于系统区域设置和`fopen`函数的内部行为。对于分隔符,该模块采用了一种独特的“固定宽度”格式。`SetParamWidth`方法设置每个字段的固定宽度(默认18个字符),`WriteFileContent`方法会用空格填充不足的字段,从而形成类似表格的对齐效果。这虽然不是传统的逗号或制表符分隔,但提供了一种简单、可读性强的数据格式。
**节段来源**
- [OperTxtFile.cpp](file://cpp/Tools/OperTxtFile.cpp)
## URF专有数据格式解析
`OperUrfFile.cpp`模块专门用于解析和生成URFUniversal Resistivity File)专有数据格式,这是一种用于地球物理电阻率测量的行业标准文本格式。
### 解析规则
URF文件采用纯文本格式,包含注释行、元数据行和数据行。该模块的解析规则主要体现在`WriteUrfPoleInfo``WriteUrfPoleInfo_3D`等方法中:
1. **头部信息**:通过`WriteUrfHeadInfo``Write3DUrfHeadInfo`写入文件描述和单位信息。
2. **几何信息**:以`:Geometry`为分隔符,后跟`ID,X,Y,Z`的列标题,然后是电极的坐标数据。电极坐标的生成逻辑(如温纳装置、偶极装置等)由`WriteElecByAR`系列方法根据不同的测量阵列(AR)类型实现。
3. **测量信息**:以`:Measurements`为分隔符,后跟`A, B, M, N, V/I(ohm), I(mA), Error(%), Chargeability(mV/V)`的列标题,然后是实际的测量数据。
该模块通过`WriteElecByCrossHoleGeomative`等方法从数据库(如`TTaskBindElecInfo`表)中查询电极坐标,实现了URF文件与项目数据的动态绑定。
**节段来源**
- [OperUrfFile.cpp](file://cpp/Tools/OperUrfFile.cpp)
## 三维测量结果数据存储
`Res3DDatFile.cpp`模块负责将三维测量结果以结构化的二进制布局存储到`.dat`文件中,该文件用于后续的数据处理和成像。
### 结构化存储机制
#### 二进制布局
该模块使用Windows API的`CreateFile``WriteFile`函数直接写入二进制数据。文件布局分为三个部分:
1. **头部**:包含标题、网格列数、行数、列间距、行间距、介质类型和记录点总数。所有数据均以ASCII文本形式写入,每项后跟换行符。
2. **记录数据**:包含多个测量记录。每个记录由A、B、M、N四个电极的坐标(X, Y)和电阻率值`R0`组成。坐标和`R0`均以固定格式的文本写入。
3. **尾部**:写入5个零值,作为文件结束的标记。
#### 元数据嵌入
元数据(如标题、网格尺寸、间距、介质类型)在文件头部以明文形式嵌入。`SetTitle``SetGriding``SetSpacing``SetMedium`等方法用于设置这些元数据。
#### 版本兼容性处理
代码中通过`VAL_ZERO``VAL_MINUS_ONE`等常量定义了默认值和无效值,这有助于在不同版本的软件之间保持一定的兼容性。例如,当电极ID为-1时,表示该电极未使用。然而,代码中未发现显式的版本号字段或复杂的向后/向前兼容性逻辑,其兼容性主要依赖于固定的文件结构和数据类型。
**节段来源**
- [Res3DDatFile.cpp](file://cpp/Tools/Res3DDatFile.cpp)
- [Res3DDatFileRecord.cpp](file://cpp/Tools/Res3DDatFileRecord.cpp)
## 应用实例
上述数据格式处理模块在Geomative Studio中有着广泛的应用:
- **项目缓存**`Markup.cpp`用于读写`CACHE`目录下的`project.xml``testzone.xml`文件,保存和恢复项目状态。
- **任务脚本导出**`OperTxtFile.cpp`用于生成`.map``.rul`等脚本文件,`OperUrfFile.cpp`用于将任务导出为标准的URF格式,便于与其他软件交换数据。
- **结果文件生成**`Res3DDatFile.cpp`用于将三维测量结果生成`.dat`文件,供后续的反演和成像模块使用。
## 性能优化与错误容忍策略
### 性能优化
- **内存效率**`Markup.cpp`的分段数组设计避免了大文件解析时的内存抖动。
- **I/O效率**`Res3DDatFile.cpp``OperUrfFile.cpp`都使用了`fflush`来控制缓冲区刷新,平衡了性能和数据安全性。
- **算法优化**`Markup.cpp`中的`x_Hash`函数使用了简单的加法哈希,确保了在编码查找时的快速响应。
### 错误容忍策略
- **输入验证**:所有模块在关键操作前都进行了严格的输入验证,例如检查文件指针是否为空、参数是否在有效范围内。
- **异常处理**:通过`GetLastError()`获取系统错误码,并使用`AfxMessageBox``MessageBoxEx`向用户报告详细的错误信息(支持中英文)。
- **资源清理**:在析构函数和`CloseFile`方法中确保文件句柄被正确关闭,防止资源泄漏。
## 结论
Geomative Studio的数据格式处理体系结构清晰,各模块职责分明。`Markup.cpp`提供了一个高效、内存友好的XML处理方案;`OperTxtFile.cpp``OperUrfFile.cpp`分别处理通用文本和行业标准格式;`Res3DDatFile.cpp`则实现了专有的二进制结果存储。这些模块共同支撑了项目的配置管理、数据交换和结果保存功能。尽管在编码转换和版本兼容性方面有进一步提升的空间,但其整体设计稳健,错误处理完善,为地球物理测量软件的可靠运行提供了坚实的基础。
@@ -0,0 +1,274 @@
# 文件操作封装
<cite>
**本文档引用的文件**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [FileOperTools.h](file://h/FileOperTools.h)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp)
- [MyCopyFile.h](file://h/MyCopyFile.h)
- [AutoLock.cpp](file://cpp/Lock/AutoLock.cpp)
- [AutoLock.h](file://h/Lock/AutoLock.h)
- [RingBuffer.h](file://h/RingBuffer.h)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 简介
本文档全面解析`FileOperTools.cpp``MyCopyFile.cpp`中封装的文件系统操作功能。重点描述跨平台路径处理(支持Windows风格路径)、目录创建与遍历、文件锁定机制、原子性文件写入、大文件复制优化(缓冲区管理、异步I/O)等关键技术实现。说明这些工具类在项目配置保存、缓存管理、日志写入和数据导出等场景中的使用模式。提供接口调用示例,包括错误码处理、权限异常恢复和磁盘空间预检等最佳实践。分析其与MFC CFile类的差异与优势,以及在多线程环境下的线程安全设计。
## 项目结构
项目包含多个核心目录,其中`cpp/Tools`目录下存放了文件操作相关的工具类实现。`FileOperTools.cpp``MyCopyFile.cpp`位于此目录,分别提供基础文件操作和高级文件复制功能。`h/`目录下有对应的头文件声明。项目还包含日志管理、锁机制等辅助组件,支持文件操作的完整功能实现。
```mermaid
graph TD
subgraph "核心工具"
FileOperTools["FileOperTools.cpp<br/>基础文件操作"]
MyCopyFile["MyCopyFile.cpp<br/>高级文件复制"]
end
subgraph "辅助组件"
AutoLock["AutoLock.cpp<br/>自动锁机制"]
RingBuffer["RingBuffer.h<br/>环形缓冲区"]
end
subgraph "日志系统"
Log["日志文件<br/>(log\\general\\*.txt)"]
end
FileOperTools --> Log
MyCopyFile --> Log
FileOperTools --> AutoLock
MyCopyFile --> AutoLock
```
**图示来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
- [AutoLock.cpp](file://cpp/Lock/AutoLock.cpp#L1-L12)
**本节来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
## 核心组件
`FileOperTools`类提供基础文件操作功能,包括目录复制、文件存在性检查、文件删除、日志写入等。`MyCopyFile`类专注于大文件的高效复制,支持进度监控和传输中断处理。两个类都采用单例模式或静态成员实现,确保全局访问的一致性。通过临界区(CRITICAL_SECTION)实现线程安全的日志写入,避免多线程环境下的数据竞争。
**本节来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
- [MyCopyFile.h](file://h/MyCopyFile.h#L1-L48)
## 架构概述
文件操作工具采用分层架构设计,上层为功能接口,中层为业务逻辑,底层为系统API调用。`FileOperTools`负责通用文件操作,`MyCopyFile`专注于文件传输场景。两者共享日志系统和线程安全机制。通过`AutoLock`类实现RAII风格的锁管理,确保异常安全的资源释放。
```mermaid
graph TB
subgraph "接口层"
A["CopyFolder"]
B["IsFileExist"]
C["DeleteDirectory"]
D["TransferFile"]
E["GetTransferInfo"]
end
subgraph "逻辑层"
F["FileOperTools"]
G["MyCopyFile"]
end
subgraph "安全层"
H["CRITICAL_SECTION"]
I["AutoLock"]
end
subgraph "系统层"
J["Windows API"]
K["KERNEL32"]
end
A --> F
B --> F
C --> F
D --> G
E --> G
F --> H
G --> H
H --> I
F --> J
G --> J
J --> K
```
**图示来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
- [AutoLock.cpp](file://cpp/Lock/AutoLock.cpp#L1-L12)
## 详细组件分析
### FileOperTools 分析
`CFileOperTools`类实现了一个单例模式的文件操作工具,提供目录复制、文件删除、日志管理等功能。通过`GetInstance()`方法获取唯一实例,确保全局状态的一致性。类中使用临界区保护日志文件的并发写入,避免多线程环境下的数据损坏。
#### 类图
```mermaid
classDiagram
class CFileOperTools {
+CFileOperTools()
+~CFileOperTools()
+static CFileOperTools* GetInstance()
+bool CopyFolder(CString strSrcPath, CString strDstPath)
+bool IsFileExist(CString strFileInfo)
+bool WriteComLog(const CString& strInfo)
+bool WriteComLog(unsigned char *pszData, int iDateLen)
+bool DeleteDirectory(CString strDirPath)
+bool DeleteFileDirect(CString strFilePath)
+CString GetDstFilePathFolder()
+bool GeneralLogName()
+bool DealGeneralLogFunc()
-void CloseComLog()
-static UINT DealGeneralLogThread(LPVOID lParam)
-static CFileOperTools* m_pFileOper
-FILE* m_pComLog
-CRITICAL_SECTION *m_pWriteLogSection
-CString m_strGeneralLogName
}
```
**图示来源**
- [FileOperTools.h](file://h/FileOperTools.h#L11-L42)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
#### 目录复制流程
```mermaid
flowchart TD
Start([开始复制目录]) --> CheckPath["检查源路径和目标路径"]
CheckPath --> PathValid{"路径有效?"}
PathValid --> |否| ReturnError["返回错误"]
PathValid --> |是| RemoveTrailing["移除路径末尾反斜杠"]
RemoveTrailing --> CreateDir["创建目标目录"]
CreateDir --> FindFiles["查找源目录文件"]
FindFiles --> HasNext{"有更多文件?"}
HasNext --> |否| ReturnSuccess["返回成功"]
HasNext --> |是| GetNextFile["获取下一个文件"]
GetNextFile --> IsDot{"是.或..?"}
IsDot --> |是| FindFiles
IsDot --> |否| IsDir{"是目录?"}
IsDir --> |是| RecursiveCopy["递归复制子目录"]
RecursiveCopy --> FindFiles
IsDir --> |否| CopyFile["复制文件"]
CopyFile --> CheckError{"复制失败?"}
CheckError --> |是| LogError["记录错误并返回"]
CheckError --> |否| FindFiles
```
**图示来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L58-L124)
**本节来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
### MyCopyFile 分析
`CMyCopyFile`类专注于大文件的高效、可靠传输。利用Windows API的`CopyFileEx`函数实现带进度回调的文件复制,支持传输过程中的取消操作。通过静态成员变量实现全局日志管理,确保传输日志的集中记录。
#### 类图
```mermaid
classDiagram
class CMyCopyFile {
+CMyCopyFile()
+~CMyCopyFile()
+bool TransferFile(CString strSrcDir, CString strDstDir, CString strFileName)
+void GetTransferInfo(DWORD &dwTransferedBytes, DWORD &dwTotalFileSize)
+int GetTansferLastError()
+void Initialize()
-void PrintLog(const CString& strLog)
-static DWORD CALLBACK CopyProgressInfo(LARGE_INTEGER, LARGE_INTEGER, ...)
-static FILE* g_pCopyFileLog
-static CRITICAL_SECTION* g_pLogCriticSec
-LARGE_INTEGER m_liTransferedBytes
-LARGE_INTEGER m_liTotalFileSize
-int m_iErrorCode
-BOOL m_bIsCancelCopy
}
```
**图示来源**
- [MyCopyFile.h](file://h/MyCopyFile.h#L18-L48)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
#### 文件传输流程
```mermaid
sequenceDiagram
participant Client as "客户端"
participant MyCopyFile as "CMyCopyFile"
participant OS as "操作系统"
Client->>MyCopyFile : TransferFile(参数)
MyCopyFile->>MyCopyFile : 检查源文件属性
MyCopyFile->>MyCopyFile : 删除目标文件
MyCopyFile->>OS : CopyFileEx(带进度回调)
loop 进度更新
OS->>MyCopyFile : 调用CopyProgressInfo
MyCopyFile->>MyCopyFile : 更新传输进度
MyCopyFile->>MyCopyFile : 检查设备连接状态
MyCopyFile->>MyCopyFile : 记录日志
MyCopyFile-->>OS : 返回PROGRESS_CONTINUE
end
OS-->>MyCopyFile : 复制完成
MyCopyFile-->>Client : 返回结果
```
**图示来源**
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L76-L107)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L137-L154)
**本节来源**
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
- [MyCopyFile.h](file://h/MyCopyFile.h#L1-L48)
## 依赖分析
文件操作工具依赖于Windows API进行底层文件操作,使用MFC的CString类处理字符串。通过临界区实现线程同步,依赖系统时间函数获取时间戳。`MyCopyFile`类还依赖`DetcGD10Dev`类检测设备连接状态,实现智能传输中断。
```mermaid
graph LR
FileOperTools --> WindowsAPI
MyCopyFile --> WindowsAPI
FileOperTools --> MFC
MyCopyFile --> MFC
FileOperTools --> AutoLock
MyCopyFile --> AutoLock
MyCopyFile --> DetcGD10Dev
AutoLock --> WindowsAPI
subgraph "外部依赖"
WindowsAPI["Windows API"]
MFC["MFC框架"]
end
subgraph "内部依赖"
DetcGD10Dev["DetcGD10Dev.cpp"]
end
```
**图示来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L5-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L5-L155)
- [AutoLock.cpp](file://cpp/Lock/AutoLock.cpp#L1-L12)
**本节来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
- [AutoLock.cpp](file://cpp/Lock/AutoLock.cpp#L1-L12)
## 性能考虑
文件操作工具在设计时考虑了性能优化。`CopyFolder`方法采用递归方式遍历目录,对于大型目录树可能产生较深的调用栈。`MyCopyFile`使用`CopyFileEx` API,该API内部实现了高效的缓冲区管理和异步I/O,适合大文件传输。日志系统通过临界区保护,但频繁的日志写入可能成为性能瓶颈。建议在生产环境中合理控制日志级别。
## 故障排除指南
常见问题包括文件访问权限不足、磁盘空间不足、路径过长等。`FileOperTools`在操作失败时会记录详细的错误码,可通过`GetLastError()`获取具体原因。`MyCopyFile`提供`GetTansferLastError()`方法获取传输错误码。当遇到"临界区创建失败"错误时,可能是系统资源不足。建议检查磁盘空间、文件权限和系统资源使用情况。
**本节来源**
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
- [MyCopyFile.cpp](file://cpp/Tools/MyCopyFile.cpp#L1-L155)
## 结论
`FileOperTools``MyCopyFile`提供了完整的文件系统操作解决方案。前者侧重于通用文件操作,后者专注于大文件传输场景。两者都实现了线程安全的日志记录,通过临界区保护共享资源。与MFC CFile类相比,这些工具类提供了更高层次的抽象和更丰富的功能,特别是在目录操作和进度监控方面。在多线程环境下,通过RAII风格的`AutoLock`类确保了资源管理的安全性。
@@ -0,0 +1,186 @@
# 状态与流程控制
<cite>
**本文档引用文件**
- [HandleProcessor.cpp](file://cpp/Tools/HandleProcessor.cpp)
- [HandleProcessor.h](file://h/HandleProcessor.h)
- [StateProcessor.cpp](file://cpp/Tools/StateProcessor.cpp)
- [StateProcessor.h](file://h/StateProcessor.h)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp)
- [TransferCtrl.h](file://h/TransferCtrl.h)
- [Constant.h](file://h/Constant.h)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
- [DevManager.h](file://h/DevManager.h)
</cite>
## 目录
1. [引言](#引言)
2. [核心组件协同工作机制](#核心组件协同工作机制)
3. [状态机设计模式](#状态机设计模式)
4. [消息分发机制](#消息分发机制)
5. [数据传输控制机制](#数据传输控制机制)
6. [测量任务全链路流程](#测量任务全链路流程)
7. [状态同步与异常恢复](#状态同步与异常恢复)
8. [结论](#结论)
## 引言
本文档系统化阐述GeomativeStudio项目中HandleProcessor、StateProcessor和TransferCtrl三个核心组件在设备交互中的协同工作机制。这些组件共同构成了系统状态管理、流程控制和数据传输的核心架构,确保了设备连接、就绪、测量、离线等状态的可靠转换,以及底层通信事件向UI层和业务逻辑层的高效分发。
## 核心组件协同工作机制
HandleProcessor、StateProcessor和TransferCtrl三个组件在系统中扮演着不同的角色,通过紧密协作实现设备交互的完整控制流程。HandleProcessor负责句柄的生成与解析,将设备ID和类型信息编码到32位DWORD中,实现了高效的对象标识管理。StateProcessor则专注于状态表示的转换,将内部状态值与UI显示状态进行映射。TransferCtrl作为通信控制核心,继承自CTcpClient,负责与设备建立TCP连接,发送控制指令并接收响应数据。
这三个组件的协同工作始于设备连接:当设备上线时,TransferCtrl接收设备上线通知,通过DevManager获取设备信息,使用HandleProcessor生成设备句柄,然后通过StateProcessor将设备状态转换为UI可识别的形式进行显示。在测量过程中,TransferCtrl发送测量指令,HandleProcessor处理任务相关的句柄信息,StateProcessor管理测量状态的转换。这种分工明确的架构设计确保了系统各层之间的松耦合和高内聚。
**Section sources**
- [HandleProcessor.h](file://h/HandleProcessor.h#L12-L26)
- [StateProcessor.h](file://h/StateProcessor.h#L12-L19)
- [TransferCtrl.h](file://h/TransferCtrl.h#L34-L76)
## 状态机设计模式
系统采用基于枚举和条件判断的状态机设计模式,管理设备从连接、就绪、测量到离线的完整生命周期。状态转换主要通过TransferCtrl组件中的RecvCommRspMsg方法实现,该方法持续监听通信通道,根据接收到的不同命令字触发相应的状态转换。
设备状态主要包括:离线(PZ_STATE_OFFLINE)、在线(PZ_STATE_ONLINE)、就绪、测量中等。当设备首次连接时,系统接收到EN_RECV_DEVICE_ONLINE(0x71)命令,设备状态从离线转换为在线。随后通过EN_CTRL_SYN_DEV_PARAM(0x02)命令同步设备参数,完成初始化后进入就绪状态。当用户启动测量任务时,系统发送EN_CTRL_MEASURE_DATA(0x06)命令,设备状态转换为测量中。在测量过程中,系统持续接收实时测量数据,直到任务完成或用户手动停止。
状态转换的条件主要基于通信协议中的命令字和响应码。例如,当接收到EN_RECV_DEVICE_OFFLINE命令时,无论当前处于何种状态,都会转换为离线状态。这种基于事件驱动的状态机设计确保了状态转换的确定性和可靠性,避免了非法状态的出现。
```mermaid
stateDiagram-v2
[*] --> 离线
离线 --> 在线 : 接收到EN_RECV_DEVICE_ONLINE
在线 --> 就绪 : 完成设备参数同步
就绪 --> 测量中 : 发送EN_CTRL_MEASURE_DATA命令
测量中 --> 就绪 : 测量完成或用户停止
测量中 --> 离线 : 接收到EN_RECV_DEVICE_OFFLINE
就绪 --> 离线 : 接收到EN_RECV_DEVICE_OFFLINE
在线 --> 离线 : 接收到EN_RECV_DEVICE_OFFLINE
```
**Diagram sources **
- [Constant.h](file://h/Constant.h#L132-L134)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L532-L534)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L245-L490)
## 消息分发机制
系统采用Windows消息机制和回调函数相结合的方式实现底层通信事件向UI层和业务逻辑层的分发。TransferCtrl组件作为消息的接收端,通过RecvCommRspMsg方法从通信通道接收原始数据包,解析协议头后根据命令字类型决定消息的分发路径。
对于控制类消息(EN_RECV_CTRL_CMD),系统通过Windows消息机制将其分发到注册的UI窗口。例如,设备上线/下线通知通过WM_MSG_NOTIFY_DEVICE_ON_OR_OFF消息发送到主窗口,触发设备列表的更新。对于实时测量数据(EN_REAL_TIME_TESTING_DATA),系统直接调用注册的回调函数,将数据传递给数据处理模块进行实时显示。
消息分发的关键在于NetWorkOper组件中的RegeditRealMsgCall和UnRegeditRealMsgCall方法,它们管理着实时消息接收者的注册表。当UI组件需要接收实时数据时,先注册自己的窗口句柄,当不再需要接收时则注销。这种发布-订阅模式确保了消息只发送给当前活跃的接收者,避免了资源浪费和潜在的内存泄漏。
```mermaid
flowchart TD
A[通信通道] --> B{RecvCommRspMsg}
B --> C[解析协议头]
C --> D{命令字类型}
D --> |EN_RECV_DEVICE_ONLINE| E[发送WM_MSG_NOTIFY_DEVICE_ON_OR_OFF]
D --> |EN_CTRL_MEASURE_DATA| F[调用实时数据回调函数]
D --> |EN_RECV_PLC_STATUS| G[更新PLC状态显示]
D --> |EN_REAL_TIME_TESTING_DATA| H[传递给数据处理模块]
E --> I[UI层更新设备列表]
F --> J[实时数据显示]
G --> K[状态面板更新]
H --> L[数据存储与分析]
```
**Diagram sources **
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L245-L490)
- [NetWorkOper.cpp](file://cpp/Tools/NetWorkOper.cpp#L343-L364)
- [Constant.h](file://h/Constant.h#L195)
## 数据传输控制机制
TransferCtrl组件实现了完整的数据传输控制机制,包括流量控制、进度监控和错误恢复。流量控制通过序列号(m_iSeriNo)和确认机制实现,每个发送的数据包都有唯一的序列号,接收方必须返回相应的确认响应,确保数据的有序传输。
进度监控主要通过测量任务的状态跟踪实现。系统维护着当前测量点的序号,通过比较已接收数据点和总测量点数来计算进度百分比。在实时测量过程中,系统每接收一个数据包就更新进度显示,让用户了解任务的执行情况。
错误恢复机制是系统可靠性的关键。当通信超时或数据校验失败时,系统会根据错误类型采取不同的恢复策略。对于临时性错误如通信超时,系统会自动重试发送;对于数据校验失败,则会请求重传;对于严重的连接中断,则会尝试重新连接设备。这种分层的错误处理策略确保了系统在各种异常情况下的稳定运行。
```mermaid
sequenceDiagram
participant UI as UI层
participant Business as 业务逻辑层
participant TransferCtrl as TransferCtrl
participant Device as 设备
UI->>Business : 启动测量任务
Business->>TransferCtrl : 发送EN_CTRL_MEASURE_DATA
TransferCtrl->>Device : 发送测量指令
Device-->>TransferCtrl : 返回确认响应
TransferCtrl-->>Business : 通知测量开始
loop 数据接收
Device->>TransferCtrl : 发送测量数据
TransferCtrl->>TransferCtrl : CRC校验
alt 校验成功
TransferCtrl-->>Business : 分发测量数据
Business-->>UI : 更新进度显示
else 校验失败
TransferCtrl->>Device : 请求重传
end
end
Device->>TransferCtrl : 发送任务完成通知
TransferCtrl-->>Business : 通知测量完成
Business-->>UI : 显示完成状态
```
**Diagram sources **
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L133-L180)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L245-L490)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L515)
## 测量任务全链路流程
从任务启动到数据接收完成的全链路流程展示了系统各组件的协同工作。流程始于用户在UI层选择测量任务并点击开始按钮,触发业务逻辑层的测量控制模块。
首先,系统检查设备连接状态,通过TransferCtrl的GetConnectStatus方法确认通信链路正常。然后,使用HandleProcessor生成任务相关的句柄信息,准备测量参数。接着,通过TransferCtrl发送EN_CTRL_SET_TASK_PARAM命令设置任务参数,等待设备确认。
设备确认后,系统发送EN_CTRL_MEASURE_DATA命令启动测量。在测量过程中,TransferCtrl持续接收实时数据包,通过RecvCommRspMsg方法解析数据,并使用StateProcessor将测量状态转换为UI可显示的形式。每接收一个有效数据包,系统就更新进度条并存储数据到数据库。
当设备发送任务完成通知或用户手动停止时,测量流程进入结束阶段。系统发送停止命令,等待设备确认,然后清理资源,更新任务状态为完成。在整个流程中,异常处理机制始终监控着各个环节,确保任何异常都能被及时捕获和处理。
```mermaid
flowchart TD
A[用户启动测量任务] --> B[检查设备连接状态]
B --> C{连接正常?}
C --> |是| D[生成任务句柄]
C --> |否| Z[显示连接错误]
D --> E[设置测量参数]
E --> F[发送EN_CTRL_SET_TASK_PARAM]
F --> G{设备确认?}
G --> |是| H[发送EN_CTRL_MEASURE_DATA]
G --> |否| Y[重试或报错]
H --> I[开始接收测量数据]
I --> J{数据有效?}
J --> |是| K[更新进度, 存储数据]
J --> |否| L[请求重传]
K --> M{任务完成?}
L --> M
M --> |是| N[发送停止命令]
M --> |否| I
N --> O[等待设备确认]
O --> P[清理资源]
P --> Q[更新任务状态]
Q --> R[显示完成结果]
```
**Diagram sources **
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L48-L76)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L133-L180)
- [DevManager.h](file://h/DevManager.h#L21-L22)
## 状态同步与异常恢复
状态同步和异常恢复是确保系统稳定运行的关键。状态同步主要通过定期的心跳包(EN_REQ_HEART_BEAT_PACKET)和状态查询实现。系统每隔一段时间发送心跳包,确认设备在线状态,同时通过EN_REQ_DOWNLOAD_DEV_INFO命令获取设备最新状态,确保本地状态与设备实际状态一致。
异常恢复策略分为多个层次:通信层异常通过TCP重连机制处理;数据层异常通过CRC校验和重传机制处理;应用层异常通过状态回滚和任务重启处理。例如,当检测到通信中断时,系统会自动尝试重新连接设备,连接成功后重新同步状态,必要时重启测量任务。
最佳实践指南建议:在关键操作前始终验证设备状态;对重要数据进行双重校验;设置合理的超时时间避免无限等待;记录详细的日志便于问题排查;提供用户友好的错误提示和恢复选项。这些实践确保了系统在各种异常情况下的可用性和可靠性。
**Section sources**
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L48-L76)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L492-L522)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h#L546)
## 结论
HandleProcessor、StateProcessor和TransferCtrl三个组件通过精巧的设计和紧密的协作,构建了GeomativeStudio系统稳定可靠的设备交互基础。HandleProcessor的句柄管理机制实现了高效的对象标识,StateProcessor的状态转换功能确保了UI显示的准确性,而TransferCtrl的通信控制能力则保障了数据传输的可靠性。这三者共同构成了一个完整、健壮的状态与流程控制系统,为地质测量任务的顺利执行提供了坚实的技术支撑。
@@ -0,0 +1,269 @@
# 网络设备操作
<cite>
**本文档引用的文件**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp)
- [NetWorkOper.h](file://h\NetWorkOper.h)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp)
- [TcpClient.h](file://h\TcpClient.h)
- [TransferCtrl.cpp](file://cpp\Tools\TransferCtrl.cpp)
- [TransferCtrl.h](file://h\TransferCtrl.h)
- [CtrlProtocolDef.h](file://h\CtrlProtocolDef.h)
- [Constant.h](file://h\Constant.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
本文档全面记录了`NetWorkOper.cpp`实现的网络设备交互功能,重点描述了设备发现机制(基于IP广播或组播)、网络连接建立、远程状态查询与控制指令发送流程。详细说明了TCP通信协议的封装方式、连接保持机制及断线重连策略。结合`TcpClient`组件,阐述了异步通信模型、数据包分帧处理和网络异常捕获方法。提供了网络配置参数说明(如端口、超时时间)和性能调优建议,并列举了与GD10设备进行网络通信的典型代码片段,包括设备上线检测和远程固件升级场景。
## 项目结构
项目结构清晰地组织了所有源代码和资源文件,主要分为以下几个部分:
- `CACHE`:缓存文件
- `DB`:数据库相关文件
- `Install`:安装文件和日志
- `LOG`:运行日志
- `Release`:发布版本文件
- `cpp`C++源代码
- `h`:头文件
- `res`:资源文件
- 根目录下的配置文件和解决方案文件
**Section sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [NetWorkOper.h](file://h\NetWorkOper.h#L1-L176)
## 核心组件
`NetWorkOper`类是网络设备交互的核心组件,负责管理网络连接、数据传输和设备状态。它通过`TcpClient`组件实现TCP通信,并通过`TransferCtrl`类封装控制信息。`NetWorkOper`类还实现了设备发现、状态查询和控制指令发送等功能。
**Section sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [NetWorkOper.h](file://h\NetWorkOper.h#L1-L176)
## 架构概述
系统架构采用客户端-服务器模式,通过TCP协议实现网络通信。`NetWorkOper`类作为客户端,负责与服务器建立连接、发送控制指令和接收响应。`TcpClient`类提供了底层的TCP通信功能,包括连接管理、数据发送和接收。`TransferCtrl`类封装了控制信息,确保数据的正确传输。
```mermaid
graph TB
subgraph "客户端"
NetWorkOper[NetWorkOper]
TransferCtrl[TransferCtrl]
TcpClient[TcpClient]
end
subgraph "服务器"
Server[服务器]
end
NetWorkOper --> TransferCtrl
TransferCtrl --> TcpClient
TcpClient --> Server
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [TransferCtrl.cpp](file://cpp\Tools\TransferCtrl.cpp#L1-L615)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
## 详细组件分析
### NetWorkOper 分析
`NetWorkOper`类是网络设备交互的核心,负责管理网络连接和数据传输。它通过`TcpClient`组件实现TCP通信,并通过`TransferCtrl`类封装控制信息。
#### 类图
```mermaid
classDiagram
class CNetWorkOper {
+CTcpClient* m_tcpClient
+bool m_bIsRunning
+CWinThread* m_pThread
+CRITICAL_SECTION m_MutexSec
+bool m_bIsTransfer
+STSendDataInfo m_stSendCtrlInfo
+STRespDataInfo m_stRespDataInfo
+bool m_bIsNeedResp
+bool m_bIsNeedSend
+BYTE m_ucCmd
+PNOTIFY_FUNC m_pNotifyFunc
+PNOTIFY_DEV_FUNC m_pDevNotifyFunc
+LPVOID m_pNotifyParam
+char m_chSendBuf[MAX_SND_BUF]
+char m_chRcvCtrlBuf[MAX_RCV_CTRLBUF]
+LPVOID m_pDevNotfiyParam
+bool m_bIsSuspend
+bool m_bIsGetDevAddr
+CRITICAL_SECTION m_netCs
+CRITICAL_SECTION m_realHwndCs
+list<HWND> m_listRealHwnd
+CNetWorkOper()
+~CNetWorkOper()
+bool Initialize()
+bool StartConnect(CString strDstIP, int iPort)
+bool StartWork()
+bool TransferOper(BYTE ucCmd, STSendDataInfo* pSendCtrl, bool bIsNeedResp, STRespDataInfo* pRespInfo, PNOTIFY_FUNC pNotifyFunc = NULL, LPVOID lpParam = NULL)
+bool GetLinkStatus()
+bool SendCtrlMsgDirect(BYTE ucCmd, UINT32 uiDevID, BYTE ucDevType, const char* pData, WORD wDataLen)
+int RecvMsgDirect(char* pData, int* pRecvLen, int iMaxRecvLen, int iTimeout)
+void RegisterDevNotify(PNOTIFY_DEV_FUNC pNotfiy, LPVOID pWnd)
+void SetSuspendForThread(bool bIsSuspend)
+bool SendLoginMsgBrocast()
+bool ProcPlcStatus(char* pData, int iLen)
+void ClearNotfiyFuncInfo()
+void PutRequestPacket(const STRequestPacket& clsRequestPacket)
+void RegeditRealMsgCall(HWND hWnd)
+void UnRegeditRealMsgCall(HWND hWnd)
+CRITICAL_SECTION* GetNetCriticalSection()
+void ClearSendInfo()
+void ReleaseData(void* pData)
+void ConsumeDataFromQue(void * pData)
+virtual void ThreadFunction()
}
class CTcpClient {
+CString m_strIP
+WORD m_wPort
+int m_iSndBuf
+int m_iRcvBuf
+SOCKET m_sockClient
+bool m_bIsConnect
+bool m_bIsInitialed
+char* m_pClearRcvBuf
+CTcpClient()
+~CTcpClient()
+void SetSocketBuf(int iSndBuf, int iRcvBuf)
+bool CloseConnect()
+bool GetConnectStatus()
+bool Disconnect()
+bool ClearRecvBuffer()
+virtual bool Initialize()
+virtual bool ConnectToServer(CString strIP, WORD wPort)
+virtual bool ReConnect()
+virtual int RecvRspMsg(char* pData, int* pLen, int iMaxLen, int iTimeout)
+virtual bool SendCtrlInfo(const char*pCtrlHeader, const char* pData, WORD wDataLen, BYTE DataType = 1)
+virtual int RecvCtrlMsg(char* pData, int* pLen, int iMaxLen, int iTimeout)
+virtual int GetCurrCtrlCmd()
+virtual bool InitialClient()
+virtual UINT32 GetPlcAddr()
+bool InitailTcp()
+bool ConnectServer(CString strAddr, WORD wPort)
+int SendData(const char*pData, int iLen)
+bool RecvData(char*pData, int iLen, int &iActualLen)
}
class CTransferCtrl {
+BYTE m_ucCmd
+bool m_bIsNeedSendCtrMsg
+int m_iLastErrNo
+int m_iSeriNo
+int m_iLastSeriNo
+int m_ucDevType
+BYTE m_ucCtrlCmd
+UINT32 m_uiPlcAddr
+CTransferCtrl()
+~CTransferCtrl()
+bool Initialize()
+int RecvRspMsg(char* pData, int* pLen, int iMaxLen, int iTimeout)
+int RecvCtrlMsg(char* pData, int* pLen, int iMaxLen, int iTimeout)
+bool IsCtrlMsg(BYTE ucCMd)
+int GetCurrCtrlCmd()
+bool InitialClient()
+inline bool IsPlcStatusMsg(BYTE ucCmd, BYTE ucSrcType)
+UINT32 GetPlcAddr()
+void ClearCtrlInfo()
+bool IsNeedSendCtrl()
+bool SendCtrlInfoToDev(const STTransCtrlInfo * pTransInfo, const char* pData, WORD wDataLen, BYTE ucDataType = 1)
+bool SendCtrlInfo(const char*pCtrlHeader, const char* pData, WORD wDataLen, BYTE DataType = 1)
+bool RecvEnoughMsg(char* pData, int iLen, int iTimeOut)
+int RecvCommRspMsg(char* pRcvData, int* pRcvLen, int iMaxRcvLen, int iRcvTimeout, bool bIsRcvCtrl = false)
}
CNetWorkOper --> CTcpClient : "uses"
CNetWorkOper --> CTransferCtrl : "uses"
CTransferCtrl --> CTcpClient : "extends"
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [NetWorkOper.h](file://h\NetWorkOper.h#L1-L176)
- [TransferCtrl.cpp](file://cpp\Tools\TransferCtrl.cpp#L1-L615)
- [TransferCtrl.h](file://h\TransferCtrl.h#L1-L79)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
- [TcpClient.h](file://h\TcpClient.h#L1-L76)
### 设备发现与连接建立
`NetWorkOper`类通过广播登录消息实现设备发现。`SendLoginMsgBrocast`方法发送广播消息,服务器接收到后会响应设备上线通知。`StartConnect`方法用于建立与服务器的TCP连接。
#### 序列图
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Server as "服务器"
Client->>Client : SendLoginMsgBrocast()
Client->>Server : 发送广播登录消息
Server->>Client : 响应设备上线通知
Client->>Client : 处理设备上线通知
Client->>Server : StartConnect(IP, Port)
Server->>Client : 连接成功
Client->>Client : 初始化网络操作
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L241-L248)
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L74-L97)
### 数据传输与控制指令
`NetWorkOper`类通过`TransferOper`方法发送控制指令,并通过`RecvMsgDirect`方法接收响应。`TransferCtrl`类封装了控制信息,确保数据的正确传输。
#### 序列图
```mermaid
sequenceDiagram
participant Client as "客户端"
participant Server as "服务器"
Client->>Client : TransferOper(ucCmd, pSendCtrl, bIsNeedResp, pRespInfo)
Client->>Client : 封装控制信息
Client->>Server : 发送控制指令
Server->>Client : 响应控制指令
Client->>Client : 解析响应数据
Client->>Client : 调用回调函数
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L128-L155)
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L206-L217)
## 依赖分析
`NetWorkOper`类依赖于`TcpClient``TransferCtrl`类,`TransferCtrl`类继承自`TcpClient`类。`TcpClient`类提供了底层的TCP通信功能,`TransferCtrl`类封装了控制信息,`NetWorkOper`类负责管理网络连接和数据传输。
```mermaid
graph TD
NetWorkOper[NetWorkOper] --> TransferCtrl[TransferCtrl]
TransferCtrl --> TcpClient[TcpClient]
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [TransferCtrl.cpp](file://cpp\Tools\TransferCtrl.cpp#L1-L615)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
## 性能考虑
为了提高性能,`NetWorkOper`类采用了异步通信模型,通过多线程处理数据传输。`TcpClient`类设置了较大的接收和发送缓冲区,以减少网络延迟。`TransferCtrl`类通过序列号和CRC校验确保数据的完整性和正确性。
**Section sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
- [TransferCtrl.cpp](file://cpp\Tools\TransferCtrl.cpp#L1-L615)
## 故障排除指南
常见的网络问题包括连接失败、数据传输错误和设备离线。可以通过检查日志文件、验证网络配置和重启设备来解决这些问题。`NetWorkOper`类提供了详细的日志记录功能,有助于诊断和解决问题。
**Section sources**
- [NetWorkOper.cpp](file://cpp\Tools\NetWorkOper.cpp#L1-L706)
- [TcpClient.cpp](file://cpp\Tools\TcpClient.cpp#L1-L449)
- [FileOperTools.cpp](file://cpp\Tools\FileOperTools.cpp#L1-L420)
## 结论
`NetWorkOper`类实现了完整的网络设备交互功能,通过`TcpClient``TransferCtrl`类提供了可靠的TCP通信和数据传输。系统架构清晰,组件职责明确,性能优化得当,能够满足复杂的网络通信需求。
@@ -0,0 +1,286 @@
# 设备交互工具
<cite>
**本文档引用的文件**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [GD10OperCmd.h](file://h/GD10OperCmd.h)
- [NetWorkOper.cpp](file://cpp/Tools/NetWorkOper.cpp)
- [NetWorkOper.h](file://h/NetWorkOper.h)
- [OperPLC.cpp](file://cpp/Tools/OperPLC.cpp)
- [OperPLC.h](file://h/OperPLC.h)
- [HandleProcessor.cpp](file://cpp/Tools/HandleProcessor.cpp)
- [HandleProcessor.h](file://h/HandleProcessor.h)
- [StateProcessor.cpp](file://cpp/Tools/StateProcessor.cpp)
- [StateProcessor.h](file://h/StateProcessor.h)
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp)
- [TransferCtrl.h](file://h/TransferCtrl.h)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
- [Constant.h](file://h/Constant.h)
- [TcpClient.h](file://h/TcpClient.h)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h)
</cite>
## 目录
1. [引言](#引言)
2. [GD10设备通信指令集](#gd10设备通信指令集)
3. [网络设备发现与远程控制](#网络设备发现与远程控制)
4. [PLC设备读写操作](#plc设备读写操作)
5. [设备状态机与消息处理](#设备状态机与消息处理)
6. [数据传输控制](#数据传输控制)
7. [典型交互流程](#典型交互流程)
8. [最佳实践](#最佳实践)
9. [结论](#结论)
## 引言
GeomativeStudio系统通过一系列工具组件实现与外部设备的交互,包括GD10主机、PLC控制器和网络设备。本文档系统化记录了这些工具组件的设计与实现,重点分析GD10设备通信指令集、网络设备发现与控制、PLC设备读写操作,以及设备状态管理、消息分发和数据传输控制机制。这些组件共同构成了系统与外部设备通信的核心基础设施。
## GD10设备通信指令集
`GD10OperCmd`组件定义了与GD10设备交互的完整指令集,通过模拟微机操作来管理设备上的文件和配置。该组件采用单例模式,确保全局唯一实例。
指令集主要包括:
- **工程管理**`project_add``project_delete`方法用于在GD10设备上创建和删除工程,通过操作`localhost.xml`和MAC地址对应的XML文件来维护工程目录映射。
- **测区管理**`testzone_add``testzone_delete`方法用于管理测区,创建相应的目录并在`project.xml`中添加或删除测区记录。
- **脚本管理**`script_add``script_delete`方法用于管理测量脚本,支持VES、ERI、ERT等不同类型,操作`scripts.xml`文件和脚本文件。
- **任务管理**`meas_delete`方法用于删除测量任务,同时清理XML配置和相关数据文件。
- **参数配置**`set_param`方法用于设置设备参数,解析以分号分隔的参数字符串,并更新`equipment.xml`文件中的相应节点。
- **设备注册管理**`unregister_user`方法用于注销用户,删除对应的用户配置文件。
所有操作都包含详细的错误处理和日志记录,确保操作的可靠性和可追溯性。
**Section sources**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L53-L1213)
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L18-L52)
## 网络设备发现与远程控制
`NetWorkOper`组件实现了网络设备的发现、状态查询和远程控制功能。该组件基于TCP通信,通过`CTransferCtrl`进行底层数据传输。
主要功能包括:
- **连接管理**`StartConnect`方法建立与服务器的TCP连接,`StartWork`方法启动数据处理线程。
- **设备发现**:通过监听特定控制命令(如`EN_RECV_NOTIFY_DEVICE_ONLINE`)来发现网络上的设备,并通过`CDetcGD10Dev`管理器注册设备。
- **状态查询**`TransferOper`方法用于发送状态查询命令,支持同步和异步模式,通过回调函数通知结果。
- **远程控制**`SendCtrlMsgDirect`方法用于发送控制命令,`RecvMsgDirect`方法用于接收响应。
- **实时数据处理**:通过`RegeditRealMsgCall`注册实时数据接收窗口,当接收到实时测量数据时,通过Windows消息机制通知注册的窗口。
该组件采用生产者-消费者模式,通过临界区保护共享数据,确保多线程环境下的数据一致性。
```mermaid
sequenceDiagram
participant Client as "客户端"
participant NetWorkOper as "NetWorkOper"
participant TransferCtrl as "TransferCtrl"
participant Server as "服务器"
Client->>NetWorkOper : StartConnect(IP, Port)
NetWorkOper->>TransferCtrl : ConnectToServer()
TransferCtrl->>Server : TCP连接
Server-->>TransferCtrl : 连接成功
TransferCtrl-->>NetWorkOper : 连接状态
NetWorkOper->>NetWorkOper : StartWork()
NetWorkOper->>NetWorkOper : ThreadFunction()
loop 消息循环
NetWorkOper->>TransferCtrl : RecvCtrlMsg()
alt 设备上线通知
TransferCtrl-->>NetWorkOper : EN_RECV_NOTIFY_DEVICE_ONLINE
NetWorkOper->>DevManager : AddRemoteDevice()
NetWorkOper->>MainFrm : WM_MSG_NOTIFY_DEVICE_ON_OR_OFF
else PLC状态
TransferCtrl-->>NetWorkOper : EN_RECV_PLC_STATUS
NetWorkOper->>TaskDataOper : InsertPlcStatusData()
else 实时测量数据
TransferCtrl-->>NetWorkOper : EN_REAL_TIME_TESTING_DATA
NetWorkOper->>RealWnd : WM_NET_RECV_REAL_TIME_DATA
else 控制命令
TransferCtrl-->>NetWorkOper : EN_RECV_CTRL_CMD
NetWorkOper->>DevNotify : 回调通知
end
alt 需要发送命令
NetWorkOper->>TransferCtrl : SendCtrlInfo()
TransferCtrl->>Server : 发送控制命令
TransferCtrl->>TransferCtrl : RecvRspMsg()
TransferCtrl-->>NetWorkOper : 响应数据
NetWorkOper->>NotifyFunc : 回调通知
end
end
```
**Diagram sources**
- [NetWorkOper.cpp](file://cpp/Tools/NetWorkOper.cpp#L26-L706)
- [NetWorkOper.h](file://h/NetWorkOper.h#L107-L173)
**Section sources**
- [NetWorkOper.cpp](file://cpp/Tools/NetWorkOper.cpp#L26-L706)
- [NetWorkOper.h](file://h/NetWorkOper.h#L107-L173)
## PLC设备读写操作
`OperPLC`组件封装了对PLC设备的读写操作,提供了一个简洁的接口来控制PLC的电源状态。
主要功能包括:
- **命令生成**`GetPlcCmdInfo`方法根据命令类型生成相应的PLC控制数据包,支持`EN_PLC_POWER_ON``EN_PLC_POWER_OFF`命令。
- **响应解析**`ParsePlcOperResInfo`方法解析PLC返回的状态信息,验证数据长度并提取操作结果。
- **具体命令实现**`GetPowerOnCmd``GetPowerOffCmd`方法构建具体的控制数据包,设置相应的控制位。
该组件通过`STRemPlcDataInfo`结构体定义PLC数据格式,确保与设备的通信协议一致。
```mermaid
classDiagram
class COperPLC {
+GetInstance() COperPLC*
+GetPlcCmdInfo(BYTE, char*, int&, int) void
+ParsePlcOperResInfo(char*, int) int
-GetPowerOnCmd(char*, int&) void
-GetPowerOffCmd(char*, int&) void
-m_pOperPlc COperPLC*
}
class STRemPlcDataInfo {
+ucPacketIndex BYTE
+ucCtrlK1 BYTE
+ucCtrlK2 BYTE
+ucCtrlK3 BYTE
+ucCtrlK4 BYTE
+ucCtrlK5 BYTE
+ucCtrlK6 BYTE
+ucResult BYTE
}
COperPLC --> STRemPlcDataInfo : "使用"
```
**Diagram sources**
- [OperPLC.cpp](file://cpp/Tools/OperPLC.cpp#L18-L121)
- [OperPLC.h](file://h/OperPLC.h#L24-L39)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
**Section sources**
- [OperPLC.cpp](file://cpp/Tools/OperPLC.cpp#L18-L121)
- [OperPLC.h](file://h/OperPLC.h#L24-L39)
## 设备状态机与消息处理
`HandleProcessor``StateProcessor`组件在设备状态机管理、消息分发和事件处理中扮演关键角色。
`HandleProcessor`负责句柄的生成和解析,将ID和类型信息编码到32位DWORD中:
- **句柄生成**`GenerateHandle`方法将27位ID和5位类型组合成一个唯一的句柄。
- **句柄解析**`AnalyseHandle``GetIDFromHandle``GetStyleFromHandle`方法从句柄中提取ID和类型信息。
`StateProcessor`负责状态与图像索引之间的转换:
- **状态转换**`ChangeToImageState``ChangeToItemState`方法在树控件的状态图像索引和内部状态值之间进行转换。
这些组件为系统提供了统一的标识和状态管理机制,简化了设备和对象的管理和显示。
```mermaid
classDiagram
class CHandleProcessor {
+GenerateHandle(DWORD, UINT) DWORD
+GenerateNewHandle(DWORD, UINT) DWORD
+AnalyseHandle(DWORD, DWORD&, UINT&) void
+GetIDFromHandle(DWORD) DWORD
+GetStyleFromHandle(DWORD) UINT
}
class CStateProcessor {
+ChangeToImageState(UINT) UINT
+ChangeToItemState(UINT) UINT
}
CHandleProcessor : HANDLE_OFFSET = 27
CStateProcessor : STATE_OFFSET = 12
```
**Diagram sources**
- [HandleProcessor.cpp](file://cpp/Tools/HandleProcessor.cpp#L19-L96)
- [HandleProcessor.h](file://h/HandleProcessor.h#L12-L28)
- [StateProcessor.cpp](file://cpp/Tools/StateProcessor.cpp#L19-L41)
- [StateProcessor.h](file://h/StateProcessor.h#L12-L22)
- [Constant.h](file://h/Constant.h)
**Section sources**
- [HandleProcessor.cpp](file://cpp/Tools/HandleProcessor.cpp#L19-L96)
- [HandleProcessor.h](file://h/HandleProcessor.h#L12-L28)
- [StateProcessor.cpp](file://cpp/Tools/StateProcessor.cpp#L19-L41)
- [StateProcessor.h](file://h/StateProcessor.h#L12-L22)
## 数据传输控制
`TransferCtrl`组件负责数据传输过程的流量控制与状态监控,继承自`CTcpClient`,实现了协议特定的通信逻辑。
主要功能包括:
- **协议封装**`SendCtrlInfoToDev`方法构建符合`STCtrlProtoHeader`格式的数据包,包含ID码、源/目的地址、命令码、序列号和CRC校验。
- **数据接收**`RecvCommRspMsg`方法处理接收的数据,包括查找协议头、长度校验、CRC校验和命令类型识别。
- **状态管理**:维护当前命令、设备类型、序列号等状态信息,用于响应匹配和错误检测。
- **错误处理**:实现超时、数据错序、CRC错误等多种错误处理机制。
该组件通过`RecvEnoughMsg`确保完整接收数据包,并通过`IsCtrlMsg``IsPlcStatusMsg`识别特殊类型的控制消息。
```mermaid
flowchart TD
A[开始发送] --> B[构建STCtrlProtoHeader]
B --> C[计算总长度]
C --> D[复制数据内容]
D --> E[计算CRC校验]
E --> F[发送数据]
F --> G{发送成功?}
G --> |是| H[更新状态信息]
G --> |否| I[记录错误日志]
H --> J[结束]
I --> J
K[开始接收] --> L[接收协议头]
L --> M{找到ID码?}
M --> |否| N[跳过字节]
N --> O{超过最大跳过字节?}
O --> |是| P[返回错误]
O --> |否| L
M --> |是| Q[校验长度]
Q --> R{长度有效?}
R --> |否| P
R --> |是| S[接收剩余数据]
S --> T[校验CRC]
T --> U{CRC正确?}
U --> |否| P
U --> |是| V[解析命令类型]
V --> W[返回结果]
```
**Diagram sources**
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L27-L615)
- [TransferCtrl.h](file://h/TransferCtrl.h#L34-L78)
- [CtrlProtocolDef.h](file://h/CtrlProtocolDef.h)
**Section sources**
- [TransferCtrl.cpp](file://cpp/Tools/TransferCtrl.cpp#L27-L615)
- [TransferCtrl.h](file://h/TransferCtrl.h#L34-L78)
## 典型交互流程
### 设备连接初始化
1. 调用`NetWorkOper::StartConnect`建立TCP连接
2. 调用`NetWorkOper::StartWork`启动消息处理线程
3. 系统自动发现在线设备并更新设备列表
### 参数配置
1. 调用`GD10OperCmd::set_param`设置参数
2. 构造参数字符串(如`"gain,2;range,1"`
3. 方法解析参数并更新`equipment.xml`
### 实时监控
1. 调用`NetWorkOper::RegeditRealMsgCall`注册实时数据窗口
2. 当接收到实时测量数据时,系统发送`WM_NET_RECV_REAL_TIME_DATA`消息
3. 注册窗口处理消息并更新显示
## 最佳实践
### 超时处理
- 所有网络操作都应设置合理的超时时间
- 使用`STRespDataInfo`中的`iTimeout`字段配置超时
- 超时后应进行重试或错误处理
### 指令重发
- 对于关键操作,实现自动重试机制
- 使用序列号跟踪命令,避免重复执行
- 在`TransferCtrl`中维护命令状态,确保可靠性
### 状态同步
- 通过`NetWorkOper`的设备上线/下线通知保持状态同步
- 使用`HandleProcessor`确保对象标识的一致性
- 定期查询设备状态,验证同步状态
## 结论
GeomativeStudio的设备交互工具组件提供了一个完整、可靠的外部设备通信框架。通过`GD10OperCmd``NetWorkOper``OperPLC`等组件的协同工作,系统能够有效地管理GD10设备、网络设备和PLC控制器。`HandleProcessor``StateProcessor`提供了统一的状态和标识管理,而`TransferCtrl`确保了数据传输的可靠性和效率。这些组件的设计体现了良好的分层架构和关注点分离原则,为系统的稳定运行提供了坚实的基础。
@@ -0,0 +1,353 @@
# 设备指令系统
<cite>
**本文档引用的文件**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [GD10OperCmd.h](file://h/GD10OperCmd.h)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [DevManager.h](file://h/DevManager.h)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [IOManager.h](file://h/IOManager.h)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [FileOperTools.h](file://h/FileOperTools.h)
- [Markup.h](file://h/Markup.h)
- [Constant.h](file://h/Constant.h)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc16.h](file://h/Crc16.h)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [Crc32.h](file://h/Crc32.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
本文档深入解析了GD10设备指令系统的实现,重点分析了`GD10OperCmd.cpp`文件中的设备指令系统。文档详细说明了GD10设备通信协议的命令格式、参数编码规则与响应解析机制,涵盖了指令分类(如设备配置、数据采集、状态查询等)、命令帧结构、校验机制(CRC16/CRC32)及超时重传策略。通过代码示例展示了典型指令的构造与解析流程,如启动测量任务、读取设备参数等。同时,文档解释了与DevManager、IOManager等模块的集成方式,以及在不同设备状态下的指令调度逻辑,并提供了常见通信异常的诊断方法和处理建议。
## 项目结构
GeomativeStudio项目是一个用于地质勘探设备管理的软件系统,其核心功能围绕GD10设备的通信、配置和数据管理展开。项目结构清晰地分为多个模块,每个模块负责特定的功能领域。
```mermaid
graph TB
subgraph "核心模块"
DevManager["设备管理器<br>(Managers/DevManager.cpp)"]
IOManager["输入输出管理器<br>(Managers/IOManager.cpp)"]
GD10OperCmd["GD10操作命令<br>(Tools/GD10OperCmd.cpp)"]
end
subgraph "工具与支持模块"
DetcGD10Dev["GD10设备检测<br>(Operator/DetcGD10Dev.cpp)"]
FileOperTools["文件操作工具<br>(Tools/FileOperTools.cpp)"]
Markup["XML解析工具<br>(Tools/Markup.cpp)"]
Crc16["CRC16校验<br>(Tools/Crc16.cpp)"]
Crc32["CRC32校验<br>(Tools/Crc32.cpp)"]
end
subgraph "常量与头文件"
Constant["常量定义<br>(h/Constant.h)"]
Headers["头文件<br>(h/*.h)"]
end
GD10OperCmd --> DevManager
GD10OperCmd --> IOManager
GD10OperCmd --> DetcGD10Dev
GD10OperCmd --> FileOperTools
GD10OperCmd --> Markup
GD10OperCmd --> Crc16
GD10OperCmd --> Crc32
DetcGD10Dev --> FileOperTools
DevManager --> Headers
IOManager --> Headers
Constant --> Headers
```
**图源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [Markup.h](file://h/Markup.h)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [Constant.h](file://h/Constant.h)
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [Constant.h](file://h/Constant.h)
## 核心组件
`GD10OperCmd`类是实现GD10设备指令系统的核心组件,它通过模拟微机操作来管理连接到Geomative Studio的GD10设备。该类提供了对设备上文件系统的操作接口,包括工程、测区、脚本和测量任务的增删改查。所有操作都依赖于`DetcGD10Dev`类来检测和确认设备的连接状态,并使用`Markup`类来解析和修改设备上的XML配置文件。`FileOperTools`类则提供了底层的文件和目录操作功能,确保了操作的原子性和日志记录。
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L1213)
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
## 架构概述
GD10设备指令系统的架构是一个典型的分层设计,从上层应用逻辑到底层硬件交互,各层职责分明。
```mermaid
graph TD
A[用户界面] --> B[GD10OperCmd]
B --> C[DetcGD10Dev]
B --> D[FileOperTools]
B --> E[Markup]
C --> F[USB设备]
D --> G[文件系统]
E --> H[XML文件]
B --> I[Crc16/Crc32]
subgraph "应用层"
A
B
end
subgraph "服务层"
C
D
E
I
end
subgraph "数据层"
F
G
H
end
```
**图源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [Markup.h](file://h/Markup.h)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
## 详细组件分析
### GD10操作命令分析
`CGD10OperCmd`类是整个指令系统的核心,它封装了所有与GD10设备交互的命令。
#### 类结构与关系
```mermaid
classDiagram
class CGD10OperCmd {
+static CGD10OperCmd* m_pGD10Oper
+CGD10OperCmd()
+~CGD10OperCmd()
+static CGD10OperCmd* GetInstance()
+int project_add(const char *mac, const char *projectcn)
+bool project_delete(const char *mac, const char *projectcn)
+int testzone_add(const char *projectcn, const char *testzonecn, const char *type)
+int testzone_delete(const char *projectcn, const char *testzonecn)
+int script_add(const char *scriptcn, const char *scriptname, const char *mudiumid)
+bool script_delete(const char *scriptcn)
+bool meas_delete(const char *projectcn, const char *testzonecn, const char *measuringcn)
+bool set_param(const CString& strParam)
+bool unregister_user(CString strMacAddr)
+bool loadDeviceMarkDataFromGD()
+static UINT LoadDevMarkDataFormGDThread(LPVOID lParam)
}
class CDetcGD10Dev {
+static CDetcGD10Dev* m_pDetcDev
+bool m_bGD10DevIsCon
+CString m_strDevAddr
+CDetcGD10Dev()
+~CDetcGD10Dev()
+static CDetcGD10Dev* GetInstance()
+bool IsGD10DevConnect()
+CString GetGD10DevAddr()
+void DetectGD10Dev()
}
class CFileOperTools {
+static CFileOperTools* m_pFileOper
+FILE* m_pComLog
+CRITICAL_SECTION *m_pWriteLogSection
+CFileOperTools()
+~CFileOperTools()
+static CFileOperTools* GetInstance()
+bool CopyFolder(CString strSrcPath, CString strDstPath)
+bool IsFileExist(CString strFileInfo)
+bool WriteComLog(const CString& strInfo)
+bool DeleteDirectory(CString strDirPath)
+bool DeleteFileDirect(CString strFilePath)
}
class CMarkup {
+CMarkup()
+~CMarkup()
+bool Load(const char* szFileName)
+bool Save(const char* szFileName)
+bool FindChildElem(const char* szName)
+bool IntoElem()
+bool AddChildElem(const char* szName, const char* szData)
+bool RemoveChildElem()
+CString GetChildData()
+bool SetChildData(const char* szData)
+void ResetPos()
}
CGD10OperCmd --> CDetcGD10Dev : "使用"
CGD10OperCmd --> CFileOperTools : "使用"
CGD10OperCmd --> CMarkup : "使用"
CDetcGD10Dev --> CFileOperTools : "使用"
```
**图源**
- [GD10OperCmd.h](file://h/GD10OperCmd.h#L1-L55)
- [DetcGD10Dev.h](file://h/DetcGD10Dev.h#L1-L38)
- [FileOperTools.h](file://h/FileOperTools.h#L1-L42)
- [Markup.h](file://h/Markup.h#L1-L266)
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L1-L1213)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L1-L189)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L1-L420)
#### 指令执行流程
```mermaid
sequenceDiagram
participant UI as "用户界面"
participant GD10Cmd as "CGD10OperCmd"
participant DetDev as "CDetcGD10Dev"
participant FileTools as "CFileOperTools"
participant Markup as "CMarkup"
UI->>GD10Cmd : project_add(mac, projectcn)
GD10Cmd->>DetDev : IsGD10DevConnect()
alt 设备未连接
GD10Cmd-->>UI : 显示错误消息
return
end
GD10Cmd->>DetDev : GetGD10DevAddr()
GD10Cmd->>FileTools : MakeSureDirectoryPathExists()
alt 创建目录失败
GD10Cmd-->>UI : 显示错误消息
return
end
GD10Cmd->>Markup : 加载 localhost.xml
GD10Cmd->>Markup : 在 path_dictionary 中添加 path 元素
GD10Cmd->>Markup : 保存 localhost.xml
GD10Cmd->>Markup : 加载 mac.xml
GD10Cmd->>Markup : 在 path_dictionary 中添加 path 元素
GD10Cmd->>Markup : 保存 mac.xml
GD10Cmd-->>UI : 返回成功 (1)
```
**图源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L53-L105)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L61-L68)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp#L261-L324)
- [Markup.h](file://h/Markup.h)
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp#L53-L105)
### 设备检测与管理分析
`CDetcGD10Dev`类负责检测和管理GD10设备的连接状态,为上层指令系统提供设备可用性保障。
#### 设备检测流程
```mermaid
flowchart TD
Start([开始检测设备]) --> GetDrives["获取所有逻辑驱动器"]
GetDrives --> CheckDrive["遍历每个驱动器"]
CheckDrive --> IsRemovable{"是否为可移动驱动器?"}
IsRemovable --> |是| GetVolName["获取卷标名称"]
IsRemovable --> |否| NextDrive["下一个驱动器"]
GetVolName --> CompareName{"卷标是否为 'GD10' 或 'GD20'?"}
CompareName --> |是| SetAddr["设置设备地址"]
CompareName --> |否| CheckDir["检查是否存在 \\SD\\equipment 目录"]
CheckDir --> |存在| SetAddr
CheckDir --> |不存在| NextDrive
SetAddr --> SetConnected["设置连接状态为 true"]
SetConnected --> End([设备检测完成])
NextDrive --> CheckDrive
```
**图源**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L100-L174)
**节源**
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp#L61-L68)
## 依赖分析
GD10设备指令系统依赖于多个核心组件和工具类,这些依赖关系确保了系统的稳定性和功能完整性。
```mermaid
graph TD
GD10OperCmd --> DetcGD10Dev
GD10OperCmd --> FileOperTools
GD10OperCmd --> Markup
GD10OperCmd --> Crc16
GD10OperCmd --> Crc32
DetcGD10Dev --> FileOperTools
DevManager --> GD10OperCmd
IOManager --> GD10OperCmd
subgraph "GD10指令系统"
GD10OperCmd
DetcGD10Dev
end
subgraph "工具类"
FileOperTools
Markup
Crc16
Crc32
end
subgraph "其他管理器"
DevManager
IOManager
end
```
**图源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
- [Markup.h](file://h/Markup.h)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [DevManager.cpp](file://cpp/Managers/DevManager.cpp)
- [IOManager.cpp](file://cpp/Managers/IOManager.cpp)
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
## 性能考虑
在设计和实现GD10设备指令系统时,性能是一个重要的考量因素。系统通过以下方式优化性能:
1. **单例模式**`CGD10OperCmd``CDetcGD10Dev`类都采用了单例模式,避免了重复创建对象的开销。
2. **线程安全**`CFileOperTools`类在写日志时使用了临界区(CRITICAL_SECTION)来保证线程安全,防止多线程并发写入导致的日志混乱。
3. **异步操作**`loadDeviceMarkDataFromGD`方法通过创建独立线程来执行文件复制和删除操作,避免了阻塞主线程,提高了用户体验。
4. **错误重试**:在删除文件或复制文件时,系统会进行多次重试(最多3次),以应对可能的文件占用或I/O错误,提高了操作的鲁棒性。
## 故障排除指南
当GD10设备指令系统出现通信异常时,可以按照以下步骤进行诊断和处理:
**节源**
- [GD10OperCmd.cpp](file://cpp/Tools/GD10OperCmd.cpp)
- [DetcGD10Dev.cpp](file://cpp/Operator/DetcGD10Dev.cpp)
- [FileOperTools.cpp](file://cpp/Tools/FileOperTools.cpp)
### 常见问题与解决方案
| 问题现象 | 可能原因 | 解决方案 |
| :--- | :--- | :--- |
| "请确保主机设备连上Geomative Studio!" | GD10设备未正确连接或未被识别 | 1. 检查USB线缆连接是否牢固。<br>2. 确认设备卷标是否为'GD10'或'GD20'。<br>3. 检查`LOG\detect_gd20_log.txt`日志文件以获取详细检测信息。 |
| "添加工程到本机失败" | `localhost.xml`文件损坏或权限不足 | 1. 检查`sd\users\localhost.xml`文件是否存在且可写。<br>2. 查看`LOG\general\`目录下的日志文件,确认具体的错误代码。 |
| "创建工程失败" | SD卡空间不足或文件系统错误 | 1. 检查GD10设备的SD卡剩余空间。<br>2. 尝试格式化SD卡(注意备份数据)。 |
| "删除文件失败" | 文件被其他进程占用 | 1. 确保没有其他程序正在访问GD10设备的文件。<br>2. 系统会自动重试3次,若仍失败,重启软件或设备后重试。 |
| "解析输入参数错误" | `set_param`方法的输入字符串格式不正确 | 1. 确认输入参数为逗号分隔的键值对,如`"key1,value1;key2,value2"`。<br>2. 检查键名是否存在于`equipment.xml`文件中。 |
## 结论
通过对`GD10OperCmd.cpp`及其相关组件的深入分析,我们全面理解了GD10设备指令系统的工作原理。该系统通过一个清晰的分层架构,将设备检测、文件操作、XML解析和指令执行等功能解耦,实现了对GD10设备的高效管理和控制。系统设计考虑了单例模式、线程安全和异步操作等性能优化策略,并通过详细的日志记录和错误处理机制保证了系统的稳定性。对于开发者而言,理解这些核心组件的交互方式和设计模式,对于维护和扩展系统功能至关重要。
@@ -0,0 +1,87 @@
# 辅助工具
<cite>
**本文档引用的文件**
- [GUCodeCreator.cpp](file://cpp/Tools/GUCodeCreator.cpp)
- [GUCodeCreator.h](file://h/GUCodeCreator.h)
- [excel.cpp](file://cpp/Tools/excel.cpp)
- [excel.h](file://h/excel.h)
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp)
- [checkupdate.h](file://h/checkupdate.h)
- [floatedit.cpp](file://cpp/Tools/floatedit.cpp)
- [floatedit.h](file://h/floatedit.h)
- [GetProcessInfo.cpp](file://cpp/Tools/GetProcessInfo.cpp)
- [GetProcessInfo.h](file://h/GetProcessInfo.h)
- [SaveInIP.cpp](file://cpp/Tools/SaveInIP.cpp)
- [SaveInIP.h](file://h/SaveInIP.h)
- [SaveInRes.cpp](file://cpp/Tools/SaveInRes.cpp)
- [SaveInRes.h](file://h/SaveInRes.h)
</cite>
## 目录
1. [设备唯一标识码生成工具](#设备唯一标识码生成工具)
2. [测量数据导出工具](#测量数据导出工具)
3. [版本检查工具](#版本检查工具)
4. [浮点数输入控件增强工具](#浮点数输入控件增强工具)
5. [系统进程信息获取工具](#系统进程信息获取工具)
6. [测量结果存储优化工具](#测量结果存储优化工具)
7. [集成应用场景](#集成应用场景)
8. [资源管理与异常防护](#资源管理与异常防护)
## 设备唯一标识码生成工具
`GUCodeCreator`工具类负责生成设备的唯一标识码(GU Code),该标识码结合了设备的MAC地址和时间戳信息,用于设备认证和授权管理。该工具通过获取网卡的MAC地址,并将其与当前时间的总秒数进行编码,生成一个唯一的字符串标识。
**Section sources**
- [GUCodeCreator.cpp](file://cpp/Tools/GUCodeCreator.cpp#L1-L395)
- [GUCodeCreator.h](file://h/GUCodeCreator.h)
## 测量数据导出工具
`excel`工具类提供了将测量数据导出为Excel文件的功能。该工具利用COM接口与Microsoft Excel应用程序进行交互,实现了数据格式设置、单元格合并和图表生成等高级功能。通过该工具,用户可以将测量结果以结构化的表格形式保存,并进行可视化展示。
**Section sources**
- [excel.cpp](file://cpp/Tools/excel.cpp#L1-L800)
- [excel.h](file://h/excel.h)
## 版本检查工具
`checkupdate`工具类实现了软件版本检查机制,包含请求构建、响应解析和更新提示流程。该工具通过HTTP协议向指定服务器请求版本信息文件,解析响应内容并与本地版本进行比较,从而判断是否需要更新。当检测到新版本时,会提示用户进行更新操作。
**Section sources**
- [checkupdate.cpp](file://cpp/Tools/checkupdate.cpp#L1-L800)
- [checkupdate.h](file://h/checkupdate.h)
## 浮点数输入控件增强工具
`floatedit`工具类对浮点数输入控件进行了增强处理,限制了用户输入的整数位和小数位长度,并支持负数输入。该工具通过重写`OnChar``OnKillfocus`等消息处理函数,实现了对输入字符的过滤和格式化,确保用户输入的数据符合预设的精度要求。
**Section sources**
- [floatedit.cpp](file://cpp/Tools/floatedit.cpp#L1-L282)
- [floatedit.h](file://h/floatedit.h)
## 系统进程信息获取工具
`GetProcessInfo`工具类提供了获取系统进程信息的方法,包括根据进程名获取进程ID、根据进程ID获取进程名以及获取父进程ID等。该工具利用Windows API函数`CreateToolhelp32Snapshot``Process32First`等,实现了对系统进程的枚举和查询功能。
**Section sources**
- [GetProcessInfo.cpp](file://cpp/Tools/GetProcessInfo.cpp#L1-L169)
- [GetProcessInfo.h](file://h/GetProcessInfo.h)
## 测量结果存储优化工具
`SaveInIP``SaveInRes`工具类对测量结果的存储进行了优化,支持多种装置类型的测量数据格式转换和文件存储。这些工具根据不同的测量方法和装置类型,将原始测量数据转换为特定格式的文本文件,并写入磁盘。同时,还支持地形信息的写入和文件头的生成。
**Section sources**
- [SaveInIP.cpp](file://cpp/Tools/SaveInIP.cpp#L1-L800)
- [SaveInIP.h](file://h/SaveInIP.h)
- [SaveInRes.cpp](file://cpp/Tools/SaveInRes.cpp#L1-L800)
- [SaveInRes.h](file://h/SaveInRes.h)
## 集成应用场景
这些辅助工具在设备认证、报告生成、系统监控等场景中具有广泛的应用。例如,在设备认证场景中,`GUCodeCreator`生成的唯一标识码可用于验证设备的合法性;在报告生成场景中,`excel`工具可将测量数据导出为Excel文件,便于生成专业的报告;在系统监控场景中,`GetProcessInfo`工具可用于监控关键进程的运行状态。
## 资源管理与异常防护
在使用这些工具时,需要注意资源管理和异常防护。例如,在使用`excel`工具时,应确保Excel应用程序的正确初始化和释放,避免资源泄漏;在使用`checkupdate`工具时,应处理网络请求失败等异常情况,确保程序的稳定性。此外,还应合理使用内存和文件句柄等系统资源,避免资源耗尽导致程序崩溃。
@@ -0,0 +1,330 @@
# 串口通信
<cite>
**本文引用的文件列表**
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp)
- [SComPort.h](file://h/SComPort.h)
- [Constant.h](file://h/Constant.h)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp)
</cite>
## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)
## 简介
本文围绕 SComPort 类对 Windows 串口通信进行系统化技术文档化,重点覆盖:
- 串口初始化流程:CreateFile 创建句柄、SetupComm 配置缓冲区、SetCommState 设置波特率(115200)与数据格式(8位数据位、无校验、1位停止位)
- 异步 I/O 操作中 OVERLAPPED 结构的应用,以及 ReadFile/WriteFile 与 GetOverlappedResult 的配合实现非阻塞读写
- ClearCommError 和 PurgeComm 在错误处理与缓冲区清理中的作用
- 基于事件驱动的通信模式:EV_RXCHAR 接收字符事件与 EV_BREAK 断线检测的实现原理
- 串口打开、数据收发、异常处理与资源释放的完整调用示例
- 多线程环境下线程安全策略与注意事项
## 项目结构
SComPort 类位于工具模块中,提供串口底层封装与上层命令交互能力;相关常量定义位于公共头文件;UI 层通过消息回调处理断线事件。
```mermaid
graph TB
subgraph "工具层"
SComPort["SComPort 类<br/>cpp/Tools/SComPort.cpp"]
SComPortWinXP["SComPort 兼容实现<br/>cpp/Tools/SComPort_winxp.cpp"]
Header["头文件声明<br/>h/SComPort.h"]
Const["常量定义<br/>h/Constant.h"]
end
subgraph "界面层"
MainFrm["主窗口消息处理<br/>cpp/Views/MainFrm.cpp"]
end
SComPort --> Header
SComPort --> Const
SComPortWinXP --> Header
SComPortWinXP --> Const
MainFrm --> |"WM_BREAKLINE"| SComPort
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L126-L221)
- [SComPort.h](file://h/SComPort.h#L14-L71)
- [Constant.h](file://h/Constant.h#L224-L232)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L126-L221)
- [SComPort.h](file://h/SComPort.h#L14-L71)
- [Constant.h](file://h/Constant.h#L224-L232)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
## 核心组件
- SComPort 类:封装串口打开、配置、读写、Zmodem 收发、断线检测、日志与缓冲区清理等能力
- 常量定义:READBUFFER_SIZE、WRITEBUFFER_SIZE、COMM_TIMEOUT 等
- 主窗口消息处理:接收 WM_BREAKLINE 并关闭对应串口、更新设备状态
章节来源
- [SComPort.h](file://h/SComPort.h#L14-L71)
- [Constant.h](file://h/Constant.h#L224-L232)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
## 架构总览
SComPort 采用 Windows 异步串口 I/O 模式,使用 OVERLAPPED 结构与事件对象实现非阻塞读写;通过 SetCommMask 注册事件掩码,结合 WaitCommEvent 实现事件驱动的接收路径;ClearCommError 用于查询错误与队列长度;PurgeComm 用于清理发送/接收缓冲区;断线检测通过独立线程轮询端口是否存在并发送 WM_BREAKLINE 给 UI。
```mermaid
sequenceDiagram
participant UI as "UI/调用方"
participant Port as "SComPort"
participant WinIO as "Windows 串口API"
participant Mask as "事件掩码(EV_* )"
participant Thread as "断线检测线程"
UI->>Port : 打开串口(OpenComm)
Port->>WinIO : CreateFile(异步标志)
Port->>WinIO : SetupComm(读写缓冲区大小)
Port->>WinIO : Get/SetCommState(波特率115200/8N1)
Port->>WinIO : SetCommMask(EV_RXCHAR/EV_BREAK/...)
Note over Port,WinIO : 初始化完成
UI->>Port : 发送数据(SendDataDirectly)
Port->>WinIO : WriteFile(Overlapped)
WinIO-->>Port : ERROR_IO_PENDING
Port->>WinIO : WaitForSingleObject(Overlapped.hEvent)
WinIO-->>Port : 完成事件
Port->>WinIO : GetOverlappedResult(获取字节数)
UI->>Port : 接收数据(ReceiveDataDirectly)
Port->>WinIO : WaitCommEvent(EV_RXCHAR)
WinIO-->>Port : 事件触发
Port->>WinIO : ClearCommError(查询InQue)
Port->>WinIO : ReadFile(Overlapped)
WinIO-->>Port : ERROR_IO_PENDING
Port->>WinIO : WaitForSingleObject(Overlapped.hEvent)
WinIO-->>Port : 完成事件
Port->>WinIO : GetOverlappedResult(获取字节数)
Thread->>WinIO : CreateFile(轮询)
WinIO-->>Thread : ERROR_FILE_NOT_FOUND
Thread->>UI : SendMessage(WM_BREAKLINE)
UI->>Port : CloseComm()
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L382-L420)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L746-L779)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
## 详细组件分析
### 串口初始化流程
- 句柄创建:使用 CreateFile 打开端口,启用 FILE_FLAG_OVERLAPPED 以支持异步 I/O
- 缓冲区配置:SetupComm 设置读写缓冲区大小(由常量 READBUFFER_SIZE/WRITEBUFFER_SIZE 决定)
- 参数设置:GetCommState 获取当前 DCB,随后设置 BaudRate=115200、ByteSize=8、Parity=NOPARITY、StopBits=ONESTOPBIT,并调用 SetCommState 应用
- 事件掩码:SetCommMask 注册 EV_BREAK、EV_CTS、EV_DSR、EV_ERR、EV_RING、EV_RLSD、EV_RXCHAR、EV_RXFLAG、EV_TXEMPTY 等事件
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L155-L196)
- [Constant.h](file://h/Constant.h#L224-L226)
### 异步 I/O 与 OVERLAPPED
- 发送路径:SendDataDirectly 中为 OVERLAPPED.hEvent 创建事件对象,调用 WriteFile;若返回 ERROR_IO_PENDING,则通过 WaitForSingleObject 等待事件;最终通过 GetOverlappedResult 获取实际写入字节数
- 接收路径:ReceiveDataDirectly 中先调用 ClearCommError 查询 cbInQue,再根据需要调用 ReadFile;同样在 ERROR_IO_PENDING 情况下等待 Overlapped.hEvent 并用 GetOverlappedResult 获取结果
- Zmodem 路径:ZmodemSendDataDirectly/ZmodemReceiveDataDirectly 使用独立 OVERLAPPED 对象,发送前可 PurgeComm 清理发送缓冲区,接收时根据 cbInQue 读取
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L716-L763)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L765-L900)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L382-L420)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L609-L744)
### 错误处理与缓冲区清理
- ClearCommError:用于查询通信错误标志与输入缓冲区字节数,避免在无数据时盲目 ReadFile
- PurgeComm:在关闭串口或需要清空缓冲区时使用 PURGE_RXCLEAR/PURGE_TXCLEAR/PURGE_RXABORT/PURGE_TXABORT 清理接收/发送队列
- 日志输出:通过 PrintLogLast 将通信过程与错误信息写入日志文件,便于定位问题
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L323-L335)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L585-L595)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L937-L952)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L267-L284)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L429-L439)
### 事件驱动通信模式
- WaitCommEvent:在接收路径中等待 EV_RXCHAR 等事件触发,避免忙等
- EV_BREAK:断线检测通过 SetCommMask 注册 EV_BREAK,并结合断线检测线程轮询端口是否存在,当端口不可访问时向 UI 发送 WM_BREAKLINE
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L189-L196)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L188-L196)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L224-L255)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
### 断线检测线程与 UI 回调
- 断线检测线程:SComPortDetectBreakThreadFun 循环尝试以独占方式打开端口,若返回 ERROR_FILE_NOT_FOUND 则判定断线,调用 CloseComm 并通过 SendMessage(WM_BREAKLINE) 通知 UI
- UI 处理:OnBreakLine 中根据通信 ID 关闭对应串口、更新设备状态、刷新界面
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L224-L255)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
### 串口打开、数据收发、异常处理与资源释放的完整调用示例
- 打开串口:OpenComm -> CreateFile -> SetupComm -> Get/SetCommState -> SetCommMask
- 发送数据:SendDataDirectly -> WriteFile(Overlapped) -> WaitForSingleObject -> GetOverlappedResult
- 接收数据:WaitCommEvent(EV_RXCHAR) -> ClearCommError(cbInQue) -> ReadFile(Overlapped) -> WaitForSingleObject -> GetOverlappedResult
- 异常处理:ERROR_IO_PENDING 分支、超时处理、错误日志
- 资源释放:CloseComm -> PurgeComm -> CloseHandle
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L126-L223)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
### 多线程环境下的线程安全策略
- 断线检测线程:独立线程轮询端口,避免阻塞主线程;线程内对串口句柄的创建/关闭遵循最小化持有原则
- 串口对象成员:构造函数初始化成员变量,析构函数负责关闭句柄与日志;代码中保留了 m_CriticalSection 字段但未启用,建议在多线程并发访问同一实例时增加互斥保护
- UI 与串口交互:通过消息 WM_BREAKLINE 解耦断线检测与 UI 更新,降低锁竞争
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L31-L57)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L87-L91)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L30-L57)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L86-L90)
## 依赖关系分析
- SComPort 依赖 Windows 串口 APICreateFile、SetupComm、GetCommState、SetCommState、SetCommMask、WaitCommEvent、ReadFile、WriteFile、GetOverlappedResult、ClearCommError、PurgeComm、CloseHandle
- 常量 READBUFFER_SIZE/WRITEBUFFER_SIZE、COMM_TIMEOUT 来自公共头文件
- UI 通过消息 WM_BREAKLINE 与串口断线检测解耦
```mermaid
graph LR
SComPort["SComPort 类"] --> WinAPI["Windows 串口API"]
SComPort --> Const["常量定义"]
SComPort --> UI["主窗口消息处理"]
WinAPI --> |"CreateFile/SetupComm/SetCommState/SetCommMask"| Port["物理串口"]
WinAPI --> |"WaitCommEvent/ReadFile/WriteFile"| Port
WinAPI --> |"ClearCommError/PurgeComm"| Port
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
- [Constant.h](file://h/Constant.h#L224-L232)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L268-L284)
- [Constant.h](file://h/Constant.h#L224-L232)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
## 性能考量
- 缓冲区大小:READBUFFER_SIZE/WRITEBUFFER_SIZE 为 15360 字节,适合批量传输场景;可根据设备数据速率调整
- 超时控制:COMM_TIMEOUT 为 500ms,用于等待事件或 I/O 完成;在高延迟链路中可适当增大
- 异步 I/OOVERLAPPED + 事件对象避免阻塞,提高吞吐;注意避免多个操作共享同一 OVERLAPPED 导致状态混淆
- 日志输出:频繁写日志会影响性能,建议在调试阶段开启,生产环境关闭或降频
[本节为通用指导,无需列出具体文件来源]
## 故障排查指南
- 打开失败:检查 CreateFile 返回值与 GetLastError;确认端口名称格式(含“\\\\.\”前缀)
- 设置参数失败:检查 SetCommState 返回值;确认 DCB 字段赋值顺序与目标平台兼容性
- 无数据可读:使用 ClearCommError 检查 cbInQue;确保已注册 EV_RXCHAR 事件并正确等待
- 发送卡住:确认 WriteFile 返回 ERROR_IO_PENDING 后正确等待 Overlapped.hEvent;检查线程优先级与事件对象创建
- 断线误报:断线检测线程轮询间隔与 UI 回调需匹配;避免重复创建线程导致资源泄漏
- 日志定位:通过 PrintLogLast 输出的时间戳与错误码快速定位问题
章节来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L156-L223)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L323-L335)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L937-L952)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L155-L196)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L286-L380)
- [SComPort_winxp.cpp](file://cpp/Tools/SComPort_winxp.cpp#L560-L607)
## 结论
SComPort 类通过 Windows 异步串口 API 提供了稳定可靠的串口通信能力,覆盖初始化、异步读写、事件驱动接收、断线检测与资源管理。结合 ClearCommError 与 PurgeComm,能够有效处理缓冲区与错误状态;通过 WM_BREAKLINE 与 UI 解耦,提升用户体验。建议在多线程并发访问同一实例时增加互斥保护,并根据实际数据速率调整缓冲区与超时参数。
[本节为总结性内容,无需列出具体文件来源]
## 附录
### 关键流程图:ReceiveDataDirectly
```mermaid
flowchart TD
Start(["进入 ReceiveDataDirectly"]) --> CheckHandle["检查串口句柄是否有效"]
CheckHandle --> |无效| Fail["返回失败"]
CheckHandle --> |有效| ClearErr["ClearCommError 查询错误与InQue"]
ClearErr --> InQueZero{"InQue 是否为0"}
InQueZero --> |是| ReturnFalse["返回 FALSE"]
InQueZero --> |否| CalcNeed["计算需要读取字节数(min(最大读取, InQue))"]
CalcNeed --> Read["ReadFile(Overlapped)"]
Read --> Pending{"返回值是否为 FALSE"}
Pending --> |是 且 错误码为 ERROR_IO_PENDING| Wait["WaitForSingleObject(Overlapped.hEvent)"]
Wait --> Gor["GetOverlappedResult 获取字节数"]
Gor --> Compare["比较实际字节数与期望字节数"]
Compare --> Log["记录日志"]
Log --> ReturnTrue["返回成功"]
Pending --> |否| CheckRead["检查 ReadFile 返回字节数"]
CheckRead --> Log2["记录日志"]
Log2 --> ReturnTrue
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L306-L401)
### 关键流程图:SendDataDirectly
```mermaid
flowchart TD
Start(["进入 SendDataDirectly"]) --> CheckHandle["检查串口句柄与输入参数"]
CheckHandle --> |无效| Fail["返回失败"]
CheckHandle --> |有效| Prepare["准备 OVERLAPPED.hEvent 与写缓冲区"]
Prepare --> Write["WriteFile(Overlapped)"]
Write --> Pending{"返回值是否为 FALSE"}
Pending --> |是 且 错误码为 ERROR_IO_PENDING| Wait["WaitForSingleObject(Overlapped.hEvent)"]
Wait --> Timeout{"等待结果"}
Timeout --> |超时| Purge["关闭句柄并返回失败"]
Timeout --> |成功| ReturnTrue["返回成功"]
Pending --> |否| ReturnTrue
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L527-L576)
### 关键流程图:断线检测线程
```mermaid
flowchart TD
Loop["循环检测"] --> TryOpen["尝试以独占方式打开串口"]
TryOpen --> OpenFail{"打开失败?"}
OpenFail --> |是 且 错误码为 文件不存在| Break["标记断线并关闭串口"]
Break --> PostMsg["发送 WM_BREAKLINE 给 UI"]
PostMsg --> Exit["退出线程"]
OpenFail --> |否| CloseHandle["关闭句柄并继续循环"]
CloseHandle --> Sleep["Sleep(短时间)"]
Sleep --> Loop
```
图表来源
- [SComPort.cpp](file://cpp/Tools/SComPort.cpp#L225-L256)
- [MainFrm.cpp](file://cpp/Views/MainFrm.cpp#L254-L328)
@@ -0,0 +1,250 @@
# 文件传输协议
<cite>
**本文档引用的文件**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp)
- [Zmodem.h](file://h/Zmodem.h)
- [_zmodem.h](file://h/_zmodem.h)
- [FileTransfer.h](file://h/FileTransfer.h)
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp)
- [SComPort.h](file://h/SComPort.h)
</cite>
## 目录
1. [引言](#引言)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构概述](#架构概述)
5. [详细组件分析](#详细组件分析)
6. [依赖分析](#依赖分析)
7. [性能考虑](#性能考虑)
8. [故障排除指南](#故障排除指南)
9. [结论](#结论)
## 引言
本文档全面阐述了基于Zmodem协议的可靠文件传输实现。该协议在Geomative Studio项目中用于设备与主机之间的固件升级、脚本文件和测量数据传输。Zmodem协议通过其强大的错误恢复机制、断点续传功能和高效的二进制传输,确保了在串行通信等不可靠信道上的数据完整性。本分析将深入解析Zmodem类的核心状态机,包括ZRQINIT请求初始化、ZRINIT应答、ZFILE文件信息帧、ZDATA数据帧、ZEOF文件结束和ZFIN传输结束等关键帧类型的交互流程。同时,文档将详细说明CRC-32校验、断点续传(通过ZRPOS携带文件偏移量)和错误恢复(ZCRC校验请求)机制的工作原理,并分析SendSingleFile和ReceiveSingleFile方法中数据分块读写、帧头封装(SendBinaryHeader)和数据帧发送(SendDataFrame)的实现细节。
## 项目结构
Geomative Studio项目是一个用于地质测量设备管理的复杂软件系统。文件传输功能是其核心模块之一,主要由`cpp/Tools/`目录下的工具类实现。与文件传输直接相关的文件包括`Zmodem.cpp``Zmodem.h`,它们实现了Zmodem协议的核心逻辑。`Zmodem`类继承自`FileTransfer`基类,后者定义了文件传输的通用接口。为了支持Zmodem协议,项目还包含了`Crc16.cpp``Crc32.cpp`文件,用于计算数据校验和。通信底层由`SComPort`类处理,它提供了与串口设备通信的接口。整个文件传输模块通过`FileTransfer`模块进行集成,为上层应用提供统一的文件收发功能。
```mermaid
graph TB
subgraph "文件传输模块"
Zmodem[Zmodem.cpp<br/>Zmodem协议实现]
FileTransfer[FileTransfer.cpp<br/>传输基类]
Crc32[Crc32.cpp<br/>CRC-32校验]
Crc16[Crc16.cpp<br/>CRC-16校验]
end
subgraph "通信模块"
SComPort[SComPort.h<br/>串口通信]
end
subgraph "应用层"
FileTransferModule[FileTransfer模块<br/>文件传输应用]
end
Zmodem --> FileTransfer
Zmodem --> Crc32
Zmodem --> Crc16
Zmodem --> SComPort
FileTransferModule --> Zmodem
```
**Diagram sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1-L1940)
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp#L1-L47)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
- [SComPort.h](file://h/SComPort.h#L1-L74)
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1-L1940)
- [Zmodem.h](file://h/Zmodem.h#L1-L143)
- [FileTransfer.h](file://h/FileTransfer.h#L1-L49)
## 核心组件
Zmodem协议的核心组件是`Zmodem`类,它封装了协议的所有状态机和数据处理逻辑。该类通过继承`FileTransfer`基类,实现了`Send``Receive`两个核心接口。`Zmodem`类维护了多个关键状态变量,如`transmitted_file_position`(已发送的文件位置)、`received_file_position`(已接收的文件位置)和`receiver_wants_crc32`(接收方是否支持CRC-32),这些变量共同驱动着协议的状态转换。协议的可靠性主要依赖于`SendBinaryHeader``ReadHeader``SendDataFrame`等方法,它们负责数据的封装、解析和校验。此外,`GetRinitHeader``SyncWithReceiver`等方法处理了协议的初始化和同步过程,确保了通信双方的协调一致。
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L33-L1940)
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
## 架构概述
Zmodem协议的架构是一个典型的请求-响应式状态机,其核心是围绕一系列预定义的帧类型(Frame Types)进行交互。整个传输过程始于发送方的`ZRQINIT`请求,接收方通过`ZRINIT`帧进行应答,完成初始化握手。随后,发送方通过`ZFILE`帧发送文件名和元数据,接收方确认后,数据传输进入主循环,由`ZDATA`数据帧和`ZACK`确认帧构成。当文件传输完毕,发送方发送`ZEOF`帧,接收方确认后,发送方再发送`ZFIN`帧结束整个会话。该架构通过`ZRPOS`帧实现了断点续传,通过`ZCRC`帧实现了错误恢复,并通过`CAN`序列提供了紧急中断机制。
```mermaid
sequenceDiagram
participant 发送方 as 发送方 (Zmodem)
participant 接收方 as 接收方 (Zmodem)
发送方->>接收方 : ZRQINIT (请求初始化)
接收方->>发送方 : ZRINIT (初始化应答)
发送方->>接收方 : ZFILE (文件信息)
接收方->>发送方 : ZRPOS (请求从指定位置开始)
loop 数据传输
发送方->>接收方 : ZDATA (数据帧)
接收方->>发送方 : ZACK (确认)
end
发送方->>接收方 : ZEOF (文件结束)
接收方->>发送方 : ZRPOS (确认)
发送方->>接收方 : ZFIN (会话结束)
接收方->>发送方 : 'OO' (确认)
```
**Diagram sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L87-L126)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L385-L451)
- [_zmodem.h](file://h/_zmodem.h#L15-L36)
## 详细组件分析
### Zmodem状态机分析
Zmodem协议的状态机是其可靠性的核心。状态机的运行由`Send``Receive`方法驱动,它们分别调用`SendSingleFile``ReceiveSingleFile`来处理单个文件的传输。状态转换主要通过`ReadHeader`方法接收的帧类型来触发。例如,在`SendSingleFile`方法中,一个`ZCRC`帧会触发发送方重新计算并发送文件的CRC校验值;一个`ZRPOS`帧则会触发发送方跳转到指定的文件偏移量继续发送。在`ReceiveSingleFile`方法中,状态机通过`SendHexHeader`主动发送`ZRPOS`帧来请求数据,根据接收到的`ZDATA``ZEOF``ZSKIP`等帧来决定后续动作。这种基于事件驱动的状态机设计,使得协议能够灵活应对各种网络状况。
```mermaid
stateDiagram-v2
[*] --> 初始化
初始化 --> 发送文件信息 : ZFILE
发送文件信息 --> 等待响应 : 发送ZFILE
等待响应 --> 数据传输 : ZRPOS
等待响应 --> 跳过文件 : ZSKIP
等待响应 --> 错误恢复 : ZCRC
数据传输 --> 发送数据帧 : ZDATA
发送数据帧 --> 等待确认 : 发送ZDATA
等待确认 --> 数据传输 : ZACK
等待确认 --> 错误恢复 : TIMEOUT
错误恢复 --> 发送数据帧 : 重发
发送数据帧 --> 文件结束 : ZEOF
文件结束 --> 等待结束 : 发送ZEOF
等待结束 --> 传输结束 : ZRPOS
传输结束 --> [*] : ZFIN
```
**Diagram sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L258)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L558-L775)
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L775)
### 校验与错误恢复机制分析
Zmodem协议通过CRC校验和超时重试机制来保证数据的完整性。协议支持CRC-16和CRC-32两种校验算法,由`receiver_wants_crc32`标志位决定。`Crc32``Crc16`类实现了校验算法,其中`Crc32`类使用了预计算的查找表来提高计算效率。当接收方检测到数据帧的CRC校验失败时,它会发送一个`ZNAK`(否定应答)或`ZCRC`(请求校验)帧。发送方在`SyncWithReceiver`方法中处理这些错误,根据情况重发数据或重新同步。`ReadDataFrame`方法是错误处理的关键,它会循环读取数据直到收到正确的`GOTCRCW`等结束标志,否则会返回`ZERROR`。此外,协议还定义了`TIMEOUT``GARBAGE_COUNT`等错误码,用于处理超时和垃圾数据。
```mermaid
flowchart TD
A[开始读取数据帧] --> B{是否为ZDLE?}
B --> |是| C[读取转义字符]
B --> |否| D{是否为有效数据?}
D --> |是| E[更新CRC校验]
D --> |否| F[处理XON/XOFF]
C --> G{转义字符类型}
G --> H[ZCRCE/G/Q/W] --> I[读取4字节CRC]
G --> J[ZCAN] --> K[返回ZCAN]
G --> L[ZRUB0/1] --> M[替换为0x7f/0xff]
I --> N{CRC校验是否通过?}
N --> |是| O[返回GOTCRC*]
N --> |否| P[返回ZERROR]
E --> Q{缓冲区满?}
Q --> |否| B
Q --> |是| R[返回ZERROR]
```
**Diagram sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1154-L1340)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1154-L1340)
- [Crc32.cpp](file://cpp/Tools/Crc32.cpp#L1-L51)
- [Crc16.cpp](file://cpp/Tools/Crc16.cpp#L1-L54)
### 数据传输流程分析
文件传输的核心流程由`SendSingleFile``ReceiveSingleFile`方法实现。发送流程始于`Send`方法,它首先发送`ZRQINIT`帧,然后进入`SendSingleFile`。在`SendSingleFile`中,发送方构造包含文件名和长度的`ZFILE`帧并发送,随后等待接收方的`ZRPOS`指令。一旦收到`ZRPOS`,发送方调用`SendFileContents`方法,该方法循环读取文件数据,封装成`ZDATA`帧发送,并根据策略(如`ZCRCW`)等待接收方的`ZACK`确认。接收流程始于`Receive`方法,它调用`WakeUpSender`发送`ZRINIT`帧,然后进入`ReceiveSingleFile`。接收方通过循环发送`ZRPOS`帧来请求数据,`ReadDataFrame`方法负责接收和校验`ZDATA`帧,并将有效数据写入文件。整个流程通过`m_WriteBuff``buff`等缓冲区来优化I/O性能。
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L153-L378)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L558-L775)
## 依赖分析
Zmodem协议的实现依赖于多个层次的组件。最底层是`SComPort`类,它通过`ZmodemSendDataDirectly``ZmodemReceiveDataDirectly`方法提供串口的直接读写能力。`Zmodem`类依赖于`Crc32``Crc16`类来计算数据校验和。`Zmodem`类本身是`FileTransfer`抽象基类的具体实现,它必须实现`Send``Receive`两个纯虚函数。此外,`Zmodem`类还依赖于标准C库的文件操作函数(如`fopen`, `fread`, `fclose`)来处理本地文件。这些依赖关系清晰地划分了职责,使得协议逻辑与底层通信、文件系统和校验算法解耦。
```mermaid
classDiagram
class FileTransfer {
<<abstract>>
+Send(files[] char*) BOOL
+Receive(Path CString) BOOL
-error(fmt char*, ...)
-status(fmt char*, ...)
}
class Zmodem {
-port CSComPort*
-file FILE*
-file_length long
-byte_count long
-receiver_wants_crc32 int
-received_file_position long
+Send(files[] char*) BOOL
+Receive(Path CString) BOOL
-SendSingleFile(name char*) int
-ReceiveSingleFile(Path CString) int
-SendBinaryHeader(length int, type int, header char*) void
-ReadHeader(header char*) int
-SendDataFrame(buffer char*, length int, frameend int) void
}
class Crc32 {
-crc unsigned long
+Crc32(init_value unsigned long)
+update(c int) void
+value() unsigned long
}
class Crc16 {
-crc unsigned short
+Crc16(init_value unsigned short)
+update(c int) void
+value() unsigned short
}
class CSComPort {
+ZmodemSendDataDirectly(pDataBuff char*, iDataSize int) BOOL
+ZmodemReceiveDataDirectly(pDataBuff char*, iDataSize int*) BOOL
}
FileTransfer <|-- Zmodem : "继承"
Zmodem --> Crc32 : "使用"
Zmodem --> Crc16 : "使用"
Zmodem --> CSComPort : "使用"
```
**Diagram sources**
- [FileTransfer.h](file://h/FileTransfer.h#L26-L46)
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
- [Crc32.h](file://h/Crc32.h#L12-L28)
- [Crc16.h](file://h/Crc16.h#L12-L27)
- [SComPort.h](file://h/SComPort.h#L14-L73)
**Section sources**
- [FileTransfer.h](file://h/FileTransfer.h#L26-L46)
- [Zmodem.h](file://h/Zmodem.h#L31-L140)
- [Crc32.h](file://h/Crc32.h#L12-L28)
- [Crc16.h](file://h/Crc16.h#L12-L27)
- [SComPort.h](file://h/SComPort.h#L14-L73)
## 性能考虑
Zmodem协议的性能主要体现在其高效的二进制传输和流控机制上。协议通过`ZBIN32``ZBIN`帧头支持二进制传输,避免了ASCII编码的开销。`SendDataFrame`方法在发送数据时,会根据`receiver_wants_crc32`标志选择使用CRC-32或CRC-16,CRC-32虽然计算开销稍大,但能提供更强的错误检测能力。为了优化性能,代码中使用了`m_WriteBuff`写缓冲区,将多个`SendChar`调用合并为一次`ZmodemSendDataDirectly`调用,减少了系统调用的次数。在接收端,`ReadBuff`方法也实现了读缓冲,提高了数据读取效率。此外,`receiver_buffer_length`参数允许接收方告知发送方其缓冲区大小,从而实现简单的流量控制,防止接收方缓冲区溢出。
## 故障排除指南
当文件传输失败时,应首先检查日志文件(如`log\\zmodemSZLog.txt``log\\zmodemRZLog.txt`)。常见的错误包括:
- **ZERROR**: 通用错误,可能由多种原因引起,需结合上下文日志分析。
- **TIMEOUT**: 通信超时,检查串口连接、波特率设置和设备电源。
- **ZCAN**: 通信被对方取消,检查是否有用户手动中断或设备异常。
- **Bad CRC**: 数据校验失败,通常由通信线路噪声或波特率不匹配引起。
- **Open file failed**: 无法创建或打开文件,检查目标路径权限和磁盘空间。
代码中通过`error``status`方法输出详细的调试信息,并通过`PrintLogLast`方法将时间戳信息写入日志,便于问题定位。对于`ZCRC`错误,协议会自动请求重传,但如果错误频繁发生,则应检查物理连接。
**Section sources**
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L28-L47)
- [Zmodem.cpp](file://cpp/Tools/Zmodem.cpp#L1906-L1921)
- [FileTransfer.cpp](file://cpp/Tools/FileTransfer.cpp#L28-L46)
## 结论
本文档详细分析了Geomative Studio项目中基于Zmodem协议的文件传输实现。该实现是一个功能完整、健壮可靠的协议栈,它通过精心设计的状态机、强大的CRC校验和灵活的断点续传机制,确保了在复杂工业环境下的数据传输成功率。`Zmodem`类的代码结构清晰,职责分明,通过继承和组合的方式,有效地管理了与底层通信、文件系统和校验算法的依赖关系。该模块为设备的固件升级、脚本部署和数据回传提供了坚实的基础,是Geomative Studio系统不可或缺的核心组件。

Some files were not shown because too many files have changed in this diff Show More