Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

what does server.process(delay) do? #82

Closed
vstadnytskyi-FDA opened this issue May 25, 2022 · 6 comments
Closed

what does server.process(delay) do? #82

vstadnytskyi-FDA opened this issue May 25, 2022 · 6 comments

Comments

@vstadnytskyi-FDA
Copy link
Contributor

vstadnytskyi-FDA commented May 25, 2022

Hi All,

I have found a docstring for server.process(delay)


        """
        Process server transactions.
        :param float delay: Processing time in second
        This method should be called so frequent so that the incoming channel access
        requests are answered in time. Normally called in the loop::
            server = SimpleServer()
            ...
            while True:
                server.process(0.1)
        """

but I still do not understand what it does exactly. For example, I have a server that reads my laptop's CPU MEMORY BATTERY etc. This is an example of ca server I made.

from pcaspy import SimpleServer, Driver
import random

prefix = 'simple_daq:'
pvdb = {
    'CPU' : {
        'value': 0.0,
        'prec' : 1,
        'scan' : 1,
        'count': 1,
        'unit': '%'
    },
    'MEMORY' : {
        'value': 0.0,
        'prec' : 1,
        'scan' : 1,
        'count': 1,
        'unit': 'GB'
    },
    'BATTERY' : {
        'value': 0.0,
        'prec' : 1,
        'scan' : 1,
        'count': 1,
        'unit': '%'
       },
    'TIME' : {
        'type' : 'str',
        'value': 'time',
        'scan' : 1,
            },
    'dt' : {
        'value': 2.0,
        'prec' : 1,
        'scan' : 1,
        'count': 1,
        'unit': 's' 
    },
}


class myDriver(Driver):
    def __init__(self):
        super(myDriver, self).__init__()

    def read(self, reason):
        from time import ctime, time
        import psutil
        if reason == 'TIME':
            value = ctime(time())
        elif reason == 'CPU':
            value = psutil.cpu_percent()
        elif reason == 'BATTERY':
            if psutil.sensors_battery() is not None:
                value = psutil.sensors_battery().percent
            else:
                value = np.nan
        elif reason == 'MEMORY':
            value = psutil.virtual_memory().used / (1024**3)
        else:
            value = self.getParam(reason)
        return value

if __name__ == '__main__':
    from time import sleep,ctime, time
    server = SimpleServer()
    server.createPV(prefix, pvdb)
    driver = myDriver()
    while True:
        server.process(driver.getParam('dt'))
        print(ctime(time()))

It works well. I have added print statement into while True loop to see how often it is called. I noticed that it is called many many time instead of every 2 seconds.

Wed May 25 18:35:41 2022
Wed May 25 18:35:41 2022
Wed May 25 18:35:41 2022
Wed May 25 18:35:41 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:43 2022
Wed May 25 18:35:45 2022

I had a GUI and a terminal client connected. When I disconnected them the rate dropped down to 2 second. So it seems to me that server.process processes one request at a time. And I assume that the read() function in MyDriver is called every time server.process is called. Is it correct? Is there some documentation on what is actually happening.

I prefer driver instance or device instance to push update whenever is needed instead of server pulling updates on a timer. for example, if a PV on a server changes I want all clients to get an updated ASAP

@xiaoqiangwang
Copy link
Collaborator

The SimpleServer.process spends as much time as delay in wait for client transactions. The actual time varies.

In the following modification, the driver polls the CPU/MEMORY status and pushes the updates to the monitoring clients in a thread.

import threading
import time

import psutil

from pcaspy import SimpleServer, Driver

prefix = 'simple_daq:'
pvdb = {
    'CPU' : {
        'value': 0.0,
        'prec' : 1,
        'unit': '%'
    },
    'MEMORY' : {
        'value': 0.0,
        'prec' : 1,
        'unit': 'GB'
    },
    'BATTERY' : {
        'value': 0.0,
        'prec' : 1,
        'unit': '%'
       },
    'TIME' : {
        'type' : 'str',
        'value': 'time',
    },
    'dt' : {
        'value': 2.0,
        'prec' : 1,
        'unit': 's'
    },
}


class myDriver(Driver):
    def __init__(self):
        super(myDriver, self).__init__()
        threading.Thread(target=self.poll, daemon=True).start()

    def poll(self):
        while True:
            self.setParam('TIME', time.ctime())
            self.setParam('CPU', psutil.cpu_percent())
            if psutil.sensors_battery() is not None:
                self.setParam('BATTERY', psutil.sensors_battery().percent)
            self.setParam('MEMORY', psutil.virtual_memory().used / (1024**3))

            self.updatePVs()

            time.sleep(self.getParam('dt'))


if __name__ == '__main__':
    server = SimpleServer()
    server.createPV(prefix, pvdb)
    driver = myDriver()
    while True:
        server.process(driver.getParam('dt'))
        print(time.ctime())

@vstadnytskyi-FDA
Copy link
Contributor Author

Thank you this is very useful!

The SimpleServer.process spends as much time as delay in wait for client transactions.

What is appropriate time for delay. I see you use 0.1 s, why not use 0.01s or 1 second. How should go about picking dt?

@xiaoqiangwang
Copy link
Collaborator

0.1 is quite arbitrarily chosen. Also because it is called in a tight loop, the server actually is always waiting for requests from the clients and driver.

dt is the how frequent the driver updates the values. It is not coupled with the processing period.

@vstadnytskyi-FDA
Copy link
Contributor Author

vstadnytskyi-FDA commented Jun 3, 2022

The fastest update I can get it around 1 second. Is it something in my code that takes time or there is limitation on the server side? I want to update around 0.3 seconds, then it does not feel like there is a gap between updates.

server: https://github.com/vstadnytskyi/caproto-sandbox/blob/master/caproto_sandbox/transition/pcaspy/simple_daq_push_server.py
gui: https://github.com/vstadnytskyi/caproto-sandbox/blob/master/caproto_sandbox/simple_daq/gui.py

even with poll function running in a thread I also get 1second per update. I cannot go faster

@vstadnytskyi-FDA
Copy link
Contributor Author

The fastest update I can get it around 1 second. Is it something in my code that takes time or there is limitation on the server side? I want to update around 0.3 seconds, then it does not feel like there is a gap between updates.

server: https://github.com/vstadnytskyi/caproto-sandbox/blob/master/caproto_sandbox/transition/pcaspy/simple_daq_push_server.py gui: https://github.com/vstadnytskyi/caproto-sandbox/blob/master/caproto_sandbox/simple_daq/gui.py

even with poll function running in a thread I also get 1second per update. I cannot go faster

I have figured it! I need to change SCAN to small value for each PV. what is the purpose of SCAN? is it the fastest update allowed?

@xiaoqiangwang
Copy link
Collaborator

scan fields controls how often the read method is called. Any value is allowed, there is no checking. But the system has to be able to cope with it.

On the other hand, the modified program structure is my comment is preferred, because it uses only one thread.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants