class_docstr: NetGear is exclusively designed to transfer video frames synchronously and asynchronously between interconnecting systems over the network in real-time.
  
  NetGear implements a high-level wrapper around PyZmQ python library that contains python bindings for ZeroMQ - a high-performance asynchronous distributed messaging library
  that provides a message queue, but unlike message-oriented middleware, its system can run without a dedicated message broker.
  
  NetGear also supports real-time Frame Compression capabilities for optimizing performance while sending the frames directly over the network, by encoding the frame before sending
  it and decoding it on the client's end automatically in real-time.
  
  !!! info
      NetGear API now internally implements robust *Lazy Pirate pattern* (auto-reconnection) for its synchronous messaging patterns _(i.e. `zmq.PAIR` & `zmq.REQ/zmq.REP`)_
      at both Server and Client ends, where its API instead of doing a blocking receive, will:
  
      * Poll the socket and receive from it only when it's sure a reply has arrived.
      * Attempt to reconnect, if no reply has arrived within a timeout period.
      * Abandon the connection if there is still no reply after several requests.
  
  NetGear as of now seamlessly supports three ZeroMQ messaging patterns:
  
  - `zmq.PAIR` _(ZMQ Pair Pattern)_
  - `zmq.REQ/zmq.REP` _(ZMQ Request/Reply Pattern)_
  - `zmq.PUB/zmq.SUB` _(ZMQ Publish/Subscribe Pattern)_
  
  _whereas the supported protocol are: `tcp` and `ipc`_.
  
  ??? tip "Modes of Operation"
  
      * **Primary Modes**
  
          NetGear API primarily has two modes of operations:
  
          * **Send Mode:** _which employs `send()` function to send video frames over the network in real-time._
  
          * **Receive Mode:** _which employs `recv()` function to receive frames, sent over the network with *Send Mode* in real-time. The mode sends back confirmation when the
          frame is received successfully in few patterns._
  
      * **Exclusive Modes**
  
          In addition to these primary modes, NetGear API offers applications-specific Exclusive Modes:
  
          * **Multi-Servers Mode:** _In this exclusive mode, NetGear API robustly **handles multiple servers at once**, thereby providing seamless access to frames and unidirectional
          data transfer from multiple Servers/Publishers across the network in real-time._
  
          * **Multi-Clients Mode:** _In this exclusive mode, NetGear API robustly **handles multiple clients at once**, thereby providing seamless access to frames and unidirectional
          data transfer to multiple Client/Consumers across the network in real-time._
  
          * **Bidirectional Mode:** _This exclusive mode **provides seamless support for bidirectional data transmission between between Server and Client along with video frames**._
  
          * **Secure Mode:** _In this exclusive mode, NetGear API **provides easy access to powerful, smart & secure ZeroMQ's Security Layers** that enables strong encryption on
          data, and unbreakable authentication between the Server and Client with the help of custom certificates/keys that brings cheap, standardized privacy and authentication
          for distributed systems over the network._
class_docstr_tok: ["NetGear","is","exclusively","designed","to","transfer","video","frames","synchronously","and","asynchronously","between","interconnecting","systems","over","the","network","in","real-time",".","NetGear","implements","a","high-level","wrapper","around","PyZmQ","python","library","that","contains","python","bindings","for","ZeroMQ","-","a","high-performance","asynchronous","distributed","messaging","library","that","provides","a","message","queue",",","but","unlike","message-oriented","middleware",",","its","system","can","run","without","a","dedicated","message","broker",".","NetGear","also","supports","real-time","Frame","Compression","capabilities","for","optimizing","performance","while","sending","the","frames","directly","over","the","network",",","by","encoding","the","frame","before","sending","it","and","decoding","it","on","the","client","'s","end","automatically","in","real-time",".","!","!","!","info","NetGear","API","now","internally","implements","robust","*","Lazy","Pirate","pattern","*","(","auto-reconnection",")","for","its","synchronous","messaging","patterns","_","(","i.e",".","`","zmq.PAIR","`","&","`","zmq.REQ\/zmq.REP","`",")","_","at","both","Server","and","Client","ends",",","where","its","API","instead","of","doing","a","blocking","receive",",","will",":","*","Poll","the","socket","and","receive","from","it","only","when","it","'s","sure","a","reply","has","arrived",".","*","Attempt","to","reconnect",",","if","no","reply","has","arrived","within","a","timeout","period",".","*","Abandon","the","connection","if","there","is","still","no","reply","after","several","requests",".","NetGear","as","of","now","seamlessly","supports","three","ZeroMQ","messaging","patterns",":","-","`","zmq.PAIR","`","_","(","ZMQ","Pair","Pattern",")","_","-","`","zmq.REQ\/zmq.REP","`","_","(","ZMQ","Request\/Reply","Pattern",")","_","-","`","zmq.PUB\/zmq.SUB","`","_","(","ZMQ","Publish\/Subscribe","Pattern",")","_","_whereas","the","supported","protocol","are",":","`","tcp","`","and","`","ipc","`","_",".","?","?","?","tip","``","Modes","of","Operation","''","*","*","*","Primary","Modes","*","*","NetGear","API","primarily","has","two","modes","of","operations",":","*","*","*","Send","Mode",":","*","*","_which","employs","`","send","(",")","`","function","to","send","video","frames","over","the","network","in","real-time._","*","*","*","Receive","Mode",":","*","*","_which","employs","`","recv","(",")","`","function","to","receive","frames",",","sent","over","the","network","with","*","Send","Mode","*","in","real-time",".","The","mode","sends","back","confirmation","when","the","frame","is","received","successfully","in","few","patterns._","*","*","*","Exclusive","Modes","*","*","In","addition","to","these","primary","modes",",","NetGear","API","offers","applications-specific","Exclusive","Modes",":","*","*","*","Multi-Servers","Mode",":","*","*","_In","this","exclusive","mode",",","NetGear","API","robustly","*","*","handles","multiple","servers","at","once","*","*",",","thereby","providing","seamless","access","to","frames","and","unidirectional","data","transfer","from","multiple","Servers\/Publishers","across","the","network","in","real-time._","*","*","*","Multi-Clients","Mode",":","*","*","_In","this","exclusive","mode",",","NetGear","API","robustly","*","*","handles","multiple","clients","at","once","*","*",",","thereby","providing","seamless","access","to","frames","and","unidirectional","data","transfer","to","multiple","Client\/Consumers","across","the","network","in","real-time._","*","*","*","Bidirectional","Mode",":","*","*","_This","exclusive","mode","*","*","provides","seamless","support","for","bidirectional","data","transmission","between","between","Server","and","Client","along","with","video","frames","*","*","._","*","*","*","Secure","Mode",":","*","*","_In","this","exclusive","mode",",","NetGear","API","*","*","provides","easy","access","to","powerful",",","smart","&","secure","ZeroMQ","'s","Security","Layers","*","*","that","enables","strong","encryption","on","data",",","and","unbreakable","authentication","between","the","Server","and","Client","with","the","help","of","custom","certificates\/keys","that","brings","cheap",",","standardized","privacy","and","authentication","for","distributed","systems","over","the","network._"]
docstr: A Server end method, that sends the data and frames over the network to Client(s).
  
  Parameters:
      frame (numpy.ndarray): inputs numpy array(frame).
      message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).
  
  **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.
docstr_tok: ["A","Server","end","method",",","that","sends","the","data","and","frames","over","the","network","to","Client","(","s",")",".","Parameters",":","frame","(","numpy.ndarray",")",":","inputs","numpy","array","(","frame",")",".","message","(","any",")",":","input","for","sending","additional","data","_","(","of","any","datatype","except","`","numpy.ndarray","`",")","_","to","Client","(","s",")",".","*","*","Returns",":","*","*","Data","_","(","of","any","datatype",")","_","in","selected","exclusive","modes",",","otherwise","None-type","."]
code: def send(self, frame, message=None):
      """
      A Server end method, that sends the data and frames over the network to Client(s).
  
      Parameters:
          frame (numpy.ndarray): inputs numpy array(frame).
          message (any): input for sending additional data _(of any datatype except `numpy.ndarray`)_ to Client(s).
  
      **Returns:** Data _(of any datatype)_ in selected exclusive modes, otherwise None-type.
  
      """
      # check whether `receive_mode` is disabled
      if self.__receive_mode:
          # raise value error and exit
          self.__terminate = True
          raise ValueError(
              "[NetGear:ERROR] :: `send()` function cannot be used while receive_mode is enabled. Kindly refer vidgear docs!"
          )
  
      if not (message is None) and isinstance(message, np.ndarray):
          logger.warning(
              "Skipped unsupported `message` of datatype: {}!".format(
                  type(message).__name__
              )
          )
          message = None
  
      # define exit_flag and assign value
      exit_flag = True if (frame is None or self.__terminate) else False
  
      # check whether exit_flag is False
      if not (exit_flag) and not (frame.flags["C_CONTIGUOUS"]):
          # check whether the incoming frame is contiguous
          frame = np.ascontiguousarray(frame, dtype=frame.dtype)
  
      # handle JPEG compression encoding
      if self.__jpeg_compression:
          if self.__jpeg_compression_colorspace == "GRAY":
              if frame.ndim == 2:
                  # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11
                  frame = np.expand_dims(frame, axis=2)
              frame = simplejpeg.encode_jpeg(
                  frame,
                  quality=self.__jpeg_compression_quality,
                  colorspace=self.__jpeg_compression_colorspace,
                  fastdct=self.__jpeg_compression_fastdct,
              )
          else:
              frame = simplejpeg.encode_jpeg(
                  frame,
                  quality=self.__jpeg_compression_quality,
                  colorspace=self.__jpeg_compression_colorspace,
                  colorsubsampling="422",
                  fastdct=self.__jpeg_compression_fastdct,
              )
  
      # check if multiserver_mode is activated and assign values with unique port
      msg_dict = (
          dict(port=self.__port) if self.__multiserver_mode else dict()
      )
  
      # prepare the exclusive json dict
      msg_dict.update(
          dict(
              terminate_flag=exit_flag,
              compression={
                  "dct": self.__jpeg_compression_fastdct,
                  "ups": self.__jpeg_compression_fastupsample,
                  "colorspace": self.__jpeg_compression_colorspace,
              }
              if self.__jpeg_compression
              else False,
              message=message,
              pattern=str(self.__pattern),
              dtype=str(frame.dtype)
              if not (self.__jpeg_compression)
              else "",
              shape=frame.shape
              if not (self.__jpeg_compression)
              else "",
          )
      )
  
      # send the json dict
      self.__msg_socket.send_json(
          msg_dict, self.__msg_flag | zmq.SNDMORE
      )
      # send the frame array with correct flags
      self.__msg_socket.send(
          frame,
          flags=self.__msg_flag,
          copy=self.__msg_copy,
          track=self.__msg_track,
      )
  
      # check if synchronous patterns, then wait for confirmation
      if self.__pattern < 2:
          # check if Bidirectional data transmission is enabled
          if self.__bi_mode or self.__multiclient_mode:
              # handles return data
              recvd_data = None
  
              socks = dict(self.__poll.poll(self.__request_timeout))
              if socks.get(self.__msg_socket) == zmq.POLLIN:
                  # handle return data
                  recv_json = self.__msg_socket.recv_json(
                      flags=self.__msg_flag
                  )
              else:
                  logger.critical(
                      "No response from Client, Reconnecting again..."
                  )
                  # Socket is confused. Close and remove it.
                  self.__msg_socket.setsockopt(zmq.LINGER, 0)
                  self.__msg_socket.close()
                  self.__poll.unregister(self.__msg_socket)
                  self.__max_retries -= 1
  
                  if not (self.__max_retries):
                      if self.__multiclient_mode:
                          logger.error(
                              "All Clients failed to respond on multiple attempts."
                          )
                      else:
                          logger.error(
                              "Client failed to respond on multiple attempts."
                          )
                      self.__terminate = True
                      raise RuntimeError(
                          "[NetGear:ERROR] :: Client(s) seems to be offline, Abandoning."
                      )
  
                  # Create new connection
                  self.__msg_socket = self.__msg_context.socket(
                      self.__msg_pattern
                  )
                  if isinstance(self.__connection_address, list):
                      for _connection in self.__connection_address:
                          self.__msg_socket.connect(_connection)
                  else:
                      # handle SSH tunneling if enabled
                      if self.__ssh_tunnel_mode:
                          # establish tunnel connection
                          ssh.tunnel_connection(
                              self.__msg_socket,
                              self.__connection_address,
                              self.__ssh_tunnel_mode,
                              keyfile=self.__ssh_tunnel_keyfile,
                              password=self.__ssh_tunnel_pwd,
                              paramiko=self.__paramiko_present,
                          )
                      else:
                          # connect normally
                          self.__msg_socket.connect(
                              self.__connection_address
                          )
                  self.__poll.register(self.__msg_socket, zmq.POLLIN)
                  # return None for mean-time
                  return None
  
              # save the unique port addresses
              if (
                  self.__multiclient_mode
                  and not recv_json["port"] in self.__port_buffer
              ):
                  self.__port_buffer.append(recv_json["port"])
  
              if recv_json["return_type"] == "ndarray":
                  recv_array = self.__msg_socket.recv(
                      flags=self.__msg_flag,
                      copy=self.__msg_copy,
                      track=self.__msg_track,
                  )
                  # check if encoding was enabled
                  if recv_json["compression"]:
                      # decode JPEG frame
                      recvd_data = simplejpeg.decode_jpeg(
                          recv_array,
                          colorspace=recv_json["compression"][
                              "colorspace"
                          ],
                          fastdct=self.__jpeg_compression_fastdct
                          or recv_json["compression"]["dct"],
                          fastupsample=self.__jpeg_compression_fastupsample
                          or recv_json["compression"]["ups"],
                      )
                      # check if valid frame returned
                      if recvd_data is None:
                          self.__terminate = True
                          # otherwise raise error and exit
                          raise RuntimeError(
                              "[NetGear:ERROR] :: Received compressed frame `{}` decoding failed with flag: {}.".format(
                                  recv_json["compression"],
                                  self.__ex_compression_params,
                              )
                          )
  
                      if (
                          recv_json["compression"]["colorspace"]
                          == "GRAY"
                          and recvd_data.ndim == 3
                      ):
                          # patch for https://gitlab.com/jfolz/simplejpeg/-/issues/11
                          recvd_data = np.squeeze(recvd_data, axis=2)
                  else:
                      recvd_data = np.frombuffer(
                          recv_array, dtype=recv_json["array_dtype"]
                      ).reshape(recv_json["array_shape"])
              else:
                  recvd_data = recv_json["data"]
  
              return (
                  (recv_json["port"], recvd_data)
                  if self.__multiclient_mode
                  else recvd_data
              )
          else:
              # otherwise log normally
              socks = dict(self.__poll.poll(self.__request_timeout))
              if socks.get(self.__msg_socket) == zmq.POLLIN:
                  recv_confirmation = self.__msg_socket.recv()
              else:
                  logger.critical(
                      "No response from Client, Reconnecting again..."
                  )
                  # Socket is confused. Close and remove it.
                  self.__msg_socket.setsockopt(zmq.LINGER, 0)
                  self.__msg_socket.close()
                  self.__poll.unregister(self.__msg_socket)
                  self.__max_retries -= 1
  
                  if not (self.__max_retries):
                      logger.error(
                          "Client failed to respond on repeated attempts."
                      )
                      self.__terminate = True
                      raise RuntimeError(
                          "[NetGear:ERROR] :: Client seems to be offline, Abandoning!"
                      )
  
                  # Create new connection
                  self.__msg_socket = self.__msg_context.socket(
                      self.__msg_pattern
                  )
                  # handle SSH tunneling if enabled
                  if self.__ssh_tunnel_mode:
                      # establish tunnel connection
                      ssh.tunnel_connection(
                          self.__msg_socket,
                          self.__connection_address,
                          self.__ssh_tunnel_mode,
                          keyfile=self.__ssh_tunnel_keyfile,
                          password=self.__ssh_tunnel_pwd,
                          paramiko=self.__paramiko_present,
                      )
                  else:
                      # connect normally
                      self.__msg_socket.connect(
                          self.__connection_address
                      )
                  self.__poll.register(self.__msg_socket, zmq.POLLIN)
  
                  return None
  
              # log confirmation
              self.__logging and logger.debug(recv_confirmation)
  
file_path: vidgear/vidgear/gears/netgear.py
imports: import os
  import time
  import string
  import secrets
  import numpy
  import logging
  from threading import Thread
  from collections import deque
  from os.path import expanduser
  from .helper import logger_handler, generate_auth_certificates, check_WriteAccess, check_open_port, import_dependency_safe, logcurr_vidgear_ver
label_desc: Use image node_id 4 for calling the NetGear obj's underlying member method code with example usage: obj.send(frame, message) and returns: unknown, None, None