2016-03-20 5 views
3

ちょっとしたセキュリティプロジェクトでは、48ビット(8オクテット)をタイムスタンプに変換しようとしています。私はDNP3プロトコルからのいくつかのネットワークパケットを扱っています。私はforeach DNP3クラスオブジェクトのタイムスタンプ値をデコードしようとしています。DNP3時刻からタイムスタンプまで48ビット(6オクテット)をPythonのタイムスタンプに変換する

DNP3標準によると、「DNP3時間(UINT48形式):1970年1月1日開始からのミリ秒数で表される絶対時間値」。

私は日時に変換する必要があり、次のオクテットがあります

# List of DNP3 timestamps 
DNP3_ts = [] 

# Feb 20, 2016 00:27:07.628000000 UTC 
DNP3_ts.append('\xec\x58\xed\xf9\x52\x01') 

# Feb 20, 2016 00:34:08.107000000 UTC 
DNP3_ts.append('\x6b\xc3\xf3\xf9\x52\x01') 

# Feb 20, 2016 00:42:40.460000000 UTC 
DNP3_ts.append('\xcc\x94\xfb\xf9\x52\x01') 

# Feb 20, 2016 00:56:47.642000000 UTC 
DNP3_ts.append('\x1a\x82\x08\xfa\x52\x01') 

# Feb 20, 2016 00:56:48.295000000 UTC 
DNP3_ts.append('\xa7\x84\x08\xfa\x52\x01') 

# Feb 20, 2016 00:58:21.036000000 UTC 
DNP3_ts.append('\xec\xee\x09\xfa\x52\x01') 

# Feb 20, 2016 01:17:09.147000000 UTC 
DNP3_ts.append('\x9b\x25\x1b\xfa\x52\x01') 

# Feb 20, 2016 01:49:05.895000000 UTC 
DNP3_ts.append('\xe7\x64\x38\xfa\x52\x01') 

# Feb 20, 2016 01:58:30.648000000 UTC 
DNP3_ts.append('\xf8\x02\x41\xfa\x52\x01') 

for ts in DNP3_ts: 
    print [ts] 

だから私は、次の手順を見つけ出す必要があります。誰もがこれらの手順で私を助けることができる場合

# 1. Converting the octets into a 48bit Integer (which can't be done in python) 

# 2. Using datetime to calculate time from 01/01/1970 

# 3. Convert current time to 48bits (6 octets) 

を非常に感謝しています!

答えて

3

これらのバイトを簡単に組み合わせて、ある程度の128ビット整数を作成することができます。bitwise operations。それぞれのオクテットをord()でuint8に変換し、8の異なる倍数だけ左にシフトすると、すべてが48ビット数の別の場所を占有します。

DNP3 encodes the bytes in a reverse order。これを視覚化するには、左から右にA〜FとAのビットをaaaaaaaaなどと呼ぶオクテットを入れてください。オクテットから48ビットの番号まで、この順序を達成してください。あなたはミリ秒を持っていたら

 A  B  C  D  E  F 
ffffffff eeeeeeee dddddddd cccccccc bbbbbbbb aaaaaaaa 

、秒単位の浮動小数点数を取得するために1000でそれらを分割し、datetime.datetime.utcfromtimestamp()にそれを渡します。 strftime()メソッドを使用して、このdatetimeオブジェクトをさらにフォーマットすることができます。これを実現するコードは、

from datetime import datetime 

def dnp3_to_datetime(octets): 
    milliseconds = 0 
    for i, value in enumerate(octets): 
     milliseconds = milliseconds | (ord(value) << (i*8)) 

    date = datetime.utcfromtimestamp(milliseconds/1000.) 
    return date.strftime('%b %d, %Y %H:%M:%S.%f UTC') 

です。DNP3回ごとにこの関数を呼び出すと、次のような結果が得られます。

Feb 19, 2016 14:27:07.628000 UTC 
Feb 19, 2016 14:34:08.107000 UTC 
Feb 19, 2016 14:42:40.460000 UTC 
Feb 19, 2016 14:56:47.642000 UTC 
Feb 19, 2016 14:56:48.295000 UTC 
Feb 19, 2016 14:58:21.036000 UTC 
Feb 19, 2016 15:17:09.147000 UTC 
Feb 19, 2016 15:49:05.895000 UTC 
Feb 19, 2016 15:58:30.648000 UTC 

これらの結果は、正確に8時間遅れることがわかります。私はこの矛盾を理解することはできませんが、私のアプローチが間違っているとは思わない。

日時からDNP3時間に移動するには、converting the time to a timestamp of millisecondsから開始します。次に、DNP3オクテットを構築するには、8ビットずつ右シフトしてマスキングします。

def datetime_to_dnp3(date=None): 
    if date is None: 
     date = datetime.utcnow() 
    seconds = (date - datetime(1970, 1, 1)).total_seconds() 
    milliseconds = int(seconds * 1000) 
    return ''.join(chr((milliseconds >> (i*8)) & 0xff) for i in xrange(6)) 

あなたは、引数なしで呼び出した場合、それはあなたの現在の時間を与えるだろうが、あなたは、任意の特定の日時を指定するオプションがあります。たとえば、datetime(1970, 1, 1)\x00\x00\x00\x00\x00\x00datetime(1970, 1, 1, 0, 0, 0, 1000)(1970年代以降1ミリ秒)は\x01\x00\x00\x00\x00\x00を返します。

DNP3のバイト数によっては、印刷しようとすると奇妙な記号が表示されることがあります。バイトがまだそこに残っていても心配しないでください。Pythonがそれらを文字にエンコードしようとしているだけです。お互いに干渉して個々のバイトを見たい場合は、単にlist(DNP3_ts[i])と印刷してください。 '\x52'R(多くのASCII印刷可能文字に似ています)として印刷されますが、同等です。入力から整数を取得する

+0

あなたは伝説です!詳細な説明をありがとうございます!タイムスタンプはオーストラリアのブリスベン(UTC + 10)です。私は、機器のクロックに間違った設定があるかもしれないと思う。ここに示すのは、ブリスベンのタイムゾーンからUTC 0への変換で、時間は以前に提供された時間[World Clock converter](http://www.timeanddate.com/worldclock/converted.html?iso=20160219T1420&p1=0&p2= 47)。乾杯! – nicRodz

+0

@nicRodzそれは素晴らしいです。私は、エポックの日時に対して正しい結果を得ることができたという事実からいくらか相殺されなければならないことはほとんど確信していました。 – Reti43

1

は、Python 3バイト:

>>> millis = int.from_bytes(b'\xec\x58\xed\xf9\x52\x01', 'little') 
>>> millis 
1455892027628 

「エポックからのミリ秒」として整数を解釈する:

>>> from datetime import datetime, timedelta 
>>> utc_time = datetime(1970, 1, 1) + timedelta(milliseconds=millis) 
>>> str(utc_time) 
'2016-02-19 14:27:07.628000' 

注:結果と異なりますあなたの質問(Feb 20, 2016 00:27:07.628)のコメントに記載されています。

あなたは符号なし整数にリトルエンディアンの順序でバイトを変換するには、古いバージョンのPythonをサポートする必要がある場合:

>>> import binascii 
>>> int(binascii.hexlify(b'\xec\x58\xed\xf9\x52\x01'[::-1]), 16) 
1455892027628