Program Listing for File IOConnection.cpp

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

//
// Created by Aleksey Timin on 11/18/19.
//

#include "IOConnection.h"
#include "eip/CommonPacketItemFactory.h"
#include "eip/CommonPacket.h"
#include "cip/connectionManager/NetworkConnectionParams.h"
#include "utils/Buffer.h"
#include "utils/Logger.h"

namespace eipScanner {
    using utils::Logger;
    using utils::LogLevel;
    using utils::Buffer;
    using eip::CommonPacket;
    using eip::CommonPacketItemFactory;
    using cip::connectionManager::NetworkConnectionParams;

    IOConnection::IOConnection()
            : _o2tNetworkConnectionId{0}
            , _t2oNetworkConnectionId{0}
            , _o2tAPI{0}
            , _t2oAPI{0}
            , _o2tDataSize{0}
            , _t2oDataSize{0}
            , _o2tFixedSize(true)
            , _t2oFixedSize(true)
            , _o2tTimer{0}
            , _t2o_timer{0}
            , _connectionTimeoutMultiplier{0}
            , _connectionTimeoutCount{0}
            , _o2tSequenceNumber{0}
            , _t2oSequenceNumber{0}
            , _serialNumber{0}
            , _transportTypeTrigger{0}
            , _o2tRealTimeFormat{0}
            , _t2oRealTimeFormat{0}
            , _sequenceValueCount{0}
            , _connectionPath(0)
            , _originatorVendorId{0}
            , _originatorSerialNumber{0}
            , _outputData()
            , _receiveDataHandle([](auto, auto, auto) {})
            , _closeHandle([]() {})
            , _sendDataHandle([](auto) {})
            , _lastHandleTime(std::chrono::steady_clock::now()) {
    }

    IOConnection::~IOConnection() = default;

    void IOConnection::setDataToSend(const std::vector<uint8_t> &data) {
        _outputData = data;
    }

    void IOConnection::setReceiveDataListener(ReceiveDataHandle handle) {
        _receiveDataHandle = std::move(handle);
    }

    void IOConnection::setSendDataListener(SendDataHandle handle) {
            _sendDataHandle = std::move(handle);
    }

    void IOConnection::setCloseListener(CloseHandle handle) {
        _closeHandle = std::move(handle);
    }

    void IOConnection::notifyReceiveData(const std::vector<uint8_t> &data) {
        Buffer buffer(data);
        cip::CipUdint runtimeHeader = 0;
        cip::CipUint sequenceValueCount = 0;

        if (_t2oRealTimeFormat) {
            buffer >> runtimeHeader;
        }

        if ((_transportTypeTrigger & NetworkConnectionParams::CLASS1) > 0
            || (_transportTypeTrigger & NetworkConnectionParams::CLASS3) > 0) {
            buffer >> sequenceValueCount;
        }

        std::vector<uint8_t> ioData(data.begin() + buffer.pos(), data.end());
        if (_t2oFixedSize && ioData.size() != _t2oDataSize) {
            Logger(LogLevel::WARNING) << "Connection T2O_ID=" << _t2oNetworkConnectionId
                << " has fixed size " << _t2oDataSize << " bytes but " << ioData.size()
                << " bytes were received. Ignore this data.";
        } else {
            _connectionTimeoutCount = 0;
            _receiveDataHandle(runtimeHeader, sequenceValueCount,
                               ioData);
        }
    }

    bool IOConnection::notifyTick() {
        auto now = std::chrono::steady_clock::now();
        auto sinceLastHandle =
            std::chrono::duration_cast<std::chrono::milliseconds>(now - _lastHandleTime);

        auto periodInMicroS = sinceLastHandle.count() * 1000;
        _connectionTimeoutCount += periodInMicroS;
        if (_connectionTimeoutCount > _connectionTimeoutMultiplier * _t2oAPI) {
            Logger(LogLevel::WARNING) << "Connection SeriaNumber=" << _serialNumber << " is closed by timeout";
            _closeHandle();
            return false;
        }

        _lastHandleTime = now;

        _o2tTimer += periodInMicroS;
        if (_o2tTimer >= _o2tAPI) {
            _o2tSequenceNumber++;
            CommonPacket commonPacket;
            CommonPacketItemFactory factory;
            commonPacket << factory.createSequenceAddressItem(_o2tNetworkConnectionId, _o2tSequenceNumber);

            Buffer buffer;
            if ((_transportTypeTrigger & NetworkConnectionParams::CLASS1) > 0
                || (_transportTypeTrigger & NetworkConnectionParams::CLASS3) > 0) {
                buffer << ++_sequenceValueCount;
            }

            if (_o2tRealTimeFormat) {
                cip::CipUdint header = 1; //TODO: Always RUN
                buffer << header;
            }

            _o2tTimer = 0;
            _sendDataHandle(_outputData);
            if (_o2tFixedSize && _outputData.size() != _o2tDataSize)  {
                Logger(LogLevel::WARNING) << "Connection O2T_ID=" << _o2tNetworkConnectionId
                                          << " has fixed size " << _o2tDataSize << " bytes but " << _outputData.size()
                                          << " bytes are to send. Don't send this data.";
            } else {
                buffer << _outputData;
                commonPacket << factory.createConnectedDataItem(buffer.data());

                _socket->Send(commonPacket.pack());
            }
        }

        return true;
    }
}