NTPとは?
NTPとはNetwork Time Protocolで、ネットワーク上の機器と同期正しい時間させるための通信プロトコルです。これは基本の基本のプロトコルです。
通信方法
NTPにはServerとClientがあります。NTP Serverは正しい時間をNTP に提供するだと考えるのは大丈夫でしょう。流れとしてはNTP ServerからNTP Clientのリクエスト待ち、時間同期のリクエストが来たら現在時間を贈ります。
NTPプロトコル
NTPプロトコルのFormatは以下になります。長さは48パイドで、最初はバージョン、モード、Delayなどの基本設定で、NTPサーバが返してくる時刻は、32~39バイトはBの時間で、40~47バイトはCの時間に格なります。
プロトコルについて詳しくはこちら:
https://tools.ietf.org/html/rfc4330
実装
SiemensではNTPのライブラリが公開されており、ライブラリを使用すればとりあえずなにも考えなくよいと思いますが、せっかくですので、まずライブラリの使い方・実装などを一通り紹介し、そのあとライブラリの中身を見てみましょう。
ダウンロード
以下のLinkをアクセスします。
自分はV15.1を使っていますので、TIAV15.1のZipとPDFドキュメントをダウンロードしましょう。
インストール
まずZIPを解凍します。
中身はこんな感じですね。
Global libraries>Open Libraryし、先解凍したFolderの中にあるLSNTP.al15_1を選び、開くます。
TypesのLSNP_ServerとLSNP_ParamをProgram Blocksに引っばって於けばOKです。
構成
自分は実機もっていませんので(在宅ワークだもんー)SimAdv・普通LinuxOSと組み合わせてテストします。
プログラム
そのままLSNTP_Serverを呼び出して、hwIDはLocal~PROFINET_interfaceに割り付けて、initのInputをONして初期化しすればOKです。
テスト
このライブラリを使えます。
https://pypi.org/project/ntplib/
Code
>>> import ntplib >>> from time import ctime >>> c = ntplib.NTPClient() >>> host=’192.168.11.50′ >>> response = c.request(host, version=3) >>> response.offset >>> response.version >>> ctime(response.tx_time) |
ライブラリの詳細
Flow
まず下図はライブラリの基本流れです:
ライブラリはリクエスト待ちなので、まず初期化>接続外し>接続する>リクエストもらったかどうか>もらったら長さ48のデータかどうか>送信>送信終わり>リクエスト待ちのようなイメージです。
こちらはエラー発生のFlowになります。
statSend.tel
Connectionの作りなどは大体にていますのでここで詳し説明しません。説明したいのはどうやってNTPプロトコルを組むだけです。NTP Clientに送信するとき使うのはこのstatSend.telです。
まずflagsを見ましょう。
flags[0]
Line181。MODE_SERVERは定数で2#00011100が入っています。あ、SiemensのライブラリはAll大文字の変数は大体定数です。
では、2#00011100はなんですか?
#statSend.tel.flags[0] := #MODE_SERVER; // Mode – No Warning, NTP Version 3, Server |
なので、Commentも書いていますが、No Warning,NTP Version 3,Server Modeです。
flags[2]
次の NTP パケット送出までの最大時間間隔 (秒) に対して 、log2 の対数を取った数値が入ります。例えば 10 の場合は 2 ** 10 乗 = 1024 秒となります。
#statSend.tel.flags[2] := #statRcv.tel.flags[2]; //Poll – Peer polling intervall copied from request |
flags[3]
NTP を扱う機器のシステムクロックが使用できる精度を保存しています。Commentでは、
Clock Precision -6 = -0,015625s (for S7-1200)と書いてますが、出所不明。
#statSend.tel.flags[3] := #CLOCK_PRECISION; |
rootDelay
正直いいますと、よくわからない。0のままにします。
#statSend.tel.rootDelay := 0; // Root Delay |
rootDispersion
これもいまいちわかりません。
#statSend.tel.rootDispersion := 0; // Root Dispersion |
referenceId
ClientがどのようなNTP Serverのデバイスを同期してるか。
//Set reference identifier according to RFC 4330, only LOCL, DCF and GPS used CASE #referenceId OF 1: Strg_TO_Chars(Strg := ‘DCF$00’, Cnt => #tempNumChar, Chars := #statSend.tel.referenceId, pChars := 0); 2: Strg_TO_Chars(Strg := ‘GPS$00’, Cnt => #tempNumChar, Chars := #statSend.tel.referenceId, pChars := 0); ELSE Strg_TO_Chars(Strg := ‘LOCL’, Cnt => #tempNumChar, Chars := #statSend.tel.referenceId, pChars := 0); END_CASE; |
timestamps.receive.sec
自身が前回相手からNTPパケットを受信した時間です。時刻同期の計算に使われます。
色々な変換していますね。細かくばらしますと、
最後の1+2+DELTA_1900_1970+DELTA_1970_1990は現在時間の秒数をもらうだめです。NTP のタイムスタンプは UTC (Coordinated Universal Time:順番が違うように思うかもしれませんが合っています) を前提として、1900/1/1 0:00:00 を起点 (=0) としています
// Convert System Time (UTC) to NTP Time #statSend.tel.timestamps.receive.sec := (DATE_TO_UDINT(DTL_TO_DATE(#tempSysTime)) * #SEC_PER_DAY) + (TOD_TO_UDINT(DTL_TO_TOD(#tempSysTime)) / 1000) + #DELTA_1970_1990 + #DELTA_1900_1970; |
statSend.tel.timestamps.receive.secFra
下位32ビットは小数点以下なんです。
#statSend.tel.timestamps.receive.secFrac := LREAL_TO_UDINT(UDINT_TO_LREAL(#tempSysTime.NANOSECOND) * 2 ** 32 / 10 ** 9); |
WireShark
Time Frameがすべて同じなのでここでALL説明しません。
最後はWiresharkで見てみましょう。
このTimeスタンプはどう変換するでしょうか。
切り分けとしては:
答えは、3828487166、です。
これは、1900年1月1日 0時0分0秒からの秒数を示している。これを時、分、秒に換算していきます。
日にちは、
3828487166/86400=44311.194
残りの秒数は:3828487166%44311.194=16766
時間は、16766/3600=4.567,WireSharkからみたらまぁ、4時間ですね。
残りの秒数は:16,766%3600=2366
分は2366/60=39.43.. まぁ、ざっくりの計算なので、38分とはあっていますね。
このような感じで計算します。
今回は少し長かったですが、お疲れ様っす。ー
コメント
S7-1500のCPU PropetiesのNTP modeより、こっちでやるほうが便利やね
S7-1500のNTP ModeはClientですからね!