ROS How to pass a callback variable into another file? - python

I have a callback function in a file "color.py" that constantly recieves incoming data but updates a global variable every 3 seconds:
last_color = ""
last_calltime = datetime.strptime("00:00:00","%H:%M:%S")
def callback(data):
global last_calltime, last_color
cube = data.data
t = time.localtime()
tf = time.strftime("%H:%M:%S",t)
current_time = datetime.strptime(tf, "%H:%M:%S")
delta_time = current_time - last_calltime
if abs(delta_time.total_seconds()) >= 3:
if "Red" in cube:
last_color = "Red"
elif "Green" in cube:
last_color = "Green"
else:
last_color = "Blue"
t = time.localtime()
tf = time.strftime("%H:%M:%S",t)
last_calltime = datetime.strptime(tf, "%H:%M:%S")
return last_color
if __name__ == '__main__':
try:
rospy.init_node('color')
rospy.Subscriber ('/topic',String, callback)
rospy.spin()
In another file, I need to reference this "last_color" variable that updates. So far the function I have in this file:
from color import last_color
a = 0
def observe_color(a)
if a == 0:
return last_color
else:
return "None"
I need this to return the updated variable from the "color.py" file but it only returns the initialized value for last_color which is "". How can I do this?

When you import something in Python, you're effectively asking it to execute everything in that module in the same runtime as your current program.
Suppose I have some module color.py with variable last_color='green'
And in a different program observer.py I have from color import last_color
The last_color that exists in color.py and the last_color that exists in observer.py are different places in computer memory - they do not reference the same string value.
A quick (and maybe dirty) solution to this is just to re-import the module every time you want to observe one of its member variables.

I don't think it's gonna work that way. How about save the value of last_color to a file and monitor it ? Because to me you run 2 separated programs, 1 with the ROS and another just to monitor last_color
while True:
with open(filename, 'r') as f:
color = f.readline()
time.sleep(timesteps)
When write to file in the main, you could also append the read value and also the time, to get timeline data.

Since this is all done in ROS it would be easiest to simply have the separate file initialize as a ros node with a subscriber. If for some reason that's not the case you should make color.py a class and have the other file initialize an object. Take the following simplified example:
color.py
class colorPy:
def __init__(self):
self.last_color = None
rospy.init_node('some_node')
tmp = rospy.Subscriber('/some_topic', self.callback)
def callback(self, msg):
self.last_color = msg.data
other file
from color import colorPy
from time import sleep
local_var = colorPy()
sleep(5)
print(local_var.last_color)

Related

Display properly output messages using npyscreen

I have an script that collects initial information using npyscreen and then display multiple messages as it progresses. However, these messages are displayed in an unorganized way (see below). Is there a way to populate those messages in the correct way (without tabs)?
Something
Something
Something
This is the code
import npyscreen, time
class Test(npyscreen.NPSApp):
def main(self):
F = npyscreen.Form(name = "Test")
color = F.add(npyscreen.TitleText, name = "Color:")
F.edit()
if(color.value == "Something"):
self.call_function_to_display_messages(color.value)
def call_function_to_display_messages(self, color):
print(color)
print(color)
print(color)
time.sleep(10)
if __name__ == "__main__":
App = Test()
App.run()
The solution is to add the carriage return at the beginning the of print statement.
print("\r"+color)

How to use the value of a variable as an argument

I am trying to use the value of a variable, which is a string, as an argument and I keep getting "'Str' not callable' error. I haven't used str as a variable name I can make the code work with eval, however, I've read dire warnings about eval, so am unsure what to do. My code is below.
from time import sleep
from binance.client import Client
from binance.websockets import BinanceSocketManager
class s33():
def __init__(self):
self.client = Client("", "")
self.bm = BinanceSocketManager(self.client)
def process_trade_message(self, message):
global count, conn_key
print(count)
if count >= 10:
print('closing socket')
# use either stop_socket or close, or both
self.bm.stop_socket(conn_key)
self.bm.close()
# reset the count
count = 0
def go(self, sockb):
global count, conn_key
print(sockb['1'])
sock = 'self.bm.'+sockb['1']
print(sock)
count = 0
conn_key = sock(self.process_trade_message)
self.bm.start()
if __name__ == '__main__':
while True:
s = s33()
socka = {'1':'start_miniticker_socket'}
s.go(socka)
sleep(20)
print('sleeping')
I have read people recommend using a dict, So I passed the dict as the arg and tried to extract the string in the function,which is the code below. I tried to extract the string and pass that as an arg to the function. s.go(socka['1'], I tried passing the just the variable as an arg, socka = 'start_miniticker_socket' and I can get that to work if I use eval('self.bm'+socka) I tried the percentage sign with no luck. Not sure how to do this without using eval. I am still fairly new and can't find an alternative answer after several hours of searching that works. any help would be appreciated.
I think what people meant when suggesting a dict is something like this:
class s33():
# Codes...
def go(self, func_name):
global count, conn_key
print(func_name)
mapper = {
'start_miniticker_socket': self.bm.start_miniticker_socket
}
# Get self.bm.start_miniticker or None
sock = mapper.get(func_name)
print(sock)
if sock:
count = 0
conn_key = sock(self.process_trade_message)
self.bm.start()
else:
pass # Handle when sock is None
if __name__ == '__main__':
while True:
s = s33()
socka = 'start_miniticker_socket'
s.go(socka)
sleep(20)
print('sleeping')

How to communicate variable between two functions using add_event_detect?

To simplify a complicated project, I have one switch and 1 pushbutton. What I want is to have code do separate functions when the button is pressed for both positions of the switch.
So I have two files. The main file sets up some gpio and event detects. Another file has a callback function defined for the pushbutton. Here is the main file:
import Adafruit_GPIO as GPIO
gpio = GPIO.get_platform_gpio()
global staff_mode
staff_mode = 0
Bg1 = 24
global audio_latch
audio_latch = 13
global image_latch
image_latch = 26
def staff_mode_audio():
staff_mode_write(0)
global staff_mode
if not gpio.input(audio_latch) and gpio.input(image_latch):
staff_mode=0;
print('You are in audio setup')
print(staff_mode)
def staff_mode_image():
staff_mode_write(1)
global staff_mode
if not gpio.input(image_latch) and gpio.input(audio_latch):
staff_mode=1;
print('You are in image setup')
gpio.add_event_detect(Bg1, GPIO.FALLING, callback = lambda x:grid_input(1,page_select,current_mode,staff_mode), bouncetime=debounce_time)
gpio.add_event_detect(image_latch, GPIO.FALLING, callback = lambda x: staff_mode_image(), bouncetime=300)
gpio.add_event_detect(audio_latch, GPIO.FALLING, callback = lambda x: staff_mode_audio(), bouncetime=300)
try:
while True:
signal.pause()
except KeyboardInterrupt:
gpio.cleanup()
The key being that the the lines image_select_write('num') is defined in another file but basically just writes a 0 or 1 to a text file. The next file is:
def staff_mode_write(num):
file=open('image_select.txt', 'w')
file.write(str(num))
file.close()
def staff_mode_read():
file=open('image_select.txt', 'rt')
image_select=int(file.read())
file.close()
return image_select
def grid_input(grid_select,page_select,current_mode,staff_mode):
staff_mode = staff_mode_read()
if (staff_mode == 0):
#Do stuff
staff_mode_write(1)
else:
#Do other stuff
staff_mode_write(0)
So the callback function grid_input reads the text file to determine what function the pushbutton should perform. I tried unsucessfully to communicate the value of staff_mode using global variables.
This solution worked but it was clunky. How can I communicate the status of staff_mode without using a text file? Thank you!
You should indeed be able to use a global for this. However you have to declare the global wherever it is used. You can't just declare it at the top of your module. In other words you have to change grid_input to this to access the global.
def grid_input(grid_select,page_select,current_mode):
global staff_mode
if (staff_mode == 0):
#Do stuff
else:
#Do other stuff
However the goal of your code is not that clear to me so I think you could probably come up with a better approach.

Python function call within a property of a class

I am trying to use the "setx" function of a Property in a Class to do some processing of date information that I get from excel. I have a few of my own functions that do the data processing which I tested outside the class, and they worked just fine. But when I move them into the class they suddenly become invisible unless I use the self. instance first. When I use the self.My_xldate_as_tuple() method I get an error:
My_xldate_as_tuple() takes 1 positional argument but 2 were given
Even though the code is EXACTLY what i used outside the class before and it worked.
Before moving into the Property Set block, I was doing the processing of date data outside of the class and setting the variables from outside of the class. That gets clunky when I have about 15 different operations that are all based on when the NumDates Property change. I'm showing shortened versions of both the working set of code and the non-working set of code. What is going on with the self. call that changes how the function takes inputs?
Broken Code:
class XLDataClass(object):
_NumDates = []
TupDates = []
def getNumDates(self): return self._NumDates
def setNumDates(self, value):
self._NumDates = value
self.TupDates = list(map(self.My_xldate_as_tuple,value)) #Error here
#This version doesn't work either, since it can't find My_xldate_as_tuple anymore
self.TupDates = list(map(My_xldate_as_tuple,value))
def delNumDates(self):del self._NumDates
NumDates = property(getNumDates,setNumDates,delNumDates,"Ordinal Dates")
#exact copy of the My_xldate_as_tuple function that works outside the class
def My_xldate_as_tuple(Date):
return xlrd.xldate_as_tuple(Date,1)
#Other code and functions here
#end XlDataClass
def GetExcelData(filename,rowNum,titleCol):
csv = np.genfromtxt(filename, delimiter= ",")
NumDates = deque(csv[rowNum,:])
if titleCol == True:
NumDates.popleft()
return NumDates
#Setup
filedir = "C:/Users/blahblahblah"
filename = filedir + "/SamplePandL.csv"
xlData = XLDataClass()
#Put csv data into xlData object
xlData.NumDates= GetExcelData(filename,0,1)
Working Code:
class XLDataClass(object):
NumDates = []
TupDates = []
#Other code and functions here
#end XlDataClass
#exact copy of the same function outside of the class, which works here
def My_xldate_as_tuple(Date):
return xlrd.xldate_as_tuple(Date,1)
def GetExcelData(filename,rowNum,titleCol):
csv = np.genfromtxt(filename, delimiter= ",")
NumDates = deque(csv[rowNum,:])
if titleCol == True:
NumDates.popleft()
return NumDates
#Setup
filedir = "C:/Users/blahblahblah"
filename = filedir + "/SamplePandL.csv"
xlData = XLDataClass()
#Put csv data into xlData object
xlData.NumDates = GetExcelData(filename,0,1)
#same call to the function that was inside the Setx Property of the class, but it works here.
xlData.TupDates = list(map(self.My_xldate_as_tuple,value))
Instance methods in Python require an explicit self in the argument list. Inside the class, you need to write your method definition like:
def My_xldate_as_tuple(self, Date):

Accessible variables at the root of a python script

I've declared a number of variables at the start of my script, as I'm using them in a number of different methods ("Functions" in python?). When I try to access them, I can't seem to get their value = or set them to another value for that matter. For example:
baseFile = open('C:/Users/<redacted>/Documents/python dev/ATM/Data.ICSF', 'a+')
secFile = open('C:/Users/<redacted>/Documents/python dev/ATM/security.ICSF', 'a+')
def usrInput(raw_input):
if raw_input == "99999":
self.close(True)
else:
identity = raw_input
def splitValues(source, string):
if source == "ident":
usrTitle = string.split('>')[1]
usrFN = string.split('>')[2]
usrLN = string.split('>')[3]
x = string.split('>')[4]
usrBal = Decimal(x)
usrBalDisplay = str(locale.currency(usrBal))
elif source == "sec":
usrPIN = string.split('>')[1]
pinAttempts = string.split('>')[2]
def openAccount(identity):
#read all the file first. it's f***ing heavy but it'll do here.
plString = baseFile.read()
xList = plString.split('|')
parm = str(identity)
for i in xList:
substr = i[0:4]
if parm == substr:
print "success"
usrString = str(i)
else:
lNumFunds = lNumFunds + 1
splitValues("ident", usrString)
When I place baseFile and secFile in the openAccount method, I can access the respective files as normal. However, when I place them at the root of the script, as in the example above, I can no longer access the file - although I can still "see" the variable.
Is there a reason to this? For reference, I am using Python 2.7.
methods ("Functions" in python?)
"function" when they "stand free"; "methods" when they are members of a class. So, functions in your case.
What you describe does definitely work in python. Hence, my diagnosis is that you already read something from the file elsewhere before you call openAccount, so that the read pointer is not at the beginning of the file.

Categories

Resources