Overslaan naar inhoud
CTech Digital
  • Startpagina
  • Odoo services
  • AI services
  • Contact
  • 0
  • Nederlands (BE) English (UK) Français
CTech Digital
  • 0
    • Startpagina
    • Odoo services
    • AI services
    • Contact
  • Nederlands (BE) English (UK) Français

8/6/25

  • Alle blogs
  • Daily blog
  • 8/6/25
  • 8 juni 2025 in
    CTech Metrology, Luc Wens

    To end the proxy, we can send a TAG_COMMAND_QUIT, this is caught in the main of the proxy and will end it.

    So in analogy of SendRequest, we added SendMessage to CProxyDevice, but this resulted in link errors because of the windows SDK define for SendMessage, which changes this to SendMessageA in our case, .... sigh. So on suggestion of our good friend Gemini I added Track in the middle.

    CProxyDevice still has a number of functions that need to be deleted:

    SendCommand(LPCTSTR iCommand, bool StopProxy = true);

    SendCommandWaitReturn(LPCTSTR iCommand, bool StopProxy = true, bool CheckHandshake = true);

    SendCommand(std::unique_ptr<TiXmlElement> &XMLElement, bool StopProxy = true);

    SendCommandWaitReturn(std::unique_ptr<TiXmlElement> &XMLElement, bool StopProxy = true, bool CheckHandshake = true);


    By getting rid of these functions we force all proxy interactions to use CTrack::Message instead of the XML.

    Closing proxies on exit

    In this scenario, the proxy is not closed:

    • Start engine
    • Type 'h'
    • Type 'q'
    • => engine closes, proxy remains open

    The reason is that the hardware detection is done on the static instance of CProxyTestDevice, and this will continue to exist until after our main thread in engine. But in the main of engine we call CCommunicationObject::CloseAllConnections() in the shutdown, which will forcefully close all communications. 

    So by the time the static CProxyTestDevice meets its destructor, it cannot longer communicate with the proxy to send a TAG_COMMAND_QUIT in StopProxy.

    Sending a TAG_COMMAND_QUIT in CloseAllConnections is not an option because not all connections are necessarily proxy communications.

    1. A possible solution is to perform the hardware detection not on the static instance, but rather on a dynamic copy, and copy the result back to the static afterwards.
    2. Another solution is to add a routine ShutDownProxies just before CloseAllConnections that iterates through all static proxy devices and orders them to send a quit to their proxies.
    3. Or we could also have the proxies closed after the hardware detection, but at the other hand we want to prevent that proxies get opened and closed all the time.

    In any case we don't want proxy specifics to end up in the more general communication libraries. 

    So I went for the second option: see CProxyDevice::StopAllProxies(). For this I needed to add a template type search to NodeFactory because CProxyDevice is not derived from Node. 

    Solving race conditions for free TCP ports

    When doing a detection with the template and Vicon active, we get the next 

    We can see in the top that both vicon and the template choose port 40001, but only template will finally connect, the vicon proxy sees that 40001 is taken and starts a server on 40002, but our device is still on 40001.

    To solve this we'll have to implement a proxy register map as static in CProxyDevice.

    For each device, identified by its proxy exe name, we should keep track of the process info and the port, which are now members of CProxyDevice itself.

    We make a struct containing a port and process information and link that to the string key.

    As with CCommunicationObject, we'll use a Phoenix singleton with mutex protection to store the proxy register.

    We need to update:

    • StartProxy
    • StopProxy

    StartProxy

    Old routine:

    • m_TCP_IPServer is not empty: return because our proxy is running somewhere else
    • Check if proxy is running
      • No
        • Compose exe
        • Find available free port
        • call ProcessStart and keep process info
      • Yes
        • Use ListTcpConnectionsForApp to get the TCP port

    New routine

    • if m_TCP_IPServer is not empty: return 
    • Check register : static routine RegisterStartProxy(const std::string ProxyExe,int& TCPport, ProcessInfo& processInfo)
      • Create Key of ProxyExe
      • In register YES
        • return TCP port and ProcessInfo
        • If the proxy is registered but not running, then maybe it crashed, we remove it from the register and proceed with the NO case
      • NO
        • Find a free TCP port starting from TCP_PORT_START
          • Exclude ports already in register => make set<int> of taken ports
          • Start Port = TCP_PORT_START
          • While InvalidPort
            • if Port in set => invalid = true
            • if !PortFree => invalid = true
            • if invalid Port++
          • Register port under Proxy key
          • Start Proxy and store process info 
          • If fail to start, remove entry, return false

    Does our map need to keep track of all proxydevice instances? A reason for doing this would be if we want to stop all proxydevices at shutdown, as we need to send a message with TAG_COMMAND_QUIT . Keeping track of pointers can be a challenge, we would have to work with weak_ptrs which we would need to extract from strong pointers from CNode. On the other hand we could also establish a quick local tcp client connection and send the quit message.

    Another approach, since all the proxies are designed to handle keyboard input, and to terminate when 'q' is typed. So we could have our main program get a handle to the console input and send a letter q.

    This approach is certainly worth a try and is probably more robust then making a temporary client and send a quit message, or keeping a list of weak pointers to proxy devices and use that to terminate.


    Starting a proxy multiple times

    This could be the case when we have multiple laser trackers and we use one proxy for each tracker.

    In that case we also need to provide the serial number, the key in the proxy database is then a combination of proxy exe and serial.

    Since the class CProxyDevice stands on its own, we could add a serial string to it, but as soon as we combine with CDevice, we get 2 serials, so to prevent conflicts here we'll have a pure virtual GetSerialForProxy function for CProxyDevice to resolve this.

    Result

    Now we get a new non-taken port for the second device.

    A quick test showed that all works correctly for :

    • Hardware detection
    • Configuration detection
    • Closing proxies on engine exit
    in Daily blog
    7/6/25
    Implementing messages in Vicon driver
    Copyright © CTech
    Nederlands (BE) | English (UK) | Français
    Aangeboden door Odoo - De #1 Open source e-commerce