Program Listing for File EPath.cpp

Return to documentation for file (src/cip/EPath.cpp)

//
// Created by Aleksey Timin on 11/16/19.
//
#include <stdexcept>
#include "utils/Buffer.h"
#include "EPath.h"

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

    enum class EPathSegmentTypes : CipUsint  {
        CLASS_8_BITS = 0x20,
        CLASS_16_BITS = 0x21,
        INSTANCE_8_BITS = 0x24,
        INSTANCE_16_BITS = 0x25,
        ATTRIBUTE_8_BITS = 0x30,
        ATTRIBUTE_16_BITS = 0x31,
    };

    EPath::EPath()
            : _classId{0}
            , _objectId{0}
            , _attributeId{0}
            , _size{0}{

    }

    EPath::EPath(CipUint classId)
        : _classId{classId}
        , _objectId{0}
        , _attributeId{0}
        , _size{1} {

    }

    EPath::EPath(CipUint classId, CipUint objectId)
            : _classId{classId}
            , _objectId{objectId}
            , _attributeId{0}
            , _size{2} {

    }

    EPath::EPath(CipUint classId, CipUint objectId, CipUint attributeId)
            : _classId{classId}
            , _objectId{objectId}
            , _attributeId{attributeId}
            , _size{3} {
    }

    std::vector<uint8_t> EPath::packPaddedPath(bool use_8_bit_path_segments) const {
        if (use_8_bit_path_segments)
        {
            Buffer buffer(_size*2);

            auto classSegment = static_cast<CipUsint>(EPathSegmentTypes::CLASS_8_BITS);
            buffer << classSegment << static_cast<CipUsint>(_classId);

            if (_size > 1) {
                auto instanceSegment = static_cast<CipUsint>(EPathSegmentTypes::INSTANCE_8_BITS);
                buffer << instanceSegment << static_cast<CipUsint>(_objectId);

                if (_size > 2) {
                    auto attributeSegment = static_cast<CipUsint>(EPathSegmentTypes::ATTRIBUTE_8_BITS);
                    buffer << attributeSegment << static_cast<CipUsint>(_attributeId);
                }
            }

            return buffer.data();
        }
        else
        {
            Buffer buffer(_size*4);

            auto classSegment = static_cast<CipUint>(EPathSegmentTypes::CLASS_16_BITS);
            buffer << classSegment << _classId;

            if (_size > 1) {
                auto instanceSegment = static_cast<CipUint>(EPathSegmentTypes::INSTANCE_16_BITS);
                buffer << instanceSegment << _objectId;

                if (_size > 2) {
                    auto attributeSegment = static_cast<CipUint>(EPathSegmentTypes::ATTRIBUTE_16_BITS);
                    buffer << attributeSegment << _attributeId;
                }
            }

            return buffer.data();
        }
    }

    CipUint EPath::getClassId() const {
        return _classId;
    }

    CipUint EPath::getObjectId() const {
        return _objectId;
    }

    CipUint EPath::getAttributeId() const {
        return _attributeId;
    }

    CipUsint EPath::getSizeInWords(bool use_8_bit_path_segments) const {
        if (use_8_bit_path_segments) {
            return _size;
        }
        else {
            return _size*2;
        }
    }

    std::string EPath::toString() const {
        std::string msg = "[classId=" + std::to_string(_classId);
        if (_size > 1) {
            msg += " objectId=" + std::to_string(_objectId);
            if (_size > 2) {
                msg += " attributeId=" + std::to_string(_attributeId);
            }
        }

        msg += "]";
        return msg;
    }

    void EPath::expandPaddedPath(const std::vector<uint8_t> &data) {
        Buffer buffer(data);

        _classId = 0;
        _objectId = 0;
        _attributeId = 0;
        _size = 0;

        for (int i = 0; i < data.size() && !buffer.empty(); ++i) {
            EPathSegmentTypes segmentType;
            CipUsint ignore = 0;
            CipUsint byte;
            CipUint word;
            buffer >> reinterpret_cast<CipUsint&>(segmentType);
            switch (segmentType) {
                case EPathSegmentTypes::CLASS_8_BITS:
                    buffer >> byte;
                    _classId = byte;
                    break;
                case EPathSegmentTypes::CLASS_16_BITS:
                    buffer >> ignore >> word;
                    _classId = word;
                    break;
                case EPathSegmentTypes::INSTANCE_8_BITS:
                    buffer >> byte;
                    _objectId = byte;
                    break;
                case EPathSegmentTypes::INSTANCE_16_BITS:
                    buffer >> ignore >> word;
                    _objectId = word;
                    break;
                case EPathSegmentTypes::ATTRIBUTE_8_BITS:
                    buffer >> byte;
                    _attributeId = byte;
                    break;
                case EPathSegmentTypes::ATTRIBUTE_16_BITS:
                    buffer >> ignore >> word;
                    _attributeId = word;
                    break;
                default:
                    throw std::runtime_error("Unknown EPATH segment =" + std::to_string(static_cast<int>(segmentType)));
            }
        }

        if (!buffer.isValid()) {
            throw std::runtime_error("Wrong EPATH format");
        }

        if (_classId > 0) {
            _size++;

            if (_objectId > 0) {
                _size++;

                if (_attributeId > 0) {
                    _size++;
                }
            }
        }
    }

    bool EPath::operator==(const EPath &other) const {
        return _size == other._size
            &&_classId == other._classId
            && _objectId == other._objectId
            && _attributeId == other._attributeId;
    }
}
}