综合平台编程器项目的远程存储
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

139 line
4.3 KiB

  1. #include "register_address.h"
  2. #include <algorithm>
  3. #include <cctype>
  4. #include <limits>
  5. namespace {
  6. // 判断寄存器区域是否为项目支持的 M 区或 D 区
  7. bool isSupportedArea(RegisterArea area)
  8. {
  9. // 地址值对象只承认项目定义的 M 区和 D 区
  10. return area == RegisterArea::M || area == RegisterArea::D;
  11. }
  12. } // namespace
  13. // 解析文本中的 M/D 区域和数字下标,并返回具体失败原因
  14. RegisterAddressParseResult parseRegisterAddress(const std::string &text)
  15. {
  16. // 从两端找到第一个非空白字符,后续解析只处理去掉首尾空白后的内容
  17. const auto first = std::find_if_not(
  18. text.begin(), text.end(), [](unsigned char value) { return std::isspace(value); });
  19. const auto last = std::find_if_not(
  20. text.rbegin(), text.rend(), [](unsigned char value) { return std::isspace(value); })
  21. .base();
  22. if (first >= last)
  23. {
  24. // 首尾空白之外没有任何内容,说明用户没有输入地址
  25. return {false, {RegisterArea::M, 0}, RegisterAddressParseError::Empty};
  26. }
  27. // 将首字符统一转成大写,使 m10 和 M10 的解析结果保持一致
  28. const char area_character = static_cast<char>(
  29. std::toupper(static_cast<unsigned char>(*first)));
  30. RegisterArea area = RegisterArea::M;
  31. if (area_character == 'M')
  32. {
  33. area = RegisterArea::M;
  34. }
  35. else if (area_character == 'D')
  36. {
  37. area = RegisterArea::D;
  38. }
  39. else
  40. {
  41. // 首字符不是 M 或 D,返回“不支持的区域”错误并保留默认地址
  42. return {false, {RegisterArea::M, 0}, RegisterAddressParseError::UnsupportedArea};
  43. }
  44. if (first + 1 == last)
  45. {
  46. // 只有区域字母而没有数字下标,例如单独输入 M 或 D
  47. return {false, {area, 0}, RegisterAddressParseError::InvalidFormat};
  48. }
  49. int index = 0;
  50. // 逐字符读取下标,避免直接调用字符串转数字函数带来格式不透明的问题
  51. for (auto current = first + 1; current != last; ++current)
  52. {
  53. const unsigned char character = static_cast<unsigned char>(*current);
  54. if (!std::isdigit(character))
  55. {
  56. // 下标部分只能由十进制数字组成
  57. return {false, {area, 0}, RegisterAddressParseError::InvalidFormat};
  58. }
  59. const int digit = *current - '0';
  60. if (index > (std::numeric_limits<int>::max() - digit) / 10)
  61. {
  62. // 在执行 index * 10 + digit 前先检查,防止整数溢出
  63. return {false, {area, 0}, RegisterAddressParseError::OutOfRange};
  64. }
  65. index = index * 10 + digit;
  66. }
  67. RegisterAddress address{area, index};
  68. // 解析完成后再检查项目边界,区分格式正确但下标超范围的地址
  69. return address.isValid()
  70. ? RegisterAddressParseResult{true, address, RegisterAddressParseError::None}
  71. : RegisterAddressParseResult{false, address, RegisterAddressParseError::OutOfRange};
  72. }
  73. // 使用指定区域和下标创建寄存器地址对象
  74. RegisterAddress::RegisterAddress(RegisterArea area, int index)
  75. : area_(area),
  76. index_(index)
  77. {
  78. }
  79. // 返回地址所属的寄存器区域
  80. RegisterArea RegisterAddress::area() const
  81. {
  82. return area_;
  83. }
  84. // 返回地址的数字下标
  85. int RegisterAddress::index() const
  86. {
  87. return index_;
  88. }
  89. // 检查区域和下标是否符合项目支持的地址范围
  90. bool RegisterAddress::isValid() const
  91. {
  92. return isSupportedArea(area_)
  93. && index_ >= kMinimumIndex && index_ <= kMaximumIndex;
  94. }
  95. // 将地址转换为 M10 或 D4000 这样的显示文本
  96. std::string RegisterAddress::toString() const
  97. {
  98. const char *areaName = nullptr;
  99. switch (area_)
  100. {
  101. case RegisterArea::M:
  102. areaName = "M";
  103. break;
  104. case RegisterArea::D:
  105. areaName = "D";
  106. break;
  107. default:
  108. // 未知枚举值不能伪装成有效 PLC 地址
  109. return "InvalidRegisterAddress";
  110. }
  111. return std::string(areaName) + std::to_string(index_);
  112. }
  113. // 判断当前地址与另一个地址是否完全相同
  114. bool RegisterAddress::operator==(const RegisterAddress &other) const
  115. {
  116. return area_ == other.area_ && index_ == other.index_;
  117. }
  118. // 判断当前地址与另一个地址是否不同
  119. bool RegisterAddress::operator!=(const RegisterAddress &other) const
  120. {
  121. return !(*this == other);
  122. }