果報は寝て待て: 6月 2022

2022年6月9日木曜日

pythonでラインの稼働状況を棒グラフに表示

 目的は、ちまたでよく見る、工場の自動ラインの稼働状況をモニタで表示することです。当然お金をかければなんぼでもあります。

今のところこんな風に表示されます。

 

 

 

やりたいことです。

① 1時間当たりの出来高を棒グラフで表示します。

② とりあえず48時間前まで表示して1時間ごとに更新します。

③ 棒の下には、月/日/時を表示します。

④真ん中の青い線は、出来高の目標線で、プログラムの立ち上げ時に入力します。

 

次に構成です。

① 自動ラインの最終工程から、完品が1台出るたびに2秒間の信号を出力します。

  三菱シーケンサーから、24vで取り出してます。

② 24vをフォトカプラで受けてaruduino unoで取り込みます。写真ではフォトカプラが6個並んでますが、今回は1個しか使ってません。



aruduino のプログラムです。


// data logger
//degital2~degital7 の6本のi/oを0.5秒毎にpythonへ送信する
//

int signal_1 = 2; // degital入力2 を signal_1に取り込む
int signal_2 = 3; // 
int signal_3 = 4; // 
int signal_4 = 5; // 
int signal_5 = 6; // 
int signal_6 = 7; // 



int led1  = 13;

int keep_sig1_on = 0;  // signal_1がonの状態を読み、ダブルカウント防止
int keep_sig2_on = 0;  //
int keep_sig3_on = 0;  //
int keep_sig4_on = 0;  //
int keep_sig5_on = 0;  //
int keep_sig6_on = 0;  //



void setup(){
    pinMode( signal_1, INPUT_PULLUP );
    pinMode( signal_2, INPUT_PULLUP );
    pinMode( signal_3, INPUT_PULLUP );
    pinMode( signal_4, INPUT_PULLUP );
    pinMode( signal_5, INPUT_PULLUP );
    pinMode( signal_6, INPUT_PULLUP );
    pinMode( 13, OUTPUT );
        
    Serial.begin( 9600 );

}

void loop(){
  //  signal_1がlowになったら keep_sig1_onを 1にする
    if(keep_sig1_on == 0){                    
      if(digitalRead(signal_1) == LOW ){
        keep_sig1_on = 1;
      }
    }
    else{
       if(digitalRead(signal_1) == HIGH ){  
         keep_sig1_on = 0;
        }
    }
  //  signal_2がlowになったら keep_sig2_onを 1にする
    if(keep_sig2_on == 0){                    
      if(digitalRead(signal_2) == LOW ){
        keep_sig2_on = 1;
      }
    }
    else{
       if(digitalRead(signal_2) == HIGH ){  
         keep_sig2_on = 0;
        }
    }
  //  signal_3がlowになったら keep_sig3_onを 1にする
    if(keep_sig3_on == 0){                    
      if(digitalRead(signal_3) == LOW ){
        keep_sig3_on = 1;
      }
    }
    else{
       if(digitalRead(signal_3) == HIGH ){  
         keep_sig3_on = 0;
        }
    }
  //  signal_4がlowになったら keep_sig4_onを 1にする
    if(keep_sig4_on == 0){                    
      if(digitalRead(signal_4) == LOW ){
        keep_sig4_on = 1;
      }
    }
    else{
       if(digitalRead(signal_4) == HIGH ){  
         keep_sig4_on = 0;
        }
    }
  //  signal_5がlowになったら keep_sig5_onを 1にする
    if(keep_sig5_on == 0){                    
      if(digitalRead(signal_5) == LOW ){
        keep_sig5_on = 1;
      }
    }
    else{
       if(digitalRead(signal_5) == HIGH ){  
         keep_sig5_on = 0;
        }
    }
  //  signal_6がlowになったら keep_sig6_onを 1にする
    if(keep_sig6_on == 0){                    
      if(digitalRead(signal_6) == LOW ){
        keep_sig6_on = 1;
      }
    }
    else{
       if(digitalRead(signal_6) == HIGH ){  
         keep_sig6_on = 0;
        }
    }
    

    
//  シリアルポートへ出力 bou_ok_count,ng_count,nc_count
    Serial.flush();
    
    Serial.print( keep_sig1_on );
    Serial.print( keep_sig2_on );
    Serial.print( keep_sig3_on );
    Serial.print( keep_sig4_on );
    Serial.print( keep_sig5_on );
    Serial.println( keep_sig6_on );
    

    

// 各トリガーが掛かれば、LED1を点燈する

    if(digitalRead(signal_1) == LOW){
      digitalWrite(led1,HIGH);
    }
    else if( digitalRead(signal_2) == LOW){
      digitalWrite(led1,HIGH);
    }
    else if( digitalRead(signal_3) == LOW ){
      digitalWrite(led1,HIGH);  
    }  
    else if ( digitalRead(signal_4) == LOW ){
      digitalWrite(led1,HIGH);
    }
    else if ( digitalRead(signal_5) == LOW){
      digitalWrite(led1,HIGH);
    }
    else{
      digitalWrite(led1,LOW);
    }
    
    delay(500 );
}

 

aruduinoは、ただ0.5秒ごとに 000000 をusbでpythonに送ってて、完品の信号が入力されたら 000001 を送信して、2秒たったらまた 000000 に戻すだけの仕事です。

③ LINUXMINT32BIT版に入ってるpython3 で、aruduinoからの信号を受け取り、1時間ごとに集計してリストに加え、tkinterで棒グラフ表示します。

 

# -*- coding: utf-8 -*-
"""
Created on Fri Apr 22 21:52:16 2022
7.1 for windows
@author: osada
"""

import tkinter as tk
import serial as sl
import time
import re
import datetime as dt

signal_1 = 0
signal_2 = 0
signal_3 = 0
signal_4 = 0
signal_5 = 0
signal_6 = 0

old_signal_1 = 1
old_signal_2 = 1
old_signal_3 = 1
old_signal_4 = 1
old_signal_5 = 1
old_signal_6 = 1

deki__count1 = 0
deki__count2 = 0
deki__count3 = 0
deki__count4 = 0
deki__count5 = 0
deki__count6 = 0
ave_774 = int(input("774Lの時間当たりの平均数を入力してください"))
data = []
#data= [10,15,20,15,10,14,12,10,5,7,15,16,9,14,12,11]
data_hour = []
#data_hour = ["04/27 21","04/27 20","04/27 19","04/27 18","04/27 17","04/27 16","04/27 15","04/27 14","04/27 13","04/27 12","04/27 11","04/27 10","04/27 09","04/27 08","04/27 07","04/27 06"]
now_time_hour = dt . datetime . now ( ).strftime("%m/%d %H")
data_hour.insert(0,now_time_hour) # data_hourリストの末尾に now_time_hour を追加する
triger_flug = 0
old_line = None
clear_count_triger = None
#ウインドを作成
root=tk.Tk()
root.geometry('1000x700')
root.title('canvasの使い方')

finFlag = True
serial = None

serial = sl.Serial('/dev/ttyACM0', 9600, timeout=0)
#serial = sl.Serial('COM5', 9600, timeout=0)  #

while( serial.is_open is False):
    time.sleep(100)

#図形を壁画するキャンバスをウインド上に作成
canvas = tk.Canvas(root, width = 1000, height = 650)
canvas.grid(column=1, row=3, columnspan=1,rowspan=8)
now_time_hour = dt . datetime . now ( ).strftime("%m/%d %H")
#stop_time=3600 #目標終了時間

#無限ループ
while True:
    #  シリアルポートの操作
    line = None
    if ( serial.is_open):
        line = serial.readline()
        new_line = line.strip().decode('utf-8')
        #print(new_line)
        
    if len(new_line) == 13 and old_line != new_line :
        
        now_time = dt . datetime . now ( ).strftime("%Y/%m/%d %H:%M:%S")
        #print(now_time)

        print("new_line ",new_line)
        #print("old_line ",old_line)
        signal_1 = re.search(r'A(.+)B',new_line).group(1)
        #print("signal_1 ",signal_1)
        signal_2 = re.search(r'B(.+)C',new_line).group(1)
        #print("signal_2 ",signal_2)
        signal_3 = re.search(r'C(.+)D',new_line).group(1)
        #print("signal_3 ",signal_3)
        signal_4 = re.search(r'D(.+)E',new_line).group(1)
        #print("signal_4 ",signal_4)
        signal_5 = re.search(r'E(.+)F',new_line).group(1)
        #print("signal_5 ",signal_5)
        signal_6 = re.search(r'F(.+)G',new_line).group(1)
        #print("signal_6 ",signal_6)
        print(now_time,"      ",signal_1,signal_2,signal_3,signal_4,signal_5,signal_6)
        old_line = new_line
        
    if signal_1 != old_signal_1 : # シグナル1に変化があった
        if signal_1 == "1" :
            deki__count1 = deki__count1 + 1 # 出来高カウントに1をプラス
            print("deki__count1  ",deki__count1)
        old_signal_1 = signal_1
        print(clear_count_triger,data)
    
    clear_count_triger = dt . datetime . now ( ).strftime("%M%S") #"%M%S" 毎時0分0秒がトリガー
    #print(clear_count_triger,data) # 0000
        
    if clear_count_triger == "0000" and triger_flug == 0 :
        now_time_hour = dt . datetime . now ( ).strftime("%m/%d %H")
        data.insert(0,deki__count1) # dataリストの末尾に deki__count1 を追加する
        data_hour.insert(0,now_time_hour) # data_hourリストの末尾に now_time_hour を追加する
        
        if len(data) > 48 : # dataリストが48要素を超えたら先頭を削除する
            data.pop()
        if len(data_hour) > 49 : # dataリストが48要素を超えたら先頭を削除する
            data_hour.pop()
        deki__count1 = 0 # deki__count1をクリアー
        triger_flug = 1
        now_time = dt . datetime . now ( ).strftime("%Y/%m/%d %H:%M:%S")
        print(now_time,data)        
        print(now_time,data_hour)
    if clear_count_triger != "0000" and triger_flug == 1:
        triger_flug = 0    
   
    canvas.delete("rect") # タグ"rect"を消去する
    canvas.create_text(500, 40, text= "774Lの過去48hの時間当たり生産数  " ,tag="rect",fill="black",font=('Helvetica 40 bold'))
    canvas.create_line(960-20*len(data), 600-(ave_774)*20, 1000 ,600-(ave_774)*20,tag="rect",fill="Blue",width = 2)
    for i in range(len(data)):
        canvas.create_rectangle(980-20*i,600-20*data[i],995-20*i,600,tag="rect",fill = 'green')
        canvas.create_text(988-20*i, 590-20*data[i], text= str(data[i]),tag="rect",fill="black",font=('Helvetica 8 bold'))
        canvas.create_text(988-20*i, 630, text= str(data_hour[i+1]),tag="rect",fill="black",font=('Helvetica 8 bold'), angle=90)
    root.update()
    

root.mainloop()

6ライン分の稼働状況を1台で管理できるよう6桁の文字列を送れるようにしてあります。

でもこれ以上拡張することはなさそうです。