diff --git a/modem_emu.py b/modem_emu.py new file mode 100755 index 0000000..f0958ad --- /dev/null +++ b/modem_emu.py @@ -0,0 +1,181 @@ +#!/usr/bin/env python3 +import sys +import serial +import re +import socket +import codecs +import threading +import queue +from time import sleep + + +class Socket: + def __init__(self, modem_emu, socket_id, port): + self.modem_emu = modem_emu + self.socket_id = socket_id + self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + self.sock.settimeout(1) + self.sock.bind(('', port)) + self._stop_event = threading.Event() + self.receiver_thread = threading.Thread(target=self.receiver_t) + self.receiver_thread.start() + self.incoming = queue.Queue() + + def receiver_t(self): + while True: + try: + data, src = self.sock.recvfrom(512) + except socket.timeout: + data, src = None, None + if src is not None: + src_ip, src_port = src + sleep(1.5) + print('#### received message from {}:{}: {}'.format( + src_ip, src_port, data)) + self.incoming.put((self.socket_id, src_ip, src_port, data)) + self.modem_emu.urcs.put((self.socket_id, len(data))) + if self._stop_event.is_set(): + break + + def close(self): + self._stop_event.set() + self.receiver_thread.join() + self.sock.close() + + def send(self, message, remote_ip, remote_port): + self.sock.sendto(message, (remote_ip, remote_port)) + + +class ModemEmu: + def __init__(self, serial_dev): + self.ser = serial.Serial(serial_dev, 9600) + self.next_socket_id = 100 + self.socket_dct = {} + self.urcs = queue.Queue() + self.serial_lock = threading.Lock() + self.at_cmd_thread = threading.Thread(target=self.at_cmd_t) + self.urc_thread = threading.Thread(target=self.urc_t) + + def get_next_socket_id(self): + ret = self.next_socket_id + self.next_socket_id += 1 + return ret + + def at(self, line, match): + print('cmd=AT') + self.ser.write(b'OK\r\n') + + def at_nrb(self, line, match): + print('cmd=AT+NRB') + for sid in self.socket_dct: + self.socket_dct[sid].close() + self.socket_dct.clear() + self.next_socket_id = 100 + self.ser.write(b'OK\r\n') + + def at_nsocr(self, line, match): + listen_port = int(match.group(1)) + receive_control = int(match.group(2)) + print('cmd=AT+NSOCR; listen_port={}; receive_control={}'.format( + listen_port, receive_control)) + socket_id = self.get_next_socket_id() + sock = Socket(self, socket_id, listen_port) + self.socket_dct[socket_id] = sock + self.ser.write(bytes('{}\r\n'.format(socket_id), 'ASCII')) + self.ser.write(b'OK\r\n') + + def at_nsocl(self, line, match): + socket_id = int(match.group(1)) + print('cmd=AT+NSOCL; socket={}'.format(socket_id)) + try: + sock = self.socket_dct.pop(socket_id) + except KeyError: + print('at_nsocl: unknown socket') + else: + sock.close() + self.ser.write(b'OK\r\n') + + def at_nsost(self, line, match): + socket_id = int(match.group(1)) + remote_ip = match.group(2) + remote_port = int(match.group(3)) + length = int(match.group(4)) + data = match.group(5) + print('cmd=AT+NSOST; socket={}; remote_ip={}; remote_port={}; length={}; ' + 'data={}'.format(socket_id, remote_ip, remote_port, length, data)) + try: + sock = self.socket_dct[socket_id] + except KeyError: + print('at_nsost: unknown socket') + else: + message = codecs.decode(data, 'hex') + sock.send(message, remote_ip, remote_port) + sent_length = length + self.ser.write(bytes('{},{}\r\n'.format(socket_id, sent_length), 'ASCII')) + self.ser.write(b'OK\r\n') + + def at_nsorf(self, line, match): + socket_id = int(match.group(1)) + req_length = int(match.group(2)) + print('cmd=AT+NSORF; socket={}; req_length={}'.format(socket_id, + req_length)) + try: + sock = self.socket_dct[socket_id] + except KeyError: + print('at_nsorf: unknown socket') + self.ser.write(b'ERROR\r\n') + else: + socket_id_2, src_ip, src_port, data = sock.incoming.get() + assert socket_id == socket_id_2 + assert req_length == len(data) + remaining_length = 0 + resp = '{},{},{},{},{},{}\r\n'.format( + socket_id, src_ip, src_port, len(data), + codecs.encode(data, 'hex').decode('ASCII'), remaining_length) + self.ser.write(bytes(resp, 'ASCII')) + self.ser.write(b'OK\r\n') + + def run(self): + self.at_cmd_thread.start() + self.urc_thread.start() + + def at_cmd_t(self): + while True: + line = self.ser.readline() + self.serial_lock.acquire() + line = line.strip() + print(line) + lst = [ + (b'AT$', self.at), + (b'AT\+NRB$', self.at_nrb), + (b'AT\+NSOCR=DGRAM,17,(\d+),([0-1])', self.at_nsocr), + (b'AT\+NSOCL=(\d+)', self.at_nsocl), + (b'AT\+NSOST=(\d+),(\d+\.\d+\.\d+\.\d+),(\d+),(\d+),([0-9a-fA-F]+)', self.at_nsost), + (b'AT\+NSORF=(\d+),(\d+)', self.at_nsorf), + ] + for regex, action in lst: + m = re.match(regex, line) + if m: + action(line, m) + break + else: + self.ser.write(b'ERROR\r\n') + print("") + self.serial_lock.release() + + def urc_t(self): + while True: + urc = self.urcs.get() + socket_id, length = urc + msg = '+NSONMI:{},{}\r\n'.format(socket_id, length) + self.serial_lock.acquire() + self.ser.write(bytes(msg, encoding='ASCII')) + self.serial_lock.release() + + +if len(sys.argv) != 2: + print('Usage: {} device'.format(sys.argv[0])) + sys.exit(1) +serial_dev = sys.argv[1] +modem_emu = ModemEmu(serial_dev) +modem_emu.run()