この場合、 modbusTCPおよびOPCUAライブラリのクライアント部分を使用します。
その結果、スレーブのマスターとして機能するhttpサーバーを取得し、 スレーブモードで機能します。

Modbusマスター
modbus TCPウィザードを構成するには、必要なライブラリをインポートします。
import modbus_tk import modbus_tk.defines as cst import modbus_tk.modbus_tcp as modbus_tcp
IPアドレスとポート、および応答を待つためのタイムアウトでウィザードを初期化する必要があります。
master = modbus_tcp.TcpMaster(host='127.0.0.1', port=502) master.set_timeout(2)
レジスタの名前とセルアドレスを示す、 スレーブデバイスのポーリングの周期機能について説明します。
def getModbus(): while True: try: data= master.execute(rtu, cst.READ_INPUT_REGISTERS,0,1 ) except Exception as e: print (e) time.sleep(1) #rtu – RTU modbus # cst.READ_INPUT_REGISTERS – , : #cst.READ_INPUT_REGISTERS #cst.READ_DISCRETE_INPUTS #cst.READ_COILS #cst.READ_HOLDING_REGISTERS
次に、別のスレッドthreadでポーリングサイクルを開始する必要があります 。
modb = threading.Thread(target=getModbus) modb.daemon = True modb.start()
その結果、 IPアドレス 127.0.0.1およびポート502のmodbusTCPプロトコルを使用したスレーブデバイスの周期的なポーリングが開始され、READ_INPUT_REGISTERSレジスタが読み取られ、0x00にある値がデータ変数に書き込まれます。
OPCUAクライアント
OPCUAサーバーからデータを受信するには、 freeopcuaライブラリを接続する必要があります
from opcua import ua, Client
新しいクライアント接続を作成します。
url="opc.tcp://127.0.0.1:4840/server/" try: client = Client(url) client.connect() root = client.get_root_node() except Exception as e: print(e)
OPCサーバーには、継承の厳密な階層があり、 親と子の正確な定義があるため、多数のネストされたオブジェクトを含む非常に複雑なシステムを構築できます。 しかし、この場合、今日ではそのような多くの関数は必要ありませんでした。そのため、 Objectsのルートフォルダーにノードを作成して値を割り当てることに限定しました。 このようなオブジェクト-> MyNode-> MyNodeValueのようになりましたが、より複雑なシステムの構築にはこのメソッドは受け入れられないことを認めなければなりません。
obj = root.get_child(["0:Objects"]) objChild= obj.get_children() for i in range(0,len(objChild)): unitsChild.append(i) unitsChild[i]=objChild[i].get_children() parName=val_to_string(objChild[i].get_browse_name())[2:] for a in range(0, len( unitsChild[i] ) ): valName=val_to_string(unitsChild[i][a].get_browse_name())[2:] try: valData=unitsChild[i][a].get_value() data =unitsChild[i][a].get_data_value() st=val_to_string(data.StatusCode) ts= data.ServerTimestamp.isoformat() tsc= data.SourceTimestamp.isoformat() except Exception as e: print(e)
変数の値はvalDataで直接確認でき、 StatusCodeはstに書き込まれ、 tsおよびtscはそれぞれServerTimestampおよびSourceTimestampのタイムスタンプが記録されます。
スレーブをポーリングする場合、ラウンドロビンポーリングも別のスレッドで実行されますが、イベントをサブスクライブする方が適切です。
Json Webサーバー
Webサーバーを作成するには、ライブラリが必要です。
from http.server import BaseHTTPRequestHandler, HTTPServer import json import base64
サーバー自体を起動するのは難しくなく、コマンドは2つだけで、ネットワークには多くの説明と例があります。
server_address = (“127.0.0.1”, 8080) httpd = server_class(server_address, handler_class) try: httpd.serve_forever() except Exception as e: print(e) httpd.server_close()
最も興味深いことは、テストのためにChromeまたはFirefoxブラウザーから作成されたサーバーに接続する必要が生じたときに始まりました。
refuse_connectを常に表示します。
ネットワークを少し検索して、解決策を見つけました-do_GET関数に追加する必要があります:
self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Credentials', 'true')
これで、正常に機能するWebサーバーにアクセスできましたが、オープンアクセスでしたが、何らかの認証、ログインとパスワードによるアクセスを確立したいと思います。
判明したように、ヘッダーを使用してこれを行うのは特に難しいことではありません。
例
def do_GET(self): global key if self.headers.get('Authorization') == None: self.do_AUTHHEAD() response = { 'success': False, 'error': 'No auth header received'} self.wfile.write(bytes(json.dumps(response), 'utf-8')) elif self.headers.get('Authorization') == 'Basic ' + str(key): resp=[] self.send_response(200) self.send_header('Allow', 'GET, OPTIONS') self.send_header("Cache-Control", "no-cache") self.send_header('Content-type','application/json') self.send_header('Access-Control-Allow-Origin', 'null') self.send_header('Access-Control-Allow-Credentials', 'true') self.send_header('Access-Control-Allow-Methods', 'GET, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'X-Request, X-Requested-With') self.send_header("Access-Control-Allow-Headers", "Authorization") self.end_headers() req=str(self.path)[1:] if(req == "all" ): try: for i in range(0,units): resp.append({varName[i]:[reg[i],varNameData[i]]}) i+=1 self.wfile.write(json.dumps( resp ).encode()) except Exception as e: print('all',e) else: for i in range(0,units): if(req == varName[i] ): try: resp =json.dumps({ varName[i]:varNameData[i] } ) self.wfile.write(resp.encode()) except Exception as e: print(e) i+=1 else: self.do_AUTHHEAD() response = { 'success': False, 'error': 'Invalid credentials'} self.wfile.write(bytes(json.dumps(response), 'utf-8'))
ブラウザーを使用して接続しようとすると、認証が実行されてデータが転送されますが、パーサーなしでブラウザーからデータを受信することはお勧めできません。JavaScryptでGETメソッドを使用し、htmlページのスクリプトを使用してXMLHttpRequest()関数を使用してデータを受信することを想定しています。 しかし、そのような実装では、ブラウザーは最初にGETメソッドではなくOPTIONSメソッドによってリクエストを送信し、GETメソッドによってリクエストが実行された後にのみresponse = 200を受信する必要があります。
別の機能を追加しました:
def do_OPTIONS(self): self.send_response(200) self.send_header('Access-Control-Allow-Credentials', 'true') self.send_header('Access-Control-Allow-Origin', 'null') self.send_header('Access-Control-Allow-Methods', 'GET,OPTIONS') self.send_header('Access-Control-Allow-Headers', 'X-Request, X-Requested-With') self.send_header("Access-Control-Allow-Headers", "origin, Authorization, accept") self.send_header('Content-type','application/json') self.end_headers()
この関数が接続されると、 「Access-Control-Allow-Origin」を使用してチェックが実行され、 「null」に設定されていない場合、交換は行われません。
ログインとパスワードでアクセスできるようになりました。ブラウザーはシナリオに従ってデータを交換しますが、SSLデータ暗号化を整理することをお勧めします。 これを行うには、サーバーを起動する前にSSL証明書ファイルを作成し、次の行を追加します。
httpd.socket = ssl.wrap_socket (httpd.socket, certfile=pathFolder+'json_server.pem',ssl_version=ssl.PROTOCOL_TLSv1, server_side=True)
もちろんこれは自己署名証明書ですが、いずれにしてもオープンプロトコルよりも優れています。
HTMLページのスクリプト内のデータを処理するには、上記のXMLHttpRequest()関数を使用できます。
例
xmlhttp=new XMLHttpRequest(); xmlhttp.open("GET","http://192.168.0.103:8080/all",true); xmlhttp.setRequestHeader("Authorization", "Basic " + btoa(login+":"+password)); xmlhttp.withCredentials = true; xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send(null); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { resp= xmlhttp.responseText; parseResp=JSON.parse(resp); } }
JSON Configuratorの説明
以下は、スクリプトを実行するためのコンフィギュレーター設定の説明例です。
ウィンドウの外観とコントロールボタンの目的:

タスクがパラメーター付きの温度センサーからデータを受信することであるとしましょう:
プロトコル: modbusTCP
IPアドレス: 192.168.0.103
ポート: 502
RTU: 1
登録: READ_INPUT_REGISTERS(0x04)
住所: 0
変数名: tempSensor_1
JSONサーバーでこのデータを印刷します。
フォーマット: json
IPアドレス: 192.168.0.103
ポート: 8080
ログイン: 111
パスワード: 222
json.pyを実行し、 左上に新しいサーバーボタン(+)を追加し、名前を指定して保存します。
次に、作成したインスタンスを作成し、Webサーバーのパラメーターを入力する必要があります。

スレーブデバイスのポーリングパラメータ、この場合は温度センサーを書き留めます。

その後、スクリプトの保存ボタンをクリックすると、Windowsの場合はweb_( データベース内のサーバーの数).batまたはLinuxの場合はweb_(データベース内のサーバーの数).shという名前のファイルがscrフォルダーに表示されます。 スクリプトの実行パスはこのファイルに書き込まれます。
この場合、Windowsの例、 web_15.batファイル:
rem 'ScadaPy Web JSON v.3.14'
rem Web ' '
rem Http '192.168.0.103'
rem Http '8080'
start c:\Python35\python.exe F:\scadapy\main\source\websrv.py 15 F:\scadapy\main\db\webDb.db
保存ボタンの隣にあるボタンをクリックすると、すぐにスクリプトを実行できます(すべてのボタンにはツールチップが装備されています)。
起動後、起動と接続に関する情報を含むコンソールウィンドウが表示されます。

ブラウザを起動したら 、接続文字列_https://192.168.0.103:8080 / allを書き込み、パスワードを入力すると、Chromeで次のように表示されます。

またはFirefoxの場合:

実行中のサーバーのコンソールには、接続セッションに関する情報が表示されます。

この場合、パラメーターはすべて GETリクエストで入力されているため、サーバーで構成されているすべての変数のデータを取得します。 変数の数を増やすと、現在使用されていないデータを受信して処理する必要があるため、これは完全に正しいわけではありません。そのため、値を処理する必要がある変数の直接名を入力する方が良いでしょう: tempSensor_1
この場合:
リクエスト-tempSensor_1
答えは{"tempSensor_1":[2384]}です。
JavaScript処理
リクエストの形成とレスポンスの処理をhtmlページに統合する方法を少し説明したいと思います。
XMLHttpRequest()関数を使用してリクエストを実行できますが、現在利用可能な他の接続方法があります。 接続に成功し、ステータス200を取得したら 、 JSON.parse()関数を実行するだけで十分です。
クエリ実行の周期性を確立するには、タイマーを実行する必要があります。
例。
function getTemp() { var dataReq='tempSensor_1'; var login='111', passw='222'; var ip='192.168.0.103'; var port='8080'; if (window.XMLHttpRequest) { xmlhttp=new XMLHttpRequest(); } else { xmlhttp=new ActiveXObject("Microsoft.XMLHTTP"); } xmlhttp.open("GET","https://"+ip+":"+port+"/"+dataReq,true); xmlhttp.setRequestHeader("Authorization", "Basic " + btoa(login+":"+passw)); xmlhttp.withCredentials = true; xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); xmlhttp.send(null); xmlhttp.onreadystatechange=function() { if (xmlhttp.readyState==4 && xmlhttp.status==200) { resp= xmlhttp.responseText; parseResp=JSON.parse(resp); data=parseResp.tempSensor_1[0]; log("Val :" + data +"\n"); resp=data*0.1; } } }
受信したデータをさまざまなウィジェットで表示する例。

OPCUAサーバーからデータを受信すると、JSON応答の構造はわずかに変わりますが、わずかに変わります。 いずれにせよ、そこを理解することは難しくありません。
githubのダウンロードリンク