ダミーサーバの実装

ダミーサーバの実装#

今回、TwinCATのUDP通信のクライアントソフトを評価するため、ダミーのUDPサーバを実装します。どの言語でも構いませんが、Python上でasyncioフレームワークを使った方法が最も簡単です。このスクリプトを動作させるWindowsコンピュータ上では、ポート9998のUDPの受信を許可するようにファイヤウォール設定を行う必要があります。

  • UDPサーバは、ポート9998番にメッセージを受信したら、表 6.4に従いそれぞれの値を変数にマップします。

  • 受信後は、送り主のIPアドレスとポートに対して表 6.5のメッセージを応答します。

    表 6.4 Pythonのダミーサーバへの送信データ#

    先頭バイト数

    Python側型指定

    TwinCAT型指定

    サイズ

    送信データ

    0

    5Byteの文字列

    STRING(4)

    5Byte

    ‘SCOM’ に続き、NULL文字を1byte合計5Byteを送る。

    4

    unsigned short

    UINT

    2Byte

    PLC側のカウンタ値。

    6

    long long int

    ULINT

    8Byte

    16#FFFFFFFFFFFFFFFF 固定値

    表 6.5 Pythonのダミーサーバからの応答データ#

    先頭バイト数

    Python側型指定

    TwinCAT型指定

    サイズ

    送信データ

    0

    5Byteの文字列

    STRING(4)

    5Byte

    ‘RCOM’ に続き、NULL文字を1byte合計5Byteを送る。

    4

    unsigned short

    UINT

    2Byte

    サーバ側のカウンタ値。

    6

    float

    REAL

    4Byte

    PLC側のカウンタ値

リスト 6.13 UDPサーバの実装#
 1import asyncio
 2from struct import pack, unpack
 3import random
 4
 5class DummyMeasurementMachineProtocol:
 6
 7    def __init__(self):
 8        self.counter = 0;
 9
10    def connection_made(self, transport):
11        self.transport = transport
12
13    def datagram_received(self, data, addr):
14        """
15        受信イベントハンドラ
16         struct.pack/unpackは以下のドキュメント参照のこと。
17         https://docs.python.org/ja/3/library/struct.html
18         PLCからデータ受信したバイトデータ data を以下の変数へ展開。
19         '<'   : リトルエンディアンのバイト配列として解釈
20         '5s'  : 5Byte 文字列を、 commandへ展開し、decode()処理して文字列型へ
21         'H'   : unsigned short型(PLCではUINT型)としてsender_sequenceに展開
22         'Q'   : unsigned long long int 型 (PLCではULINT型)としてvalueに展開
23        """
24        command, sender_sequence, value = unpack('<5sHQ', data)
25        command = command.decode()
26        # 標準出力への表示
27        print(f"Received {command}, {sender_sequence}, {value} from {addr}")
28        print(f"Send sequence : {self.counter} to {addr}")
29        """
30        PLCに送信する電文データをバイトデータへパック
31         '<'   : リトルエンディアンのバイト配列に組み立てる
32         '5s'  : 5Byte 文字列として、'RCOM'という文字にNULLを付加したバイトデータをセット
33         'H'   : unsigned short型(PLCではUINT型)として受信回数のカウンタself.counter値をセット
34         'f'   : float 型 (PLCではREAL型)としてsender_sequenceを型変換してセット
35        """
36        send_data = pack('<5sHf', b'RCOM\x00', self.counter, sender_sequence)
37        # 組み立てた電文をPLCに送り返す。
38        self.transport.sendto(send_data, addr)
39        # シーケンス番号を繰り上げる。unsigned shortの最大値(65535)になったらリセット。
40        if self.counter < 65535:
41            self.counter += 1;
42        else:
43            self.counter = 0;
44
45
46loop = asyncio.get_event_loop()
47print("Starting UDP server")
48# ポート9998のUDPで全ホストからの接続を待つ。
49listen = loop.create_datagram_endpoint(
50        DummyMeasurementMachineProtocol,
51        local_addr=('0.0.0.0', 9998)
52    )
53transport, protocol = loop.run_until_complete(listen)
54
55try:
56    loop.run_forever()
57except KeyboardInterrupt:
58    pass
59
60transport.close()
61loop.close()