センサデータ閲覧用サイトの作成
昨年度、産業用ロボットのセンシングを行いデータを収集するプログラムを作成した。
このプログラムによって得られたデータをWEB上で閲覧可能とするプログラムを今回新たに作成した。
作成には、Pythonを用いたCGI、HTML、CSSなどを使用している。
作成したプログラムの紹介
- 機能の紹介
- センサデータを表示するCGI
機能の紹介
次の図は、実際に作成したサイトの初期画面である。
使用手順としては、まず初めにロボット番号入力欄にロボット識別する番号を入力する。
すると、次の図のように保存されているセンサデータと、制約条件入力欄が表示される。
利用可能な制約は、製造番号の指定、部品番号の指定、期間の指定、表示項目の指定である。
また、絞り込みの条件については、複数の指定も可能となっている。
センサデータを表示するCGI
センサデータを表示する際には、CGIを用いてサーバ上に保存されているデータを読み込み表示している。
以下に実際に作成したPythonプログラムを処理ごとに示す。
なお、プログラムの全容は項の最下部に示す。
フォームを取得する処理
次のプログラムは、フォーム情報を取得するものである。
今回作成したセンサデータ閲覧用サイトでは、データの制約条件付き検索機能が利用可能である。
この機能の実現のため、ユーザがWEBサイト上で入力した制約情報を受け取る処理が必要となる。
プログラム中にHTMLを作成する処理があるが、これは制約条件を保持するため、再度フォームを送信させることを目的とした処理である。
今回は、Pythonを用いてCGIを作成しており、モジュールは「CGI」を使用している。
#フォームのまとめ
formNames=["robotNumber","selectSenser","serialNumber","partNumber","termOption"]
hideInput=""
for formName in formNames:
dataList=forms.getlist(formName)
for data in dataList:
hideInput+=f"<input type=checkbox name={formName} value={data} checked>{data}</input>"
#期間フォームの取得
def getNum(ele):
result = re.sub(r"\D", "", ele)
result=int(result)
return result
termForm=["start","end"]
termData=[]
formChecker=True
for formName in termForm:
dataList=forms.getlist(formName)
if not dataList:
formChecker=False
break
else:
for data in dataList:
termData.append(data)
if formChecker:
if getNum(termData[0]) <= getNum(termData[1]):
text=termData[0]+","+termData[1]
tmpTermOption=text
hideInput+=f"<input type=checkbox name=termOption value={text} checked>{text}</input>"
else:
errorMes+="<li>開始時刻が終了時刻を超過していました"
制約条件に一致するデータの読み込み
次のプログラムは、上記プログラムにて得られた制約条件に一致するデータを読み込む処理(一部)である。
データはサーバ上に保存されており、閲覧用のデータは最大500件までの最新のデータを保持している。
#センサデータ読み込み条件
disabledRobotNum=""
readCheck=False
if forms.getlist("robotNumber"):
robot="robot"+forms.getlist("robotNumber")[0]
sensorDataFilePath=GeneralSensorDataFilePath+robot+"_view.csv"
if os.path.isfile(sensorDataFilePath):
readCheck=True
#disabled robot number
disabledRobotNum="disabled"
#センサデータの読み込み
checktd=False
start=""
end=""
sensorData=[]
if readCheck:
#create data
with open(sensorDataFilePath,"r") as f:
reader=csv.reader(f)
sensorData= [row for row in reader]
#データ数の取得
dataLen=len(sensorData[0])
#項目の抜出
sensorDataItems=sensorData[0]
sensorData=sensorData[1:]
#開始時刻と終了時刻の取得
def createDatetime(data):
data = datetime.datetime.strptime(data, '%Y%m%d%H%M%S%f')
data=data.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]
return data
ii=1
start=createDatetime(sensorData[0][ii])
end=createDatetime(sensorData[-1][ii])
#条件チェック
def checkWord(dataList,word,index):
result=[]
for data in dataList:
if data[index]==word:
result.append(data)
return result
formNames=["serialNumber","partNumber"]
#製造番号用i2
ii=2
dataList=forms.getlist("serialNumber")
for data in dataList:
sensorData=checkWord(sensorData,data,ii)
#部品番号用i3
ii=3
dataList=forms.getlist("partNumber")
for data in dataList:
sensorData=checkWord(sensorData,data,ii)
#期間指定用i1
ii=1
dataList=forms.getlist("termOption")
if tmpTermOption:
dataList.append(tmpTermOption)
resultSensorData=[]
hozIndex=[]
for data in dataList:
term=data.split(",")
for i,ele in enumerate(sensorData):
if getNum(term[0]) <= int(ele[ii]) <= getNum(term[1]):
if not i in hozIndex:
resultSensorData.append(ele)
hozIndex.append(i)
if dataList:
sensorData=resultSensorData
プログラムの全容(一部伏せ字としてある)
#!/usr/bin/python3
# -*- coding: UTF-8 -*-
import csv
import os
import re
import cgi
import cgitb
import datetime
cgitb.enable()
forms = cgi.FieldStorage()
errorMes=""
tmpTermOption=""
# definition
GeneralSensorDataFilePath="---"
itemSelector=""
#ヘッダーの設定
def htmlHeaders():
print("Content-Type: text/html") # HTML is following
print() # blank line, end of headers
htmlHeaders()
#フォームのまとめ
formNames=["robotNumber","selectSenser","serialNumber","partNumber","termOption"]
hideInput=""
for formName in formNames:
dataList=forms.getlist(formName)
for data in dataList:
hideInput+=f"<input type=checkbox name={formName} value={data} checked>{data}</input>"
#期間フォームの取得
def getNum(ele):
result = re.sub(r"\D", "", ele)
result=int(result)
return result
termForm=["start","end"]
termData=[]
formChecker=True
for formName in termForm:
dataList=forms.getlist(formName)
if not dataList:
formChecker=False
break
else:
for data in dataList:
termData.append(data)
if formChecker:
if getNum(termData[0]) <= getNum(termData[1]):
text=termData[0]+","+termData[1]
tmpTermOption=text
hideInput+=f"<input type=checkbox name=termOption value={text} checked>{text}</input>"
else:
errorMes+="<li>開始時刻が終了時刻を超過していました"
#センサデータ読み込み条件
disabledRobotNum=""
readCheck=False
if forms.getlist("robotNumber"):
robot="robot"+forms.getlist("robotNumber")[0]
sensorDataFilePath=GeneralSensorDataFilePath+robot+"_view.csv"
if os.path.isfile(sensorDataFilePath):
readCheck=True
#disabled robot number
disabledRobotNum="disabled"
#センサデータの読み込み
checktd=False
start=""
end=""
sensorData=[]
if readCheck:
#create data
with open(sensorDataFilePath,"r") as f:
reader=csv.reader(f)
sensorData= [row for row in reader]
#データ数の取得
dataLen=len(sensorData[0])
#項目の抜出
sensorDataItems=sensorData[0]
sensorData=sensorData[1:]
#開始時刻と終了時刻の取得
def createDatetime(data):
data = datetime.datetime.strptime(data, '%Y%m%d%H%M%S%f')
data=data.strftime('%Y-%m-%dT%H:%M:%S.%f')[:-3]
return data
ii=1
start=createDatetime(sensorData[0][ii])
end=createDatetime(sensorData[-1][ii])
#条件チェック
def checkWord(dataList,word,index):
result=[]
for data in dataList:
if data[index]==word:
result.append(data)
return result
formNames=["serialNumber","partNumber"]
#製造番号用i2
ii=2
dataList=forms.getlist("serialNumber")
for data in dataList:
sensorData=checkWord(sensorData,data,ii)
#部品番号用i3
ii=3
dataList=forms.getlist("partNumber")
for data in dataList:
sensorData=checkWord(sensorData,data,ii)
#期間指定用i1
ii=1
dataList=forms.getlist("termOption")
if tmpTermOption:
dataList.append(tmpTermOption)
resultSensorData=[]
hozIndex=[]
for data in dataList:
term=data.split(",")
for i,ele in enumerate(sensorData):
if getNum(term[0]) <= int(ele[ii]) <= getNum(term[1]):
if not i in hozIndex:
resultSensorData.append(ele)
hozIndex.append(i)
if dataList:
sensorData=resultSensorData
#Colam cut
postedItemSelector=forms.getlist("itemSelector")
checked=""
displayCol=[]
for ii,val in enumerate(sensorDataItems) :
checked="checked"
#check null
if postedItemSelector:
if not val in postedItemSelector:
checked=""
itemSelector+=f"<input type=checkbox name=itemSelector value={val} {checked}>{val}</input>"
#check display col
displayCol.append(checked)
#data cut
resultSensorData=[]
resultSensorDataItems=[]
for ii,check in enumerate(displayCol) :
if check:
for rowii,ele in enumerate(sensorData) :
resultSensorData.append([])
resultSensorData[rowii].append(ele[ii])
resultSensorDataItems.append(sensorDataItems[ii])
sensorData=resultSensorData
sensorDataItems=resultSensorDataItems
#HTML表の作成
table=""
#tr要素
table+="<tr>"
for ele in sensorDataItems:
table+=f"<th>{ele}</th>"
table+="</tr>"
#td要素
for data in sensorData:
if data:
checktd=True
table+="<tr>"
for ele in data:
table+=f"<td>{ele}</td>"
table+="</tr>"
#td要素数のチェック
if not checktd:
table="<tr><th>条件を変更してください</th></tr>"
#エラーチェック
if errorMes:
errorMes="<p>入力エラー:<br>"+errorMes+"</p>"
else:
errorMes="<br>"
#HTML構成
htmlPart1=f"""
<p>ロボット番号入力欄</p>
<input name="robotNumber" {disabledRobotNum}>
<button {disabledRobotNum}>入力</button>
"""
htmlPart2=f"""
<p>絞り込み条件↓</p>
<p>製造番号入力欄</p>
<input name="serialNumber">
<button>追加</button>
<p>部品番号入力欄</p>
<input name="partNumber">
<button>追加</button>
<p>期間指定</p>
<p>開始</p>
<input type="datetime-local" name="start" step=".001" min="{start}" max="{end}">
<p>終了</p>
<input type="datetime-local" name="end" step=".001" min="{start}" max="{end}">
<button>追加</button>
"""
htmlPart3=f"""
{errorMes}
<p>条件一覧</p>
{hideInput}
<button>更新する</button>
<hr>
<p>現在表示中の項目</p>
{itemSelector}
<button>更新する</button>
"""
htmlPart4=f"""
<table border="1">
{table}
</table>
"""
#HTML条件分岐
#check for part2 and 4
formNames=["robotNumber"]
for formName in formNames:
checker = forms.getlist(formName)
if not checker:
htmlPart2=""
htmlPart4=""
#HTML本文生成
html=f"""
<html>
<head>
<title>センサデータ閲覧|ユーハン工業</title>
<meta charset="utf-8"></meta>
<link href="---" rel="stylesheet">
</head>
<body>
<div class="wrap">
<header>
<div class="headerLeft">
<h1>センシング産業用ロボット</h1>
</div>
<div class="headerRight">
<ul>
<li><a href="---">データ閲覧</a>
<li><a href="---">部品番号切替え</a>
</ul>
</div>
</header>
<main>
<form method=POST>
{htmlPart1}
{htmlPart2}
{htmlPart3}
</form>
<p>センサデータ表示エリア</p>
<div class="overflow">
{htmlPart4}
</div>
</div>
</body>
</html>
"""
print(f"""
{html}
""")
参考
このサイトを作った人

水野翔太
福知山公立大学情報学部情報学科
2024年卒業予定
やまもとよしのふゼミ所属 3回生
連絡先:
32045088[at]fukuchiyama.ac.jp
(@マークに置き換えてご利用ください)