EXOR#Part20_Direct Socketを使ってみよう

今回の記事ではEXOR JMobile RuntimeのDirect Socket機能を使用し、非標準のProtocolとSocket通信する手順を説明します。

さ、FAを楽しもう!

前書き

いつも私の技術ブログとYouTubeチャンネルをご覧いただき、心より感謝申し上げます。また、いまFullさん(full@桜 八重 (@fulhause) / X)と共に毎週水曜日の夜にお届けしている「高橋クリス」ラジオ番組を運営しています。

技術は独り占めせず、届けるもの

私たちは工場の生産技術や制御に関する技術情報を、ブログや動画などで無料公開しています。「知識は誰でもアクセスできるべき」という信念のもと、現場で役立つ具体的なノウハウやトラブル事例などを発信してきました。すべて無料で続けているのは、「知らなかったせいで困る人」を少しでも減らしたいからです。

また、もしあなたの現場で…

  • 「このPLCとデバイスの組み合わせ、ちゃんと動くのかな?」
  • 「EtherCAT通信でうまくいかない部分を検証してほしい」
  • 「新しいリモートI/Oを試したいけど社内に検証環境がない」

など、困っている構成や試してみたいアイデアがあれば、ぜひお知らせください。機器の貸出や構成の共有が可能であれば、検証し、記事や動画で発信します(ご希望に応じて匿名対応も可能です)。

支援のかたち

現在、私達の活動はほぼ無償で続けており、記事や動画の制作には、時間と検証環境の整備が必要です。この活動を継続的にコンテンツを提供するためには、皆様の温かいご支援が大変重要です。

メンバーシップ(ラジオの応援)

Fullさんとのラジオをより充実させるための支援プランです。

https://note.com/fulhause/membership/join

Amazonギフトリスト

コンテンツ制作に必要な機材・書籍をリストにしています。

https://www.amazon.co.jp/hz/wishlist/ls/H7W3RRD7C5QG?ref_=wl_share

Patreon(ブログ・動画活動への応援)

月額での小さなご支援が、記事の執筆・検証環境の充実につながります。

https://www.patreon.com/user?u=84249391

Paypal

小さな支援が大きな力になります。

https://paypal.me/soup01threes?country.x=JP&locale.x=ja_JP

知ってたら助かること、届けたいだけです

あなたの応援が、知識の共有をもっと自由で持続可能なものにしてくれます。これからもどうぞよろしくお願いします。

soup01threes*gmail.com

https://x.com/3threes2

技術はひとりじゃもったいない。

Reference Link

http://soup01.com/ja/category/exor-ja/

Direct Socket?

Direct Socketプロトコルは、ソケット関数への低レベルアクセスを可能にする汎用プロトコルです。

このプロトコルを使用することで、アプリケーション自体は、専用のプロトコルを開発することなく、IPベースのプロトコルを実装することができます。

Direct Socketプロトコルは標準(タグインターフェース)プロトコルとして使用できますが、JavaScriptからプロトコルを利用できるようにするためのDoCommandインターフェースの適切な実装も用意されています。

このプロトコルは、クライアントソケットタイプでのみ使用可能です。

このプロトコルは、クライアントソケットを1つしかサポートしません。アプリケーションで多数のソケットが必要な場合は、このプロトコルがマルチインスタンスに対応しているため、複数のプロトコルをインストールすることができます。

Implementation1

最初にSuffix・PrefixなしのDirect Socket、つまり一番シンプルな検証をします。

Server Side

最初にRaspberrypi側から構築します。

Raspberrypiに簡単なTCPサーバを作成し、Python3で起動してください。

import socket
from datetime import datetime

HOST = “0.0.0.0”
PORT = 5000

server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

server.bind((HOST, PORT))
server.listen(5)

print(f”TCP server listening on {HOST}:{PORT}”)

while True:
conn, addr = server.accept()
print(f”connected from {addr}”)

with conn:
while True:
data = conn.recv(1024)

if not data:
print(“client disconnected”)
break


received_text = data.decode(“utf-8″, errors=”replace”).strip()

now = datetime.now().strftime(“%Y-%m-%d %H:%M:%S”)

response = f”{received_text} | Pi Server get it | {now}\r\n”

print(“recv:”, received_text)
print(“send:”, response.strip())

conn.sendall(response.encode(“utf-8”))

EXOR Side

次はEXOR側を構築します。

Protocol追加

JMobile Studioを起動し、Protocol→+で新規Protocolを追加します。

Drop-ListからDirect Socketを選択します。

プロトコルパラメータは、接続の確立方法やトークンの交換方法を定義します。これらのパラメータは通常、プロジェクトによって定義されます。多くのパラメータは変数としてもアクセスできるため、実行時に変更することが可能です。

Socket Type

通信に使用するソケットの種類。選択肢は UDP または TCP です。

Remote IP Address

文字列で、リモートデバイスのIPアドレスを示します。

Remote Port

整数でリモートデバイスが使用するポートを示します。

Local IP Address

文字列でローカルデバイスのIPアドレスを示します。UDPを使用する場合は必須です。

Local Port

整数でローカルデバイスが使用するポートを示します。UDPを使用する場合は必須です。

Broadcast Type

使用する放送の種類で選択肢は「グローバル」または「ローカル」です。

Rx Token Prefix

読み取りトークンのPrefixを、16進数で指定された文字列として示します。

Rx Token Suffix

読み取りトークンのSuffixを、16進数で指定された文字列として示します。

Token Gap

トークン間の間隔をミリ秒単位で示します。

Tx Token Prefix

Socketから送信されるトークンのPrefixを、16進数で指定された文字列として示します。

Tx Token Suffix

送信されたトークンのSuffixを、16進数で指定された文字列として示します。

Hexadecimal Tokens

  • チェック済み = トークンが16進数である
  • 未チェック = トークンが16進数ではない

Token Queue Size

キュー内のトークンの数を、1 から 10000 までの整数値で示します(デフォルト:100)。

結果

それでDirect Socketを作成しました。

Tag作成

次はTagsをクリックします。

下図のようにDirect Socketに使う変数を全部宣言します。

画面

最後に下図のようなDirect Socket操作画面を作成します。

結果

最初にRemote IP address・Remote PortをServer側に合わせて設定します。

Connect変数に1を入力し、HMI→Serverに接続します。

HMIとServerの間接続OKならConnection Status=1です。

Server側にも接続されたログが表示されました。

次はToken To Sendに文字列を入力しましょう。

Done!Token Received欄からServerの返答が格納されました。

Server側でもデータの送受信を確認できました。

Implementation2

次はPrefixとSuffix機能も試してみましょう。

Socket通信でPrefix(接頭辞)とSuffix(接尾辞)を使う最大の理由は、「データの区切り(デリミタ)を明確にして、データの断片化や結合を防ぐため」です。

TCPによるSocket通信は、データを「ストリーム」として扱います。そのため、送信側が「こんにちは」と「さようなら」という2つのメッセージを送っても、受信側ではこんにちはさようならとくっついて届いたり、こんとにちはさようならに分裂して届いたりします(これをパケットの断片化・結合と言います)。

これを正しく1つのメッセージとして切り出すために、PrefixやSuffixが活躍します。

Suffix(接尾辞)の使い方

データの後ろに特定の終了合図をくっつける方法です。

  • 何を使うか: 主に \n(改行コード)、\r\n(CRLF)、または \0(ヌル文字)などの固定のバイトデータ。
  • どう機能するか: 受信側は、データを読み込みながら「終了合図(例:\n)が来るまで待つ」という処理をします。

Prefix(接頭辞)の使い方

データの前に、これから送る「データの長さ(バイト数)」などをあらかじめ書いておく方法です。

  • 何を使うか: データの長さを表す固定バイト(2バイトや4バイトの整数)。
  • どう機能するか:
    • 受信側は、まず先頭の数バイト(Prefix)だけを読み込んで「あ、次に続くデータは100バイトなんだな」と把握する。
    • その後、正確に100バイト分だけデータを読み込む。

Server 側

最初にRaspberrypi側から構築します。

こちらは改造したPython TCP Serverのスクリプトです。

import socket
from datetime import datetime

HOST = “0.0.0.0”
PORT = 5000

# Hexadecimal Tokens = ON
# Tx Token Prefix = 41 -> A
# Tx Token Suffix = 5A -> Z
# Rx Token Prefix = 41 -> A
# Rx Token Suffix = 5A -> Z
PREFIX = b”A”
SUFFIX = b”Z”


def now_text() -> str:
return datetime.now().strftime(“%Y-%m-%d %H:%M:%S”)


def bytes_to_hex(data: bytes) -> str:
return data.hex(” “)


def remove_prefix_suffix(data: bytes) -> bytes:
data = data.strip()

if data.startswith(PREFIX):
data = data[len(PREFIX):]

if data.endswith(SUFFIX):
data = data[:-len(SUFFIX)]

return data


def main():
server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
server.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

server.bind((HOST, PORT))
server.listen(5)

print(f”TCP server listening on {HOST}:{PORT}”)
print(“EXOR Prefix/Suffix HEX token test”)
print(“Expected EXOR -> Pi : Atest1Z”)
print(“Expected hex : 41 74 65 73 74 31 5a”)

while True:
conn, addr = server.accept()
print(“=” * 60)
print(f”connected from {addr}”)

with conn:
while True:
data = conn.recv(1024)

if not data:
print(“client disconnected”)
break

print(“-” * 60)
print(“recv raw :”, data)
print(“recv hex :”, bytes_to_hex(data))

body = remove_prefix_suffix(data)
received_text = body.decode(“utf-8″, errors=”replace”)

print(“recv text after remove token:”, received_text)

response_text = f”{received_text} | Pi Server get it | {now_text()}”
response = PREFIX + response_text.encode(“utf-8”) + SUFFIX

print(“send raw :”, response)
print(“send hex :”, bytes_to_hex(response))

conn.sendall(response)


if __name__ == “__main__”:
main()

結果

TCP Serverを起動します。

PrefixとSuffixを41と5aに設定します。

次はToken To Sendに文字列を入力します。

次はToken Acknowledgeを1にします。

Done!ServerからA+返事+Zの文字列が返ってきました。

Server側でもデータの送受信を確認できました。

ダウンロード

こちらのLinkから今回の記事のプロジェクトをDownloadできます。

https://github.com/soup01Threes/JMobile/blob/main/JMobile_Tutorial_DirectSocket.zip

シェアする

  • このエントリーをはてなブックマークに追加

フォローする