IoTで使うPython入門 Step4-Python3ソケット⑤DMM 34461A連続描画

 連載の第3回でDMMの測定データの連続読み出し、第4回でグラフの描画を扱いました。ここでは、二つを合体させます。

  •  連続読み出しは、止まることなく続ける。最大どのくらいまで変数にデータを保存できるかわからないが、読んだデータを変数dataに追加していく
  •  dataから値を読み出してグラフを書いていく。こちらもどんどんグラフを描いていく

 つまり、二つのプログラムを独立して同時に走らせるようにしたいので、threadingライブラリを導入します。

プログラム

 データの読み出しは、

  p2 = threading.Thread(target = dataAcquition) 

 グラフは、

  p1 = threading.Thread(target = plotter)

の二つのスレッドを作ります。データの読み出しスレッドを先に起動し、少しデータが溜まったらグラフのスレッドを起動します。

 plotter()とdataAcquition()の両方の関数で、global dataとして同じ変数を読み書きをしています。同時にアクセスが行われることが考えられますが、セマフォなどを使った調停はとっていません。plotter()は読み出しだけで、dataAcquition()はデータの追加だけなので、たまたまうまく動いているのかもしれません。

import socket
import time
from matplotlib.lines import Line2D
import matplotlib.pyplot as plt
import matplotlib.animation as animation
import threading

data   = []

class Scope:
    def __init__(self, ax, maxt=10, dt=0.2):
        self.ax = ax
        self.dt = dt
        self.maxt = maxt
        self.tdata = [0]
        self.ydata = [2.49]
        self.line = Line2D(self.tdata, self.ydata)
        self.ax.add_line(self.line)
        self.ax.set_ylim(0, 3)
        self.ax.set_xlim(0, self.maxt)

    def update(self, i):
        global data
        lastt = self.tdata[-1]
        if lastt > self.tdata[0] + self.maxt:  # reset the arrays
            #print('reset time(xscale)')
            self.tdata = [self.tdata[-1]]
            self.ydata = [self.ydata[-1]]
            self.ax.set_xlim(self.tdata[0], self.tdata[0] + self.maxt)
            self.ax.figure.canvas.draw()

        t = self.tdata[-1] + self.dt
        self.tdata.append(t)
        
        try:
            #print('read data length ', len(data))
            self.ydata.append(data[i])
        except:
            data = data
        #print('tdata ', self.tdata[i] , 'ydata ', self.ydata[i])
        #print(data)
        self.line.set_data(self.tdata, self.ydata)
        return self.line,

def plotter():
    global data
    print(len(data), 'read\n-----')
    fig, ax = plt.subplots()
    scope = Scope(ax)
    ani = animation.FuncAnimation(fig, scope.update, interval=2, blit=True)
    plt.show()

    #w = animation.PillowWriter(fps=20)
    #ani.save('animation_test.gif', writer=w)

def dataAcquition():
    ipAddr = "K-34461A-16054.local"
    ports  = 5025
    global data
    message = '*RST;*CLS\n'\
    'CONF:VOLT:DC 10, MAX;\n'\
    'SENS:VOLT:DC:APER 20E-6;\n'\
    'VOLT:DC:NPLC MIN;\n'\
    'VOLT:DC:ZERO:AUTO OFF;\n'\
    'VOLT:DC:NULL:STAT OFF;\n'\
    '\n'\
    'TRIG:SOURce IMMediate;\n'\
    'TRIG:COUNt INFinity;\n'\
    'TRIG:DEL 0;\n'\
    'FORMat ASCii;\n'

    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
        s.connect((ipAddr, ports))
        s.send(message.encode())
        s.send(b'*OPC?\n')

        while s.recv(2):  # 1=ready
            s.send(b'INIT\n')
            print('init')
            break
        while 1:
            try:
                s.send(b'DATA:POIN?\n')
                cc = (str(s.recv(6))[3:-3])
                print('new data is ', cc)
                m = 'R? ' + cc + '\n'
                s.send(m.encode())
                s1 = str(s.recv(2))[3:4]    # digit
                s2 = int(s.recv(int(s1)))   # read data count
                time.sleep(0.1)             # need wait
                s3 = str(s.recv(s2 + 1))[2:-3]  # b'  \n' deleted
                s4 = s3.split(',')
                f = [float(f) for f in s4]
                #print(f, '\ntotal ', len(f), 'read\n-----')
                data = data + f
                print(len(data), 'total\n-----')
                time.sleep(0.5)
            except KeyboardInterrupt:
                print('CTRL-C')
                break

p1 = threading.Thread(target = plotter)

p2 = threading.Thread(target = dataAcquition)

p2.start()
time.sleep(1)
p1.start()
print('\n-----start----- ')

 グラフを保存する

 w = animation.PillowWriter(fps=20)
ani.save('animation_test.gif', writer=w)

は、2画面記録しますが、以降の更新された画面をGIFファイルに書き込んでくれません。plt.show()は、ずっとデータを更新してグラフを描画します。しかし、データの読み取りのほうが早く、グラフの描画は1桁ぐらい遅いです。
 Pythonが変数の消費するメモリがどこくらいまで許されるのかわかりませんが、いつかは、破たんしてプログラムは止まってしまうでしょう。

 プログラムの中断は、CTRL-Sで画面の更新を止め、10秒以上経過後にCTRL-Cを押します。

 実行例です。DMMの入力には、50Hzぐらいの発振器の出力をつないでいます。