Example #1
0
class MiniPMU(object):

    def __init__(self, name: str='', dime_address: str='ipc:///tmp/dime',
                 pmu_idx: list=list(), max_store: int=1000, pmu_ip: str='0.0.0.0', pmu_port: int=1410,
                 **kwargs):
        """
        Create a MiniPMU instance for PMU data streaming over Mininet.

        Assumptions made for

        Parameters
        ----------
        name
        dime_address
        pmu_idx
        max_store
        pmu_ip
        pmu_port
        kwargs
        """
        assert name, 'PMU Receiver name is empty'
        assert pmu_idx, 'PMU idx is empty'
        self.name = name
        self.dime_address = dime_address
        self.pmu_idx = pmu_idx
        self.max_store = max_store

        # for recording
        self.max_store_record = 30 * 600  # 600 seconds

        self.reset = True
        self.pmu_configured = False
        self.pmu_streaming = False

        self.reset_var()

        self.dimec = Dime(self.name, self.dime_address)
        self.pmu = Pmu(ip=pmu_ip, port=pmu_port)

    def reset_var(self, retain_data=False):
        """
        Reset flags and memory
        :return: None
        """
        if not self.reset:
            return

        self.bus_name = []
        self.var_idx = {'am': [],
                        'vm': [],
                        'w': [],
                        }

        self.fn = 60
        self.Vn = []

        self.Varheader = list()
        self.Idxvgs = dict()
        self.SysParam = dict()
        self.SysName = dict()
        self.Varvgs = ndarray([])

        self.t = ndarray([])
        self.data = ndarray([])
        self.count = 0

        # recording storage
        if not retain_data:
            self.t_record = ndarray([])
            self.data_record = ndarray([])
            self.count_record = 0
            self.counter_replay = 0  # replay index into `data_record` and `t_record`
            self.record_state = RecordState.IDLE

        self.last_data = None
        self.last_t = None

    def start_dime(self):
        """
        Starts the dime client stored in `self.dimec`
        """
        # logger.info('Connecting to server at {}'.format(self.dime_address))
        assert self.dimec.start()

        # logger.info('DiME client connected')

    def respond_to_sim(self):
        """
        DEPRECIATED: Respond with data streaming configuration to the simulator

        :return: None
        """
        pass

    def get_bus_name(self):
        """
        Return bus names based on ``self.pmu_idx`` and store bus names to
        ``self.bus_name``

        :return: list of bus names
        """

        # assign generic bus names
        self.bus_name = list(self.pmu_idx)

        for i in range(len(self.bus_name)):
            self.bus_name[i] = 'Bus_' + str(self.bus_name[i])

        # assign names from SysName if present
        if len(self.SysName) > 0:
            for i in range(len(self.bus_name)):
                self.bus_name[i] = self.SysName['Bus'][self.pmu_idx[i] - 1]

        # logger.debug('PMU names changed to: {}'.format(self.bus_name))
        return self.bus_name

    def get_bus_Vn(self):
        """
        Retrieve Bus.Vn

        Returns
        -------

        """
        self.Vn = [1] * len(self.pmu_idx)

        for i, idx in enumerate(self.pmu_idx):
            self.Vn[i] = self.SysParam['Bus'][idx][1] * 1000  # get Vn

        # logger.info('Retrieved bus Vn {}'.format(self.Vn))

    def config_pmu(self):
        """
        Sets the ConfigFrame2 of the PMU

        :return: None
        """

        self.cfg = ConfigFrame2(pmu_id_code=self.pmu_idx[0],  # PMU_ID
                           time_base=1000000,  # TIME_BASE
                           num_pmu=1,  # Number of PMUs included in data frame
                           station_name=self.bus_name[0],  # Station name
                           id_code=self.pmu_idx[0],  # Data-stream ID(s)
                           data_format=(True, True, True, True),  # Data format - POLAR; PH - REAL; AN - REAL; FREQ - REAL;
                           phasor_num=1,  # Number of phasors
                           analog_num=1,  # Number of analog values
                           digital_num=1,  # Number of digital status words
                            channel_names=["V_PHASOR", "ANALOG1", "BREAKER 1 STATUS",
                            "BREAKER 2 STATUS", "BREAKER 3 STATUS", "BREAKER 4 STATUS", "BREAKER 5 STATUS",
                            "BREAKER 6 STATUS", "BREAKER 7 STATUS", "BREAKER 8 STATUS", "BREAKER 9 STATUS",
                            "BREAKER A STATUS", "BREAKER B STATUS", "BREAKER C STATUS", "BREAKER D STATUS",
                            "BREAKER E STATUS", "BREAKER F STATUS", "BREAKER G STATUS"],  # Channel Names
                           ph_units=[(0, 'v')],  # Conversion factor for phasor channels - (float representation, not important)
                           an_units=[(1, 'pow')],  # Conversion factor for analog channels
                           dig_units=[(0x0000, 0xffff)],  # Mask words for digital status words
                           f_nom=60.0,  # Nominal frequency
                           cfg_count=1,  # Configuration change count
                           data_rate=30)  # Rate of phasor data transmission)

        self.hf = HeaderFrame(self.pmu_idx[0],  # PMU_ID
                              "MiniPMU <{name}> {pmu_idx}".format(name=self.name, pmu_idx = self.pmu_idx))  # Header Message

        self.pmu.set_configuration(self.cfg)
        self.pmu.set_header(self.hf)
        # self.pmu.run()

    def find_var_idx(self):
        """
        Returns a dictionary of the indices into Varheader based on
        `self.pmu_idx`. Items in `self.pmu_idx` uses 1-indexing.

        For example, if `self.pmu_idx` == [1, 2], this function will return
        the indices of
         - Idxvgs.Pmu.vm[0] and Idxvgs.Pmu.vm[1] as vm
         - Idxvgs.Pmu.am[0] and Idxvgs.Pmu.am[1] as am
         - Idxvgs.Bus.w_Busfreq[0] and Idxvgs.Bus.w_Busfreq[1] as w
        in the dictionary `self. var_idx` with the above fields.

        :return: ``var_idx`` in ``pmudata``
        """
        npmu = len(self.Idxvgs['Pmu']['vm'][0])

        self.var_idx['vm'] = [int(i) - 1 for i in self.pmu_idx]
        self.var_idx['am'] = [npmu + int(i) - 1 for i in self.pmu_idx]
        self.var_idx['w'] = [2 * npmu + int(i) - 1 for i in self.pmu_idx]

    # TODO: make it static
    @property
    def vgsvaridx(self):
        return array(self.var_idx['vm'] +
                     self.var_idx['am'] +
                     self.var_idx['w'], dtype=int)

    def init_storage(self, flush=False):
        """
        Initialize data storage `self.t` and `self.data`

        :return: if the storage has been reset
        """
        # TODO: make it more efficient??
        ret = False

        if self.count % self.max_store == 0:
            self.t = zeros(shape=(self.max_store, 1), dtype=float)
            self.data = zeros(shape=(self.max_store, len(self.pmu_idx * 3)),
                              dtype=float)
            self.count = 0
            ret = True
        else:
            ret = False

        if (self.count_record % self.max_store_record == 0) or (flush is True):
            self.t_record = zeros(shape=(self.max_store_record, 1),
                                  dtype=float)
            self.data_record = zeros(shape=(self.max_store_record,
                                     len(self.pmu_idx * 3)), dtype=float)
            self.count_record = 0
            self.counter_replay = 0

            ret = ret and True
        else:
            ret = False

        return ret

    def sync_and_handle(self):
        """
        Sync and call data processing functins

        :return:
        """
        ret = False

        var = self.dimec.sync()

        if var is False or None:
            return ret

        # if self.reset is True:
        #     logger.info('[{name}] variable <{var}> synced.'
        #                 .format(name=self.name, var=var))

        data = self.dimec.workspace[var]

        if var in ('SysParam', 'Idxvgs', 'Varheader'):
            # only handle these three variables during reset cycle

            if self.reset is True:
                self.__dict__[var] = data
            # else:
            #     logger.info('{} not handled outside reset cycle'.format(var))

        elif var == 'pmudata':
            # only handle pmudata during normal cycle
            if self.reset is False:
                # logger.info('In, t={:.4f}'.format(data['t']))
                self.handle_measurement_data(data)
            # else:
            #     logger.info('{} not handled during reset cycle'.format(var))

        # handle SysName any time
        elif var == 'SysName':
            self.__dict__[var] = data
            self.get_bus_name()

        elif var == 'DONE' and data == 1:
            self.reset = True
            self.reset_var(retain_data=True)

        elif var == 'pmucmd' and isinstance(data, dict):
            cmd = ''
            if data.get('record', 0) == 1:
                # start recording
                if self.record_state == RecordState.IDLE \
                        or self.record_state == RecordState.RECORDED:

                    self.record_state = RecordState.RECORDING
                    cmd = 'start recording'
                # else:
                #     logger.warning('cannot start recording in state {}'
                #                    .format(self.record_state))

            elif data.get('record', 0) == 2:
                # stop recording if started
                if self.record_state == RecordState.RECORDING:
                    cmd = 'stop recording'
                    self.record_state = RecordState.RECORDED
                # else:
                #     logger.warning('cannot stop recording in state {}'
                #                    .format(self.record_state))

            if data.get('replay', 0) == 1:
                # start replay
                if self.record_state == RecordState.RECORDED:
                    cmd = 'start replay'
                    self.record_state = RecordState.REPLAYING
                # else:
                #     logger.warning('cannot start replaying in state {}'
                #                    .format(self.record_state))
            if data.get('replay', 0) == 2:
                # stop replay but retain the saved data
                if self.record_state == RecordState.REPLAYING:
                    cmd = 'stop replay'
                    self.record_state = RecordState.RECORDED
                # else:
                #     logger.warning('cannot stop replaying in state {}'
                #                    .format(self.record_state))
            if data.get('flush', 0) == 1:
                # flush storage
                cmd = 'flush storage'
                self.init_storage(flush=True)
                self.record_state = RecordState.IDLE

            # if cmd:
            #     logger.info('[{name}] <{cmd}>'.format(name=self.name, cmd=cmd))

        # else:
        #     logger.info('[{name}] {cmd} not handled during normal ops'
        #                 .format(name=self.name, cmd=var))

        return var

    def handle_measurement_data(self, data):
        """
        Store synced data into self.data and return in a tuple of (t, values)

        :return: (t, vars)
        """
        self.init_storage()

        self.data[self.count, :] = data['vars'][0, self.vgsvaridx].reshape(-1)
        self.t[self.count, :] = data['t']
        self.count += 1

        # record
        if self.record_state == RecordState.RECORDING:
            self.data_record[self.count_record, :] = \
                    data['vars'][0, self.vgsvaridx].reshape(-1)

            self.t_record[self.count_record, :] = data['t']
            self.count_record += 1

        self.last_data = data['vars']
        self.last_t = data['t']

        return data['t'], data['vars']

    def run(self):
        """
        Process control function

        :return None
        """
        self.start_dime()
        self.pmu.run()

        while True:
            if self.reset is True:
                # receive init and respond
                # logger.info('[{name}] Entering reset mode..'
                #             .format(name=self.name))

                while True:
                    var = self.sync_and_handle()

                    if var is False:
                        time.sleep(0.01)

                    if len(self.Varheader) > 0\
                            and len(self.Idxvgs) > 0\
                            and len(self.SysParam) > 0 \
                            and len(self.SysName) > 0:

                        self.find_var_idx()
                        self.get_bus_Vn()

                        break

                self.respond_to_sim()

                if self.pmu_configured is False:
                    self.config_pmu()
                    self.pmu_configured = True

                self.reset = False

            # logger.debug('Entering sync and short sleep...')

            var = self.sync_and_handle()
            time.sleep(0.001)

            if var is False:
                continue

            elif var == 'pmudata':
                if self.pmu.clients and not self.reset:

                    if self.record_state == RecordState.REPLAYING:
                        # prepare recorded data
                        npmu = len(self.pmu_idx)
                        v_mag = self.data_record[self.counter_replay, :npmu] * self.Vn[0]
                        v_ang = wrap_angle(self.data_record[self.counter_replay, npmu:2*npmu])
                        v_freq = self.data_record[self.counter_replay, 2*npmu:3*npmu] * self.fn
                        self.counter_replay += 1

                        # at the end of replay, reset
                        if self.counter_replay == self.count_record:
                            self.counter_replay = 0
                            self.record_state = RecordState.RECORDED

                    else:
                        # use fresh data
                        v_mag = self.last_data[0, self.var_idx['vm']] * self.Vn[0]
                        v_ang = wrap_angle(self.last_data[0, self.var_idx['am']])
                        v_freq = self.last_data[0, self.var_idx['w']] * self.fn

                    # TODO: add noise to data

                    try:
                        # TODO: fix multiple measurement (multi-bus -> one PMU case)
                        self.pmu.send_data(phasors=[(v_mag, v_ang)],
                                           analog=[9.99],
                                           digital=[0x0001],
                                           #freq=(v_freq-60)*1000
                                           freq = v_freq
                                           )

                        # logger.info('Out, f={f:.5f}, vm={vm:.1f}, am={am:.2f}'.format(f=v_freq[0], vm=v_mag[0], am=v_ang[0]))

                    except Exception as e:
                        logger.exception(e)
        "BREAKER 9 STATUS", "BREAKER A STATUS", "BREAKER B STATUS",
        "BREAKER C STATUS", "BREAKER D STATUS", "BREAKER E STATUS",
        "BREAKER F STATUS", "BREAKER G STATUS"
    ],  # Channel Names
    [
        (0, "v"), (0, "v"), (0, "v")
    ],  # Conversion factor for phasor channels - (float representation, not important)
    [(1, "pow")],  # Conversion factor for analog channels
    [(0x0000, 0xffff)],  # Mask words for digital status words
    50,  # Nominal frequency
    1,  # Configuration change count
    30)  # Rate of phasor data transmission)

pmu.set_configuration(cfg)
pmu.set_header(
    "Hey! I'm randomPMU! Guess what? I'm sending random measurements values!")

pmu.run()

while True:
    if pmu.clients:
        pmu.send_data(phasors=[
            (random.uniform(215.0, 240.0), random.uniform(-0.1, 0.3)),
            (random.uniform(215.0, 240.0), random.uniform(1.9, 2.2)),
            (random.uniform(215.0, 240.0), random.uniform(3.0, 3.14))
        ],
                      analog=[9.91],
                      digital=[0x0001])

pmu.join()
Example #3
0

        if pmu.clients:  # Check if there is any connected PDCs
            savedata = input('press 0 if you want to save data, press 1 for sending bad data, normal operation otherwise:')
            if savedata == '0':
# savedata will be replaced with the data that has been sent from hacker.py
                print('Storing Data')
                input1 = random.uniform(215.0, 240.0)
                input2 = random.uniform(-0.1, 0.3)
                input3 = random.uniform(215.0, 240.0)
                input4 = random.uniform(1.9, 2.2)
                input5 = random.uniform(215.0, 240.0)
                input6 = random.uniform(3.0, 3.14)
                pmu.send_data(phasors=[(input1, input2),
                                       (input3, input4),
                                       (input5, input6)],
                              analog=[9.91],
                              digital=[0x0001])
                stored.append(input1)
                stored.append(input2)
                stored.append(input3)
                stored.append(input4)
                stored.append(input5)
                stored.append(input6)
            elif savedata == '1':
#hackerPMU starts injecting stored data. This will be replaced with data from hacker.py
                print('Sending Bad Data')
                pmu.send_data(phasors=[(stored[i], stored[i+1]),
                                       (stored[i+2], stored[i+3]),
                                       (stored[i+4], stored[i+5])],
                              analog=[9.91],
Example #4
0
            Voltage_AI_6 = AI_6 * 3.3 / 1024
            Voltage_AI_7 = AI_7 * 3.3 / 1024
            Voltage_AI_8 = AI_8 * 3.3 / 1024
            DI_1 = GPIO.input(11)  #Read Input Pin 11 as a Digital In
            DI_2 = GPIO.input(13)  #Read Input Pin 13 as a Digital In
            DI_3 = GPIO.input(29)  #Read Input Pin 29 as a Digital In
            DI_4 = GPIO.input(31)  #Read Input Pin 31 as a Digital In
            hour = time.localtime().tm_hour
            minute = time.localtime().tm_min
            seconds = time.localtime().tm_sec
            system_time = hour * 10000 + minute * 100 + seconds
            file = open("/sys/class/thermal/thermal_zone0/temp")
            system_temp = float(file.read()) / 1000
            file.close()
            pmu.send_data(phasors=[(AI_1, 0), (AI_2, 0), (AI_3, 0), (AI_4, 0)],
                          analog=[AI_1, AI_2, AI_3, system_time, system_temp],
                          digital=[DI_1, DI_2, DI_3, DI_4])

            rr = client.read_input_registers(0, 8, unit=1)
            rq = client.write_registers(
                0, [AI_1, 1, AI_3, AI_4, AI_5, AI_6, AI_7, AI_8], unit=1)
            assert (rq.function_code < 0x80)  #if FC>0x80 --> Error
            rr = client.read_holding_registers(0, 8, unit=1)

            rr = client.read_discrete_inputs(0, 4, unit=1)
            rq = client.write_coils(0, [1, DI_2, DI_3, DI_4], unit=1)
            rr = client.read_coils(0, 4, unit=1)

    client.close()
    pmu.join()
Example #5
0
                       3,  # Number of phasors
                       1,  # Number of analog values
                       1,  # Number of digital status words
                       ["VA", "VB", "VC", "ANALOG1", "BREAKER 1 STATUS",
                        "BREAKER 2 STATUS", "BREAKER 3 STATUS", "BREAKER 4 STATUS", "BREAKER 5 STATUS",
                        "BREAKER 6 STATUS", "BREAKER 7 STATUS", "BREAKER 8 STATUS", "BREAKER 9 STATUS",
                        "BREAKER A STATUS", "BREAKER B STATUS", "BREAKER C STATUS", "BREAKER D STATUS",
                        "BREAKER E STATUS", "BREAKER F STATUS", "BREAKER G STATUS"],  # Channel Names
                       [(0, "v"), (0, "v"),
                        (0, "v")],  # Conversion factor for phasor channels - (float representation, not important)
                       [(1, "pow")],  # Conversion factor for analog channels
                       [(0x0000, 0xffff)],  # Mask words for digital status words
                       50,  # Nominal frequency
                       1,  # Configuration change count
                       30)  # Rate of phasor data transmission)

    pmu.set_configuration(cfg)
    pmu.set_header("Hey! I'm randomPMU! Guess what? I'm sending random measurements values!")

    pmu.run()

    while True:
        if pmu.clients:
            pmu.send_data(phasors=[(random.uniform(215.0, 240.0), random.uniform(-0.1, 0.3)),
                                   (random.uniform(215.0, 240.0), random.uniform(1.9, 2.2)),
                                   (random.uniform(215.0, 240.0), random.uniform(3.0, 3.14))],
                          analog=[9.91],
                          digital=[0x0001])

    pmu.join()