Python writing twice but randomly? - python

So I am currently working on a program that was handed down to me from a previous coworker and I am working through a strange bug. When reading data output from 2 separate serial sources byte by byte, python will write to the same cell in the .csv file as well as the console.
import serial
from datetime import datetime
import os
pressure_passed = False
arduino_passed = False
file_passed = False
BAUD_RATE = 115200
GARBAGE_CYCLES = 3 # how many cycles to ignore before logging data
garbage_cycle = 0
# Save data to log file
def LogData(startTime, pressureData, arduinoData, file):
global garbage_cycle
if garbage_cycle < GARBAGE_CYCLES:
garbage_cycle += 1
else:
delta = datetime.now() - startTime
ms = delta.total_seconds() * 1000
dataString = "{:0.2f}, {}, {}\n".format(ms, pressureData, arduinoData)
file.write(dataString)
file.flush()
print(dataString, end = "")
# Get the COM port for the Mark-10 Series 5
while not pressure_passed:
try:
pressure_com = input("Enter Mark-10 Series 5 COM Port #: ")
pressure_ser = serial.Serial("COM" + str(pressure_com), BAUD_RATE)
pressure_passed = True
except:
print("Invalid COM Port, please enter a valid port.\n-----")
# Get the COM port for the Arduino
while not arduino_passed:
try:
arduino_com = input("Enter Ardunio COM Port #: ")
arduino_ser = serial.Serial("COM" + str(arduino_com), BAUD_RATE)
arduino_passed = True
except:
print("Invalid COM Port, please enter a valid port.\n-----")
# Get the name for the log file
while not file_passed:
try:
file_name = input("Enter log file name: ")
# Add extension if not already given
if "." not in file_name:
file_name += ".csv"
log_file = open(file_name, "a")
# Add header row to log file
if os.stat(log_file.name).st_size == 0:
log_file.write("time (ms), pressure, rate (deg/ms)")
file_passed = True
except:
print("Invalid file, or could not open the file specified.\n-----")
start = datetime.now()
# Variables to read serial input
pressure_data = ""
last_pressure = ""
arduino_data = ""
last_arduino = ""
# Main program loop
# Serial is read from byte by byte to better sync the two devices
while True:
try:
x_changed = False
y_changed = False
# Read from Mark-10 serial if available
# x is a byte read from the serial line, converted to ascii
if pressure_ser.in_waiting > 0:
x = pressure_ser.read().decode('ascii')
x_changed = True
# Read from Arduino serial if available
# y is a byte read from the serial line, converted to ascii
if arduino_ser.in_waiting > 0:
y = arduino_ser.read().decode('ascii')
y_changed = True
# If new data received, check if we should log it
if x_changed:
if x == '\n': # New line detected, log the accumulated data
if last_pressure != pressure_data:
LogData(start, last_pressure, last_arduino, log_file)
last_pressure = pressure_data
pressure_data = ""
elif x != '\r': # Otherwise, add the read character to the string
pressure_data += x
if y_changed:
if y == '\n': # New line detected, log the accumulated data
if last_arduino != arduino_data:
LogData(start, last_pressure, last_arduino, log_file)
last_arduino = arduino_data
arduino_data = ""
elif y != '\r': # Otherwise, add the read character to the string
arduino_data += y
except Exception as e:
print(e)
if arduino_ser.isOpen():
arduino_ser.close()
if pressure_ser.isOpen():
pressure_ser.close()
log_file.close()
break
Here is what the file is spitting out, IE the double printing to a single cell. Sample of the data
Any advice is much appreciated, thank you all!

It looks like when a new pressure is read in, but the value has not changed from last time, then it's not resetting the string that's accumulating all the characters and it doubles up. Then on the next pass, when the REAL pressure hasn't changed, it compares the doubled to the non-doubled and writes again, and vice-versa.
Try unindenting the line that resets the string to remove it from the if clause:
# If new data received, check if we should log it
if x_changed:
if x == '\n': # New line detected, log the accumulated data
if last_pressure != pressure_data:
LogData(start, last_pressure, last_arduino, log_file)
last_pressure = pressure_data
pressure_data = ""
elif x != '\r': # Otherwise, add the read character to the string
pressure_data += x
Then the same thing for the arduino value block.
Your logs will probably be much shorter now.

I like your username! My guess is that it is reading from the serial too quickly and going through the loop twice before the arduino has time to change the value of in_waiting.
At the top of your code add:
import time
And in the LogData function add:
time.sleep(0.1)
Give that a shot and let me know if it helps. 0.1s may be too long for your application but it is a good test to see if this is the issue. If it is, you can play around with the amount of time it sleeps.

Based on the sample output provided, I think it's not writing twice but rather the following specific condition is met occasionally which calls two identical LogData() lines.
Only when Condition 1 AND Condition 2 are met - the data is written "twice". Note that the LogData() call is same in both conditions.
Condition 1:
# If new data received, check if we should log it
if x_changed:
if x == '\n': # New line detected, log the accumulated data
if last_pressure != pressure_data:
LogData(start, last_pressure, last_arduino, log_file)
Condition 2:
if y_changed:
if y == '\n': # New line detected, log the accumulated data
if last_arduino != arduino_data:
LogData(start, last_pressure, last_arduino, log_file)

Related

How to send a message to process zero, from all other processes?

How to send a message to process zero, from all other processes?
I'm using mpi4py, with Python 2, and was following this example Parallel programming Research
Why does this line fail, and what fixes it?
searchResult = comm.recv(source=tempRank)
My code below (appears to) works fine until it reaches the line above. I put print statements above and below this line, so I pretty sure this is the problem.
My expectation was ... processor zero will receive a message from each processor, but it does not. The program seems to just hang and do nothing. Here is the program.
import time
from random import randint
from random import shuffle
from mpi4py import MPI
import sys
comm = MPI.COMM_WORLD
rank = comm.Get_rank()
size = MPI.COMM_WORLD.Get_size()
name = MPI.Get_processor_name()
if rank == 0:
starttime=time.time()
searchResult=False
someNumber = 0
data = list(range(1,8))
chunks = [ [] for _ in range(size) ]
for i,chunk in enumerate(data):
chunks[i % size].append(chunk)
else:
data=None
chunks=None
someNumber=None
# scatter data to all processors
data=comm.scatter(chunks,root=0)
# give another variable to each processor
someNumber = comm.bcast(someNumber,root=0)
print('im rank=',rank,', my data=', data, ' searching for someNumber = ',someNumber)
searchResult=False
for num in data:
if someNumber == num:
print('found the someNumber')
searchResult=True
break
if searchResult == False:
print('someNumber not found')
searchResult=False
# Now, at this point, I want all processors (including processor 0)
# to send processor 0 a message
tempRank=rank
# attempting to send process 0 a message from all other processes
# (does/can processor 0 send itself a message?)
if rank == 0:
print('this line prints one time, and program hangs')
searchResult = comm.recv(source=tempRank)
print('this line never prints, so whats wrong with previous line?')
else:
comm.send(searchResult,dest=0)
if rank == 0:
if searchResult == True:
print('found the someNumber, everyone stop searching .. how to make all processes stop?')
print('elapsedtime = {}'.format(time.time()-starttime))
else:
print('no one found the someNumber')
print('elapsedtime = {}'.format(time.time()-starttime))

How to read contents of a LibreOffice writer annotation from a python macro

LibreOffice writer allows the user to insert annotations(notes/comments) within the text.
My issue is I am unable to find a method to access the contents of a line specific annotation.
The following python code looks for selected/highlighted text and then strips out everything except a formatted time code (e.g. 01:10:23 or 11:10) which it converts into seconds.
If no text has been selected, it selects the entire current line and attempts to find the time code. However, the time code could be in an annotation.
I have managed to get a list of all of the annotations within the document, commented out at the start of the code but it is of no use to me.
I have been unable to discover a method of divining
a) whether the current line has an annotation or
b) how to access its contents.
If anyone has managed to achieve this, I'd appreciate any pointers.
def fs2_GoToTimestamp(*args):
#get the doc from the scripting context which is made available to all scripts
desktop = XSCRIPTCONTEXT.getDesktop()
model = desktop.getCurrentComponent()
oSelected = model.getCurrentSelection()
#access annotations for the whole document
# oEnum = model.getTextFields().createEnumeration()
# cursor = desktop.getCurrentComponent().getCurrentController().getViewCursor()
# while oEnum.hasMoreElements():
# oField = oEnum.nextElement()
# cursor.gotoRange(oField,False)
# print (cursor.getPosition())
# if oField.supportsService('com.sun.star.text.TextField.Annotation'):
# print (oField.Content)
# x = oField.getAnchor()
# print (dir(x))
oText = ""
try: #Grab the text selected/highlighted
oSel = oSelected.getByIndex(0)
oText= oSel.getString()
except:pass
try:
if oText == "": # Nothing selected grab the whole line
cursor = desktop.getCurrentComponent().getCurrentController().getViewCursor()
cursor.gotoStartOfLine(False) #move cursor to start without selecting (False)
cursor.gotoEndOfLine(True) #now move cursor to end of line selecting all (True)
oSelected = model.getCurrentSelection()
oSel = oSelected.getByIndex(0)
oText= oSel.getString()
# Deselect line to avoid inadvertently deleting it on next keystroke
cursor.gotoStartOfLine(False)
except:pass
time = str(oText)
valid_chars=('0123456789:')
time = ''.join(char for char in time if char in valid_chars)
if time.count(":") == 1:
oM, oS = time.split(":")
oH = "00"
elif time.count(":") == 2:
oH,oM,oS = time.split(":")
else:
return None
if len(oS) != 2:
oS=oS[:2]
try:
secs = int(oS)
secs = secs + int(oM) * 60
secs = secs + int(oH) *3600
except:
return None
seek_instruction = 'seek'+str(secs)+'\n'
#Now do something with the seek instruction
Enumerate the annotations and use getAnchor() to find out where each is located. This answer is based on https://wiki.openoffice.org/wiki/Documentation/DevGuide/Text/Editing_Text#Text_Contents_Other_Than_Strings.
Your code is close to working.
while oEnum.hasMoreElements():
oField = oEnum.nextElement()
if oField.supportsService('com.sun.star.text.TextField.Annotation'):
xTextRange = oField.getAnchor()
cursor.gotoRange(xTextRange, False)
Instead of print (dir(x)), an introspection tool such as XrayTool or MRI will give better information. It makes the API docs easier to figure out.
With much needed help from Jim K a self answer is posted below. I have commented where I believe it will help most.
#!/usr/bin/python
from com.sun.star.awt.MessageBoxButtons import BUTTONS_OK
from com.sun.star.awt.MessageBoxType import INFOBOX
def fs2_GoToTimestamp(*args):
desktop = XSCRIPTCONTEXT.getDesktop()
model = desktop.getCurrentComponent()
oSelected = model.getCurrentSelection()
doc = XSCRIPTCONTEXT.getDocument()
parentwindow = doc.CurrentController.Frame.ContainerWindow
cursor = desktop.getCurrentComponent().getCurrentController().getViewCursor()
try:
CursorPos = cursor.getText().createTextCursorByRange(cursor)#Store original cursor position
except:# The cursor has been placed in the annotation not the text
mess = "Position cursor in the text\nNot the comment box"
heading = "Positioning Error"
MessageBox(parentwindow, mess, heading, INFOBOX, BUTTONS_OK)
return None
oText = ""
try: #Grab the text selected/highlighted
oSel = oSelected.getByIndex(0)
oText= oSel.getString()
except:pass
try:
if oText == "": # Nothing selected grab the whole line
store_position = 0
cursor.gotoStartOfLine(False) #move cursor to start without selecting (False)
cursor.gotoEndOfLine(True) #now move cursor to end of line selecting all (True)
oSelected = model.getCurrentSelection()
oSel = oSelected.getByIndex(0)
oText= oSel.getString()
y = cursor.getPosition()
store_position = y.value.Y
# Deselect line to avoid inadvertently deleting it on next user keystroke
cursor.gotoStartOfLine(False)
if oText.count(":") == 0:
# Still nothing found check for an annotation at this location
#enumerate through annotations for the whole document
oEnum = model.getTextFields().createEnumeration()
while oEnum.hasMoreElements():
oField = oEnum.nextElement()
if oField.supportsService('com.sun.star.text.TextField.Annotation'):
anno_at = oField.getAnchor()
cursor.gotoRange(anno_at,False)
pos = cursor.getPosition()
if pos.value.Y == store_position: # Found an annotation at this location
oText = oField.Content
break
# Re-set cursor to original position after enumeration & deselect
cursor.gotoRange(CursorPos,False)
except:pass
time = str(oText)
valid_chars=('0123456789:')
time = ''.join(char for char in time if char in valid_chars) #Strip out all invalid characters
if time.count(":") == 1: # time 00:00
oM, oS = time.split(":")
oH = "00"
elif time.count(":") == 2: # time 00:00:00
oH,oM,oS = time.split(":")
else:
return None
if len(oS) != 2: # in case time includes tenths 00:00.0 reduce to whole seconds
oS=oS[:2]
try:
secs = int(oS)
secs = secs + int(oM) * 60
secs = secs + int(oH) *3600
except:
return None
seek_instruction = 'seek'+str(secs)+'\n'
print("Seconds",str(secs))
# Do something with seek_instruction
def MessageBox(ParentWindow, MsgText, MsgTitle, MsgType, MsgButtons):
ctx = XSCRIPTCONTEXT.getComponentContext()
sm = ctx.ServiceManager
si = sm.createInstanceWithContext("com.sun.star.awt.Toolkit", ctx)
mBox = si.createMessageBox(ParentWindow, MsgType, MsgButtons, MsgTitle, MsgText)
mBox.execute()

unpack requires a string argument of length 24

I am not sure what I am doing wrong here but I am trying to open a file, trace1.flow, read the header information then throw the source IP and destination IP into dictionaries. This is done in Python running on a Fedora VM. I am getting the following error:
(secs, nsecs, booted, exporter, mySourceIP, myDestinationIP) = struct.unpack('IIIIII',myBuf)
struct.error: unpack requires a string argument of length 24
Here is my code:
import struct
import socket
#Dictionaries
uniqSource = {}
uniqDestination = {}
def int2quad(i):
z = struct.pack('!I', i)
return socket.inet_ntoa(z)
myFile = open('trace1.flow')
myBuf = myFile.read(8)
(magic, endian, version, headerLen) = struct.unpack('HBBI', myBuf)
print "Magic: ", hex(magic), "Endian: ", endian, "Version: ", version, "Header Length: ", headerLen
myFile.read(headerLen - 8)
try:
while(True):
myBuf = myFile.read(24)
(secs, nsecs, booted, exporter, mySourceIP, myDestinationIP) = struct.unpack('IIIIII',myBuf)
mySourceIP = int2quad(mySourceIP)
myDestinationIP = int2quad(myDestinationIP)
if mySourceIP not in uniqSource:
uniqSource[mySourceIP] = 1
else:
uniqSource[mySourceIP] += 1
if myDestinationIP not in uniqDestination:
uniqDestination[myDestinationIP] = 1
else:
uniqDestination[myDestinationIP] += 1
myFile.read(40)
except EOFError:
print "END OF FILE"
You seem to assume that file.read will raise EOFError on end of file, but this error is only raised by input() and raw_input(). file.read will simply return a string that's shorter than requested (possibly empty).
So you need to check the length after reading:
myBuf = myFile.read(24)
if len(myBuf) < 24:
break
Perhaps your have reached end-of-file. Check the length of myBuf:
len(myBuf)
It's probably less than 24 chars long. Also you don't need those extra parenthesis, and try to specify duplicated types using 'nI' like this:
secs, nsecs, booted, exporter, mySourceIP, myDestinationIP = struct.unpack('6I',myBuf)

Python 3.3: LAN speed test which calculates the read & write speeds and then returns the figure to Excel

I'm creating a LAN speed test which creates a data file in a specified location of a specified size and records the speed at which it is created/read. For the most part this is working correctly, there is just one problem: the read speed is ridiculously fast because all it's doing is timing how long it takes for the file to open, rather than how long it takes for the file to actually be readable (if that makes sense?).
So far I have this:
import time
import pythoncom
from win32com.client import Dispatch
import os
# create file - write speed
myPath = input('Where do you want to write the file?')
size_MB = int(input('What sized file do you want to test with? (MB)'))
size_B = size_MB * 1024 * 1024
fName = '\pydatafile'
#start timer
start = time.clock()
f = open(myPath + fName,'w')
f.write("\x00" * size_B)
f.close()
# how much time it took
elapsed = (time.clock() -start)
print ("It took", elapsed, "seconds to write the", size_MB, "MB file")
time.sleep(1)
writeMBps = size_MB / elapsed
print("That's", writeMBps, "MBps.")
time.sleep(1)
writeMbps = writeMBps * 8
print("Or", writeMbps, "Mbps.")
time.sleep(2)
# open file - read speed
startRead = time.clock()
f = open(myPath + fName,'r')
# how much time it took
elapsedRead = (time.clock() - startRead)
print("It took", elapsedRead,"seconds to read the", size_MB,"MB file")
time.sleep(1)
readMBps = size_MB / elapsedRead
print("That's", readMBps,"MBps.")
time.sleep(1)
readMbps = readMBps * 8
print("Or", readMbps,"Mbps.")
time.sleep(2)
f.close()
# delete the data file
os.remove(myPath + fName)
# record results on Excel
xl = Dispatch('Excel.Application')
xl.visible= 0
wb = xl.Workbooks.Add(r'C:\File\Location')
ws = wb.Worksheets(1)
# Write speed result
#
# loop until empty cell is found in column
col = 1
row = 1
empty = False
while not empty:
val = ws.Cells(row,col).value
print("Looking for next available cell to write to...")
if val == None:
print("Writing result to cell")
ws.Cells(row,col).value = writeMbps
empty = True
row += 1
# Read speed result
#
# loop until empty cell is found in column
col = 2
row = 1
empty = False
while not empty:
val = ws.Cells(row,col).value
print("Looking for next available cell to write to...")
if val == None:
print("Writing result to cell")
ws.Cells(row,col).value = readMbps
empty = True
row += 1
xl.Run('Save')
xl.Quit()
pythoncom.CoUninitialize()
How can I make this so the read speed is correct?
Thanks a lot
Try to actually read the file:
f = open(myPath + fName, 'r')
f.read()
Or (if the file is too large to fit in memory):
f = open(myPath + fName, 'r')
while f.read(1024 * 1024):
pass
But operating system could still make read fast by caching file content. You've just written it there! And even if you manage to disable caching, your measurement (in addition to network speed) could include the time to write data to file server disk.
If you want network speed only, you need to use 2 separate machines on LAN. E.g. run echo server on one machine (e.g. by enabling Simple TCP/IP services or writing and running your own). Then run Python echo client on another machine that sends some data to echo server, makes sure it receives the same data back and measures the turnaround time.

Python Dictionary Throwing KeyError for Some Reason

In some code I pass a dictionary (TankDict) a string from a list. This throws a KeyError, no matter what letter I put in. When I copied and pasted the dictionary out of the context of the program and passed in the same letters from a list, they came out correctly. I have also run type(TankDict) and it comes back as 'dict'.
Here is the dictionary:
TankDict = {'E':0, 'F':1, 'G':2, 'H':3, 'I':4, 'J':5,
'K':6, 'L':7, 'M':8, 'N':9,
'O':10, 'P':11, 'Q':12, 'R':13, 'S':14, 'T':15,
'U':16, 'V':17, 'W':18, 'X':19}
The error:
enter code herechannelData[1] = tank_address_dict[channelData[1]]
KeyError: 'L'
(tank_address_dict is a function argument into which TankDict is passed)
the contents of channelData: ['447', 'L', '15', 'C']
Can anyone tell me the (probably simple) reason that this happens?
EDIT: Code!
This is the function where the error is:
def getTankID(channel,tank_address_dict,PTM_dict,channel_ref):
rawChannelData = 'NA'
for line in channel_ref:
if str(channel) in line: rawChannelData = line
if(rawChannelData == 'NA'): return -1;
channelData = rawChannelData.split(' ')
channelData.extend(['',''])
channelData[1] = channelData[1][:-1]
channelData[3] = channelData[1][-1]
channelData[1] = channelData[1][:-1]
channelData[2] = channelData[1][1:]
channelData[1] = channelData[1][:1]
print channelData #debug
print 'L' in tank_address_dict
print 'E' in tank_address_dict
print 'O' in tank_address_dict
print 'U' in tank_address_dict
print type(tank_address_dict)
channelData[1] = tank_address_dict[channelData[1]]
channelData[3] = PTM_dict[channelData[3]]
return(channelData[1:])
This is the function that calls it:
def runFile(model, datafile, time_scale, max_PEs, tank_address_dict, PMT_dict, channel_ref):
#add initSerial for ser0-4
while(True):
raw_data = datafile.readline() #intake data
if(raw_data == ''): break #End while loop if the file is done
data = raw_data.split(' ') #break up the parts of each line
del data[::2] #delete the human formatting
data[2] = data[2][:-1] #rm newline (NOTE: file must contain blank line at end!)
TankID = getTankID(data[0], tank_address_dict, PMT_dict,channel_ref)
if(TankID == -1):
print '!---Invalid channel number passed by datafile---!'; break #check for valid TankID
model[TankID[0]][TankID[1]][TankID[2]] = scale(data[2],(0,max_PEs),(0,4096))
createPackets(model)
#updateModel(ser0,ser1,ser2,ser3,ser4,packet)
data[2] = data[2]*time_scale #scale time
time.sleep(data[2]) #wait until the next event
print data #debug
if(TankID != -1): print '---File',datafile,'finished---' #report errors in file run
else: print '!---File',datafile,'finished with error---!'
And this is the code that calls that:
import hawc_func
import debug_options
#begin defs
model = hawc_func.createDataStruct() #create the data structure
TankDict = hawc_func.createTankDict() #tank grid coordinate conversion table
PTMDict = hawc_func.createPMTDict() #PMT conversion table
log1 = open('Logs/log1.txt','w') #open a logfile
data = open('Data/event.txt','r') #open data
channel_ref = open('aux_files/channel_map.dat','r')
time_scale = 1 #0-1 number to scale nano seconds? to seconds
#end defs
hawc_func.runFile(model,data,4000,TankDict,PTMDict,time_scale,channel_ref)
#hawc_func.runFile(model,data,TankDict,PTMDict)
#close files
log1.close()
data.close()
#end close files
print '-----Done-----' #confirm tasks finished
tank_address_dict is created through this function, run by the 3rd block of code, then passed on through the other two:
def createTankDict():
TankDict = {'E':0, 'F':1, 'G':2, 'H':3, 'I':4, 'J':5,
'K':6, 'L': 7, 'M':8, 'N':9,
'O':10, 'P':11, 'Q':12, 'R':13, 'S':14, 'T':15,
'U':16, 'V': 17, 'W':18, 'X':19}
return TankDict
You are not passing your arguments correctly.
def runFile(model, datafile, time_scale, max_PEs, tank_address_dict, PMT_dict, channel_ref):
hawc_func.runFile(model,data,4000,TankDict,PTMDict,time_scale,channel_ref)
Here, you have max_PEs = TankDict.
That may not be your only problem. Fix that first, and if you are still having problems, update your post with your fixed code and then tell us what your new error is.

Categories

Resources