iicsys / pypmu

pyPMU - Python implementation of the IEEE C37.118 synchrophasor standard
BSD 3-Clause "New" or "Revised" License
63 stars 46 forks source link

Runtime Update of Synchrophasor Data Reporting Rate #22

Open Pratyush-das opened 4 years ago

Pratyush-das commented 4 years ago

Hi all, I am using a modified version of randomPMU to send random phasor values. Now I want to dynamically control the Synchrophasor Reporting Rate (RR) (from say 10 fps to 50fps). I have another UDP sender script which sends integers such as 10, 25, 50 referring to reporting rates in fps. So this script sends 50 and the data reporting rate changes to 50 using the following code:

pmu.set_data_rate(50)

When I change the RR, the code acknowledges that the configuration frame has changed, however, the PMU connection tester still continues to receive at the previously set rate. Only after I disconnect and re-connect the tester I get the updated frame rate. Please guide me on how to change the synchrophasor reporting rate in a dynamic way.

code for Random_PMU :

import random

from synchrophasor.frame import ConfigFrame2
#from synchrophasor.pmu import Pmu
from synchrophasor.pmumod import Pmu   #pmumod is same as pmu, I made some comments 
import socket
import struct

"""
randomPMU will listen on ip:port for incoming connections.
After request to start sending measurements - random
values for phasors will be sent.
"""

if __name__ == "__main__":

    ## Frame rate receive via UDP

    UDP_IP = "10.10.114.22"
    UDP_PORT = 25000
    sock = socket.socket(socket.AF_INET,  # Internet
                         socket.SOCK_DGRAM)  # UDP
    sock.bind((UDP_IP, UDP_PORT))
    ## PMU Config ##

    pmu = Pmu(ip="10.10.114.22", port=1410)
    pmu.logger.setLevel("DEBUG")

    cfg = ConfigFrame2(500,  # PMU_ID
                       1000000,  # TIME_BASE
                       1,  # Number of PMUs included in data frame
                       "Pratyush Random Station",  # Station name
                       1500,  # Data-stream ID(s)
                       (True, True, True, True),  # Data format - POLAR; PH - REAL; AN - REAL; FREQ - REAL;
                       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
                       50)  # Rate of phasor data transmission) # Make it variable

    pmu.set_configuration(cfg)
    pmu.set_header("Hey! I'm randomPMU!  variable Reporting Rate")

    pmu.run()

    while True:
        raw = sock.recv(1024) # Receive the required data reporting rate via UDP
        #raw, addr = sock.recvfrom(1024)
        rr = int(raw.decode("utf-8"))
        pmu.set_data_rate(rr)
        # first = True
        # if first:
        #     pmu.set_data_rate(rr)
        #     first=False

        while True:
            raw = sock.recv(1024)
            drr= int(raw.decode("utf-8"))
            print(drr)
            if drr == rr:
                if pmu.clients:
                    pmu.send_data(phasors=[(random.uniform(500.0, 600.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])
            else:
                pmu.set_data_rate(drr)
                rr=drr

    pmu.join()

Code for UDP sender:

# -*- coding: utf-8 -*-
"""
Created on Wed Jun 24 21:56:10 2020

@author: Pratyush
"""

import socket

UDP_IP = "10.10.114.22"
UDP_PORT = 25000
sock = socket.socket(socket.AF_INET,  # Internet
                     socket.SOCK_DGRAM)  # UDP
while True:
    MESSAGE = b"10"

    print("UDP target IP: %s" % UDP_IP)
    print("UDP target port: %s" % UDP_PORT)
    print("message: %s" % MESSAGE)

    sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))
poledna commented 4 years ago

I just made some changes to the code and created the above PR. To change the Reporting Rate, you'd have to change the data inside of a spawned process, but as the process was already spawned, The process doesn't know that the variable was changed, so I added a variable that works as a pipe and informs the data to the process. And changed to check in every loop if there is a change. with this changes it changes the report rate on PMU Connection Tester. But it doesn't show in the Configured Report Rate that it changed but in the CFG2 it is changed and in the Frames/sec it shows correctly the RR. Even though I explicitly asked the CFG2. It might be something with PMU connection tester or something else.

About your program I've made a couple changes in the UDP sender i just changed it so that it is oneshot

import socket

UDP_IP = "192.168.1.112"
UDP_PORT = 25000
sock = socket.socket(socket.AF_INET,  # Internet
                     socket.SOCK_DGRAM)  # UDP
# while True:
MESSAGE = b"30"

print("UDP target IP: %s" % UDP_IP)
print("UDP target port: %s" % UDP_PORT)
print("message: %s" % MESSAGE)

sock.sendto(MESSAGE, (UDP_IP, UDP_PORT))

in the random pmu i've made more substantial changes:

import random

from synchrophasor.frame import ConfigFrame2
from synchrophasor.pmu import Pmu
# from synchrophasor.pmumod import Pmu   #pmumod is same as pmu, I made some comments 
import socket
import struct
"""
randomPMU will listen on ip:port for incoming connections.
After request to start sending measurements - random
values for phasors will be sent.
"""
if __name__ == "__main__":

    ## Frame rate receive via UDP

    UDP_IP = "192.168.1.112"
    UDP_PORT = 25000
    sock = socket.socket(socket.AF_INET,  # Internet
                         socket.SOCK_DGRAM)  # UDP
    sock.bind((UDP_IP, UDP_PORT))
    ## PMU Config ##

    pmu = Pmu(ip="192.168.1.112", port=1410)
    # pmu.logger.setLevel("DEBUG")

    cfg = ConfigFrame2(1,  # PMU_ID
                       1000000,  # TIME_BASE
                       1,  # Number of PMUs included in data frame
                       "Pratyush Random Station",  # Station name
                       1500,  # Data-stream ID(s)
                       (True, True, True, True),  # Data format - POLAR; PH - REAL; AN - REAL; FREQ - REAL;
                       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
                       50)  # Rate of phasor data transmission) # Make it variable

    pmu.set_configuration(cfg)
    pmu.set_header("Hey! I'm randomPMU!  variable Reporting Rate")

    pmu.run()
    sock.settimeout(1/100)#you dont want to wait forever a data that might not come
    flag=False
    while True:
        try:
            raw = sock.recv(1024)
            drr = int(raw.decode("utf-8"))
            print(drr)
            flag=True
        except socket.timeout: #to catch the error essentially it will only store drr if there is something to store and set the flag
            pass
        if pmu.clients:
            pmu.send_data(phasors=[(random.uniform(500.0, 600.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])
        if flag: # if it received something it changes the RR 
            pmu.set_data_rate(drr)
            flag=False

    pmu.join()

this is one solution, not sure if it is maintainer approved solution or if there is an easier one. If you try to use it you should use the branch of the PR i just made. (multiprocess_variable)