#!/usr/bin/env python2 # # Read an EDI dump file and transmit over UDP # # The MIT License (MIT) # # Copyright (c) 2015 Matthias P. Braendli # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import sys import struct from crc import crc16 from reedsolo import RSCodec import socket import time UDP_IP = "239.20.64.1" UDP_PORT = 12002 class BufferedFile: def __init__(self, fname): self.buf = [] if fname == "-": self.fd = sys.stdin else: self.fd = open(fname, "rb") def read(self, n): if not self.buf: return self.fd.read(n) else: if len(self.buf) < n: self.buf.extend(self.fd.read(n - len(self.buf))) if len(self.buf) == n: ret = b"".join(self.buf) self.buf = [] else: ret = b"".join(self.buf[:n]) del self.buf[:n] return ret def peek(self, n): dat = self.fd.read(n) self.buf.extend(dat) return dat pft_head_struct = "!2sH3B3BH" pft_rs_head_struct = "!2B" pft_addr_head_struct = "!2H" af_head_struct = "!2sLHBc" class EDI: def __init__(self): self.last_send_time = time.time() self.sock = socket.socket(socket.AF_INET, # Internet socket.SOCK_DGRAM) # UDP def send_udp(self, message): self.sock.sendto(message, (UDP_IP, UDP_PORT)) def decode(self, stream): sync = stream.peek(2) if len(sync) < 2: return False if sync == "PF": return self.decode_pft(stream) elif sync == "AF": return self.decode_af(stream, is_stream=True) def decode_pft(self, stream): headerdata = stream.read(12) header = struct.unpack(pft_head_struct, headerdata) psync, pseq, findex1, findex2, findex3, fcount1, fcount2, fcount3, fec_ad_plen = header findex = (findex1 << 16) | (findex2 << 8) | findex3 fcount = (fcount1 << 16) | (fcount2 << 8) | fcount3 fec = (fec_ad_plen & 0x8000) != 0x00 addr = (fec_ad_plen & 0x4000) != 0x00 plen = fec_ad_plen & 0x3FFF rs_k = 0 rs_z = 0 if fec: rs_head = stream.read(2) rs_k, rs_z = struct.unpack(pft_rs_head_struct, rs_head) headerdata += rs_head addr_source = 0 addr_dest = 0 if addr: addr_head = stream.read(4) addr_source, addr_dest = struct.unpack(pft_addr_head_struct, addr_head) headerdata += addr_head # read CRC crc_data = stream.read(2) crc = struct.unpack("!H", crc_data)[0] crc_calc = crc16(headerdata) crc_calc ^= 0xFFFF crc_ok = crc_calc == crc time_now = time.time() if findex == 0: if self.last_send_time + 24e-3 > time_now: delay = self.last_send_time + 24e-3 - time_now print("Sleeping for {} ms".format(1000 * delay)) time.sleep(delay) self.last_send_time = time_now if crc_ok: payload = stream.read(plen) self.send_udp(headerdata + crc_data + payload) return crc_ok def decode_af(self, in_data, is_stream=False): if is_stream: headerdata = in_data.read(10) else: headerdata = in_data[:10] sync, plen, seq, ar, pt = struct.unpack(af_head_struct, headerdata) if sync != "AF": return False crc_flag = (ar & 0x80) != 0x00 revision = ar & 0x7F if is_stream: payload = in_data.read(plen) crc_data = in_data.read(2) crc = struct.unpack("!H", crc_data)[0] else: payload = in_data[10:10+plen] crc_data = in_data[10+plen:10+plen+2] crc = struct.unpack("!H", crc_data)[0] crc_calc = crc16(headerdata) crc_calc = crc16(payload, crc_calc) crc_calc ^= 0xFFFF crc_ok = crc_calc == crc if crc_ok: self.send_udp(headerdata + payload + crc_data) return crc_ok if len(sys.argv) > 1: filename = sys.argv[1] edi_fd = BufferedFile(filename) else: edi_fd = BufferedFile("-") edi = EDI() while edi.decode(edi_fd): pass