aboutsummaryrefslogtreecommitdiffstats
path: root/edi/edisend.py
diff options
context:
space:
mode:
Diffstat (limited to 'edi/edisend.py')
-rwxr-xr-xedi/edisend.py193
1 files changed, 193 insertions, 0 deletions
diff --git a/edi/edisend.py b/edi/edisend.py
new file mode 100755
index 0000000..2bd6bc1
--- /dev/null
+++ b/edi/edisend.py
@@ -0,0 +1,193 @@
+#!/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
+