デバッグ版だけどとりあえず上げとく。いつか書き直す。
基本的にはコントローラー端末からコマンドデータを待ち、それに合わせて各処理をやっていくという構成になる。大きくはコントローラー端末とやり取りする部分とコマンドデータを解析して処理していく部分に分かれる。
コマンドデータはUDPにて送られてくるので受信待ちはスレッドにして、受信次第データを解析側にデータキューで渡してやる
コントローラー端末とやり取りをするクラス
# コントローラークラス
class ControlerSocket(threading.Thread):
# コンストラクタ
def __init__(self):
threading.Thread.__init__(self)
srcIP = "" # 受信元IP(""でどこからでも可)
srcPort = STREAMDECKLIKESERVER_PORT # 受信元ポート番号
self.SrcAddr = (srcIP, srcPort) # アドレスをtupleに格納
self.BUFSIZE = 1024 # バッファサイズ指定
self.udpServSock = socket(AF_INET, SOCK_DGRAM) # AF_INET(IPv4), SOCK_DGRAM(UDP)ソケット作成
self.udpServSock.bind(self.SrcAddr) # 受信元アドレスでバインド
self.recQueue = queue.Queue() # 受信データ用キューの生成
#UDP通信はopen()いらない?
# デストラクタ
def __del__():
#UDP通信はclose()いらない?
print("dest")
def run(self):
self.__recv()
def send(self, data, DstAddr):
data = data.encode('utf-8') # byte型に変換
self.udpServSock.sendto(data, DstAddr) # 宛先アドレスに送信
# 受信
def __recv(self):
while True:
try:
(data, addr) = self.udpServSock.recvfrom(self.BUFSIZE)
except timeout:
print("ex")
continue
if addr != 0:
print(type(data))
str_data = data.decode('utf-8') #データをstr型に変換
fromip = FROMIPADDRESS + ":" + str(addr[0]) # 返信先IPアドレスをコマンドフォーマットに変換
formport = FROMPORTADDRESS + ":" + str(addr[1]) # 返信先ポートをコマンドフォーマットに変換
self.recQueue.put(fromip + "," + formport + "," + str_data) # 受信データをキューに格納
print(f"get massage from {addr} --> {str_data} ") # 受信情報表示
if str_data == "end": # プログラム終了
break
こっちがメイン部分のコマンド解析と各種処理を行う部分
UDP受信はコントローラークラスが担当していてデータはデータキューで送られてくるのでデータキューが来たら解析して該当処理を行うだけ
# メイン
def main():
global obsws
global dicFunction
#コマンドと関数の紐づけ
it = iter(COMMANDS)
for cmd,func in zip(it,it):
dicFunction[cmd] = func
#コントローラー(esp32予定)受信用ソケット
thcs = ControlerSocket() # クラス呼び出し
thcs.setDaemon(True) # デーモンスレッドにする(joinにする方が今回は良いと思うが面倒なのでとりあえずデーモン。いつか直す
thcs.start() # スレッドとして実行
#データ待ちループ
while True:
recdata = thcs.recQueue.get() # 受信データの取り出し(データが来るまで待ち)
dicobscommand = CommandSlpit(recdata) # 受信データのコマンドとパラメタに分割
try:
result = CommandExecute(dicobscommand)
if result is not None :
print("return data")
DstAddr = (dicobscommand[FROMIPADDRESS], STREAMDECKLIKECLIANT_PORT) # アドレスをtupleに格納
thcs.send(str(result), DstAddr)
except KeyboardInterrupt:
pass
obsws.Close() #
print("enddd")
# コマンドごとに登録したメソッドを実行
# diccmd:分割したコマンド辞書
def CommandExecute(diccmd):
result = dicFunction[diccmd["cmd"]](diccmd)
return result
print("exe")
コマンド名を元に実行するメソッドを配列として作成(ここでは辞書配列を使っている)しておくことによって後々のメンテナンス性が楽になる。
コマンド文字列,実行するメソッド
で登録していけば後は勝手にうまくやってくれる。
#コマンドと関数の紐づけ
it = iter(COMMANDS)
for cmd,func in zip(it,it):
dicFunction[cmd] = func
# コマンド登録用リスト
COMMANDS = [
OBSSCENECHANGE, obsws.OBSSceneChange # OBSアクティブシーン変更
,OBSTEXTCHANGE, obsws.OBSTextChange # OBSテキストグリッド変更
]
解析部分
StreamDeckっぽいものを作る_1で決めたコマンドデータフォーマットの先頭にipとポートが付け加えられて送られてくるのでそれを分離して、パラメタとして格納していく。
基本的には「:」と「,」で分けられているだけ。
#コマンドの分離(コマンド種別とコマンドパラメタに分離)
#data:1文コマンドの文字列
#result:分離したコマンド辞書
def CommandSlpit(data):
result = {} # 空の辞書作成
datasplit = str(data).split(',') # 1コマンド分に分割
# 1コマンド分を辞書登録
for d in datasplit:
obsdic = d.split(":") # コマンド種別とコマンドパラメタ値に分割
result[obsdic[0]] = obsdic[1] # 辞書に追加
return result