Program Listing for File ParameterObject.cpp

Return to documentation for file (src/ParameterObject.cpp)

//
// Created by Aleksey Timin on 12/4/19.
//

#include "ParameterObject.h"
#include "utils/Buffer.h"
#include "utils/Logger.h"

namespace eipScanner {
    using utils::Buffer;
    using utils::Logger;
    using utils::LogLevel;
    using namespace ::eipScanner::cip;

    enum ParameterObjectAttributeIds : cip::CipUsint {
        VALUE = 1,
        LINK_PATH_SIZE = 2,
        DESCRIPTOR = 4,
        DATA_TYPE = 5,
        DATA_SIZE = 6,
        NAME_STRING = 7,
        UNIT_STRING = 8,
        HELP_STRING = 9,
        MIN_VALUE = 10,
        MAX_VALUE = 11,
        DEFAULT_VALUE = 12,
        SCALING_MULTIPLIER = 13,
        SCALING_DIVISOR = 14,
        SCALING_BASE = 15,
        SCALING_OFFSET = 16
    };

    enum DescriptorAttributeBits : cip::CipUint {
        SUPPORTS_SCALING = 1 << 2,
        READ_ONLY = 1 << 4,
    };

    ParameterObject::ParameterObject(cip::CipUint id, bool fullAttributes,
                                     const SessionInfoIf::SPtr &si)
        : ParameterObject(id, fullAttributes, si, std::make_shared<MessageRouter>()) {
    }

    ParameterObject::ParameterObject(cip::CipUint instanceId, bool fullAttributes, size_t typeSize)
            :  BaseObject(CLASS_ID, instanceId)
            , _hasFullAttributes(fullAttributes)
            , _isScalable(false)
            , _isReadOnly(false)
            , _parameter(0)
            , _value(typeSize)
            , _type(CipDataTypes::ANY)
            , _name{""}
            , _minValue(typeSize)
            , _maxValue(typeSize)
            , _defaultValue(typeSize)
            , _scalingMultiplier(1)
            , _scalingDivisor(1)
            , _scalingBase(1)
            , _scalingOffset(0)
            , _precision(0) {
    }

    ParameterObject::ParameterObject(cip::CipUint instanceId, bool fullAttributes,
                                     const SessionInfoIf::SPtr &si,
                                     const MessageRouter::SPtr& messageRouter)
            :  BaseObject(CLASS_ID, instanceId)
            , _hasFullAttributes(fullAttributes)
            , _isScalable(false)
            , _isReadOnly(false)
            , _parameter(0)
            , _value(0)
            , _type(CipDataTypes::ANY)
            , _name{""}
            , _minValue(0)
            , _maxValue(0)
            , _defaultValue(0)
            , _scalingMultiplier(1)
            , _scalingDivisor(1)
            , _scalingBase(1)
            , _scalingOffset(0)
            , _precision(0)
            , _messageRouter{messageRouter} {


        Logger(LogLevel::DEBUG) << "Read data from parameter ID=" << instanceId;

        auto response = _messageRouter->sendRequest(si,
                ServiceCodes::GET_ATTRIBUTE_SINGLE,
                EPath(CLASS_ID, instanceId,
                        ParameterObjectAttributeIds::DATA_SIZE),{});


        if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) {
            CipUsint dataSize;
            Buffer buffer(response.getData());
            buffer >> dataSize;

            _value.resize(dataSize);
            _minValue.resize(dataSize);
            _maxValue.resize(dataSize);
            _defaultValue.resize(dataSize);
        } else {
            logGeneralAndAdditionalStatus(response);
            throw std::runtime_error("Failed to read data size of the parameter");
        }

        response = messageRouter->sendRequest(si,
                cip::ServiceCodes::GET_ATTRIBUTE_ALL, cip::EPath(CLASS_ID, instanceId), {});

        if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) {
            Buffer buffer(response.getData());
            buffer >> _value;

            CipUsint linkPathSize;
            buffer >> linkPathSize;

            std::vector<uint8_t> ignore(linkPathSize);
            buffer >> ignore;

            CipWord descriptor;
            buffer >> descriptor >> reinterpret_cast<CipUsint&>(_type);

            _parameter = instanceId;
            _isScalable = descriptor & DescriptorAttributeBits::SUPPORTS_SCALING;
            _isReadOnly = descriptor & DescriptorAttributeBits::READ_ONLY;
            Logger(LogLevel::DEBUG) << "Parameter object ID=" << instanceId
                                    << " has descriptor=0x" << std::hex << descriptor
                                    << " scalable=" << _isScalable
                                    << " readonly=" << _isReadOnly;

            if (_hasFullAttributes) {
                ignore.resize(1);
                CipShortString  name, units, help;

                buffer >> ignore >> name >> units >> help
                    >> _minValue >> _maxValue >> _defaultValue;

                _name = name.toStdString();
                _units = units.toStdString();
                _help = help.toStdString();

                if (_isScalable) {
                    ignore.resize(16); // Ignore scaling attributes we read it separately due to a bug
                    buffer >> ignore
                        >> _precision;

                    std::vector<uint8_t> data;
                    for (int attrId = ParameterObjectAttributeIds::SCALING_MULTIPLIER;
                         attrId <= ParameterObjectAttributeIds::SCALING_OFFSET;
                         ++attrId) {
                        auto response = _messageRouter->sendRequest(si,
                                                               ServiceCodes::GET_ATTRIBUTE_SINGLE,
                                                               EPath(CLASS_ID, instanceId, attrId),
                                                               {});

                        if (response.getGeneralStatusCode() != GeneralStatusCodes::SUCCESS) {
                            cip::logGeneralAndAdditionalStatus(response);
                            throw std::runtime_error("Failed to read value of attribute=" + std::to_string(attrId));
                        }

                        data.insert(data.end(), response.getData().begin(), response.getData().end());
                    }

                    buffer = Buffer(data);
                    buffer >> _scalingMultiplier >> _scalingDivisor
                        >> _scalingBase >> _scalingOffset;
                }
            }

            if (!buffer.isValid()) {
                std::runtime_error("Not enough data in the response");
            }

            Logger(LogLevel::DEBUG) << "Read Parameter Object"
                << " ID=" << instanceId
                << " ValueSize=" << _value.size()
                << " ValueType=0x" << std::hex << static_cast<int>(_type)
                << " Name=" << _name;
        } else {
            cip::logGeneralAndAdditionalStatus(response);
            throw std::runtime_error("Failed to read all attributes");
        }
    }

    void ParameterObject::updateValue(const SessionInfoIf::SPtr& si) {
        auto response = _messageRouter->sendRequest(si,
                                ServiceCodes::GET_ATTRIBUTE_SINGLE,
                                EPath(CLASS_ID, getInstanceId(),
                                ParameterObjectAttributeIds::VALUE),{});

        if (response.getGeneralStatusCode() == GeneralStatusCodes::SUCCESS) {
            Buffer buffer(response.getData());
            buffer >> _value;
        } else {
            cip::logGeneralAndAdditionalStatus(response);
            throw std::runtime_error("Failed to read value");
        }
    }

    ParameterObject::~ParameterObject() = default;

    const std::string &ParameterObject::getName() const {
        return _name;
    }

    cip::CipDataTypes ParameterObject::getType() const {
        return _type;
    }

    bool ParameterObject::hasFullAttributes() const {
        return _hasFullAttributes;
    }

    bool ParameterObject::isScalable() const {
        return _isScalable;
    }

    bool ParameterObject::isReadOnly() const {
        return _isReadOnly;
    }

    const std::string &ParameterObject::getUnits() const {
        return _units;
    }

    const std::string &ParameterObject::getHelp() const {
        return _help;
    }

    const cip::CipUint &ParameterObject::getParameter() const {
        return _parameter;
    }

    CipUint ParameterObject::getScalingMultiplier() const {
        return _scalingMultiplier;
    }

    CipUint ParameterObject::getScalingDivisor() const {
        return _scalingDivisor;
    }

    CipUint ParameterObject::getScalingBase() const {
        return _scalingBase;
    }

    CipInt ParameterObject::getScalingOffset() const {
        return _scalingOffset;
    }

    CipUsint ParameterObject::getPrecision() const {
        return _precision;
    }

    void ParameterObject::setScalable(bool isScalable) {
        _isScalable = isScalable;
    }

    void ParameterObject::setReadOnly(bool isReadOnly) {
        _isReadOnly = isReadOnly;
    }

    void ParameterObject::setType(CipDataTypes type) {
        _type = type;
    }

    void ParameterObject::setName(const std::string &name) {
        _name = name;
    }

    void ParameterObject::setUnits(const std::string &units) {
        _units = units;
    }

    void ParameterObject::setHelp(const std::string &help) {
        _help = help;
    }

    void ParameterObject::setScalingMultiplier(CipUint scalingMultiplier) {
        _scalingMultiplier = scalingMultiplier;
    }

    void ParameterObject::setScalingDivisor(CipUint scalingDivisor) {
        _scalingDivisor = scalingDivisor;
    }

    void ParameterObject::setScalingBase(CipUint scalingBase) {
        _scalingBase = scalingBase;
    }

    void ParameterObject::setScalingOffset(CipInt scalingOffset) {
        _scalingOffset = scalingOffset;
    }

    void ParameterObject::setPrecision(CipUsint precision) {
        _precision = precision;
    }

    cip::CipLreal ParameterObject::actualToEngValue(cip::CipLreal actualValue) const {
        if (_isScalable) {
            return ((actualValue + _scalingOffset) * _scalingMultiplier * _scalingBase)
                   / (_scalingDivisor * std::pow(10, _precision));
        } else {
            return actualValue;
        }
    }

    cip::CipLreal ParameterObject::engToActualValue(cip::CipLreal engValue) const {
        if (_isScalable) {
            return (engValue * _scalingDivisor * std::pow(10, _precision))
                   / (_scalingMultiplier * _scalingBase) - _scalingOffset;
        } else {
            return engValue;
        }
    }
}