LCOV - code coverage report
Current view: top level - lib - lora-comms.js (source / functions) Hit Total Coverage
Test: lcov_final.info Lines: 364 364 100.0 %
Date: 2021-11-05 21:57:31 Functions: 17 17 100.0 %
Branches: 59 59 100.0 %

           Branch data     Line data    Source code
       1            [ + ]:          1 : /**
       2                 :          1 :  * Module for reading and writing LoRa packets.
       3                 :          1 :  * @module lora-comms
       4                 :          1 :  * @extends events.EventEmitter
       5                 :          1 :  */
       6                 :          1 : "use strict";
       7                 :          1 : 
       8                 :          1 : process.env.UV_THREADPOOL_SIZE = (process.env.UV_THREADPOOL_SIZE || 4) + 5;
       9                 :          1 : 
      10                 :          1 : const stream = require('stream'),
      11                 :          1 :       path = require('path'),
      12                 :          1 :       EventEmitter = require('events').EventEmitter,
      13                 :          1 :       LoRaComms = require('bindings')('lora_comms').LoRaComms;
      14                 :          1 : 
      15                 :          1 : LoRaComms.set_gw_send_hwm(LoRaComms.uplink, -1);
      16                 :          1 : LoRaComms.set_gw_send_timeout(LoRaComms.uplink, -1, -1);
      17                 :          1 : LoRaComms.set_gw_recv_timeout(LoRaComms.uplink, -1, -1);
      18                 :          1 : 
      19                 :          1 : LoRaComms.set_gw_send_hwm(LoRaComms.downlink, -1);
      20                 :          1 : LoRaComms.set_gw_send_timeout(LoRaComms.downlink, -1, -1);
      21                 :          1 : LoRaComms.set_gw_recv_timeout(LoRaComms.downlink, -1, -1);
      22                 :          1 : 
      23                 :          1 : LoRaComms.set_log_max_msg_size(1024);
      24                 :          1 : LoRaComms.set_log_write_hwm(-1);
      25                 :          1 : LoRaComms.set_log_write_timeout(-1, -1);
      26                 :          1 : 
      27                 :          1 : class LinkDuplex extends stream.Duplex
      28                 :          1 : {
      29            [ + ]:          1 :     constructor(link, options)
      30                 :         44 :     {
      31                 :         44 :         super(options);
      32                 :         44 :         this._link = link;
      33                 :         44 :         this._reading = false;
      34                 :         44 :     }
      35                 :          1 : 
      36            [ + ]:          1 :     _write(data, encoding, cb)
      37                 :         28 :     {
      38            [ + ]:         28 :         LoRaComms.send_to(this._link, data, -1, -1, -1, (err, r) =>
      39                 :         28 :         {
      40                 :         28 :             if (err)
      41            [ + ]:         28 :             {
      42                 :          5 :                 if (err.errno === LoRaComms.EBADF)
      43            [ + ]:          5 :                 {
      44                 :          4 :                     this.end();
      45                 :          4 :                 }
      46                 :          5 : 
      47                 :          5 :                 return cb(err);
      48                 :          5 :             }
      49            [ + ]:         23 : 
      50                 :         23 :             if (r !== data.length)
      51            [ + ]:         28 :             {
      52                 :          1 :                 return cb(new Error('not all data was written'));
      53                 :          1 :             }
      54            [ + ]:         22 : 
      55                 :         22 :             cb();
      56                 :         28 :         });
      57                 :         28 :     }
      58                 :          1 : 
      59            [ + ]:          1 :     _read()
      60                 :         52 :     {
      61            [ + ]:         52 :         if (this._reading) { return; }
      62            [ + ]:         35 :         this._reading = true;
      63                 :         35 : 
      64                 :         35 :         let buf = Buffer.alloc(LoRaComms.recv_from_buflen);
      65            [ + ]:         35 :         LoRaComms.recv_from(this._link, buf, -1, -1, (err, r) =>
      66                 :         35 :         {
      67                 :         35 :             this._reading = false;
      68                 :         35 :             if (err)
      69            [ + ]:         35 :             {
      70                 :         18 :                 if (err.errno === LoRaComms.EBADF)
      71            [ + ]:         18 :                 {
      72                 :         17 :                     return this.push(null);
      73                 :         17 :                 }
      74            [ + ]:          1 : 
      75            [ + ]:          1 :                 return process.nextTick(() => this.emit('error', err));
      76                 :          1 :             }
      77            [ + ]:         17 : 
      78                 :         17 :             if (this.push(buf.slice(0, r)))
      79                 :         17 :             {
      80            [ + ]:         17 :                 process.nextTick(() => this._read());
      81                 :         17 :             }
      82                 :         35 :         });
      83                 :         52 :     }
      84                 :          1 : }
      85                 :          1 : 
      86                 :          1 : class LogReadable extends stream.Readable
      87                 :          1 : {
      88            [ + ]:          1 :     constructor(get_log_message, options)
      89                 :         32 :     {
      90                 :         32 :         super(options);
      91                 :         32 :         this._get_log_message = get_log_message;
      92                 :         32 :     }
      93                 :          1 : 
      94            [ + ]:          1 :     _read()
      95                 :         45 :     {
      96                 :         45 :         let buf = Buffer.alloc(LoRaComms.get_log_max_msg_size());
      97            [ + ]:         45 :         this._get_log_message(buf, -1, -1, (err, r) =>
      98                 :         45 :         {
      99                 :         45 :             if (err)
     100            [ + ]:         45 :             {
     101                 :         31 :                 if (err.errno === LoRaComms.EBADF)
     102            [ + ]:         31 :                 {
     103                 :         30 :                     return this.push(null);
     104                 :         30 :                 }
     105            [ + ]:          1 : 
     106            [ + ]:          1 :                 return process.nextTick(() => this.emit('error', err));
     107                 :          1 :             }
     108            [ + ]:         14 : 
     109                 :         14 :             if (this.push(buf.slice(0, r)))
     110            [ + ]:         45 :             {
     111            [ + ]:         13 :                 process.nextTick(() => this.read());
     112                 :         13 :             }
     113                 :         45 :         });
     114                 :         45 :     }
     115                 :          1 : }
     116                 :          1 : 
     117                 :          1 : class lora_comms extends EventEmitter
     118                 :          1 : {
     119            [ + ]:          1 :     get LoRaComms()
     120                 :         19 :     {
     121                 :         19 :         return LoRaComms;
     122                 :         19 :     }
     123                 :          1 : 
     124            [ + ]:          1 :     constructor()
     125                 :          1 :     {
     126                 :          1 :         super();
     127                 :          1 :         this._uplink = null;
     128                 :          1 :         this._downlink = null;
     129                 :          1 :         this._active = false;
     130                 :          1 :         this._needs_reset = false;
     131                 :          1 :         this._log_info = null;
     132                 :          1 :         this._log_error = null;
     133                 :          1 :         this._logging_active = false;
     134                 :          1 :         this._logging_needs_reset = false;
     135                 :          1 :     }
     136                 :          1 : 
     137                 :          1 :     /**
     138                 :          1 :      * Start the LoRa radio, receiving and transmitting packets to
     139                 :          1 :      * {@link lora-commsuplink|uplink} and {@link lora-commsdownlink|downlink}.
     140                 :          1 :      *
     141                 :          1 :      * @memberof lora-comms
     142                 :          1 :      * @param {Object} options - Configuration options. This is passed to stream.Duplex when constructing {@link lora-commsuplink|uplink} and {@link lora-commsdownlink|downlink} and supports the following additional option:
     143                 :          1 :      * @param {string} [options.cfg_dir] - Path to directory containing LoRa radio configuration files. Defaults to `packet_forwarder_shared/lora_pkt_fwd` in the module directory.
     144                 :          1 :      */
     145            [ + ]:          1 :     start(options)
     146                 :         17 :     {
     147                 :         17 :                 options = Object.assign(
     148                 :         17 :                 {
     149                 :         17 :                         cfg_dir: path.join(__dirname, '..',
     150                 :         17 :                                'packet_forwarder_shared', 'lora_pkt_fwd')
     151                 :         17 :                 }, options);
     152                 :         17 : 
     153                 :         17 :         if (this._active)
     154            [ + ]:         17 :         {
     155                 :          1 :             return;
     156                 :          1 :         }
     157            [ + ]:         16 : 
     158                 :         16 :         if (this._needs_reset)
     159            [ + ]:         17 :         {
     160                 :         15 :             LoRaComms.reset();
     161                 :         15 :         }
     162            [ + ]:         16 : 
     163                 :         16 :         this._active = true;
     164                 :         16 :         this._needs_reset = true;
     165                 :         16 : 
     166                 :         16 :         if (options.no_streams)
     167            [ + ]:         17 :         {
     168                 :          5 :             this._uplink = null;
     169                 :          5 :             this._downlink = null;
     170                 :          5 :         }
     171            [ + ]:         11 :         else
     172                 :         11 :         {
     173                 :         11 :             this._uplink = new LinkDuplex(LoRaComms.uplink, options);
     174                 :         11 :             this._downlink = new LinkDuplex(LoRaComms.downlink, options);
     175                 :         11 :         }
     176            [ + ]:         16 : 
     177       [ + ][ + ]:         16 :         LoRaComms.start(options.cfg_dir, err => process.nextTick(() =>
     178                 :         16 :         {
     179                 :         16 :             this._active = false;
     180                 :         16 : 
     181                 :         16 :             if (this._uplink)
     182            [ + ]:         16 :             {
     183                 :         11 :                 this._uplink.push(null);
     184                 :         11 :                 this._uplink.end();
     185                 :         11 :             }
     186                 :         16 : 
     187                 :         16 :             if (this._downlink)
     188            [ + ]:         16 :             {
     189                 :         11 :                 this._downlink.push(null);
     190                 :         11 :                 this._downlink.end();
     191                 :         11 :             }
     192                 :         16 : 
     193                 :         16 :             if (err)
     194            [ + ]:         16 :             {
     195                 :          1 :                 this.emit('error', err);
     196                 :          1 :             }
     197                 :         16 : 
     198                 :         16 :             this.emit('stop');
     199                 :         16 :         }));
     200                 :         17 :     }
     201                 :          1 : 
     202                 :          1 :         /**
     203                 :          1 :      * Stop the LoRa radio.
     204                 :          1 :          *
     205                 :          1 :          * A {@link lora-comms.event:stop|stop} event is emitted when the radio
     206                 :          1 :      * stops.
     207                 :          1 :      *
     208                 :          1 :      * @memberof lora-comms
     209                 :          1 :          */
     210            [ + ]:          1 :     stop()
     211                 :         15 :     {
     212                 :         15 :         LoRaComms.stop();
     213                 :         15 :     }
     214                 :          1 : 
     215                 :          1 :     /**
     216                 :          1 :      * Duplex stream for receiving packets from the LoRa radio.
     217                 :          1 :      * Read `PUSH_DATA` packets. Write `PUSH_ACK` packets.
     218                 :          1 :      *
     219                 :          1 :      * @memberof lora-comms
     220                 :          1 :      * @type {?stream.Duplex}
     221                 :          1 :      */
     222            [ + ]:          1 :     get uplink()
     223                 :         59 :     {
     224                 :         59 :         return this._uplink;
     225                 :         59 :     }
     226                 :          1 : 
     227                 :          1 :     /**
     228                 :          1 :      * Duplex stream for transmitting packets using the LoRa radio.
     229                 :          1 :      * Read `PULL_DATA` and `TX_ACK` packets. Write `PULL_ACK` and `PULL_RESP`
     230                 :          1 :      * packets.
     231                 :          1 :      *
     232                 :          1 :      * @memberof lora-comms
     233                 :          1 :      * @type {?stream.Duplex}
     234                 :          1 :      */
     235            [ + ]:          1 :     get downlink()
     236                 :         40 :     {
     237                 :         40 :         return this._downlink;
     238                 :         40 :     }
     239                 :          1 : 
     240                 :          1 :     /**
     241                 :          1 :      * Whether the LoRa radio is switched on.
     242                 :          1 :      *
     243                 :          1 :      * @memberof lora-comms
     244                 :          1 :      * @type {boolean}
     245                 :          1 :      */
     246            [ + ]:          1 :     get active()
     247                 :         16 :     {
     248                 :         16 :         return this._active;
     249                 :         16 :     }
     250                 :          1 : 
     251                 :          1 :         /**
     252                 :          1 :      * Start logging diagnostic messages to
     253                 :          1 :      * {@link lora-commslog_info|log_info} and
     254                 :          1 :      * {@link lora-commslog_error|log_error}.
     255                 :          1 :      *
     256                 :          1 :      * @memberof lora-comms
     257                 :          1 :      * @param {Object} options - Configuration options. This is passed to stream.Readable when constructing {@link lora-commslog_info|log_info} and {@link lora-commslog_error|log_error}.
     258                 :          1 :      */
     259            [ + ]:          1 :     start_logging(options)
     260                 :         17 :     {
     261                 :         17 :         if (this._logging_active)
     262            [ + ]:         17 :         {
     263                 :          1 :             return;
     264                 :          1 :         }
     265            [ + ]:         16 : 
     266                 :         16 :         if (this._logging_needs_reset)
     267            [ + ]:         17 :         {
     268                 :         15 :             LoRaComms.reset_logging();
     269                 :         15 :         }
     270            [ + ]:         16 : 
     271                 :         16 :         this._logging_active = true;
     272                 :         16 :         this._logging_needs_reset = true;
     273                 :         16 : 
     274                 :         16 :         this._log_info = new LogReadable(LoRaComms.get_log_info_message,
     275                 :         16 :                                          options);
     276                 :         16 :         this._log_error = new LogReadable(LoRaComms.get_log_error_message,
     277                 :         16 :                                           options);
     278                 :         16 : 
     279            [ + ]:         16 :         let end_count = 0, check = () =>
     280                 :         32 :         {
     281                 :         32 :             if (++end_count === 2)
     282            [ + ]:         32 :             {
     283                 :         16 :                 this._logging_active = false;
     284                 :         16 :                 this.emit('logging_stop');
     285                 :         16 :             }
     286                 :         16 :         };
     287                 :         16 :         this._log_info.on('end', check);
     288                 :         16 :         this._log_error.on('end', check);
     289                 :         16 : 
     290                 :         16 :         LoRaComms.start_logging();
     291                 :         17 :     }
     292                 :          1 : 
     293                 :          1 :         /**
     294                 :          1 :      * Stop logging diagnostic messages.
     295                 :          1 :      *
     296                 :          1 :          * A {@link lora-comms.event:logging_stop|logging_stop} event is emitted
     297                 :          1 :      * when logging stops.
     298                 :          1 :      *
     299                 :          1 :      * @memberof lora-comms
     300                 :          1 :      */
     301            [ + ]:          1 :     stop_logging()
     302                 :          1 :     {
     303                 :          1 :         LoRaComms.stop_logging();
     304                 :          1 :         this._log_info.push(null);
     305                 :          1 :         this._log_error.push(null);
     306                 :          1 :     }
     307                 :          1 : 
     308                 :          1 :     /**
     309                 :          1 :      * Readable stream containing diagnostic messages.
     310                 :          1 :      *
     311                 :          1 :      * @memberof lora-comms
     312                 :          1 :      * @type {?stream.Readable}
     313                 :          1 :      */
     314            [ + ]:          1 :     get log_info()
     315                 :         21 :     {
     316                 :         21 :         return this._log_info;
     317                 :         21 :     }
     318                 :          1 : 
     319                 :          1 :     /**
     320                 :          1 :      * Readable stream containing diagnostic messages.
     321                 :          1 :      *
     322                 :          1 :      * @memberof lora-comms
     323                 :          1 :      * @type {?stream.Readable}
     324                 :          1 :      */
     325            [ + ]:          1 :     get log_error()
     326                 :         16 :     {
     327                 :         16 :         return this._log_error;
     328                 :         16 :     }
     329                 :          1 : 
     330                 :          1 :     /**
     331                 :          1 :      * Whether diagnostic logging is enabled.
     332                 :          1 :      *
     333                 :          1 :      * @memberof lora-comms
     334                 :          1 :      * @type {boolean}
     335                 :          1 :      */
     336            [ + ]:          1 :     get logging_active()
     337                 :         16 :     {
     338                 :         16 :         return this._logging_active;
     339                 :         16 :     }
     340                 :          1 : 
     341                 :          1 :     /**
     342                 :          1 :      * Stop event. Emitted when the radio stops.
     343                 :          1 :      *
     344                 :          1 :      * @memberof lora-comms
     345                 :          1 :      * @event stop
     346                 :          1 :      */
     347                 :          1 : 
     348                 :          1 :     /**
     349                 :          1 :      * Logging stop event. Emitted when logging stops.
     350                 :          1 :      *
     351                 :          1 :      * @memberof lora-comms
     352                 :          1 :      * @event logging_stop
     353                 :          1 :      */
     354                 :          1 : 
     355                 :          1 :     /**
     356                 :          1 :      * Error event.
     357                 :          1 :      *
     358                 :          1 :      * @memberof lora-comms
     359                 :          1 :      * @event error
     360                 :          1 :      * @param {Object} err - The error which occurred.
     361                 :          1 :      */
     362                 :          1 : }
     363                 :          1 : 
     364                 :          1 : module.exports = new lora_comms();

Generated by: LCOV version 1.14