Keeping state in a constantly running python script - python

I have a python 2.7 script which is getting pretty unweildly.
The script runs constantly, in each loop it checks a few things, compares them to the previous run and makes some decisions.
The biggest problem I have is with the variables, I have half a dozen and I am limited in how I use them due to scope.
eg;
import time
import os
LOG_FILE = "/var/log/sc.log"
CHECK_FILE_TMP = "/tmp/myfile"
CHECK_FILE_USR = "/home/snoppy/testfile"
CHECK_FILE_TMP_TIME_INTERVAL = 20
CHECK_FILE_USR_TIME_INTERVAL = 30
def main():
try:
last_file_tmp_size = 0
last_file_usr_size = 0
last_file_tmp_mtime = 0
last_file_usr_mtime = 0
last_file_tmp_check_time = 0
last_file_usr_check_time = 0
tmp_file_changed = False
usr_file_changed = False
loop_start_time = 0
print "Starting loop"
while True:
loop_start_time = time.time()
if (time.time() - last_file_tmp_check_time > CHECK_FILE_TMP_TIME_INTERVAL):
tmp_file_changed = checkFileChanged(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_size = getFileSize(CHECK_FILE_TMP)
last_file_tmp_mtime = getFileMTime(CHECK_FILE_TMP)
if(tmp_file_changed):
logChange(CHECK_FILE_TMP, last_file_tmp_size, last_file_tmp_mtime)
last_file_tmp_check_time = time.time()
....
....
sleep(1)
...
So thats sort of what I am dealing with.
I have local variables, which I seem to be stuck with, I have to pass them around into functions - I don't want to call them global.
Ideally.... if I could get a main() function like
try:
checkFile(CHECK_FILE_TMP)
checkFile(CHECK_FILE_USR)
sleep(0.1)
except:
...
as the main is so big! and I have to pass around the variables everywhere... it feels like the main function is so bloated!
Perhaps.... I might have to go for a class?

You need to identify the parts of your program that can be abstracted from their specific details and reused multiple times.
This is an art, don't expect it to be easy. You can find many books on Object oriented programming that explain the basics and give you some direction.
Here's just a quick example:
class FileChecker(object):
def __init__(self, path, interval):
self.path = path
self.interval = interval
self.last_size = None
self.last_mtime = None
self.last_check_time = None
def hasChanged(self):
...
def logChange(self):
...
class MultiFileChecker(object):
DELAY = 1
def __init__(self):
self.files = []
self.loop_start_time = 0
def addFile(self, f):
self.files.append(f)
def loop(self):
try:
print "Starting loop"
while True:
now = time.time()
self.loop_start_time = now
for f in self.files:
if now - f.last_check_time > f.interval:
if f.hasChanged():
f.logChange()
...
sleep(self.DELAY)
...
if __name__ == '__main__':
checker = MultiFileChecker()
checker.add(FileChecker("/var/log/sc.log", 10))
checker.add(FileChecker("/tmp/myfile", 20))
checker.add(FileChecker("/home/snoppy/testfile", 30))
checker.loop()

Related

Process executes functions, but they are not doing their job

I'm doing project on RPi and came across problem, that I can't solve. I'm using multiprocess to calculate wind speed with anemometer, and servo based platform to move around pv panel. In the main function I declare processes and objects:
def main():
ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5,
measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
prb = PhotoresistorBase(TR=1, TL=3, BR=0, BL=2, signifficant_diff=40)
BS = Servo(pin=12, min_val=10, max_val=50)
US = Servo(pin=18, min_val=29, max_val=43)
pv_based = Pv(6)
pv_servo = Pv(5)
...
calculate_wind_vel_p = Process(target=calculate_wind_vel, args=(ane, BS, US))
calculate_power_p = Process(target=calculate_power, args=(pv_based, pv_servo))
turn_platform_p = Process(target=turn_platform, args=(prb, BS, US))
calculate_wind_vel_p.start()
calculate_power_p.start()
turn_platform_p.start()
calculate_wind_vel_p.join()
calculate_power_p.join()
turn_platform_p.join()
Process calculate_power_p works great, problem appears in two other processes
Process calculate_wind_vel_p targets function calculate_wind_vel, which looks like this:
def calculate_wind_vel(ane, BS, US):
while True:
wind_speed = ane.calculate_mean_wind_velocity()
...
data = (datetime.datetime.now(), wind_speed)
insert_vel_data(conn, data)
time.sleep(1)
and Anemometer class, which contains calculate_mean_wind_velocity() looks like this:
class Anemometer:
def __init__(self, pin, radius, anamometer_factor, action_time,
measurment_fraquency, max_wind_speed, mesurment_time):
...
def calculate_wind_velocity(self) -> float:
rotations = 0
count = 0
endtime = time.time() + self.action_time
sensorstart = GPIO.input(self.pin)
circumference = (self.radius * 2 / 1000) * math.pi
while time.time() < endtime:
if GPIO.event_detected(self.pin):
count = count + 1
**print(count)**
if rotations==1 and sensorstart==1:
rotations = 0
rots_per_second = float(count/3/self.action_time)
wind_vel = float((rots_per_second)*circumference*self.anamometer_factor)
**print("Wind vel: " + str(wind_vel))**
return wind_vel
def calculate_mean_wind_velocity(self) -> float:
sum = 0
measure_count = int(self.mesurment_time/self.action_time)
for _ in range(measure_count):
sum += self.calculate_wind_velocity()
return float(sum/measure_count)
The Problem:
When process executes calculate_wind_vel() it jumps to ane.calculate_mean_wind_velocity(). The second function, executes calculate_wind_velocity e.g 4 times and calculate mean wind speed.
When calculate_wind_velocity() starts, program prints 'count' only once (first bolded print) and later count does not increment despite event is triggered. Than it calculate wind speed, with that one counted interruption. Later when calculate_wind_velocity() is execute again (process target has while True:) count is not incremented at all!
weirdest thing is that, when I run this code without process, just paste this into main():
ane = Anemometer(pin=38, radius=65, anamometer_factor=1, action_time=5,
measurment_fraquency=0.01, max_wind_speed=40, mesurment_time=30)
while True:
ret = ane.calculate_mean_wind_velocity()
print(ret)
time.sleep(2)
It works perfect - it calculates every single GPIO.event_detected(self.pin) and wind_speed, so it has smth to do with processes!.
Similar problem with moving platform - process executes functions from servo class, but it does not move, functions don't calculate/change state.

Python class scope lost in nested recursion function

When I attempted to write a recursive nested function within a python class, every time the recursive function completed and returned back to the first function my class property reverted back to the original state.
def main():
input = [
[1,3,1],
[1,5,1],
[4,2,1]
]
sln = Solution()
sln.findPath(input)
print("Output: " + str(sln.minPathSum))
print(*sln.minPath, sep = "->")
class Solution():
minPath = []
minPathSum = None
grid = []
def findPath(self, grid):
self.minPath = []
self.minPathSum = None
self.grid = grid
self.recurse(0,0,[])
def recurse(self, xCoord, yCoord, currentPath):
if(len(self.grid) <= yCoord):
return
if(len(self.grid[yCoord]) <= xCoord):
return
currentValue = self.grid[yCoord][xCoord]
currentPath.append(currentValue)
if(len(self.grid) - 1 == yCoord and len(self.grid[yCoord]) - 1 == xCoord):
currentPathSum = sum(currentPath)
if(self.minPathSum == None or currentPathSum < self.minPathSum):
self.minPathSum = currentPathSum
self.minPath = currentPath
else:
#right
self.recurse(xCoord + 1, yCoord, currentPath)
#down
self.recurse(xCoord, yCoord + 1, currentPath)
currentPath.pop()
return
if __name__ == "__main__":
main()
Running this results in:
Output: 7
Debugging the code within VSCode does indicate that self.minPath is getting set within the recurse function; however, it appears to be losing scope to the original class instance.
In addition I attempted to recreate the same nested situation with separate code and ended up with the following.
def main():
tst = ScopeTest()
tst.run()
print(*tst.tstArray)
print(tst.tstNumber)
class ScopeTest():
tstArray = []
tstNumber = 0
def run(self):
self.otherFunc()
def otherFunc(self):
self.tstArray = [1,2,3,4,5]
self.tstNumber = 7
if __name__ == "__main__":
main()
The above did return the expected outcome which leads me to think this has something to do with the recursion.
Just a heads up, I'm fairly new to python so I may be making a rookie mistake, but I can't seem to figure out why this is happening.
Your recurse() method is generating a currentPath parameter and when this is deemed to be correct you execute: self.minPath = currentPath. Unfortunately, you just reference the same object as currentPath which is later mutated.
Your line should be: self.minPath = currentPath[:]
You then see some contents in self.minPath
Mandatory link to Ned Batchelder
Also you can remove these lines:
minPath = []
minPathSum = None
grid = []
from just below class Solution():

access a class instance/variable from one running python file in another?

I have a problem that involves collecting data continuously from multiple sources.
My setup as it is currently, writes each data entry from each source to a MySQL db, and then, with another python program, does Select's that bring all the data together. I need to make INSERT's at roughly 1000/second and as it is my SELECT's can take 15-20 seconds each.
The whole process takes so long the data is obsolete before I get to do anything useful with it.
I have created a toy example to try and demonstrate what I am looking for.
program 1 'generateClasses':
import time
import random
from datetime import datetime
class Race:
def __init__(self,name):
hist = {}
now = datetime.now()
self.name = name
self.now = now
hist[now] = 0
self.v = 0
self.hist = hist # example variable's.
def update(self,name,v):
now = datetime.now()
hist = self.hist
hist[now] = v
self.v = v
self.now - now
self.hist = hist
class Looper:
def __init__(self,name):
self.a = Race(name)
def loop(self,name):
# simulating the streaming API
while True:
v = self.a.v
v += 1
self.a.update(name,v)
print(a,datetime.now(),v) # can i access this stream using the location displayed with the print(a)?
time.sleep(0.1) # this should be more like time.sleep(0.001)
def pickData(self,name):
v = self.v
self.loop(name)
print('The state at {} {} = '.format(self.now,self.v))
return self.hist
if __name__ == "__main__":
x = 'Some_ID'
a = Looper(x)
a.loop(x)
program 2:
from generateClasses import Looper
from datetime import datetime
import time
start_time = int((datetime.now() - datetime(1970, 1, 1)).total_seconds())
print(start_time)
x = 'Some_orher_ID'
a = Looper(x)
print('this will print')
a.loop(x)
print('this wont ever print')
a.pickdata(x)
# this last section is the functionality i am looking for in this program, but, as it is, it will never run.
x = ‘Some_ID’
while True:
now_time = int((datetime.now() - datetime(1970, 1, 1)).total_seconds())
print(start_time)
if int(now_time-start_time) == 10:
a.pickData(x)
# b.pickData(x)
# c.pickData(x)
# d.pickData(x)
# make further actions.....
What happens currently in my examples is that it creates its own loop using the class structure from the first program.
What I want it to do is call the the pickData() method from program 2 at timely intervals of my choosing on a loop running in another program.
Is my best option picking a db located in memory and getting a faster computer?
Maybe something can be done with the object location shown when you print the instance name?
I have uploaded to github if anybody fancies it..
I would be grateful of any suggestions.
also, recommendations for further reading would be appreciated also.

Timer for python functions

I'm trying to do an automatic mouse clicker. My main issue now is how to do a timer for each function, for example, function 1 works for about 15 minutes, then function 2 after the 15 minutes starts to work just one time, then comeback to function 1. And i want to function4 Works independente from the others, i want it to click everytime even if the function 1 is running ( im not sure if this is possible) here is my code:
import pyautogui, sys
pyautogui.size()
(1280, 800)
def function1():
pyautogui.click(button='left', x=619, y=266)
pyautogui.PAUSE = 3.9
pyautogui.click(button='left', x=617, y=475)
def function2():
pyautogui.click(button='left', x=624, y=347)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=615, y=431)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=315, y=483)
pyautogui.PAUSE = 5.0
pyautogui.click(button='left', x=616, y=390)
def function3 ():
pyautogui.click(button='left', x=617, y=522)
pyautogui.PAUSE = 5.0
def function4():
pyautogui.click(button='left', x=1257, y=432)
Thank you all :)
Because it is not easy to manage multiple functions with wait independently without introducing a signal and multi-thread, here is another way to manage multiple click() functions with the PyAutoGUI library.
The solution is a multi-sequencer class (called class TimerExec).
Step 1 - Use a class TimerSeq to store sequencer parameters
Only the constructor is needed
class TimerSeq:
def __init__(self, iseq, tseq, bexe, bloop):
self.iseq = iseq
self.tseq = tseq
self.bexe = bexe
self.bloop = bloop
Step 2 - Create a class TimerExec to manage a list of sequencers
The constructor create an empty list
class TimerExec:
def __init__(self):
self.list = [ ]
self.stop = True
self.chrono = -1
self.delay = -1
A simple floating-point seconds value to int milliseconds
#
# convert float seconds to milliseconds
def tomilli(self, fsec):
return int(round(fsec * 1000))
Add a new sequencer in the list
#
# append new sequences to the list
def append(self, func, loop=False):
self.list.append([func, TimerSeq(-1, -1, False, loop)])
print('list:',self.list)
Verify if the sequencer is complete or restart when loop
#
# check end of sequence or restart
def nextcheck(self, seq):
if seq[1].iseq >= len(seq[0]):
if seq[1].bloop:
seq[1].iseq = 0 # restart
return True
return False
Compute parameters for the next sequence in the current sequencer
#
# switch to the next sequence
def nextstep(self, seq):
if seq[1].iseq >= len(seq[0]):
return True
seq[1].iseq = seq[1].iseq+1
seq[1].tseq = self.tomilli(time.time())
seq[1].bexe = False
return False
Manage the current sequencer and execute the current function then
delay the next sequence
#
# explore sequence and execute when
def exestep(self, seq):
bseq = False
if seq[1].tseq < 0:
bseq = self.nextstep(seq)
else:
bseq = self.nextcheck(seq)
if bseq:
return True
pseq = seq[0][seq[1].iseq]
tnow = self.tomilli(time.time())
tdel = self.tomilli(pseq[0])
if seq[1].bexe == False:
print('execute(%d):'% (tnow-self.chrono),pseq)
# execute the selected function
pseq[1](pseq[2],pseq[3],pseq[4])
seq[1].bexe = True
tseq = seq[1].tseq
if tnow > (tseq+tdel):
bseq = self.nextstep(seq)
return bseq
Main loop function to explore all sequencers until complete or
max_delay
#
# loop to execute all sequences with max_delay (s)
def execute(self, max_delay):
print('start:',time.strftime("%H:%M:%S", time.localtime()))
self.stop = False
self.delay = self.tomilli(max_delay)
self.chrono = self.tomilli(time.time())
while self.stop == False:
tnow = self.tomilli(time.time())
#if tnow > (self.chrono + self.delay):
# break
bstop = True
for seq in self.list:
bseq = self.exestep(seq)
bstop = bstop & bseq
if bstop == True:
self.stop = True
print('stop:',time.strftime("%H:%M:%S", time.localtime()),
((tnow-self.chrono)/1000.0))
Step 3 - declare your sequencer based to your declared function
For function1(), use a 2 steps sequencer:
def function1():
pyautogui.click(button='left', x=619, y=266)
pyautogui.PAUSE = 3.9
pyautogui.click(button='left', x=617, y=475)
The sequencer is:
fct1 = [
# pyautogui.click(button='left', x=619, y=266)
[ 3.9, pyautogui.click, 'left', 619, 266 ],
# pyautogui.click(button='left', x=617, y=475)
[ 0.0, pyautogui.click, 'left', 617, 475 ]
]
Step 4 - Create TimerExec object, add sequencer then execute.
The duration is limited to 13.6 seconds maximum
tSeq = TimerExec()
tSeq.append(fct1)
tSeq.execute(13.6)

Find 'spare' time in list of python datetime objects

I have a list of events. Each event as a Start and End which are both datetime objects. I now want a list of "spare time" events which is created off the existing events, for the purposes of display. I hope that makes sense. This is the best I can come up with so far. Any help appreciated!
class Event():
Start = datetime.datetime
End = datetime.datetime
Reserved = boolean
# create a couple of events with some spare time between them...
ev1 = Event()
ev1.Start = datetime.datetime(2011,1,1,5)
ev1.End = datetime.datetime(2011,1,1,6)
ev1.Reserved = True
ev2 = Event()
ev2.Start = datetime.datetime(2011,1,1,8)
ev2.End = datetime.datetime(2011,1,1,9)
ev2.Reserved = True
events = [ev1,ev2]
# sort the events list...
events.sort(key=lambda e: e.Start)
# create a 'spare time' event based on the existing events...
allevents = []
for event in events:
spare = Event()
spare.Start = events[events.index(event)-1].End+datetime.timedelta(seconds=1)
spare.End = event.Start-datetime.timedelta(seconds=1)
spare.Reserved = False
allevents.append(spare)
allevents.append(event)
allevents.sort(key=lambda e: e.Start)
for event in allevents:
print str(event.Start), str(event.End), event.Reserved
So, this works, but I have yet to figure out how to stop and start the spare time at midnight, but before I do that, I want to know if I'm heading in the right direction to start with. Or is there a much better way?? (There usually is!)..
MFB
I would do it more this way - just to give you some more concepts (it needs more error handling, but for now it should just show the idea):
from datetime import datetime
class Venue(object):
"""Collection of events"""
def __init__(self):
self.events_list = []
def add_events(self,*args):
for e in args:
self.events_list.append(e)
def sort_list(self):
self.events_list.sort(key=lambda e: e.start_date)
def find_spare_times(self):
self.sort_list()
for event in self.events_list:
yield event.start_date
yield event.end_date
def add_spare_events(self):
l = list(self.find_spare_times())
print l
new_start,new_end = None, None
for e in l[1:]:
if new_start:
new_end = e
self.add_events(Event(new_start,new_end,res=False))
new_start = None
else:
new_start = e
new_end = None
def get_events(self):
return self.events_list
class Event(object):
"""Holds the data for an individual event"""
def __init__(self,start_date, end_date,res=True):
self.start_date = start_date
self.end_date = end_date
self.reserved = res
# create a couple of events with some spare time between them...
def main():
ev1 = Event(datetime(2011,1,1,5),datetime(2011,1,1,6))
ev2 = Event(datetime(2011,1,1,8),datetime(2011,1,1,9))
v = Venue()
v.add_events(ev1,ev2)
print v.get_events()
v.add_spare_events()
print v.get_events()
if __name__ == "__main__":
main()

Categories