182 lines
6.1 KiB
Python
182 lines
6.1 KiB
Python
|
|
#!/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()
|