#include "register_address.h" #include #include #include namespace { // 判断寄存器区域是否为项目支持的 M 区或 D 区 bool isSupportedArea(RegisterArea area) { // 地址值对象只承认项目定义的 M 区和 D 区 return area == RegisterArea::M || area == RegisterArea::D; } } // namespace // 解析文本中的 M/D 区域和数字下标,并返回具体失败原因 RegisterAddressParseResult parseRegisterAddress(const std::string &text) { // 从两端找到第一个非空白字符,后续解析只处理去掉首尾空白后的内容 const auto first = std::find_if_not( text.begin(), text.end(), [](unsigned char value) { return std::isspace(value); }); const auto last = std::find_if_not( text.rbegin(), text.rend(), [](unsigned char value) { return std::isspace(value); }) .base(); if (first >= last) { // 首尾空白之外没有任何内容,说明用户没有输入地址 return {false, {RegisterArea::M, 0}, RegisterAddressParseError::Empty}; } // 将首字符统一转成大写,使 m10 和 M10 的解析结果保持一致 const char area_character = static_cast( std::toupper(static_cast(*first))); RegisterArea area = RegisterArea::M; if (area_character == 'M') { area = RegisterArea::M; } else if (area_character == 'D') { area = RegisterArea::D; } else { // 首字符不是 M 或 D,返回“不支持的区域”错误并保留默认地址 return {false, {RegisterArea::M, 0}, RegisterAddressParseError::UnsupportedArea}; } if (first + 1 == last) { // 只有区域字母而没有数字下标,例如单独输入 M 或 D return {false, {area, 0}, RegisterAddressParseError::InvalidFormat}; } int index = 0; // 逐字符读取下标,避免直接调用字符串转数字函数带来格式不透明的问题 for (auto current = first + 1; current != last; ++current) { const unsigned char character = static_cast(*current); if (!std::isdigit(character)) { // 下标部分只能由十进制数字组成 return {false, {area, 0}, RegisterAddressParseError::InvalidFormat}; } const int digit = *current - '0'; if (index > (std::numeric_limits::max() - digit) / 10) { // 在执行 index * 10 + digit 前先检查,防止整数溢出 return {false, {area, 0}, RegisterAddressParseError::OutOfRange}; } index = index * 10 + digit; } RegisterAddress address{area, index}; // 解析完成后再检查项目边界,区分格式正确但下标超范围的地址 return address.isValid() ? RegisterAddressParseResult{true, address, RegisterAddressParseError::None} : RegisterAddressParseResult{false, address, RegisterAddressParseError::OutOfRange}; } // 使用指定区域和下标创建寄存器地址对象 RegisterAddress::RegisterAddress(RegisterArea area, int index) : area_(area), index_(index) { } // 返回地址所属的寄存器区域 RegisterArea RegisterAddress::area() const { return area_; } // 返回地址的数字下标 int RegisterAddress::index() const { return index_; } // 检查区域和下标是否符合项目支持的地址范围 bool RegisterAddress::isValid() const { return isSupportedArea(area_) && index_ >= kMinimumIndex && index_ <= kMaximumIndex; } // 将地址转换为 M10 或 D4000 这样的显示文本 std::string RegisterAddress::toString() const { const char *areaName = nullptr; switch (area_) { case RegisterArea::M: areaName = "M"; break; case RegisterArea::D: areaName = "D"; break; default: // 未知枚举值不能伪装成有效 PLC 地址 return "InvalidRegisterAddress"; } return std::string(areaName) + std::to_string(index_); } // 判断当前地址与另一个地址是否完全相同 bool RegisterAddress::operator==(const RegisterAddress &other) const { return area_ == other.area_ && index_ == other.index_; } // 判断当前地址与另一个地址是否不同 bool RegisterAddress::operator!=(const RegisterAddress &other) const { return !(*this == other); }