LCOV - code coverage report
Current view: top level - SITL - SIM_SerialDevice.cpp (source / functions) Hit Total Coverage
Test: lcov.info Lines: 58 68 85.3 %
Date: 2026-09-09 11:02:08 Functions: 9 9 100.0 %

          Line data    Source code
       1             : /*
       2             :    This program is free software: you can redistribute it and/or modify
       3             :    it under the terms of the GNU General Public License as published by
       4             :    the Free Software Foundation, either version 3 of the License, or
       5             :    (at your option) any later version.
       6             : 
       7             :    This program is distributed in the hope that it will be useful,
       8             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
       9             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      10             :    GNU General Public License for more details.
      11             : 
      12             :    You should have received a copy of the GNU General Public License
      13             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      14             :  */
      15             : /*
      16             :   base class for serially-attached simulated devices
      17             : */
      18             : 
      19             : #include <AP_HAL/AP_HAL.h>
      20             : #include <SITL/SITL.h>
      21             : 
      22             : #include "SIM_SerialDevice.h"
      23             : 
      24             : #include <stdio.h>
      25             : #include <unistd.h>
      26             : #include <fcntl.h>
      27             : 
      28             : using namespace SITL;
      29             : 
      30       25211 : SerialDevice::SerialDevice(uint16_t tx_bufsize, uint16_t rx_bufsize)
      31             : {
      32       25211 :     to_autopilot = NEW_NOTHROW ByteBuffer{tx_bufsize};
      33       25211 :     from_autopilot = NEW_NOTHROW ByteBuffer{rx_bufsize};
      34             :     // devices attached to a simulated serial port have this set when
      35             :     // the autopilot opens that port:
      36       25211 :     autopilot_baud = 0;
      37       25211 : }
      38             : 
      39   374457021 : bool SerialDevice::init_sitl_pointer()
      40             : {
      41   374457021 :     if (_sitl == nullptr) {
      42       14684 :         _sitl = AP::sitl();
      43       14684 :         if (_sitl == nullptr) {
      44           0 :             return false;
      45             :         }
      46             :     }
      47   374457021 :     return true;
      48             : }
      49             : 
      50             : #if AP_SIM_SERIALDEVICE_CORRUPTION_ENABLED
      51             : ssize_t SerialDevice::corrupt_transfer(char *buffer, const ssize_t ret, const size_t size) const
      52             : {
      53             :     if (ret > 0 && (rand() % 100) < 2) {
      54             :         // drop a random byte from returned data:
      55             :         const size_t byte_ofs_to_drop = rand() % ret;
      56             :         fprintf(stderr, "dropping byte at offset %u\n", unsigned(byte_ofs_to_drop));
      57             :         memmove(&buffer[byte_ofs_to_drop], &buffer[byte_ofs_to_drop+1], ret - byte_ofs_to_drop - 1);
      58             :         return ret - 1;
      59             :     }
      60             : 
      61             :     if (ret > 0 && size_t(ret) < size && (rand() % 100) < 2) {
      62             :         // add a random byte to the stream:
      63             :         const size_t byte_ofs_to_add = rand() % ret;
      64             :         fprintf(stderr, "adding byte at offset %u\n", unsigned(byte_ofs_to_add));
      65             :         memmove(&buffer[byte_ofs_to_add+1], &buffer[byte_ofs_to_add], ret - byte_ofs_to_add);
      66             :         buffer[byte_ofs_to_add] = rand()*256;
      67             :         return ret + 1;
      68             :     }
      69             : 
      70             :     if (ret > 0 && unsigned(ret) < size && (rand() % 100) < 2) {
      71             :         // corrupt a random byte in the stream:
      72             :         const size_t byte_ofs_to_corrupt = rand() % ret;
      73             :         fprintf(stderr, "corrupting byte at offset=%u\n", unsigned(byte_ofs_to_corrupt));
      74             :         buffer[byte_ofs_to_corrupt] = rand()*256;
      75             :         return ret;
      76             :     }
      77             : 
      78             :     return ret;
      79             : }
      80             : #endif
      81             : 
      82   373481273 : ssize_t SerialDevice::read_from_autopilot(char *buffer, const size_t size) const
      83             : {
      84   373481273 :     ssize_t ret = from_autopilot->read((uint8_t*)buffer, size);
      85             : #if AP_SIM_SERIALDEVICE_CORRUPTION_ENABLED
      86             :     ret = corrupt_transfer(buffer, ret, size);
      87             : #endif
      88             : 
      89             :     // if (ret > 0) {
      90             :     //     ::fprintf(stderr, "SIM_SerialDevice: read from autopilot (%u): (", (unsigned)ret);
      91             :     //     for (ssize_t i=0; i<ret; i++) {
      92             :     //         const uint8_t x = buffer[i];
      93             :     //         ::fprintf(stderr, "%02X", (unsigned)x);
      94             :     //     }
      95             :     //     ::fprintf(stderr, " ");
      96             :     //     for (ssize_t i=0; i<ret; i++) {
      97             :     //         ::fprintf(stderr, "%c", buffer[i]);
      98             :     //     }
      99             :     //     ::fprintf(stderr, ")\n");
     100             :     // }
     101   373481273 :     return ret;
     102             : }
     103             : 
     104    17843353 : ssize_t SerialDevice::write_to_autopilot(const char *buffer, const size_t size) const
     105             : {
     106    17843353 :     if (!is_match_baud()) {
     107         747 :         return -1;
     108             :     }
     109             : 
     110    17842606 :     const ssize_t ret = to_autopilot->write((uint8_t*)buffer, size);
     111    17842606 :     return ret;
     112             : }
     113             : 
     114   491705065 : ssize_t SerialDevice::read_from_device(char *buffer, const size_t size) const
     115             : {
     116   491705065 :     if (!is_match_baud()) {
     117       26437 :         return -1;
     118             :     }
     119             : 
     120   491678628 :     ssize_t ret = to_autopilot->read((uint8_t*)buffer, size);
     121             : #if AP_SIM_SERIALDEVICE_CORRUPTION_ENABLED
     122             :     ret = corrupt_transfer(buffer, ret, size);
     123             : #endif
     124             :     // ::fprintf(stderr, "read_from_device: (");
     125             :     // for (ssize_t i=0; i<ret; i++) {
     126             :     //     ::fprintf(stderr, "%02X", (uint8_t)buffer[i]);
     127             :     // }
     128             :     // ::fprintf(stderr, ") (\n");
     129             :     // for (ssize_t i=0; i<ret; i++) {
     130             :     //     ::fprintf(stderr, "%c", (uint8_t)buffer[i]);
     131             :     // }
     132             :     // ::fprintf(stderr, ")\n");
     133   491678628 :     return ret;
     134             : }
     135     3961493 : ssize_t SerialDevice::write_to_device(const char *buffer, const size_t size) const
     136             : {
     137     3961493 :     const ssize_t ret = from_autopilot->write((uint8_t*)buffer, size);
     138     3961493 :     return ret;
     139             : }
     140             : 
     141             : #if AP_SIM_SERIALDEVICE_NETWORK_ENABLED
     142             : /*
     143             :   attach this device to a TCP server socket.  The autopilot connects to
     144             :   this socket (e.g. with a NET_Pn port configured as a TCP client)
     145             :   instead of talking to the device over a simulated serial port
     146             :  */
     147           1 : bool SerialDevice::listen_on_tcp_port(const uint16_t port)
     148             : {
     149           1 :     listener = NEW_NOTHROW SocketAPM_native(false);
     150           1 :     if (listener == nullptr) {
     151           0 :         return false;
     152             :     }
     153           1 :     listener->reuseaddress();
     154           1 :     if (!listener->bind("127.0.0.1", port) ||
     155           2 :         !listener->listen(1) ||
     156           1 :         !listener->set_blocking(false)) {
     157           0 :         ::fprintf(stderr, "SIM: failed to listen on TCP port %u: %m\n", unsigned(port));
     158           0 :         delete listener;
     159           0 :         listener = nullptr;
     160           0 :         return false;
     161             :     }
     162           1 :     ::printf("SIM: device listening for autopilot on TCP port %u\n", unsigned(port));
     163           1 :     return true;
     164             : }
     165             : 
     166             : /*
     167             :   move bytes between the network socket and this device.  This performs
     168             :   the same role the SITL UART driver performs for serially-attached
     169             :   devices
     170             :  */
     171        7245 : void SerialDevice::network_update()
     172             : {
     173        7245 :     if (listener == nullptr) {
     174             :         // not attached to a socket
     175        3055 :         return;
     176             :     }
     177             : 
     178        7245 :     if (sock == nullptr) {
     179        3056 :         sock = listener->accept(0);
     180        3056 :         if (sock == nullptr) {
     181        3055 :             return;
     182             :         }
     183           1 :         sock->set_blocking(false);
     184             :     }
     185             : 
     186             :     char buffer[512];
     187             : 
     188             :     // device to autopilot:
     189             :     while (true) {
     190        4239 :         const ssize_t nread = read_from_device(buffer, sizeof(buffer));
     191        4239 :         if (nread <= 0) {
     192        4190 :             break;
     193             :         }
     194          49 :         if (sock->send(buffer, nread) != nread) {
     195             :             // if the autopilot is not keeping up we simply drop the data
     196           0 :             break;
     197             :         }
     198          49 :     }
     199             : 
     200             :     // autopilot to device:
     201             :     while (true) {
     202        4226 :         const ssize_t nread = sock->recv(buffer, sizeof(buffer), 0);
     203        4226 :         if (nread == 0) {
     204             :             // autopilot closed the connection; wait for it to reconnect
     205           0 :             delete sock;
     206           0 :             sock = nullptr;
     207           0 :             return;
     208             :         }
     209        4226 :         if (nread < 0) {
     210        4190 :             break;
     211             :         }
     212          36 :         write_to_device(buffer, nread);
     213          36 :     }
     214             : }
     215             : #endif  // AP_SIM_SERIALDEVICE_NETWORK_ENABLED
     216             : 
     217             : /**
     218             :  * baudrates match
     219             :  * 
     220             :  * @retval true matched baudreate
     221             :  * @retval false  unmatched baudreate
     222             :  */
     223   509548418 : bool SerialDevice::is_match_baud() const
     224             : {
     225   509548418 :     if (device_baud() != 0 && autopilot_baud != 0 && device_baud() != autopilot_baud) {
     226       27184 :         return false;
     227             :     }
     228   509521234 :     return true;
     229             : }

Generated by: LCOV version 1.14