We have a server that gets cranky if it gets too many users logging in at the same time (meaning less than 7 seconds apart). Once the users are logged in, there is no problem (one or two logging in at the same time is also not a problem, but when 10-20 try the entire server goes into a death spiral sigh).
I'm attempting to write a page that will hold onto users (displaying an animated countdown etc.) and let them through 7 seconds apart. The algorithm is simple
fetch the timestamp (t) when the last login happened
if t+7 is in the past start the login and store now() as the new timestamp
if t+7 is in the future, store it as the new timestamp, wait until t+7, then start the login.
A straight forward python/redis implementation would be:
import time, redis
SLOT_LENGTH = 7 # seconds
now = time.time()
r = redis.StrictRedis()
# lines below contain race condition..
last_start = float(r.get('FLOWCONTROL') or '0.0') # 0.0 == time-before-time
my_start = last_start + SLOT_LENGTH
r.set('FLOWCONTROL', max(my_start, now))
wait_period = max(0, my_start - now)
time.sleep(wait_period)
# .. login
The race condition here is obvious, many processes can be at the my_start = line simultaneously. How can I solve this using redis?
I've tried the redis-py pipeline functionality, but of course that doesn't get an actual value until in the r.get() call...
I'll document the answer in case anyone else finds this...
r = redis.StrictRedis()
with r.pipeline() as p:
while 1:
try:
p.watch('FLOWCONTROL') # --> immediate mode
last_slot = float(p.get('FLOWCONTROL') or '0.0')
p.multi() # --> back to buffered mode
my_slot = last_slot + SLOT_LENGTH
p.set('FLOWCONTROL', max(my_slot, now))
p.execute() # raises WatchError if anyone changed TCTR-FLOWCONTROL
break # break out of while loop
except WatchError:
pass # someone else got there before us, retry.
a little more complex than the original three lines...
Related
This kind of follows up on a question I asked prior, but I have a function, we'll call it get_temp(), that needs to be looped in order to gather the data it needs to call for another function, we'll call it notification(), to run if the if statement is satisfied. This all works great, but once it does satisfy the if statement, I need it to only send auxFunction once.
This section of the code works by getting temperature from a thermocouple, and then I'm using an if statement to call for a specific function when the temp == a specific value. The temperature data is looked at every 2 seconds in the loop, which is necessary to see the changes in the data to satisfy the if statement eventually, so I can't just run it outside of the loop.
This is the part of the code that grabs the thermocouple value:
def grab_temp(self):
temp = self.board.temp_sensor
print("Temperature is " + str(round(temp)) + " degrees")
temp=int(temp)
if temp == 200:
self.notification()
This is the concept of the code, but not the exact code. It's written a little differently, but performs as it's written above and the notification function does more than just print. just for the sake of example, I'm just having it print something
And then:
def notification(self):
print("Testing")
Again, this all works, but since the grab_temp function is inside of the loop of the rest of the code, it loops every two seconds, which in turn, prints every two seconds when the value is 200, where I need it to just perform the notification function once, when temp == 200. This is problematic because the code that will be in the notification function will be notifying me of the temperature, so I can't have it notifying me every two seconds while the temperature is 200 degrees.
What would be the best way to go about achieving this, in this instance? I've tried a few other things recommended on similar questions, but none of them worked. All that I tried returned some kind of error, which prevented the rest of the code from functioning properly.
I didn't write the entirety of this of this whole project, I'm just modifying it to do some things I want it to do, and I don't have a ton of experience in coding, but I'm learning more and more as I work through this project, and have enjoyed making these modifications, and am still learning some basics on the side.
Possible solution without explicitly using global.
Add a boolean parameter to grab_temp, we'll call it notify. Set an initial value for notify=True. At the end of grab_temp, return the appropriate boolean to turn on/off the notification. Assign grab_temp to notify within your loop.
This version will disable notifications for consecutive calls, but re-enable notifications when the condition is no longer met. You can modify what's returned from grab_temp if you only want a notification for the very first time the condition is met.
temp_values = [1,200,200,1,200,200,200,1,200]
def notification():
print('notification')
def grab_temp(i, notify=True):
temp = temp_values[i]
print("Temp is", temp)
check_temp = temp == 200
if notify and check_temp:
notification()
return not check_temp
notify = True
for i in range(len(temp_values)):
notify = grab_temp(i, notify)
Example result
Temp is 1
Temp is 200
notification
Temp is 200
Temp is 1
Temp is 200
notification
Temp is 200
Temp is 200
Temp is 1
Temp is 200
notification
Modified grab_temp to only notify for the very first time the condition is met.
def grab_temp(i, notify=True):
temp = temp_values[i]
print("Temp is", temp)
check_temp = temp == 200
if notify and check_temp:
notification()
return False
else:
return notify
Temp is 1
Temp is 200
notification
Temp is 200
Temp is 1
Temp is 200
Temp is 200
Temp is 200
Temp is 1
Temp is 200
I sadly do not have the ability to comment currently due to my lack of reputation on this website. However, I think a solution you could consider is adding a global boolean variable called 'uncalled' that defaults to true and adjust it once you've called the self.notification() portion of your code.
Beginning of Script:
uncalled = True
Printing Warning
global uncalled
if temp == 200 and uncalled:
self.notification()
uncalled = False
Using the keyword 'global' here should change it throughout your entire code and not just within the function. If this doesn't answer your question, would you mind clarifying for me in my comments? Thanks!
Extra Example:
uncalled = True
def function(x):
global uncalled
if x and uncalled:
print('hit')
uncalled = False
for i in range(5):
x = True
function(x)
Returns
hit
Each driver (vehicle) needs a 30 min break after 8 hours on-duty. Referencing a previous issue, here is my code:
# Add breaks
node_visit_transit = {}
for n in range(routing.Size()):
if n >= len(data['node_name']):
node_visit_transit[n] = 0
else:
if n in data['pickups']:
node_visit_transit[n] = int(data['load_time'][n])
else:
node_visit_transit[n] = int(data['unload_time'][n])
break_intervals = {}
for v in range(data['num_vehicles']):
break_intervals[v] = [
routing.solver().FixedDurationIntervalVar(
MAX_TIME_BETWEEN_BREAKS, MAX_TIME_BETWEEN_BREAKS, BREAK_DURATION, False, 'Break for vehicle {}'.format(v))
]
time_dimension.SetBreakIntervalsOfVehicle(
break_intervals[v], v, node_visit_transit)
However, no solution is found when this snippet is added, whereas a solution existed previously. A few questions:
What is node_visit_transit used for? From the Github code it seems to account for some form of loading time (based on load demand), so I adapted it for load/unload time here.
Am I using FixedDurationIntervalVar correctly? I would like drivers to have a BREAK_DURATION break after they've been on duty for MAX_TIME_BETWEEN_BREAKS hours, regardless of start time. I'm concerned this is forcing them to take a break at the absolute time MAX_TIME_BETWEEN_BREAKS, i.e. if MAX_TIME_BETWEEN_BREAKS = 8 then they all take a break at 08:00.
It seems like someone has figured this out here, but there is no solution example.
I try to make an python application which is responsible for reading and writting with plc through OPC.
The Problem which I am facing right now, I have to read some data from plc and according to that I have to write some data.
This is continuous process. So, I am using multithreading concept to handle this.
For an example:-
def callallsov(self):
while True:
self.allsov1sobjects.setup(self.sovelementlist)
def callallmotor(self):
while True:
self.allmotor1dobjects.setup(self.motorelementlist)
def callallanalog(self):
while True:
self.allanalogobjects.setup(self.analogelementlist)
self.writsovthread = threading.Thread(target=callallsov)
self.writemotorthread = threading.Thread(target=callallmotor)
self.writemotorthread = threading.Thread(target=callallanalog)
self.writsovthread.start()
self.writemotorthread.start()
self.writeanalogthread.start()
calling structure:
def setup(self,elementlist):
try:
n =0
self.listofmotor1D.clear()
while n< len(self.df.index):
self.df.iloc[n, 0] = Fn_Motor1D(self.com, self.df, elementlist, n)
self.listofmotor1D.append(self.df.iloc[n,0])
n = n + 1
read and write plc tag:
if tagname == self.cmd:
if tagvalue == self.gen.readgeneral.readnodevalue(self.cmd):
# sleep(self.delaytime)
self.gen.writegeneral.writenodevalue(self.runingFB, 1)
else:
self.gen.writegeneral.writenodevalue(self.runingFB, 0)
break
now each elementlist have 100 device. So I make type of each element then create runtime objects for each element.
like in case of motor, if motor command is present we need to high it's runfb.
I have to follow the same process for 100 motors device.
Thus why I use while true here to check continuously data from plc.
Due to 100 motors (large amout of data) it takes long time to write in plc.plc scan cycle is very fast.
so my fist question is I haven't use join here ....is it correct?
secondly how I can avoid this sluggishness.
I'm making an alarm that adjusts according to traffic. After messing around a bit, I decided it would be best for the program to get data from the API every 15 minutes or so. However, I noticed that if the while loop condition was met while time.sleep() was active, it wouldn't break till the full duration of time.sleep() ended. Is there any way to have it break when the condition is met, even during time.sleep()? Thanks in advance!
while datetime.datetime.now() < self.wakeuptime: #refers to alarm time
resp = requests.get(url=url, params=param)
data = json.loads(resp.content)
simplifiedtime = datetime.datetime.strptime(data["routes"][0]["legs"][0]["departure_time"]["text"], "%I:%M%p").time()
#gets suggested departure time from api
combinedsimplifiedtime=datetime.datetime.combine(today, simplifiedtime)
self.wakeuptime = combinedsimplifiedtime - datetime.timedelta(minutes=15)
#resets wakeuptime value depending on api
self.timetogo = combinedsimplifiedtime
print self.wakeuptime
time.sleep(900)
#waits 15 minutes and checks again until wakeuptime
You can use another while loop to check the current time much more frequently than the one that updates from the API. Instead of time.sleep(900) you can do:
start_pause = datetime.datetime.now()
while (datetime.datetime.now()-start_pause).seconds < 900 \
and datetime.datetime.now() < self.wakeuptime:
time.sleep(1)
This will still only run the outer loop after 900 seconds.
I need to read a temperature reading from a DS18B20 sensor using Raspberry Pi 3 and Python.
The problem is the refresh rate of the sensor (~1 sec)
I need to read from sys/bus/w1/devices/28-041670f43bff/w1_slave and use the integer i get to display a temperature on a 7 segment display connected directly to my GPIOs (not using any hardware multiplexing - i2c....etc)
In order to display a two digit temperature, I need to turn on and off the digits really fast (faster than the sensor refreshes)
This is the small piece of code used to get the integer temperature:
def temperature():
with open ("/sys/bus/w1/devices/28-041670f43bff/w1_slave") as q:
r=q.read()
temp=r[69:71]
t=int (temp)
return t
But i need to call this function many times per second in order to get a good display on the 7 segment display.
This is how i thought of doing it:
#the temperature() function returns a two digit int
while True:
GPIO.output(31,0)
GPIO.output(temp[temperature()/10], 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temp[temperature()%10], 1)
time.sleep(0.0005)
GPIO.output(37,1)
But this code just makes one digit light up, wait ~1sec, light up the other digit, wait ~1sec.....and so on.
Any ideas of how to do this are very appreciated.
Rather than implement this functionality on your own, you should instead use the libraries out there that address this particular bit of your code inherently. In this case, I'd suggest you use W1ThermSensor. You can find the documentation at:
https://github.com/timofurrer/w1thermsensor
and you can install it using:
pip install w1thermsensor
It does support the DS18B20, and offers an exact analogue to your use case in the README.
From the docs for the package:
from w1thermsensor import W1ThermSensor
sensor = W1ThermSensor()
temperature_in_celsius = sensor.get_temperature()
temperature_in_fahrenheit = sensor.get_temperature(W1ThermSensor.DEGREES_F)
temperature_in_all_units = sensor.get_temperatures([
W1ThermSensor.DEGREES_C,
W1ThermSensor.DEGREES_F,
W1ThermSensor.KELVIN
])
In many cases, particularly for popular hardware devices, you'll find that there are libraries already available to use within python, and that will all you to quickly move on to writing the bits of code unique to your own particular needs.
Note: According to the technical discussion in the following link, if the DS18B20 is set to 12-bit temperature resolution, the temperature conversion will take 750 ms, or 3/4 of a second. If you set the hardware to do 9-bit resolution, the conversion time in hardware is 93.75 ms. I suspect this is the root of your once-per-second issue.
https://www.maximintegrated.com/en/app-notes/index.mvp/id/4377
There is some discussion of this issue in this Question:
https://raspberrypi.stackexchange.com/questions/14278/how-to-change-ds18b20-reading-resolution
See the second Answer, regarding the configDS18B20 utility.
With the resolution set to 9-bit, you may be able to adjust the w1thermsensor RETRY_DELAY_SECONDS / RETRY_ATTEMPTS value combination in the source code and get what you need. It's unclear to me if the retry delay has any affect on the actual polling of the device. It looks like it is there for device finding. Though, as I said, that interval may impact polling a single device. I simply didn't read through the source code enough to see when and where it comes into play.
Happy New Year!
I'd throw the display routine into its own thread so that you don't have to think about it in your main loop. The code below should demonstrate this concept. Set "testing" to False to see if it works with your hardware.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def temperature_read(q):
# Read the temperature at one second intervals.
while True:
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put((int(r[69]), int(r[70])))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def temperature_display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
temperature = q.get()
# If it's None, we're done.
if temperature is None:
break
# Load the two digits (high and low) representing the temperature.
(temperature_h, temperature_l) = temperature
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
temperature_queue = Queue.Queue()
# Run the display in a separate thread.
temperature_display_thread = threading.Thread(target=temperature_display, args=(temperature_queue,))
temperature_display_thread.start()
# Run the reader.
try:
temperature_read(temperature_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
temperature_queue.put(None)
# Wait for the thread to end.
temperature_display_thread.join()
To support another reading (transmission), I just put it in the read loop rather than adding another thread for it. I changed the queue so that you could easily move it to another thread but I suspect you'll add more inputs so this is probably a reasonable way to do it unless the read frequency of one needs to be much different. (Even then, you could do things with counters in the loop.)
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
while True:
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
# (Will w1_slave block until the next reading? If so, this could be eliminated.)
time.sleep(1.0)
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()
Here's a version which only reads the temperature every tenth time but reads the transmission every time. I think you'll see how to easily tweak this to meet your needs.
I would make separate threads for each reader but it would complicate the thread management quite a bit.
#!/usr/bin/python
import time
import threading
import Queue
import random
# Set this to False to read the temperature from a real sensor and display it on a 7-digit display.
testing = True
def observe(q):
count = 0
while True:
# Only read the temperature every tenth time.
if (count % 10 == 0):
# Make a temperature reading.
if testing:
r = '-' * 69 + '%02d' % (random.randrange(100)) + 'blahblah' * 4
else:
r = open('/sys/bus/w1/devices/28-041670f43bff/w1_slave', 'r').read()
print 'temperature ->', r
# The temperature is represented as two digits in a long string.
# Push the digits into the queue as a tuple of integers (one per digit).
q.put(('temperature', int(r[69]), int(r[70])))
# Make a transmission reading.
if testing:
r = random.randrange(1,6)
else:
r = 0 # Put your transmission reading code here.
print 'transmission ->', r
q.put(('transmission', r))
# Wait for next reading.
if testing:
time.sleep(0.5)
else:
time.sleep(0.1)
count += 1
def display(q):
# Display the temperature.
# Temperature is two digits, stored separately (high/low) for more efficient handling.
temperature_h = temperature_l = transmission = 0
while True:
# Is there a new temperature reading waiting for us?
if not q.empty():
reading = q.get()
# If it's None, we're done.
if reading is None:
break
elif reading[0] == 'temperature':
# Load the two digits (high and low) representing the temperature.
(x, temperature_h, temperature_l) = reading
elif reading[0] == 'transmission':
(x, transmission) = reading
if testing:
print 'displayH', temperature_h
time.sleep(0.05)
print 'displayL', temperature_l
time.sleep(0.05)
print 'transmission', transmission
time.sleep(0.05)
else:
GPIO.output(31,0)
GPIO.output(temperature_h, 1) # temp is a dictionary used to know which segments to light up to show numbers
time.sleep(0.0005)
GPIO.output(31,1)
GPIO.output(37,0)
GPIO.output(temperature_l, 1)
time.sleep(0.0005)
GPIO.output(37,1)
# Clean up here. Turn off all pins?
# Make a queue to communicate with the display thread.
readings_queue = Queue.Queue()
# Run the display in a separate thread.
display_thread = threading.Thread(target=display, args=(readings_queue,))
display_thread.start()
# Observe the inputs.
try:
observe(readings_queue)
except:
# An uncaught exception happened. (It could be a keyboard interrupt.)
None
# Tell the display thread to stop.
readings_queue.put(None)
# Wait for the thread to end.
display_thread.join()