blob: cf6be30616dac4d8d4eb4faedb949d340990a513 [file] [log] [blame]
/**
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <regex>
#include <vector>
#include "minifi-cpp/core/RecordField.h"
#include "fmt/format.h"
#include "modbus/ByteConverters.h"
#include "modbus/Error.h"
#include "range/v3/algorithm/copy.hpp"
#include "range/v3/view/chunk.hpp"
#include "range/v3/view/drop.hpp"
#include "utils/StringUtils.h"
#include "utils/expected.h"
namespace org::apache::nifi::minifi::modbus {
enum class RegisterType {
holding,
input
};
class ReadModbusFunction {
public:
explicit ReadModbusFunction(const uint16_t transaction_id, const uint8_t unit_id) : transaction_id_(transaction_id), unit_id_(unit_id) {
}
ReadModbusFunction(const ReadModbusFunction&) = delete;
ReadModbusFunction(ReadModbusFunction&&) = delete;
ReadModbusFunction& operator=(ReadModbusFunction&&) = delete;
ReadModbusFunction& operator=(const ReadModbusFunction&) = delete;
virtual bool operator==(const ReadModbusFunction&) const = 0;
virtual ~ReadModbusFunction() = default;
[[nodiscard]] std::vector<std::byte> requestBytes() const;
[[nodiscard]] uint16_t getTransactionId() const { return transaction_id_; }
[[nodiscard]] uint8_t getUnitId() const { return unit_id_; }
[[nodiscard]] virtual nonstd::expected<core::RecordField, std::error_code> responseToRecordField(std::span<const std::byte> resp_pdu) const = 0;
static std::unique_ptr<ReadModbusFunction> parse(uint16_t transaction_id, uint8_t unit_id, const std::string& address);
protected:
[[nodiscard]] auto getRespBytes(std::span<const std::byte> resp_pdu) const -> nonstd::expected<std::span<const std::byte>, std::error_code>;
[[nodiscard]] virtual std::byte getFunctionCode() const = 0;
[[nodiscard]] virtual std::array<std::byte, 5> rawPdu() const = 0;
[[nodiscard]] virtual uint8_t expectedByteCount() const = 0;
const uint16_t transaction_id_;
const uint8_t unit_id_;
};
class ReadCoilStatus : public ReadModbusFunction {
public:
ReadCoilStatus(const uint16_t transaction_id, const uint8_t unit_id, const uint16_t starting_address, const uint16_t number_of_points)
: ReadModbusFunction(transaction_id, unit_id),
starting_address_(starting_address),
number_of_points_(number_of_points) {
}
[[nodiscard]] nonstd::expected<core::RecordField, std::error_code> responseToRecordField(std::span<const std::byte> resp_pdu) const override;
[[nodiscard]] std::byte getFunctionCode() const override;
[[nodiscard]] std::array<std::byte, 5> rawPdu() const override;
[[nodiscard]] uint8_t expectedByteCount() const override;
bool operator==(const ReadCoilStatus& rhs) const = default;
bool operator==(const ReadModbusFunction& rhs) const override;
static std::unique_ptr<ReadModbusFunction> parse(uint16_t transaction_id, uint8_t unit_id, std::string_view address_str, std::string_view length_str);
private:
uint16_t starting_address_{};
uint16_t number_of_points_{};
};
template<typename T>
class ReadRegisters : public ReadModbusFunction {
public:
ReadRegisters(const RegisterType register_type, const uint16_t transaction_id, const uint8_t unit_id, const uint16_t starting_address, const uint16_t number_of_points)
: ReadModbusFunction(transaction_id, unit_id),
register_type_(register_type),
starting_address_(starting_address),
number_of_points_(number_of_points) {
}
[[nodiscard]] std::array<std::byte, 5> rawPdu() const override {
std::array<std::byte, 5> result;
result[0] = getFunctionCode();
ranges::copy(toBytes(starting_address_), (result | ranges::views::drop(1) | ranges::views::take(2)).begin());
ranges::copy(toBytes(wordCount()), (result | ranges::views::drop(3) | ranges::views::take(2)).begin());
return result;
}
[[nodiscard]] uint8_t expectedByteCount() const override {
return gsl::narrow<uint8_t>(number_of_points_*std::max(sizeof(T), sizeof(uint16_t)));
}
[[nodiscard]] uint16_t wordCount() const {
return expectedByteCount() / sizeof(uint16_t);
}
[[nodiscard]] nonstd::expected<core::RecordField, std::error_code> responseToRecordField(const std::span<const std::byte> resp_pdu) const override {
const auto resp_bytes = getRespBytes(resp_pdu);
if (!resp_bytes)
return nonstd::make_unexpected(resp_bytes.error());
std::vector<T> holding_registers{};
for (auto&& register_chunk : ranges::views::chunk(*resp_bytes, std::max(sizeof(T), sizeof(uint16_t)))) {
std::array<std::byte, std::max(sizeof(T), sizeof(uint16_t))> register_value{};
ranges::copy(register_chunk, register_value.begin());
holding_registers.push_back(fromBytes<T>(std::move(register_value)));
}
if (holding_registers.size() == 1) {
T val = holding_registers.at(0);
return core::RecordField{val};
}
core::RecordArray record_array;
for (auto holding_register : holding_registers) {
record_array.emplace_back(holding_register);
}
return core::RecordField{std::move(record_array)};
}
[[nodiscard]] std::byte getFunctionCode() const override {
switch (register_type_) {
case RegisterType::holding:
return std::byte{0x03};
case RegisterType::input:
return std::byte{0x04};
default:
throw std::invalid_argument(fmt::format("Invalid RegisterType {}", magic_enum::enum_underlying(register_type_)));
}
}
bool operator==(const ReadModbusFunction& rhs) const override {
const auto read_holding_registers_rhs = dynamic_cast<const ReadRegisters<T>*>(&rhs);
if (!read_holding_registers_rhs)
return false;
return read_holding_registers_rhs->number_of_points_ == this->number_of_points_ &&
read_holding_registers_rhs->starting_address_ == this->starting_address_ &&
read_holding_registers_rhs->transaction_id_ == this->transaction_id_;
}
protected:
RegisterType register_type_{};
uint16_t starting_address_{};
uint16_t number_of_points_{};
};
} // namespace org::apache::nifi::minifi::modbus