text: import threading
  import websocket
  import gzip
  import ssl
  import logging
  from urllib import parse
  import urllib.parse
  
  from common.scripts.binance_spot.impl.utils.timeservice import get_current_timestamp
  from common.scripts.binance_spot.impl.utils.urlparamsbuilder import UrlParamsBuilder
  from common.scripts.binance_spot.impl.utils.apisignature import create_signature
  from common.scripts.binance_spot.exception.binanceapiexception import BinanceApiException
  from common.scripts.binance_spot.impl.utils import *
  from common.scripts.binance_spot.base.printobject import *
  from common.scripts.binance_spot.model.constant import *
  # Key: ws, Value: connection
  websocket_connection_handler = dict()
  
  
  def on_message(ws, message):
      websocket_connection = websocket_connection_handler[ws]
      websocket_connection.on_message(message)
      return
  
  
  def on_error(ws, error):
      websocket_connection = websocket_connection_handler[ws]
      websocket_connection.on_failure(error)
  
  
  def on_close(ws):
      websocket_connection = websocket_connection_handler[ws]
      websocket_connection.on_close()
  
  
  def on_open(ws):
      websocket_connection = websocket_connection_handler[ws]
      websocket_connection.on_open(ws)
  
  
  connection_id = 0
  
  
  class ConnectionState:
      IDLE = 0
      CONNECTED = 1
      CLOSED_ON_ERROR = 2
  
  
  def websocket_func(*args):
      connection_instance = args[0]
      connection_instance.ws = websocket.WebSocketApp(connection_instance.url,
                                                      on_message=on_message,
                                                      on_error=on_error,
                                                      on_close=on_close)
      global websocket_connection_handler
      websocket_connection_handler[connection_instance.ws] = connection_instance
      connection_instance.logger.info(
          '[Sub][' + str(connection_instance.id) + '] Connecting...')
      connection_instance.delay_in_second = -1
      connection_instance.ws.on_open = on_open
      connection_instance.ws.run_forever(sslopt={'cert_reqs': ssl.CERT_NONE})
      connection_instance.logger.info(
          '[Sub][' + str(connection_instance.id) + '] Connection event loop down')
      if connection_instance.state == ConnectionState.CONNECTED:
          connection_instance.state = ConnectionState.IDLE
  
  
  class WebsocketConnection:
  
      def __init__(self, api_key, secret_key, uri, watch_dog, request):
          self.__thread = None
          self.url = uri
          self.__api_key = api_key
          self.__secret_key = secret_key
          self.request = request
          self.__watch_dog = watch_dog
          self.delay_in_second = -1
          self.ws = None
          self.last_receive_time = 0
          self.logger = logging.getLogger('algo-trading')
          self.state = ConnectionState.IDLE
          global connection_id
          connection_id += 1
          self.id = connection_id
  
      def in_delay_connection(self):
          return self.delay_in_second != -1
  
      def re_connect_in_delay(self, delay_in_second):
          if self.ws is not None:
              self.ws.close()
              self.ws = None
          self.delay_in_second = delay_in_second
          self.logger.warning('[Sub][' + str(self.id) + '] Reconnecting after '
                              + str(self.delay_in_second) + ' seconds later')
  
      def re_connect(self):
          if self.delay_in_second != 0:
              self.delay_in_second -= 1
              self.logger.warning('In delay connection: ' +
                                  str(self.delay_in_second))
          else:
              self.connect()
  
      def connect(self):
          if self.state == ConnectionState.CONNECTED:
              self.logger.info('[Sub][' + str(self.id) + '] Already connected')
          else:
              self.__thread = threading.Thread(
                  target=websocket_func, args=[self])
              self.__thread.start()
  
      def send(self, data):
          self.ws.send(data)
  
      def close(self):
          self.ws.close()
          del websocket_connection_handler[self.ws]
          self.__watch_dog.on_connection_closed(self)
          self.logger.error('[Sub][' + str(self.id) + '] Closing normally')
  
      def on_open(self, ws):
          self.logger.info('[Sub][' + str(self.id) + '] Connected to server')
          self.ws = ws
          self.last_receive_time = get_current_timestamp()
          self.state = ConnectionState.CONNECTED
          self.__watch_dog.on_connection_created(self)
          if self.request.subscription_handler is not None:
              self.request.subscription_handler(self)
          return
  
      def on_error(self, error_message):
          if self.request.error_handler is not None:
              print('error')
              exception = BinanceApiException(
                  BinanceApiException.SUBSCRIPTION_ERROR, error_message)
              self.request.error_handler(exception)
          self.logger.error('[Sub][' + str(self.id) + '] ' + str(error_message))
  
      def on_failure(self, error):
          print('on_failure')
          self.on_error('Unexpected error: ' + str(error))
          self.close_on_error()
  
      def on_message(self, message):
          self.last_receive_time = get_current_timestamp()
          json_wrapper = parse_json_from_string(message)
  
          if json_wrapper.contain_key('status') and json_wrapper.get_string('status') != 'ok':
              error_code = json_wrapper.get_string_or_default(
                  'err-code', 'Unknown error')
              error_msg = json_wrapper.get_string_or_default(
                  'err-msg', 'Unknown error')
              self.on_error(error_code + ': ' + error_msg)
          elif json_wrapper.contain_key('err-code') and json_wrapper.get_int('err-code') != 0:
              error_code = json_wrapper.get_string_or_default(
                  'err-code', 'Unknown error')
              error_msg = json_wrapper.get_string_or_default(
                  'err-msg', 'Unknown error')
              self.on_error(error_code + ': ' + error_msg)
          elif json_wrapper.contain_key('result') and json_wrapper.contain_key('id'):
              self.__on_receive_response(json_wrapper)
          else:
              self.__on_receive_payload(json_wrapper)
  
      def __on_receive_response(self, json_wrapper):
          res = None
          try:
              res = json_wrapper.get_int('id')
          except Exception as e:
              self.on_error('Failed to parse servers response: ' + str(e))
  
          try:
              if self.request.update_callback is not None:
                  self.request.update_callback(
                      SubscribeMessageType.RESPONSE, res)
          except Exception as e:
              self.on_error('Process error: ' + str(e)
                            + ' You should capture the exception in your error handler')
  
      def __on_receive_payload(self, json_wrapper):
          res = None
          try:
              if self.request.json_parser is not None:
                  res = self.request.json_parser(json_wrapper)
          except Exception as e:
              self.on_error('Failed to parse servers response: ' + str(e))
  
          try:
              if self.request.update_callback is not None:
                  self.request.update_callback(SubscribeMessageType.PAYLOAD, res)
          except Exception as e:
              self.on_error('Process error: ' + str(e)
                            + ' You should capture the exception in your error handler')
  
          if self.request.auto_close:
              self.close()
  
      def __process_ping_on_trading_line(self, ping_ts):
          self.send('{\'op\':\'pong\',\'ts\':' + str(ping_ts) + '}')
          return
  
      def __process_ping_on_market_line(self, ping_ts):
          self.send('{\'pong\':' + str(ping_ts) + '}')
          return
  
      def close_on_error(self):
          if self.ws is not None:
              self.ws.close()
              self.state = ConnectionState.CLOSED_ON_ERROR
              self.logger.error(
                  '[Sub][' + str(self.id) + '] Connection is closing due to error')
  