Vector Clock/Version Clock

本文介绍了分布式系统中物理时钟、Lamport逻辑时钟及VectorClock的基本原理,重点阐述了Dynamo如何利用VersionClock解决数据冲突问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Vector Clock/Version Clock

physical clock

     机器上的物理时钟,不同的机器在同一个时间点取到的physical clock不一样,之间会存在一定的误差,NTP可以用来控制这个误差,同一个机房内的机器之间的时钟误差可以控制在1us以内,跨机房可以控制在几十ms。两个事件a和b,a在机器M1上physical clock为12点5分0秒6ms发生,b在机器M2上physical clock为12点5分0秒7ms发生,这并不代表a发生在b之前,因为两个机器上取到的physical clock和真实时间(这个时间就是国际标准时间UTC,可以通过原子钟,internet,卫星获得)之间都有误差。比如机器M1的physical clock比真实时间慢10ms,那么事件a实际上是在真实时间12点5分0秒16ms发生的,机器M2的physical clock比真实事件慢5ms,那么事件b的实际上是在真实时间12点5分0秒12ms发生的,显然,事件a发生在事件b之后。

Lamport’s Logical Clock

    单机系统容易给发生的所有事件定义一个全局顺序(total order),但是分布式系统没有全局时钟,很难给所有事件定义一个全局顺序。所以,Lamport定义了一种偏序关系,happens-before.记作 ->

a->b意味着所有的进程都agree事件a发生在事件b之前。

    在三种情况下,可以很容易的得到这个关系:

  1. 如果事件a和事件b是同一个进程中的并且事件a发生在事件b前面,那么a->b;

  2. 如果进程A发送一条消息m给进程B,a代表进程A发送消息m的事件,b代表进程B接收消息m的事件,那么a->b(由于消息的传递需要时间)
  3. ->满足传递性,如果a->b AND b->c => a->c.

Lamport’s Logical Clock算法如下:

  1. 每个机器本地维护一个logical clock LCi;
  2. 每个机器本地每发生一个事件设置LCi = LCi + 1,并且把结果作为这个事件的logical clock;
  3. 当机器i给机器j发送消息m时,把LCi存在消息里;
  4. 当机器j收到消息m时候,LCj = max(LCj, m timestamp)+1,结果值作为收到消息m这个事件的时间戳。

     这个算法能够保证a->b,那么a事件的logical clock比b事件的logical clock小。反过来,通过只比较两个事件的logical clock不能得到a和b的先后。

Vector Clock

     每个机器维护一个向量VC,也就是Vector Clock,这个向量VC有如下属性:

  1. VCi[i] 是到目前为止机器i上发生的事件的个数;

  2. VCi[k] 是机器i知道的机器k发生的事件的个数(即机器i对机器j的知识).

     每个机器都有一个向量(Vector),每个向量中的元素都是一个logical clock,所以取名为Vector Clock。

     通过如下算法更新Vector Clock

  1. 机器i本地发生一个事件时将VCi[i]加1;
  2. 机器i给机器j发送消息m时,将整个VCi存在消息内;
  3. 机器j收到消息m时,VCj[k]=max(VCj[k],VCi[k]),同时,VCj[j]+1.

     可以看出,Vector Clock是一种maintain因果关系(causality)的一种手段,Vector Clock在机器之间传递达到给对方传递自己已有的关于其他机器知识的目的。

Dynamo为什么需要Vector Clock(实际上是Version Clock)

     Dynamo是一个分布式Key/Value存储系统,这个Value可以是一行,包含多个列, 为了容错,每个Key/Value保存多副本,通常在不同的机器上,一般是3,后面以3为例。对外是一个最终一致性系统,即客户端A写入一个值返回成功后,在一定的时间内另外一个客户端可能读不到最新的值。通常,成功写入两个副本成功即返回给客户端成功,同时请求会异步的同步到第三个副本。然而,高可用是Dynamo的主要设计目标之一,即使在出现网络分区或者机器宕机时依然可读可写。

假设Key K有三个副本k1,k2,k3分别在M1,M2,M3上。

正常情况

M1处理写请求,M1将请求发往M2,M3,只要有一个返回,即返回客户端成功。

网络分区

如果M1和M2/M3之间网络都不通,k1被更新(持续高可用,依然给客户端返回成功),随后,其他节点(集群中任意一个节点都可以接客户端请求,并且将请求路由到正确的节点上)路由了写请求给M2(假设其他节点和M1/M3之间网络不通),k2被更新。这时,k1和k2数据不一样,最后网络恢复,三个副本进行同步时,应该保留哪个版本?如果只保留k2,即采用last write win机制,那么同步后,第一个客户端会发现它写的数据丢了。

这个时候就需要Vector Clock,更确切的说是Version Clock。

为了处理这种场景,Dynamo使用Version Clock来捕获同一个Object的不同版本之间的causality。每个Object的每个版本会有一个相关联的Version Clock, 形如[(serverA,counter),(serverB,counter),…], 通过检查同一个Object不同版本的Version Clock,可以决定是否可以完全丢弃一个版本,仅保留另外一个版本,还是需要将两个版本进行merge。如果Object的版本A的VCA包含的每项(server, counter)在版本B的VCB中都有对应项,并且counter小于等于版本B中对应项的counter(记作VCB descends VCA),那么这个Object的版本A可以被丢弃,否则需要对两个版本进行merge。

回到刚才的例子,k1被更新,Version Clock(注:此处假设k1/k2/k3三个副本之前一模一样,那么就可以省略之前的Version Clock)为[(M1,1)],k2被更新,Version Clock为[(M2,1)],随后k1/k2网络通了,他们通过比较两个Version Clock发现两个Version Clock存在冲突,不是descends的关系,那么就两个版本都保留,当客户端来读Key K的时候,两个版本的数据和对应的VC都返回给客户端,由客户端进行冲突合并,客户端进行冲突合并后写入Key K的时候,带着合并后的VC[(M1, 1), [M2, 1]]发到M1/M2,覆盖服务器版本,冲突解决。

可以看出,Vector Clock最初是为了给分布式系统的事件定序发明的,本质上是一种捕获causality的手段,只是他们捕获的是事件的关系。而Version Clock是捕获同一个数据的不同版本之间的causality.

Riak这个系统也使用了Vector Clock来做冲突合并,对Vector Clock的用法可谓比较深入,具体可以看最后两篇参考资料。

参考资料

1.   Dynamo

2.   Scalable and Accurate Causality Tracking for Eventually Consistent Stores

3.   version-vectors-are-not-vector-clocks

4.   Causality Is Expensive

5.   Vector Clocks Revisited

6.   vector-clocks-revisited-part-2-dotted-version-vectors

转载地址: 爱订阅 吴镝的博文


module softmax_data_memory ( input wire clk, input wire rst_n, // PORTA input wire [11:0]BRAM_PORTA_1_addr, input wire [31:0]BRAM_PORTA_1_din, output wire [31:0]BRAM_PORTA_1_dout, input wire BRAM_PORTA_1_en, input wire BRAM_PORTA_1_rst, input wire [3:0]BRAM_PORTA_1_we, // PORTB input wire [31:0] mem_addr, input wire read_req, output reg read_ack, output wire [31:0] read_data, input wire write_req, input wire write_we, input wire [31:0] write_data, output reg write_ack ); // 参数定义 parameter DATA_WIDTH = 32; parameter MEMORY_SIZE = 2**11; // 内存大小(以32位为单位) parameter ADDR_WIDTH = 11; // 地址位宽 (2^32 ) parameter MEM_PRIMITIVE = "auto" ; //自动选择 Block/Distributed RAM parameter MEM_INIT_FILE = "none" ; parameter READ_LATENCY_A = 1 ; parameter READ_LATENCY_B = 1 ; // 地址转换(字节地址转换为字地址) wire [ADDR_WIDTH-1:0] word_addrb = mem_addr[ADDR_WIDTH+1:2]; // 原语控制信号 wire [DATA_WIDTH-1:0] doutb; wire ena = BRAM_PORTA_1_en; wire enb = write_req || read_req; wire [0:0] wea = |BRAM_PORTA_1_we; //单bit 写使能 wire [0:0] web = write_we; //单bit 写使能 // 应答逻辑(独立于原语) always@(posedge clk or negedge rst_n) begin if(!rst_n) begin read_ack <= 1'b0; write_ack <= 1'b0; end else begin read_ack <= read_req; //读延迟1周期 write_ack <= write_req & write_we; //写延迟1周期 end end // xpm_memory_tdpram: True Dual Port RAM // Xilinx Parameterized Macro, version 2021.2 //PORTA-CPU; PORTB-SOFTMAX xpm_memory_tdpram #( .ADDR_WIDTH_A(ADDR_WIDTH), // DECIMAL .ADDR_WIDTH_B(ADDR_WIDTH), // DECIMAL .AUTO_SLEEP_TIME(0), // DECIMAL .BYTE_WRITE_WIDTH_A(DATA_WIDTH), // DECIMAL .BYTE_WRITE_WIDTH_B(DATA_WIDTH), // DECIMAL .CASCADE_HEIGHT(0), // DECIMAL .CLOCKING_MODE("common_clock"), // String .ECC_MODE("no_ecc"), // String .MEMORY_INIT_FILE(MEM_INIT_FILE), // String .MEMORY_INIT_PARAM("0"), // String .MEMORY_OPTIMIZATION("true"), // String .MEMORY_PRIMITIVE(MEM_PRIMITIVE), // String .MEMORY_SIZE(MEMORY_SIZE*DATA_WIDTH), // DECIMAL .MESSAGE_CONTROL(0), // DECIMAL .READ_DATA_WIDTH_A(DATA_WIDTH), // DECIMAL .READ_DATA_WIDTH_B(DATA_WIDTH), // DECIMAL .READ_LATENCY_A(READ_LATENCY_A), // DECIMAL .READ_LATENCY_B(READ_LATENCY_B), // DECIMAL .READ_RESET_VALUE_A("0"), // String .READ_RESET_VALUE_B("0"), // String .RST_MODE_A("SYNC"), // String .RST_MODE_B("SYNC"), // String .SIM_ASSERT_CHK(1), // DECIMAL; 0=disable simulation messages, 1=enable simulation messages .USE_EMBEDDED_CONSTRAINT(0), // DECIMAL .USE_MEM_INIT(1), // DECIMAL .USE_MEM_INIT_MMI(0), // DECIMAL .WAKEUP_TIME("disable_sleep"), // String .WRITE_DATA_WIDTH_A(DATA_WIDTH), // DECIMAL .WRITE_DATA_WIDTH_B(DATA_WIDTH), // DECIMAL .WRITE_MODE_A("no_change"), // String .WRITE_MODE_B("no_change"), // String .WRITE_PROTECT(1) // DECIMAL ) xpm_memory_tdpram_inst ( .dbiterra(), // 1-bit output: Status signal to indicate double bit error occurrence // on the data output of port A. .dbiterrb(), // 1-bit output: Status signal to indicate double bit error occurrence // on the data output of port A. .douta(BRAM_PORTA_1_dout), // READ_DATA_WIDTH_A-bit output: Data output for port A read operations. .doutb(read_data), // READ_DATA_WIDTH_B-bit output: Data output for port B read operations. .sbiterra(), // 1-bit output: Status signal to indicate single bit error occurrence // on the data output of port A. .sbiterrb(), // 1-bit output: Status signal to indicate single bit error occurrence // on the data output of port B. .addra(BRAM_PORTA_1_addr), // ADDR_WIDTH_A-bit input: Address for port A write and read operations. .addrb(word_addrb), // ADDR_WIDTH_B-bit input: Address for port B write and read operations. .clka(clk), // 1-bit input: Clock signal for port A. Also clocks port B when // parameter CLOCKING_MODE is "common_clock". .clkb(clk), // 1-bit input: Clock signal for port B when parameter CLOCKING_MODE is // "independent_clock". Unused when parameter CLOCKING_MODE is // "common_clock". .dina(BRAM_PORTA_1_din), // WRITE_DATA_WIDTH_A-bit input: Data input for port A write operations. .dinb(write_data), // WRITE_DATA_WIDTH_B-bit input: Data input for port B write operations. .ena(ena), // 1-bit input: Memory enable signal for port A. Must be high on clock // cycles when read or write operations are initiated. Pipelined // internally. .enb(enb), // 1-bit input: Memory enable signal for port B. Must be high on clock // cycles when read or write operations are initiated. Pipelined // internally. .injectdbiterra(1'b0), // 1-bit input: Controls double bit error injection on input data when // ECC enabled (Error injection capability is not available in // "decode_only" mode). .injectdbiterrb(1'b0), // 1-bit input: Controls double bit error injection on input data when // ECC enabled (Error injection capability is not available in // "decode_only" mode). .injectsbiterra(1'b0), // 1-bit input: Controls single bit error injection on input data when // ECC enabled (Error injection capability is not available in // "decode_only" mode). .injectsbiterrb(1'b0), // 1-bit input: Controls single bit error injection on input data when // ECC enabled (Error injection capability is not available in // "decode_only" mode). .regcea(1'b1), // 1-bit input: Clock Enable for the last register stage on the output // data path. .regceb(1'b1), // 1-bit input: Clock Enable for the last register stage on the output // data path. .rsta(~rst_n), // 1-bit input: Reset signal for the final port A output register stage. // Synchronously resets output port douta to the value specified by // parameter READ_RESET_VALUE_A. .rstb(~rst_n), // 1-bit input: Reset signal for the final port B output register stage. // Synchronously resets output port doutb to the value specified by // parameter READ_RESET_VALUE_B. .sleep(1'b0), // 1-bit input: sleep signal to enable the dynamic power saving feature. .wea(wea), // WRITE_DATA_WIDTH_A/BYTE_WRITE_WIDTH_A-bit input: Write enable vector // for port A input data port dina. 1 bit wide when word-wide writes are // used. In byte-wide write configurations, each bit controls the // writing one byte of dina to address addra. For example, to // synchronously write only bits [15-8] of dina when WRITE_DATA_WIDTH_A // is 32, wea would be 4'b0010. .web(web) // WRITE_DATA_WIDTH_B/BYTE_WRITE_WIDTH_B-bit input: Write enable vector // for port B input data port dinb. 1 bit wide when word-wide writes are // used. In byte-wide write configurations, each bit controls the // writing one byte of dinb to address addrb. For example, to // synchronously write only bits [15-8] of dinb when WRITE_DATA_WIDTH_B // is 32, web would be 4'b0010. ); // End of xpm_memory_tdpram_inst instantiation endmodule 这是同一块RAM,分地址真双端口独立访问吗,一共定义了几块ram
07-08
帮我把以下代码转换成QT的格式,然后生成最新的头文件和源文件发给我#include <string> #include <vector> #include <unordered_map> #include <tinyxml2.h> // 确保安装 tinyxml2 库 class ProjectManager { public: // 构造函数和析构函数 ProjectManager(); ~ProjectManager(); // 加载项目文件 bool Load(const std::string& filePath); // 保存项目文件 bool Save(const std::string& projectFilePath); // 新建项目 bool NewProject(const std::string& projectFilePath); // 打开项目 bool OpenProject(const std::string& projectFilePath); // 另存为项目 bool SaveAsProject(const std::string& newFilePath); bool closeProject(); bool UpgradeProject(); // 获取项目信息 const std::string& GetProjectName() const; double GetCreatedAt() const; double GetModifiedAt() const; const std::string& GetControllerParamsFile() const; const std::string& GetCardParamsFile() const; const std::vector<std::string>& GetBasicCodeFiles() const; const std::vector<std::string>& GetGCodeFiles() const; const std::unordered_map<std::string, std::string>& GetAdditionalParams() const; // 设置项目信息 void SetProjectName(const std::string& name); void SetControllerParamsFile(const std::string& filePath); void SetCardParamsFile(const std::string& filePath); void AddBasicCodeFile(const std::string& filePath); void AddGCodeFile(const std::string& filePath); void SetAdditionalParam(const std::string& key, const std::string& value); private: // 项目信息 struct ProjectInfo { std::string version; std::string projectName; double createdAt; double modifiedAt; std::string controllerParamsFile; std::string cardParamsFile; std::vector<std::string> basicCodeFiles; std::vector<std::string> gCodeFiles; std::unordered_map<std::string, std::string> additionalParams; std::string projectFilePath; // 项目的完整路径 }; struct AxisInfo { int axisId; std::string axisName; std::string motorType; double gearRatio; double maxSpeed; double acceleration; }; // 私有成员 ProjectInfo projectInfo_; static const double CurrentVersion; // 静态成员变量声明 // 辅助方法 bool LoadControllerParams(const std::string& filePath); bool SaveControllerParams(const std::string& filePath); bool LoadCardParams(const std::string& filePath); bool SaveCardParams(const std::string& filePath); void HandleControllerBackwardsCompatibility(); void HandleCardBackwardsCompatibility(); void HandleBackwardsCompatibility(); void AddElement(tinyxml2::XMLElement* parent, const std::string& name, const std::string& value); }; #include "ProjectManager.h" #include <chrono> #include <ctime> #include <iostream> #include <sstream> // 包含字符串流处理头文件 // 当前软件版本 const double ProjectManager::CurrentVersion = 2.0; // 辅助函数:获取当前时间并格式化为字符串 double GetCurrentTime() { std::time_t now = std::time(nullptr); return static_cast<double>(now); } // 构造函数和析构函数 ProjectManager::ProjectManager() { // 初始化默认值 projectInfo_.version = "1.0"; projectInfo_.projectName = "New Project"; projectInfo_.createdAt = GetCurrentTime(); projectInfo_.modifiedAt = projectInfo_.createdAt; projectInfo_.controllerParamsFile = "controller_params.xml"; projectInfo_.cardParamsFile = "card_params.xml"; projectInfo_.projectFilePath = ""; } ProjectManager::~ProjectManager() {} // 加载项目文件 bool ProjectManager::Load(const std::string& filePath) { tinyxml2::XMLDocument doc; if (doc.LoadFile(filePath.c_str()) != tinyxml2::XML_SUCCESS) { return false; } tinyxml2::XMLElement* root = doc.FirstChildElement("Project"); // 解析版本号 if (auto versionElem = root->FirstChildElement("Version")) { projectInfo_.version = versionElem->GetText(); } // 解析项目名称 if (auto nameElem = root->FirstChildElement("ProjectName")) { projectInfo_.projectName = nameElem->GetText(); } // 解析时间信息 if (auto createdAtElem = root->FirstChildElement("CreatedAt")) { projectInfo_.createdAt = std::stod(createdAtElem->GetText()); } if (auto modifiedAtElem = root->FirstChildElement("ModifiedAt")) { projectInfo_.modifiedAt = std::stod(modifiedAtElem->GetText()); } // 解析文件路径 if (auto controllerParamsElem = root->FirstChildElement("ControllerParamsFile")) { projectInfo_.controllerParamsFile = controllerParamsElem->GetText(); } if (auto cardParamsElem = root->FirstChildElement("CardParamsFile")) { projectInfo_.cardParamsFile = cardParamsElem->GetText(); } // 解析 Basic 代码文件路径 projectInfo_.basicCodeFiles.clear(); if (auto basicFilesElem = root->FirstChildElement("BasicCodeFiles")) { for (auto fileElem = basicFilesElem->FirstChildElement("File"); fileElem != nullptr; fileElem = fileElem->NextSiblingElement("File")) { projectInfo_.basicCodeFiles.push_back(fileElem->GetText()); } } // 解析 G 代码文件路径 projectInfo_.gCodeFiles.clear(); if (auto gcodeFilesElem = root->FirstChildElement("GCodeFiles")) { for (auto fileElem = gcodeFilesElem->FirstChildElement("File"); fileElem != nullptr; fileElem = fileElem->NextSiblingElement("File")) { projectInfo_.gCodeFiles.push_back(fileElem->GetText()); } } // 解析附加参数 projectInfo_.additionalParams.clear(); if (auto additionalParamsElem = root->FirstChildElement("AdditionalParams")) { for (auto paramElem = additionalParamsElem->FirstChildElement(); paramElem != nullptr; paramElem = paramElem->NextSiblingElement()) { projectInfo_.additionalParams[paramElem->Name()] = paramElem->GetText(); } } // 加载相关文件 if (!LoadControllerParams(projectInfo_.controllerParamsFile)) { return false; } if (!LoadCardParams(projectInfo_.cardParamsFile)) { return false; } // 设置项目文件路径 projectInfo_.projectFilePath = filePath; return true; } bool ProjectManager::Save(const std::string& projectFilePath) { // 先执行文件拷贝操作 const std::filesystem::path projectDir = std::filesystem::path(projectFilePath).parent_path(); // 拷贝运动控制器参数文件 if (!CopyPhysicalFile( currentControllerPath_, // 当前控制器参数文件路径(成员变量) projectDir / projectInfo_.controllerParamsFile)) { return false; } // 拷贝运动控制卡参数文件 if (!CopyPhysicalFile( currentCardPath_, // 当前控制卡参数文件路径(成员变量) projectDir / projectInfo_.cardParamsFile)) { return false; } //拷贝Basic代码文件 for (const auto& file : projectInfo_.basicCodeFiles) { if (!CopyPhysicalFile( currentBasicCodePath_ / file, // 当前Basic代码文件路径(成员变量) projectDir / file)) { return false; } } // 拷贝G代码文件 for (const auto& file : projectInfo_.gCodeFiles) { if (!CopyPhysicalFile( currentGCodePath_ / file, // 当前G代码文件路径(成员变量) projectDir / file)) { return false; } } // 构造 XML 文档 tinyxml2::XMLDocument doc; tinyxml2::XMLElement* root = doc.NewElement("Project"); doc.InsertEndChild(root); // 添加版本号 AddElement(root, "Version", std::to_string(CurrentVersion)); // 设置当前软件版本 // 添加项目名称 AddElement(root, "ProjectName", projectInfo_.projectName); // 添加创建时间和修改时间 AddElement(root, "CreatedAt", std::to_string(projectInfo_.createdAt)); AddElement(root, "ModifiedAt", std::to_string(GetCurrentTime())); // 更新修改时间 // 添加控制器参数文件路径 AddElement(root, "ControllerParamsFile", projectInfo_.controllerParamsFile); // 添加控制卡参数文件路径 AddElement(root, "CardParamsFile", projectInfo_.cardParamsFile); // 添加 Basic 代码文件路径 tinyxml2::XMLElement* basicCodeFiles = doc.NewElement("BasicCodeFiles"); root->InsertEndChild(basicCodeFiles); for (const auto& file : projectInfo_.basicCodeFiles) { AddElement(basicCodeFiles, "File", file); } // 添加 G 代码文件路径 tinyxml2::XMLElement* gCodeFiles = doc.NewElement("GCodeFiles"); root->InsertEndChild(gCodeFiles); for (const auto& file : projectInfo_.gCodeFiles) { AddElement(gCodeFiles, "File", file); } // 添加附加参数 tinyxml2::XMLElement* additionalParams = doc.NewElement("AdditionalParams"); root->InsertEndChild(additionalParams); for (const auto& param : projectInfo_.additionalParams) { AddElement(additionalParams, param.first, param.second); } // 保存文件 return doc.SaveFile(projectFilePath.c_str()) == tinyxml2::XML_SUCCESS; } bool ProjectManager::OpenProject(const std::string& projectFilePath) { if (!Load(projectFilePath)) { return false; } // 加载项目文件成功后,打印一些基本信息 std::cout << "Project Name: " << projectInfo_.projectName << std::endl; std::cout << "Controller Params File: " << projectInfo_.controllerParamsFile << std::endl; std::cout << "Card Params File: " << projectInfo_.cardParamsFile << std::endl; // 检查版本兼容性 double fileVersion = std::stod(projectInfo_.version); if (fileVersion > CurrentVersion) { std::cerr << "Error: Project version (" << projectInfo_.version << ") is newer than software version (" << CurrentVersion << "). Please upgrade your software." << std::endl; return false; } else if (fileVersion < CurrentVersion) { std::cout << "Warning: Project version (" << projectInfo_.version << ") is older than software version (" << CurrentVersion << "). Would you like to upgrade the project? (y/n): "; char choice; std::cin >> choice; if (choice == 'y' || choice == 'Y') { if (!UpgradeProject()) { std::cerr << "Failed to upgrade project." << std::endl; return false; } } else { return false; } } return true; } bool ProjectManager::UpgradeProject() { // 升级完成后,调用向下兼容性处理 HandleControllerBackwardsCompatibility(); HandleCardBackwardsCompatibility(); HandleCardBackwardsCompatibility(); // 保存升级后的项目文件 if (!Save(projectInfo_.projectFilePath)) { std::cerr << "Failed to save upgraded project." << std::endl; return false; } // 更新项目版本号 projectInfo_.version = std::to_string(currentVersion); std::cout << "Project upgraded successfully to version " << currentVersion << "." << std::endl; return true; } bool ProjectManager::NewProject(const std::string& projectFilePath) { // 初始化项目信息 projectInfo_.version = "1.0"; projectInfo_.projectName = "New Project"; projectInfo_.createdAt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); projectInfo_.modifiedAt = projectInfo_.createdAt; // 初始化控制器参数 projectInfo_.controllerParamsFile = "controller_params.xml"; if (!SaveControllerParams(projectInfo_.controllerParamsFile)) { return false; } // 初始化控制卡参数 projectInfo_.cardParamsFile = "card_params.xml"; if (!SaveCardParams(projectInfo_.cardParamsFile)) { return false; } // 保存项目文件 return Save(projectFilePath); } bool ProjectManager::SaveProject() { // 更新修改时间 projectInfo_.modifiedAt = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now()); // 保存控制器参数文件 if (!SaveControllerParams(projectInfo_.controllerParamsFile)) { return false; } // 保存控制卡参数文件 if (!SaveCardParams(projectInfo_.cardParamsFile)) { return false; } // 保存核心项目文件 return Save(projectInfo_.projectFilePath); } bool ProjectManager::SaveAsProject(const std::string& newFilePath) { // 更新项目文件路径 projectInfo_.projectFilePath = newFilePath; // 保存项目文件 return Save(projectFilePath); } bool ProjectManager::LoadControllerParams(const std::string& filePath) { tinyxml2::XMLDocument doc; if (doc.LoadFile(filePath.c_str()) != tinyxml2::XML_NO_ERROR) { return false; } tinyxml2::XMLElement* root = doc.FirstChildElement("ControllerParams"); // 解析控制器类型 projectInfo_.controllerType = root->FirstChildElement("ControllerType")->GetText(); // 解析轴数量 int axisCount = std::stoi(root->FirstChildElement("AxisCount")->GetText()); // 解析轴详细信息 projectInfo_.axisInfo.clear(); for (tinyxml2::XMLElement* axisNode = root->FirstChildElement("Axis"); axisNode; axisNode = axisNode->NextSiblingElement("Axis")) { AxisInfo axis; axis.axisId = std::stoi(axisNode->FirstChildElement("AxisId")->GetText()); axis.axisName = axisNode->FirstChildElement("AxisName")->GetText(); axis.motorType = axisNode->FirstChildElement("MotorType")->GetText(); axis.gearRatio = std::stod(axisNode->FirstChildElement("GearRatio")->GetText()); axis.maxSpeed = std::stod(axisNode->FirstChildElement("MaxSpeed")->GetText()); axis.acceleration = std::stod(axisNode->FirstChildElement("Acceleration")->GetText()); projectInfo_.axisInfo.push_back(axis); } return true; } bool ProjectManager::LoadCardParams(const std::string& filePath) { tinyxml2::XMLDocument doc; if (doc.LoadFile(filePath.c_str()) != tinyxml2::XML_NO_ERROR) { return false; } tinyxml2::XMLElement* root = doc.FirstChildElement("CardParams"); // 解析控制卡类型 projectInfo_.cardType = root->FirstChildElement("CardType")->GetText(); // 解析其他参数 projectInfo_.cardParams.clear(); for (auto elem = root->FirstChildElement(); elem; elem = elem->NextSiblingElement()) { if (elem->Name() == "CardType") continue; projectInfo_.cardParams[elem->Name()] = elem->GetText(); } return true; } bool ProjectManager::SaveControllerParams(const std::string& filePath) { tinyxml2::XMLDocument doc; tinyxml2::XMLElement* root = doc.NewElement("ControllerParams"); doc.InsertEndChild(root); // 添加控制器类型 AddElement(root, "ControllerType", projectInfo_.controllerType); // 添加轴数量 AddElement(root, "AxisCount", std::to_string(projectInfo_.axisInfo.size())); // 添加轴详细信息 for (const auto& axis : projectInfo_.axisInfo) { tinyxml2::XMLElement* axisNode = doc.NewElement("Axis"); root->InsertEndChild(axisNode); AddElement(axisNode, "AxisId", std::to_string(axis.axisId)); AddElement(axisNode, "AxisName", axis.axisName); AddElement(axisNode, "MotorType", axis.motorType); AddElement(axisNode, "GearRatio", std::to_string(axis.gearRatio)); AddElement(axisNode, "MaxSpeed", std::to_string(axis.maxSpeed)); AddElement(axisNode, "Acceleration", std::to_string(axis.acceleration)); } // 保存文件 return doc.SaveFile(filePath.c_str()) == tinyxml2::XML_NO_ERROR; } bool ProjectManager::SaveCardParams(const std::string& filePath) { tinyxml2::XMLDocument doc; tinyxml2::XMLElement* root = doc.NewElement("CardParams"); doc.InsertEndChild(root); // 添加控制卡类型 AddElement(root, "CardType", projectInfo_.cardType); // 添加其他参数 for (const auto& param : projectInfo_.cardParams) { AddElement(root, param.first, param.second); } // 保存文件 return doc.SaveFile(filePath.c_str()) == tinyxml2::XML_NO_ERROR; } void ProjectManager::HandleControllerBackwardsCompatibility() { double version = std::stod(projectInfo_.version); if (version == 1.0) { // 处理 v1.0 文件的缺失参数 // 例如,为新增的参数添加默认值 for (auto& axis : projectInfo_.axisInfo) { if (axis.acceleration == 0) { axis.acceleration = 500.0; // 默认值 } } } } void ProjectManager::HandleCardBackwardsCompatibility() { double version = std::stod(projectInfo_.version); if (version == 1.0) { // 处理 v1.0 文件的缺失参数 // 例如,为新增的参数添加默认值 if (projectInfo_.cardParams.find("NewParam1") == projectInfo_.cardParams.end()) { projectInfo_.cardParams["NewParam1"] = "DefaultValue1"; } } } void ProjectManager::HandleBackwardsCompatibility() { double version = std::stod(projectInfo_.version); if (version == 1.0) { // 处理 v1.0 文件的缺失参数 if (projectInfo_.additionalParams.find("NewParam1") == projectInfo_.additionalParams.end()) { projectInfo_.additionalParams["NewParam1"] = "DefaultValue1"; } } } void ProjectManager::AddElement(tinyxml2::XMLElement* parent, const std::string& name, const std::string& value) { // 获取父元素所属的XML文档对象 tinyxml2::XMLDocument* doc = parent->GetDocument(); // 创建新的子元素 tinyxml2::XMLElement* child = doc->NewElement(name.c_str()); // 设置子元素的文本内容 child->SetText(value.c_str()); // 将子元素添加到父元素中 parent->InsertEndChild(child); } bool ProjectManager::closeProject() { // 关闭项目时,先保存项目文件 //先提醒是否有未保存的修改 if (projectInfo_.modifiedAt != std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())) { std::cout << "Do you want to save the project before closing? (y/n): "; char choice; std::cin >> choice; if (choice == 'y' || choice == 'Y') { if (!SaveProject()) { return false; } } } return true; }
最新发布
07-25
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值