lexus2k / tinyproto

Tiny Software Protocol for communication over UART, SPI, etc
GNU General Public License v3.0
225 stars 51 forks source link

tinyproto Fd enhancement in case supporting multiple devices at the same time #14

Closed TPopiel closed 3 years ago

TPopiel commented 3 years ago

Multiple devices support is realized with additional pointer to user context provided during FD protocol initialization, and then the same pointer to user context is received when callbacks are called by the library.

CLAassistant commented 3 years ago

CLA assistant check
All committers have signed the CLA.

lexus2k commented 3 years ago

Hi,

Could you please provide some clarifications, case detailed description and code example, which the changes are required for? You're changing external API (and it is still unclear from the code, what the changes solve), and that can be harmful for other users.

Best regards

TPopiel commented 3 years ago

Hi,

I would like to simultaneously handle multiple devices connected to desktop PC over various COM ports. Every device implements HDLC FD protocol with use of tinyproto library. So I propose following implementation at desktop host:

  1. Implement static functions that handles raw read, write and on_recevied_callback, on_sent_callback :
static int serial_send_fd(void* context, const void* b, int s)  // call function with previously set user_context - this happens in HdlcFdProxy
            {
                try
                {
                    auto proxy = reinterpret_cast<HdlcFdProxy*>(context);
                    auto buffer = reinterpret_cast<uint8_t*>(const_cast<void*>(b));
                    std::vector<uint8_t> output;
                    std::copy_n(buffer, s, std::back_inserter(output));
                    return static_cast<int>(proxy->write(output));
                }
                catch(...)
                {
                    return -1;
                }

            }

static int serial_read_fd(void* context, void* b, int s) // call function with previously set user_context - this happens in HdlcFdProxy
            {
                try
                {
                    auto proxy = reinterpret_cast<HdlcFdProxy*>(context);
                    std::vector<uint8_t> input;
                    auto readBytes = proxy->read(input, s);
                    auto buffer = static_cast<uint8_t*>(b);
                    std::copy_n(input.begin(), input.size(), buffer);
                    return static_cast<int>(readBytes);
                }
                catch (...)
                {
                    return -1;
                }
            }

static void on_receive_callback(void* context, Tiny::IPacket &pkt) // call function with previously set user_context - this happens in HdlcFdProxy
            {
                auto proxy = reinterpret_cast<HdlcFdProxy*>(context);
                if (proxy != nullptr)
                {
                    auto data = pkt.data();
                    std::vector<uint8_t> received;
                    std::copy_n(data, pkt.size(), std::back_inserter(received));
                    proxy->onReceive(received);
                }
            }
  1. Implement HdlcFdProxy class that handles communication with particular device over COM port and is responsible for performing raw read and write operations to particular, when the class initializes HDLC FD protocol it set user_context to itself - so then static callback implementation is very easy to perform on specific proxy object.
HdlcFdProxy::HdlcFdProxy(...) : 
{
       auto proto = std::make_unique<Tiny::ProtoFdD>(static_cast<int>(tiny_fd_buffer_size_by_mtu( MAX_FRAME_SIZE, WINDOW_SIZE )));
    proto->enableCrc16();
    proto->setWindowSize(WINDOW_SIZE);
    proto->setSendTimeout(500);
    proto->setReceiveCallback( on_receive_callback );
    proto->begin( serial_send_fd , serial_read_fd , this );          // sets user_context with this!!!
}

...

size_t HdlcFdProxy::read(std::vector< uint8_t > &buffer, size_t size)
{
  if (m_connectionIsOpen)
  {
    return m_serial->read(buffer, size);
  }

  return 0;
}

size_t HdlcFdProxy::write(const std::vector< uint8_t > &data)
{
  if (m_connectionIsOpen)
  {
    return m_serial->write(data);
  }

  return 0;
}

Please let me know if still more detailed explanation or examples are required. Regards, Tomek

lexus2k commented 3 years ago

Please, check latest sources, and let me know if new code suites your needs.

TPopiel commented 3 years ago

Latest commit fully address my needs, many thanks for it! I also noticed that there is build error under MSVC - it deals with struct default initialization, could you look on that?

lexus2k commented 3 years ago

Thank you. Fixed that on my MSVC 2019. Keep an eye on the upcoming changes: cpp_api, there will be changes to API. I'm still trying to make library usage as simple as possible.

TPopiel commented 3 years ago

Since the requested capabilities are already on the master, I 'm closing my PR. Many thanks!