pyPLC/scope.py

208 lines
6.5 KiB
Python

# Scope
# Shows data in the style of an oscilloscope.
#
# Todos:
# - use correct time axis
# - allow the user to change the scaling
# - use functions intead of copy&paste code
# - support more channels
# - (and more)
#
from tkinter import *
import time
import sys # for argv
def addChannelNameToChannel(s):
global channelNames
blAlreadyPresent=0
for i in range(len(channelNames)):
if (channelNames[i]==s):
blAlreadyPresent=1
if (blAlreadyPresent==1):
#print("The name " + s + " is already present in the channel names. Nothing to do.")
return
# The new name is not known yet. Search an empty channel and store it there.
for i in range(len(channelNames)):
if (channelNames[i]==""):
channelNames[i]=s
print("Stored " + s + " as channel name for channel " + str(i))
return
# if we come here, there was no free channel, and we discard the new name.
print("Discarding name " + s)
def addChannelData(name, time, value):
# adds a data point to the channel with the given name
global channelNames
global channelData
for i in range(len(channelNames)):
if (channelNames[i]==name):
channelData[i].append([time, float(value)])
return
root = Tk()
root.geometry("750x500")
lastKey = ''
lblHelp = Label(root, justify= "left")
lblHelp['text']="press ctrl C to exit"
lblHelp.pack()
canvas_width = 750
canvas_height = 400
divisionsPerScreen = 10
c = Canvas(root,
width=canvas_width,
height=canvas_height)
c.pack()
x0Scope = 10
y0Scope = 45
xSizeScope = canvas_width-30
ySizeScope = canvas_height-50
# osci background
c.create_rectangle(x0Scope, y0Scope, x0Scope+xSizeScope, y0Scope+ySizeScope, fill="black")
# osci divisions
for i in range(1, divisionsPerScreen):
dx= i*xSizeScope/divisionsPerScreen
c.create_line(x0Scope+dx, y0Scope, x0Scope+dx, y0Scope+ySizeScope, fill="#404040")
dy= i*ySizeScope/divisionsPerScreen
c.create_line(x0Scope, y0Scope+dy, x0Scope+xSizeScope, y0Scope+dy, fill="#404040")
# osci outer border
c.create_line(x0Scope, y0Scope, x0Scope+xSizeScope, y0Scope, fill="#FFFFFF")
c.create_line(x0Scope, y0Scope, x0Scope, y0Scope+ySizeScope, fill="#FFFFFF")
c.create_line(x0Scope+xSizeScope, y0Scope+ySizeScope, x0Scope+xSizeScope, y0Scope, fill="#FFFFFF")
c.create_line(x0Scope+xSizeScope, y0Scope+ySizeScope, x0Scope, y0Scope+ySizeScope, fill="#FFFFFF")
#inputFileName = "local/pcaps_to_convert/ccm_spi_ioniq_compleo_full_charge_sequence_ended_on_charger.txt.pcap.values.txt"
inputFileName = "local/pcaps_to_convert/johu_2023-12-10_log_ionity.claralog.values.txt"
fileIn = open(inputFileName, 'r')
Lines = fileIn.readlines()
# yellow green blue red orange
#channelColors = ["#FFFF00", "#10FF10", "#4040FF", "#FF4040", "#FFC000" ]
channelColors = ["#FFFF00", "#10FF10", "#4040FF" ]
numberOfChannels = len(channelColors)
print(str(numberOfChannels) + " channels")
channelNames = []
channelData = []
for i in range(numberOfChannels):
channelNames.append("") # empty string for each channel
channelData.append([]) # empty list for each channel
channelOffsets = [] # will be appended dynamically below
channelPerDivs = [] # will be appended dynamically below
count = 0
for line in Lines:
count += 1
p1 = line.find("[")
p2 = line.find("] ")
p3 = line.find("=")
if (p1>=0) and (p2>p1) and (p3>p2+3):
strName = line[p2+2:p3]
addChannelNameToChannel(strName) # assign the found variable name to a channel, if not yet assigned
strVal=line[p3+1:].strip()
strTime=line[p1+1:p2].strip()
#print("Time >"+strTime+"<")
addChannelData(strName, strTime, strVal)
#print("Line{}: {}".format(count, line.strip()))
fileIn.close()
print("The channel names")
for i in range(numberOfChannels):
print(str(i) + " " + channelNames[i])
#print("The channel data")
#for i in range(numberOfChannels):
# print(str(i) + " " + channelNames[i])
# for k in range(len(channelData[i])):
# t = channelData[i][k][0]
# y = channelData[i][k][1]
# print("t= " + t + " y= " + str(y) )
# Hack: look which channel has the most samples.
# Todo: This is not correct, because we have to use the time stamp instead of the sample number.
maxSamples = 1
for i in range(numberOfChannels):
if maxSamples<len(channelData[i]):
maxSamples=len(channelData[i])
print("maxSamples " + str(maxSamples))
dxPerSample = xSizeScope / maxSamples
# Auto-scaling of the y axis
for i in range(numberOfChannels):
vMin=channelData[i][0][1]
vMax=vMin
for v in channelData[i]:
#print(v)
if (v[1]<vMin):
vMin = v[1]
if (v[1]>vMax):
vMax = v[1]
print("For channel " + str(i) + " we have min " + str(vMin) + " and max " + str(vMax))
vDelta = vMax - vMin
perDiv = 1
if (vDelta/perDiv>10):
perDiv=2
if (vDelta/perDiv>10):
perDiv=5
if (vDelta/perDiv>10):
perDiv=10
if (vDelta/perDiv>10):
perDiv=20
if (vDelta/perDiv>10):
perDiv=50
if (vDelta/perDiv>10):
perDiv=100
print("Scale: " + str(perDiv) + "/div")
offs = 0
while ((offs+perDiv)<vMin):
offs+=perDiv
print("Offset: " + str(offs))
channelOffsets.append(offs)
channelPerDivs.append(perDiv)
for i in range(numberOfChannels):
x = x0Scope
for v in channelData[i]:
yPix = y0Scope+ySizeScope - (v[1] - channelOffsets[i])/channelPerDivs[i] * ySizeScope / divisionsPerScreen
x1, y1 = ( x - 1 ), ( yPix - 1 )
x2, y2 = ( x + 1 ), ( yPix + 1 )
c.create_line(x1, y1, x2, y2, fill=channelColors[i])
c.create_line(x1, y2, x2, y1, fill=channelColors[i])
x+=dxPerSample
y0Legend = 0
ySizeLegend = 40
# legend background
c.create_rectangle(x0Scope, y0Legend, x0Scope+xSizeScope, y0Legend+ySizeLegend, fill="black")
for i in range(numberOfChannels):
c.create_text(x0Scope+150*i,y0Legend+0, text=channelNames[i], anchor="nw", fill=channelColors[i])
c.create_text(x0Scope+150*i,y0Legend+10, text=str(channelPerDivs[i]) + "/div", anchor="nw", fill=channelColors[i])
c.create_text(x0Scope+150*i,y0Legend+20, text="Offs " + str(channelOffsets[i]), anchor="nw", fill=channelColors[i])
lastKey = ''
root.update()
nMainloops=0
while lastKey!="x":
time.sleep(.03) # 'do some calculation'
nMainloops+=1
# print(str(nMainloops) + " " + str(nKeystrokes)) # show something in the console window
root.update()