whole_func_string: function(thisObject, data, rinfo) {
      if (typeof data === "undefined") {
          // no data; abort.
          return;
      }
      if (Object.prototype.toString.call(data) != "[object Uint8Array]") 
      {
          // no data; abort.
          return;
      }
  
      let HEADER_MINIMUM_SIZE = 2;
      let ZIP_WRAPPED_HEADER_MINIMUM_SIZE = 7;
      if (data.length < HEADER_MINIMUM_SIZE)
      {
          // not enough data for any packet; abort.
          return;
      }
  
      let isWrappedInZipFrame = false;
  
      // get the "initial" command class and command; in case of Z/IP-wrapped packets, these are the "header" (wrapping) command class and command
      let offset = 0;
      // command class
      let initialCommandClass = data[offset];
      offset += 1;
      // command
      let initialCommand = data[offset];
      offset += 1;
  
      if (initialCommandClass !== PrivateCommandClass.Zip || initialCommand !== ZipCommand.ZipPacket)
      {
          // mesage is not enclosed in a Z/IP packet; handle this special case first
          isWrappedInZipFrame = false;
  
          if (initialCommandClass === PrivateCommandClass.ZipNd && initialCommand === ZipNdCommand.NodeAdvertisement) {
              // allowed command
          } else {
              // if we do not recognize the command as an allowable "non-wrapped" incoming command/response, abort now
              return;
          }
      }
      else {
          // otherwise, the packet is a Z/IP packet; make sure it is at least large enough for a minimal Z/IP packet
          isWrappedInZipFrame = true;
  
          if (data.length < ZIP_WRAPPED_HEADER_MINIMUM_SIZE)
          {
              // not enough data for minimal Z/IP packet; abort.
              return;
          }
      }
  
      // parse message
      let ackResponse = null;
      let nackResponse = null;
      let nackWaiting = null;
      let nackQueueFull = null;
      let headerExtensionsIncluded = null;
      let sequenceNumber = null;
      let headerExtensionsLength = null;
      let commandClass = null;
      let command = null;
      let payload = null;
      if (isWrappedInZipFrame) {
          // AckRequest | AckResponse | NackResponse | NackFlags (Waiting, QueueFull, OptionError) | Reserved
          ackResponse = ((data[offset] & ZIP_PACKET_FLAGS0.ACK_RES) != 0);
          nackResponse = ((data[offset] & ZIP_PACKET_FLAGS0.NACK_RES) != 0);
          nackWaiting = ((data[offset] & ZIP_PACKET_FLAGS0.WAIT_RES) != 0);
          nackQueueFull = ((data[offset] & ZIP_PACKET_FLAGS0.NACK_QF) != 0);
          offset += 1;
          // HeaderExtIncluded | ZWaveCmdIncluded | MoreInformation | SecureOrigin | Reserved
          headerExtensionsIncluded = ((data[offset] & ZIP_PACKET_FLAGS1.HDR_EXT_INCL) != 0);
          offset += 1;
          // SequenceNumber
          sequenceNumber = data[offset];
          offset += 1;
          // Res | SourceEndPoint
          offset += 1;
          // BitAddress | DestinationEndpoint
          offset += 1;
          // Header Extensions (optional)
          headerExtensionsLength = 0;
          if (headerExtensionsIncluded) {
              // first byte of header extensions: total length
              headerExtensionsLength = data[offset];
              // NOTE: we are currently ignoring extensions
  
              offset += headerExtensionsLength;
          }
          //
          if (data.length >= ZIP_WRAPPED_HEADER_MINIMUM_SIZE + headerExtensionsLength + 2) {
              // command class
              commandClass = data[offset];
              offset += 1;
              // command
              command = data[offset];
              offset += 1;
          }
      } else {
          commandClass = initialCommandClass;
          command = initialCommand;
      }
      //
      // payload data
      payload = Buffer.alloc(data.length - offset);
      data.copy(payload, 0, offset, data.length);
      offset += payload.length;
  
      if (isWrappedInZipFrame) {
          // if this message is an ACK response, trigger any pending ACK 
          if (ackResponse) {
              let ackRegistration = thisObject.getAndRemoveAckRegistration(sequenceNumber);
              if (typeof ackRegistration !== "undefined") {
                  if (typeof ackRegistration.timeoutObject !== "undefined") {
                      clearTimeout(ackRegistration.timeoutObject);
                  }
                  if (!thisObject.checkTimeoutHasOccured(ackRegistration.timeoutTimestamp)) {
                      ackRegistration.callback(true, false); // true = ACK, false = no Z/IP protocol timeout
                  }
              }
          }
          else if (nackResponse) {
              if (nackQueueFull) {
                  // queue full: retry sending message in ten seconds
                  this.updateAckTimeout(sequenceNumber, 12000); // 10 seconds + 2 seconds wait time
                  //
                  thisObject.dtlsSocket.send(ackRegistration.data.msg, ackRegistration.data.offset, ackRegistration.data.length, ackRegistration.data.port, ackRegistration.data.host,
                      ackRegistration.data.pskIdentity, ackRegistration.data.pskPassword, ackRegistration.data.callback);            
              } else if (nackWaiting) {
                  // add 90 seconds to the ACK timeout timer 
                  thisObject.updateAckTimeout(sequenceNumber, 90000);
              } else {
                  // message was rejected
                  let ackRegistration = thisObject.getAndRemoveAckRegistration(sequenceNumber);
                  if (typeof ackRegistration !== "undefined") {
                      if (typeof ackRegistration.timeoutObject !== "undefined") {
                          clearTimeout(ackRegistration.timeoutObject);
                      }
                      if (!thisObject.checkTimeoutHasOccured(ackRegistration.timeoutTimestamp)) {
                          ackRegistration.callback(false, false); // false = NAK, false = no Z/IP protocol timeout
                      }
                  }       
              } 
          }
      }
  
      if (commandClass !== null && command !== null) {
          // for data packets: treat this message as a response and check it against the "pending command callbacks" list
          for (let iCallback = 0; iCallback < thisObject.pendingCommandCallbacks.length; iCallback++)
          {
              let commandCallback = thisObject.pendingCommandCallbacks[iCallback];
              if (commandCallback.commandClass === commandClass && commandCallback.command === command && commandCallback.isWrappedInZipFrame === isWrappedInZipFrame)
              {
                  // command class and command matches; check timeout
                  if (thisObject.checkTimeoutHasOccured(commandCallback.timeoutTimestamp))
                  {
                      // command has timed out; skip this entry as its callback will automatically be removed at timeout
                      continue;
                  }
  
                  // command class and command match and timeout has not expired; check match identifier if one exists
                  if (commandClass == ZWaveIP.CommandClass.NetworkManagementProxy && command == ZWaveIP.NetworkManagementProxyCommand.NodeListReport) {
                      if (payload.length < 1 || payload[0] != commandCallback.matchIdentifier) {
                          // NOTE: matchIdentifier is the sequenceNumber
                          // match identifier does not match; skip this entry
                          continue;
                      }
                  }
                  else if(commandClass == PrivateCommandClass.ZipNd && command == ZipNdCommand.NodeAdvertisement) {
                      if (payload.length < 2 || payload[1] != commandCallback.matchIdentifier) {
                          // NOTE: matchIdentifier is the nodeId
                          // match identifier does not match; skip this entry
                          continue;
                      }
                  }
                  // else...
  
                  // if we reach here, the response matches; deactivate the callback timer, remove the callback entry and then call the callback
                  //
                  let callback = commandCallback.callback;
                  // cancel the callback timeout timer
                  clearTimeout(commandCallback.timeoutObject);
                  // remove the callback from the pending callbacks list
                  thisObject.pendingCommandCallbacks.splice(iCallback, 1);
                  // call the callback
                  commandCallback.callback({commandClass: commandClass, command: command, data: payload}, false /* did not time out */);
                  break;
              }
          }
      }
  
      // if there was no callback registered, simply discard this packet.
  }
func_code_string: function(thisObject, data, rinfo) {
      if (typeof data === "undefined") {
          // no data; abort.
          return;
      }
      if (Object.prototype.toString.call(data) != "[object Uint8Array]") 
      {
          // no data; abort.
          return;
      }
  
      let HEADER_MINIMUM_SIZE = 2;
      let ZIP_WRAPPED_HEADER_MINIMUM_SIZE = 7;
      if (data.length < HEADER_MINIMUM_SIZE)
      {
          // not enough data for any packet; abort.
          return;
      }
  
      let isWrappedInZipFrame = false;
  
      // get the "initial" command class and command; in case of Z/IP-wrapped packets, these are the "header" (wrapping) command class and command
      let offset = 0;
      // command class
      let initialCommandClass = data[offset];
      offset += 1;
      // command
      let initialCommand = data[offset];
      offset += 1;
  
      if (initialCommandClass !== PrivateCommandClass.Zip || initialCommand !== ZipCommand.ZipPacket)
      {
          // mesage is not enclosed in a Z/IP packet; handle this special case first
          isWrappedInZipFrame = false;
  
          if (initialCommandClass === PrivateCommandClass.ZipNd && initialCommand === ZipNdCommand.NodeAdvertisement) {
              // allowed command
          } else {
              // if we do not recognize the command as an allowable "non-wrapped" incoming command/response, abort now
              return;
          }
      }
      else {
          // otherwise, the packet is a Z/IP packet; make sure it is at least large enough for a minimal Z/IP packet
          isWrappedInZipFrame = true;
  
          if (data.length < ZIP_WRAPPED_HEADER_MINIMUM_SIZE)
          {
              // not enough data for minimal Z/IP packet; abort.
              return;
          }
      }
  
      // parse message
      let ackResponse = null;
      let nackResponse = null;
      let nackWaiting = null;
      let nackQueueFull = null;
      let headerExtensionsIncluded = null;
      let sequenceNumber = null;
      let headerExtensionsLength = null;
      let commandClass = null;
      let command = null;
      let payload = null;
      if (isWrappedInZipFrame) {
          // AckRequest | AckResponse | NackResponse | NackFlags (Waiting, QueueFull, OptionError) | Reserved
          ackResponse = ((data[offset] & ZIP_PACKET_FLAGS0.ACK_RES) != 0);
          nackResponse = ((data[offset] & ZIP_PACKET_FLAGS0.NACK_RES) != 0);
          nackWaiting = ((data[offset] & ZIP_PACKET_FLAGS0.WAIT_RES) != 0);
          nackQueueFull = ((data[offset] & ZIP_PACKET_FLAGS0.NACK_QF) != 0);
          offset += 1;
          // HeaderExtIncluded | ZWaveCmdIncluded | MoreInformation | SecureOrigin | Reserved
          headerExtensionsIncluded = ((data[offset] & ZIP_PACKET_FLAGS1.HDR_EXT_INCL) != 0);
          offset += 1;
          // SequenceNumber
          sequenceNumber = data[offset];
          offset += 1;
          // Res | SourceEndPoint
          offset += 1;
          // BitAddress | DestinationEndpoint
          offset += 1;
          // Header Extensions (optional)
          headerExtensionsLength = 0;
          if (headerExtensionsIncluded) {
              // first byte of header extensions: total length
              headerExtensionsLength = data[offset];
              // NOTE: we are currently ignoring extensions
  
              offset += headerExtensionsLength;
          }
          //
          if (data.length >= ZIP_WRAPPED_HEADER_MINIMUM_SIZE + headerExtensionsLength + 2) {
              // command class
              commandClass = data[offset];
              offset += 1;
              // command
              command = data[offset];
              offset += 1;
          }
      } else {
          commandClass = initialCommandClass;
          command = initialCommand;
      }
      //
      // payload data
      payload = Buffer.alloc(data.length - offset);
      data.copy(payload, 0, offset, data.length);
      offset += payload.length;
  
      if (isWrappedInZipFrame) {
          // if this message is an ACK response, trigger any pending ACK 
          if (ackResponse) {
              let ackRegistration = thisObject.getAndRemoveAckRegistration(sequenceNumber);
              if (typeof ackRegistration !== "undefined") {
                  if (typeof ackRegistration.timeoutObject !== "undefined") {
                      clearTimeout(ackRegistration.timeoutObject);
                  }
                  if (!thisObject.checkTimeoutHasOccured(ackRegistration.timeoutTimestamp)) {
                      ackRegistration.callback(true, false); // true = ACK, false = no Z/IP protocol timeout
                  }
              }
          }
          else if (nackResponse) {
              if (nackQueueFull) {
                  // queue full: retry sending message in ten seconds
                  this.updateAckTimeout(sequenceNumber, 12000); // 10 seconds + 2 seconds wait time
                  //
                  thisObject.dtlsSocket.send(ackRegistration.data.msg, ackRegistration.data.offset, ackRegistration.data.length, ackRegistration.data.port, ackRegistration.data.host,
                      ackRegistration.data.pskIdentity, ackRegistration.data.pskPassword, ackRegistration.data.callback);            
              } else if (nackWaiting) {
                  // add 90 seconds to the ACK timeout timer 
                  thisObject.updateAckTimeout(sequenceNumber, 90000);
              } else {
                  // message was rejected
                  let ackRegistration = thisObject.getAndRemoveAckRegistration(sequenceNumber);
                  if (typeof ackRegistration !== "undefined") {
                      if (typeof ackRegistration.timeoutObject !== "undefined") {
                          clearTimeout(ackRegistration.timeoutObject);
                      }
                      if (!thisObject.checkTimeoutHasOccured(ackRegistration.timeoutTimestamp)) {
                          ackRegistration.callback(false, false); // false = NAK, false = no Z/IP protocol timeout
                      }
                  }       
              } 
          }
      }
  
      if (commandClass !== null && command !== null) {
          // for data packets: treat this message as a response and check it against the "pending command callbacks" list
          for (let iCallback = 0; iCallback < thisObject.pendingCommandCallbacks.length; iCallback++)
          {
              let commandCallback = thisObject.pendingCommandCallbacks[iCallback];
              if (commandCallback.commandClass === commandClass && commandCallback.command === command && commandCallback.isWrappedInZipFrame === isWrappedInZipFrame)
              {
                  // command class and command matches; check timeout
                  if (thisObject.checkTimeoutHasOccured(commandCallback.timeoutTimestamp))
                  {
                      // command has timed out; skip this entry as its callback will automatically be removed at timeout
                      continue;
                  }
  
                  // command class and command match and timeout has not expired; check match identifier if one exists
                  if (commandClass == ZWaveIP.CommandClass.NetworkManagementProxy && command == ZWaveIP.NetworkManagementProxyCommand.NodeListReport) {
                      if (payload.length < 1 || payload[0] != commandCallback.matchIdentifier) {
                          // NOTE: matchIdentifier is the sequenceNumber
                          // match identifier does not match; skip this entry
                          continue;
                      }
                  }
                  else if(commandClass == PrivateCommandClass.ZipNd && command == ZipNdCommand.NodeAdvertisement) {
                      if (payload.length < 2 || payload[1] != commandCallback.matchIdentifier) {
                          // NOTE: matchIdentifier is the nodeId
                          // match identifier does not match; skip this entry
                          continue;
                      }
                  }
                  // else...
  
                  // if we reach here, the response matches; deactivate the callback timer, remove the callback entry and then call the callback
                  //
                  let callback = commandCallback.callback;
                  // cancel the callback timeout timer
                  clearTimeout(commandCallback.timeoutObject);
                  // remove the callback from the pending callbacks list
                  thisObject.pendingCommandCallbacks.splice(iCallback, 1);
                  // call the callback
                  commandCallback.callback({commandClass: commandClass, command: command, data: payload}, false /* did not time out */);
                  break;
              }
          }
      }
  
      // if there was no callback registered, simply discard this packet.
  }
func_documentation_string: * INCOMING (DECRYPTED) MESSAGE HANDLER **