lowermachine/source/sim_uppermachine/modules/server.py
lyz 4f6a3953c8 feat(app): 新增了模拟上位机的软件程序
1. 新增了模拟上位机软件,方便调试下位机
2. 编写了模拟上位机的使用说明文档
3. 修改了README的相关部分
2023-06-20 18:54:42 +08:00

193 lines
7.1 KiB
Python

import socket
from threading import Thread, Event
from typing import Optional
from time import sleep, time
from queue import Queue
import re
class Server(object):
def __init__(self):
self._s = None
self._listen_th, self._receive_th = None, None
self.ip, self.port = None, None
self.client_ip, self.client_port, self.client_s = None, None, None
self._last_heartbeat = None
self._closing_callback = None
self._closing_signal = None
self._connected_callback = None
self._connected_signal = None
self._disconnected_callback = None
self._disconnected_signal = None
self._send_callback = None
self._send_signal = None
self._receive_callback = None
self._receive_signal = None
self._receive_queue = Queue()
self._timeout = 10.0
self._connection_status, self._need_recv_exit, self._need_listen_exit = Event(), Event(), Event()
def set_ip_port(self, ip: Optional[str] = None, port: Optional[int] = None):
if ip is not None:
self.ip = ip
if port is not None:
self.port = port
def set_heartbeat_timeout(self, timeout: float):
self._timeout = timeout
def set_closing_callback(self, callback):
self._closing_callback = callback
def set_closing_signal(self, signal):
self._closing_signal = signal
def set_connected_callback(self, callback):
self._connected_callback = callback
def set_connected_signal(self, signal):
self._connected_signal = signal
def set_disconnected_callback(self, callback):
self._disconnected_callback = callback
def set_disconnected_signal(self, signal):
self._disconnected_signal = signal
def set_send_callback(self, callback):
self._send_callback = callback
def set_send_signal(self, signal):
self._send_signal = signal
def set_receive_callback(self, callback):
self._receive_callback = callback
def set_receive_signal(self, signal):
self._receive_signal = signal
def _listen_thread(self, ip: str, port: int):
while not self._need_listen_exit.isSet():
if self._connection_status.isSet():
sleep(2)
continue
try:
self._s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
self._s.bind((ip, port))
self._s.listen(1)
# print("connecting...")
self.client_s, (self.client_ip, self.client_port) = self._s.accept()
# print("connected")
self._connection_status.set()
self._need_recv_exit.clear()
self._receive_th = Thread(target=self._receive_thread)
self._receive_th.start()
if self._connected_callback is not None:
self._connected_callback()
if self._connected_signal is not None:
self._connected_signal.emit()
# print("receiving...")
except BaseException as e:
pass
self._close()
def _close(self) -> None:
try:
self.client_s.shutdown(socket.SHUT_RDWR)
self.client_s.close()
except BaseException as e:
pass
self.client_s, self.client_ip, self.client_port = None, None, None
self._last_heartbeat = None
self._connection_status.clear()
if self._disconnected_callback is not None:
self._disconnected_callback()
if self._disconnected_signal is not None:
self._disconnected_signal.emit()
self._need_recv_exit.set()
def close(self) -> None:
try:
self._s.close()
except BaseException as e:
pass
if self._closing_callback is not None:
self._closing_callback()
if self._closing_signal is not None:
self._closing_signal.emit()
self._close()
self._need_recv_exit.set()
self._need_listen_exit.set()
def is_connected(self) -> bool:
return self._connection_status.isSet()
def start_listen_thread(self, ip: Optional[str] = None, port: Optional[int] = None):
if ip is not None and port is not None:
self.ip, self.port = ip, port
self._listen_th = Thread(target=self._listen_thread, args=(self.ip, self.port))
self._listen_th.start()
def _receive_thread(self):
while not self._need_recv_exit.isSet():
data = None
if not self._connection_status.isSet():
sleep(2)
continue
try:
self.client_s.settimeout(2)
data = self.client_s.recv(1024)
except BaseException as e:
pass
if self._last_heartbeat is None:
self._last_heartbeat = time()
if time() - self._last_heartbeat > self._timeout:
# print(time() - self._last_heartbeat)
# print("timeout")
self._close()
if data is not None and len(data) != 0:
if self._receive_callback is not None:
self._receive_callback()
if self._receive_signal is not None:
self._receive_signal.emit(data)
# if data != b"\xaa\x00\x00\x00\x03hb\xff\xff\xff\xbb":
# self._receive_queue.put(data)
# else:
# pass
self._last_heartbeat = time()
def send_datagram(self, datagram: bytes):
try:
self.client_s.settimeout(2)
self.client_s.sendall(datagram)
if self._send_callback is not None:
self._send_callback(datagram)
if self._send_signal is not None:
self._send_signal.emit(datagram)
except BaseException as e:
# print(e)
self._close()
@classmethod
def get_server_ip_list(cls) -> list:
addrs = socket.getaddrinfo(socket.gethostname(), None)
addrs = [i[4][0] for i in addrs]
ip4_list = [i for i in addrs if
re.match(r"^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$",
i) is not None]
ip6_list = [i for i in addrs if
re.match(
r"^([0-9a-fA-F]{1,4}:){7,7}[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,7}:$|^([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}$|^([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}$|^([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}$|^([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}$|^([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}$|^[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})$|^:((:[0-9a-fA-F]{1,4}){1,7}|:)$",
i) is not None]
ip4_list_192 = [i for i in ip4_list if re.match(r"^192\.168\.\S*$", i) is not None]
return ip4_list_192 + list(set(ip4_list) - set(ip4_list_192)) + ip6_list
if __name__ == '__main__':
# s = Server()
# s.start_listen_thread("192.168.2.125", 13452)
Server.get_server_ip_list()