# Path: phasortoolbox/message.py
# class Command():
#     """This object returns a command message in bytes.
#     Note:
#         This creates a Version 1 (0001) command message defined by IEEE Std
#         C37.118.2-2011.
#         TIME, TQ_FLAGS and MSG_TQ are used to generate SOC and FRACSEC of the
#         synchrophasor message.
# 
#     Example:
#         my_msg = Command(1,'on') # Data stream 1 turn on transmission.
#         my_msg = Command(IDCODE=2, CMD='off') # Data stream 2 turn off
#         transmission.
#         my_msg = Command(IDCODE=3, CMD='ext', EXT = b'User defined message')
#         # Send extended command frame with user defined message to the source
#         # of Data stream 3.
# 
#     Args:
#         IDCODE (int)    :Data stream ID number. Defaults to 1. 1–65534 (0 and
#         65535 are reserved).
#         CMD (str)       :Commands designated by user.
#                         Options are:
#                             'off' :Turn off transmission of data frames.
#                             'on'  :Turn on transmission of data frames.
#                             'hdr' :Send HDR frame.
#                             'cfg1':Send CFG-1 frame.
#                             'cfg2':Send CFG-2 frame.
#                             'cfg3':Send CFG-3 frame (optional command).
#                             'ext':Extended frame.
#         TIME (float)    :Epoch time. Defaults to current time.
#         TQ_FLAGS (str)  :Four bytes string indicates time quality flags.
#                         Defaults to '0000'
#         MSG_TQ (str)    :Four bytes string indicates message time quality (
#                         MSG_TQ). Defaults to '1111'
#         TIME_BASE (int) :TIME_BASE. Defaults to 16777215.
#         USER_DEF (str)  :Four bytes string codes designated by user. Defaults
#                         to '0000'
#         EXT (bytes)     :Extended frame data, 16-bit words, 0 to 65518
#                         bytes as indicated by frame size, data user defined.
#     """
# 
#     CommandCode = {
#         'off': 1,
#         'on': 2,
#         'hdr': 3,
#         'cfg1': 4,
#         'cfg2': 5,
#         'cfg3': 6,
#         'ext': 8
#     }
# 
#     def __new__(
#         self,
#         IDCODE=1,
#         CMD='off',
#         TIME=None,
#         TQ_FLAGS='0000',
#         MSG_TQ='1111',
#         TIME_BASE=16777215,
#         USER_DEF='0000',
#         EXT=b''
#     ):
#         return Message.__new__(
#             Message,
#             SYNC=b'\xaaA',
#             IDCODE=IDCODE,
#             TIME=TIME,
#             TQ_FLAGS=TQ_FLAGS,
#             MSG_TQ=MSG_TQ,
#             TIME_BASE=TIME_BASE,
#             DATA=b''.join((
#                 int('0000'+USER_DEF, 2).to_bytes(1, 'big'),
#                 self.CommandCode[CMD].to_bytes(1, 'big'),
#                 EXT
#             ))
#         )
# 
# Path: phasortoolbox/parser/parser.py
# class Parser(object):
#     """ A Parser that parses synchrphasor messages defined by IEEE Std
#     C37.118.2-2011.
# 
#     Note:
#         When parsing a stream, the parser automatically detects and stores
#         configuration messages and use the most recent received configuration
#         message to parse the subsequent data messages. Multiple synchrophasor
#         message streams can be parsed using one parser instance. The parser
#         identifies each synchrophasor message stream by using its IDCODE and
#         then apply the corresponded configuration message to parse the data
#         message.
# 
#     Example:
#     # The first example creates a command message and then creates a parser to
#     parse the message.
#         my_msg = command(CMD='off') # Creates a command message.
#         print(my_msg)               # Just to show the content.
#         my_parser = parser()        # Creates a parser
#         my_msgs = my_parser.parse(msg)  # Parse the previously created mseeage\
#                                         and returns a list of parsed messages
#         print(my_msgs[0].data.cmd.name) # Print the contant of the command
#     """
# 
#     def __init__(self, raw_cfg_pkt=None):
#         self._mini_cfgs = MiniCfgs()
#         if raw_cfg_pkt:
#             self.parse(raw_cfg_pkt)
# 
#     def parse(self, raw_bytes):
#         """Parse synchrphasor message stream
# 
# 
#         """
#         stream = []
#         _io = KaitaiStream(BytesIO(raw_bytes))
#         while not _io.is_eof():
#             try:
#                 message = PhasorMessage(_io, _mini_cfgs=self._mini_cfgs)
#                 if type(message.data) != type(b''):
#                     stream.append(message)
#             except Exception as e:
#                 LOG.debug("Parsing error.")
#                 print(len(stream))
#                 raise
#         return stream


import gc
import logging
import time
import asyncio
import struct
import socket
from concurrent.futures import Executor, ThreadPoolExecutor
from phasortoolbox.message import Command
from phasortoolbox import Parser
        """
        self.loop = loop if loop is not None else asyncio.new_event_loop()
        self.executor = executor if executor is not None else ThreadPoolExecutor()

    def _add_pdc(self, _pdc_id, _pdc_callback, loop, executor):
        self._pdc_callbacks[_pdc_id] = _pdc_callback
        self._garbage_collection = False
        self.set_loop(loop, executor)

    def _remove_pdc(self, _pdc_id):
        del(self._pdc_callbacks[_pdc_id])
        if self._pdc_callbacks == {}:
            self._garbage_collection = True
        self.set_loop()


class _TCPOnly(asyncio.Protocol):
    def __init__(self, idcode, callback=lambda data: None):
        self.idcode = idcode
        self.buf = _stream_buffer(callback)

    def data_received(self, data):
        perf_counter = time.perf_counter()
        arr_time = time.time()
        self.buf.add_bytes(data, perf_counter, arr_time)

    def connection_made(self, transport):
        self.transport = transport
        self.peername = transport.get_extra_info('peername')
        LOG.warning('Connected to: {}.'.format(str(self.peername)))

# Path: phasortoolbox/message.py
# class Command():
#     """This object returns a command message in bytes.
#     Note:
#         This creates a Version 1 (0001) command message defined by IEEE Std
#         C37.118.2-2011.
#         TIME, TQ_FLAGS and MSG_TQ are used to generate SOC and FRACSEC of the
#         synchrophasor message.
# 
#     Example:
#         my_msg = Command(1,'on') # Data stream 1 turn on transmission.
#         my_msg = Command(IDCODE=2, CMD='off') # Data stream 2 turn off
#         transmission.
#         my_msg = Command(IDCODE=3, CMD='ext', EXT = b'User defined message')
#         # Send extended command frame with user defined message to the source
#         # of Data stream 3.
# 
#     Args:
#         IDCODE (int)    :Data stream ID number. Defaults to 1. 1–65534 (0 and
#         65535 are reserved).
#         CMD (str)       :Commands designated by user.
#                         Options are:
#                             'off' :Turn off transmission of data frames.
#                             'on'  :Turn on transmission of data frames.
#                             'hdr' :Send HDR frame.
#                             'cfg1':Send CFG-1 frame.
#                             'cfg2':Send CFG-2 frame.
#                             'cfg3':Send CFG-3 frame (optional command).
#                             'ext':Extended frame.
#         TIME (float)    :Epoch time. Defaults to current time.
#         TQ_FLAGS (str)  :Four bytes string indicates time quality flags.
#                         Defaults to '0000'
#         MSG_TQ (str)    :Four bytes string indicates message time quality (
#                         MSG_TQ). Defaults to '1111'
#         TIME_BASE (int) :TIME_BASE. Defaults to 16777215.
#         USER_DEF (str)  :Four bytes string codes designated by user. Defaults
#                         to '0000'
#         EXT (bytes)     :Extended frame data, 16-bit words, 0 to 65518
#                         bytes as indicated by frame size, data user defined.
#     """
# 
#     CommandCode = {
#         'off': 1,
#         'on': 2,
#         'hdr': 3,
#         'cfg1': 4,
#         'cfg2': 5,
#         'cfg3': 6,
#         'ext': 8
#     }
# 
#     def __new__(
#         self,
#         IDCODE=1,
#         CMD='off',
#         TIME=None,
#         TQ_FLAGS='0000',
#         MSG_TQ='1111',
#         TIME_BASE=16777215,
#         USER_DEF='0000',
#         EXT=b''
#     ):
#         return Message.__new__(
#             Message,
#             SYNC=b'\xaaA',
#             IDCODE=IDCODE,
#             TIME=TIME,
#             TQ_FLAGS=TQ_FLAGS,
#             MSG_TQ=MSG_TQ,
#             TIME_BASE=TIME_BASE,
#             DATA=b''.join((
#                 int('0000'+USER_DEF, 2).to_bytes(1, 'big'),
#                 self.CommandCode[CMD].to_bytes(1, 'big'),
#                 EXT
#             ))
#         )
# 
# Path: phasortoolbox/parser/parser.py
# class Parser(object):
#     """ A Parser that parses synchrphasor messages defined by IEEE Std
#     C37.118.2-2011.
# 
#     Note:
#         When parsing a stream, the parser automatically detects and stores
#         configuration messages and use the most recent received configuration
#         message to parse the subsequent data messages. Multiple synchrophasor
#         message streams can be parsed using one parser instance. The parser
#         identifies each synchrophasor message stream by using its IDCODE and
#         then apply the corresponded configuration message to parse the data
#         message.
# 
#     Example:
#     # The first example creates a command message and then creates a parser to
#     parse the message.
#         my_msg = command(CMD='off') # Creates a command message.
#         print(my_msg)               # Just to show the content.
#         my_parser = parser()        # Creates a parser
#         my_msgs = my_parser.parse(msg)  # Parse the previously created mseeage\
#                                         and returns a list of parsed messages
#         print(my_msgs[0].data.cmd.name) # Print the contant of the command
#     """
# 
#     def __init__(self, raw_cfg_pkt=None):
#         self._mini_cfgs = MiniCfgs()
#         if raw_cfg_pkt:
#             self.parse(raw_cfg_pkt)
# 
#     def parse(self, raw_bytes):
#         """Parse synchrphasor message stream
# 
# 
#         """
#         stream = []
#         _io = KaitaiStream(BytesIO(raw_bytes))
#         while not _io.is_eof():
#             try:
#                 message = PhasorMessage(_io, _mini_cfgs=self._mini_cfgs)
#                 if type(message.data) != type(b''):
#                     stream.append(message)
#             except Exception as e:
#                 LOG.debug("Parsing error.")
#                 print(len(stream))
#                 raise
#         return stream


# Path: phasortoolbox/client.py
import gc
import logging
import time
import asyncio
import struct
import socket
from concurrent.futures import Executor, ThreadPoolExecutor
from phasortoolbox.message import Command
from phasortoolbox import Parser

        """
        self.loop = loop if loop is not None else asyncio.new_event_loop()
        self.executor = executor if executor is not None else ThreadPoolExecutor()

    def _add_pdc(self, _pdc_id, _pdc_callback, loop, executor):
        self._pdc_callbacks[_pdc_id] = _pdc_callback
        self._garbage_collection = False
        self.set_loop(loop, executor)

    def _remove_pdc(self, _pdc_id):
        del(self._pdc_callbacks[_pdc_id])
        if self._pdc_callbacks == {}:
            self._garbage_collection = True
        self.set_loop()


class _TCPOnly(asyncio.Protocol):
    def __init__(self, idcode, callback=lambda data: None):
        self.idcode = idcode
        self.buf = _stream_buffer(callback)

    def data_received(self, data):
        perf_counter = time.perf_counter()
        arr_time = time.time()
        self.buf.add_bytes(data, perf_counter, arr_time)

    def connection_made(self, transport):
        self.transport = transport
        self.peername = transport.get_extra_info('peername')
        LOG.warning('Connected to: {}.'.format(str(self.peername)))

        self.transport.write(Command(self.idcode, 'off'))