#include "register_monitor_service.h" #include "domain/virtual_register_repository.h" #include #include #include #include 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 pending; pending.reserve(static_cast(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 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(pending.size())}; } RegisterMonitorResult RegisterMonitorService::remove( const std::vector &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 &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(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> 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 &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 &RegisterMonitorService::addresses() const { return model_.addresses(); } std::vector RegisterMonitorService::values(bool communication_fault) const { std::vector 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 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 &RegisterMonitorService::points() const { return model_.points(); } std::vector RegisterMonitorService::pollAddresses() const { return pollAddressesForPoints(model_.points()); } std::vector RegisterMonitorService::pollAddressesForPoints( const std::vector &points) { std::vector 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 RegisterMonitorService::multiWordRanges() const { return multiWordRangesForPoints(model_.points()); } std::vector RegisterMonitorService::multiWordRangesForPoints( const std::vector &points) { std::vector 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 callback) { addresses_changed_callback_ = std::move(callback); } void RegisterMonitorService::setPollConfigurationValidator( std::function &, const std::vector &)> validator) { poll_configuration_validator_ = std::move(validator); }