|
- #include "register_address.h"
-
- #include <algorithm>
- #include <cctype>
- #include <limits>
-
- 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<char>(
- std::toupper(static_cast<unsigned char>(*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<unsigned char>(*current);
- if (!std::isdigit(character))
- {
- // 下标部分只能由十进制数字组成
- return {false, {area, 0}, RegisterAddressParseError::InvalidFormat};
- }
- const int digit = *current - '0';
- if (index > (std::numeric_limits<int>::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);
- }
|