|
- #include "register_monitor_service.h"
-
- #include "domain/virtual_register_repository.h"
-
- #include <algorithm>
- #include <cmath>
- #include <optional>
- #include <utility>
-
- namespace {
-
- std::string parseErrorMessage(RegisterAddressParseError error)
- {
- switch (error)
- {
- case RegisterAddressParseError::Empty:
- return "请输入 M0~M4000 或 D0~D4000 地址";
- case RegisterAddressParseError::UnsupportedArea:
- return "自由监控只支持 M 区和 D 区地址";
- case RegisterAddressParseError::OutOfRange:
- return "监控地址必须位于 M0~M4000 或 D0~D4000";
- case RegisterAddressParseError::InvalidFormat:
- case RegisterAddressParseError::None:
- default:
- return "监控地址格式无效,请输入例如 M0 或 D100";
- }
- }
-
- } // namespace
-
- RegisterMonitorService::RegisterMonitorService(RegisterRepository &repository)
- : repository_(repository)
- {
- }
-
- void RegisterMonitorService::setOfflineInitialRepository(
- VirtualRegisterRepository &repository)
- {
- offline_initial_repository_ = &repository;
- }
-
- void RegisterMonitorService::setOfflineInitialCaptureEnabled(bool enabled)
- {
- capture_offline_initial_values_ = enabled;
- }
-
- RegisterMonitorResult RegisterMonitorService::addRange(
- const std::string &start_address, int count)
- {
- return addRange(start_address, count, RegisterDataType::Int16);
- }
-
- RegisterMonitorResult RegisterMonitorService::addRange(
- const std::string &start_address, int count, RegisterDataType data_type)
- {
- const RegisterAddressParseResult parsed = parseRegisterAddress(start_address);
- if (!parsed.succeeded)
- {
- return {false, RegisterMonitorError::InvalidAddress,
- parseErrorMessage(parsed.error), 0};
- }
- if (parsed.address.area() == RegisterArea::M
- && data_type != RegisterDataType::Int16)
- {
- return {false, RegisterMonitorError::InvalidAddress,
- "M 区只支持位监控,Int32、Float32 和 Double 只允许使用 D 区", 0};
- }
- if (parsed.address.area() == RegisterArea::D
- && !registerDataTypeAddressIsValid(data_type, parsed.address))
- {
- return {false, RegisterMonitorError::InvalidAddress,
- data_type == RegisterDataType::Float64
- ? "Double 起始地址必须是 D0~D3996 范围内的偶数地址"
- : std::string(registerDataTypeDescriptor(data_type).displayName)
- + " 起始地址超出可用 D 区范围",
- 0};
- }
- const int step = registerDataTypeWordCount(data_type);
- if (count < 1
- || parsed.address.index() > RegisterAddress::kMaximumIndex - step * count + 1)
- {
- return {false, RegisterMonitorError::RangeOverflow,
- "连续监控范围不能超过地址 4000", 0};
- }
-
- std::vector<MonitorPoint> pending;
- pending.reserve(static_cast<std::size_t>(count));
- for (int offset = 0; offset < count; ++offset)
- {
- MonitorPoint point{
- RegisterAddress{
- parsed.address.area(), parsed.address.index() + offset * step},
- data_type};
- if (!model_.contains(point))
- {
- pending.push_back(point);
- }
- }
- if (pending.empty())
- {
- return {false, RegisterMonitorError::NoChange, "所选地址已经在监控表中", 0};
- }
- if (model_.addresses().size() + pending.size() > RegisterMonitorModel::kMaximumItemCount)
- {
- return {false, RegisterMonitorError::LimitExceeded,
- "自由监控最多允许 64 个地址", 0};
- }
- std::vector<MonitorPoint> candidate = model_.points();
- candidate.insert(candidate.end(), pending.cbegin(), pending.cend());
- if (poll_configuration_validator_)
- {
- const std::string validation_error = poll_configuration_validator_(
- pollAddressesForPoints(candidate), multiWordRangesForPoints(candidate));
- if (!validation_error.empty())
- {
- return {false, RegisterMonitorError::PollConfigurationRejected,
- validation_error, 0};
- }
- }
- for (const MonitorPoint &point : pending)
- {
- model_.add(point);
- }
- if (addresses_changed_callback_)
- {
- addresses_changed_callback_();
- }
- bool overlaps = false;
- for (const MonitorPoint &existing : model_.points())
- {
- const int existing_end = existing.address.index()
- + registerDataTypeWordCount(existing.dataType) - 1;
- for (const MonitorPoint &pending_point : pending)
- {
- const RegisterAddress &address = pending_point.address;
- const int pending_end = address.index()
- + registerDataTypeWordCount(pending_point.dataType) - 1;
- if (existing.address.area() == address.area()
- && existing.address.index() <= pending_end
- && address.index() <= existing_end
- && !(existing.address == address
- && existing.dataType == pending_point.dataType))
- {
- overlaps = true;
- }
- }
- }
- return {true, RegisterMonitorError::None,
- overlaps ? "已添加,但与已有监控点的 D 地址范围重叠" : std::string{},
- static_cast<int>(pending.size())};
- }
-
- RegisterMonitorResult RegisterMonitorService::remove(
- const std::vector<RegisterAddress> &addresses)
- {
- int removed_count = 0;
- for (const RegisterAddress &address : addresses)
- {
- if (model_.remove(address))
- {
- ++removed_count;
- }
- }
- if (removed_count == 0)
- {
- return {false, RegisterMonitorError::NoChange, "请先选择要删除的监控地址", 0};
- }
- if (addresses_changed_callback_)
- {
- addresses_changed_callback_();
- }
- return {true, RegisterMonitorError::None, {}, removed_count};
- }
-
- RegisterMonitorResult RegisterMonitorService::remove(
- const std::vector<MonitorPoint> &points)
- {
- int removed_count = 0;
- for (const MonitorPoint &point : points)
- {
- if (model_.remove(point))
- {
- ++removed_count;
- }
- }
- if (removed_count == 0)
- {
- return {false, RegisterMonitorError::NoChange, "请先选择要删除的监控点", 0};
- }
- if (addresses_changed_callback_)
- {
- addresses_changed_callback_();
- }
- return {true, RegisterMonitorError::None, {}, removed_count};
- }
-
- RegisterMonitorResult RegisterMonitorService::clear()
- {
- if (model_.addresses().empty())
- {
- return {false, RegisterMonitorError::NoChange, "监控表已经为空", 0};
- }
- const int removed_count = static_cast<int>(model_.addresses().size());
- model_.clear();
- if (addresses_changed_callback_)
- {
- addresses_changed_callback_();
- }
- return {true, RegisterMonitorError::None, {}, removed_count};
- }
-
- RegisterMonitorWriteResult RegisterMonitorService::writeBit(
- const RegisterAddress &address, bool value)
- {
- const RegisterWriteResult result = repository_.writeBit(address, value);
- const RegisterMonitorWriteResult active_result = result.succeeded
- ? RegisterMonitorWriteResult{true, RegisterError::None, {}}
- : RegisterMonitorWriteResult{false, result.error, result.error == RegisterError::AreaMismatch
- ? "位写入只允许访问 M 区"
- : result.error == RegisterError::InvalidAddress
- ? "写入地址无效"
- : result.error == RegisterError::Unavailable
- ? "当前寄存器源不可用"
- : "寄存器写入请求被拒绝"};
- return captureInitialBit(address, value, active_result);
- }
-
- RegisterMonitorWriteResult RegisterMonitorService::writeNumeric(
- const RegisterAddress &address, RegisterDataType data_type, double value)
- {
- if (address.isValid() && address.area() != RegisterArea::D)
- {
- return {false, RegisterError::AreaMismatch,
- "数值写入只允许访问 D 区"};
- }
- if (!registerDataTypeAddressIsValid(data_type, address))
- {
- return {false, RegisterError::InvalidAddress,
- std::string(registerDataTypeDescriptor(data_type).displayName)
- + " 写入地址无效"};
- }
- const std::optional<std::vector<std::int16_t>> encoded =
- encodeRegisterNumericValue(data_type, value);
- if (!encoded.has_value())
- {
- return {false, RegisterError::InvalidAddress,
- std::string(registerDataTypeDescriptor(data_type).displayName)
- + " 写入值无效"};
- }
- const RegisterWriteResult result = repository_.writeWords(address, *encoded);
- const RegisterMonitorWriteResult active_result = result.succeeded
- ? RegisterMonitorWriteResult{true, RegisterError::None, {}}
- : RegisterMonitorWriteResult{false, result.error,
- result.error == RegisterError::AreaMismatch
- ? "数值写入只允许访问 D 区"
- : result.error == RegisterError::InvalidAddress
- ? "写入地址无效"
- : result.error == RegisterError::Unavailable
- ? "当前寄存器源不可用"
- : "寄存器写入请求被拒绝"};
- return captureInitialWords(address, *encoded, active_result);
- }
-
- RegisterMonitorWriteResult RegisterMonitorService::captureInitialBit(
- const RegisterAddress &address, bool value,
- const RegisterMonitorWriteResult &active_result)
- {
- if (!active_result.succeeded || !capture_offline_initial_values_
- || offline_initial_repository_ == nullptr)
- {
- return active_result;
- }
- offline_initial_repository_->writeBit(address, value);
- return active_result;
- }
-
- RegisterMonitorWriteResult RegisterMonitorService::captureInitialWords(
- const RegisterAddress &address,
- const std::vector<std::int16_t> &values,
- const RegisterMonitorWriteResult &active_result)
- {
- if (!active_result.succeeded || !capture_offline_initial_values_
- || offline_initial_repository_ == nullptr)
- {
- return active_result;
- }
- offline_initial_repository_->writeWords(address, values);
- return active_result;
- }
-
- const std::vector<RegisterAddress> &RegisterMonitorService::addresses() const
- {
- return model_.addresses();
- }
-
- std::vector<MonitorValue> RegisterMonitorService::values(bool communication_fault) const
- {
- std::vector<MonitorValue> result;
- result.reserve(model_.points().size());
- for (const MonitorPoint &point : model_.points())
- {
- const RegisterAddress &address = point.address;
- MonitorValue value;
- value.address = address;
- value.dataType = point.dataType;
- value.endAddress = address.index()
- + registerDataTypeWordCount(point.dataType) - 1;
- if (address.area() == RegisterArea::M)
- {
- const BitReadResult read = repository_.readBit(address);
- value.bitValue = read.value;
- value.state = read.succeeded
- ? (communication_fault ? MonitorValueState::CommunicationFault
- : MonitorValueState::Valid)
- : MonitorValueState::Unavailable;
- }
- else
- {
- const WordsReadResult read = repository_.readWords(
- address, registerDataTypeWordCount(point.dataType));
- const std::optional<RegisterNumericValue> decoded = read.succeeded
- ? decodeRegisterNumericValue(point.dataType, read.values)
- : std::nullopt;
- if (decoded.has_value())
- {
- value.numericValue = *decoded;
- }
- value.state = decoded.has_value()
- ? (communication_fault ? MonitorValueState::CommunicationFault
- : MonitorValueState::Valid)
- : MonitorValueState::Unavailable;
- }
- result.push_back(value);
- }
- return result;
- }
-
- const std::vector<MonitorPoint> &RegisterMonitorService::points() const
- {
- return model_.points();
- }
-
- std::vector<RegisterAddress> RegisterMonitorService::pollAddresses() const
- {
- return pollAddressesForPoints(model_.points());
- }
-
- std::vector<RegisterAddress> RegisterMonitorService::pollAddressesForPoints(
- const std::vector<MonitorPoint> &points)
- {
- std::vector<RegisterAddress> result;
- for (const MonitorPoint &point : points)
- {
- for (int offset = 0; offset < registerDataTypeWordCount(point.dataType); ++offset)
- {
- result.push_back(RegisterAddress{
- point.address.area(), point.address.index() + offset});
- }
- }
- return result;
- }
-
- std::vector<RegisterWordRange> RegisterMonitorService::multiWordRanges() const
- {
- return multiWordRangesForPoints(model_.points());
- }
-
- std::vector<RegisterWordRange> RegisterMonitorService::multiWordRangesForPoints(
- const std::vector<MonitorPoint> &points)
- {
- std::vector<RegisterWordRange> result;
- for (const MonitorPoint &point : points)
- {
- const int word_count = registerDataTypeWordCount(point.dataType);
- if (point.address.area() == RegisterArea::D && word_count > 1)
- {
- result.push_back({point.address, word_count});
- }
- }
- return result;
- }
-
- void RegisterMonitorService::setAddressesChangedCallback(std::function<void()> callback)
- {
- addresses_changed_callback_ = std::move(callback);
- }
-
- void RegisterMonitorService::setPollConfigurationValidator(
- std::function<std::string(
- const std::vector<RegisterAddress> &,
- const std::vector<RegisterWordRange> &)> validator)
- {
- poll_configuration_validator_ = std::move(validator);
- }
|