前提:物理串口连接到PC上,通过串口号被PC唯一识别。
此时,物理串口通过该串口号仅能被单一线程或进程实例并占用,
其他线程或进程不能再通过该串口号与物理串口通信。这个暂称为串口独占性。解决思路:
核心思想:利用计算机软件中的socket编程,一个socket server 可以连接多个socket client,由socket server 完成多个socket client与物理串口的通信。实现过程:
1、编程语言根据物理串口的串口号实例化一个串口操作类,串口操作类负责与物理串口通信。建立串口写线程和串口读线程。其中,串口读线程不断收取物理串口输出,并存放到读缓存。串口写线程不断从写缓存取命令,由其不断发往物理串口。2、建立一个可靠的Socket Server,当有Socket Client连接时,由其将读缓存中的数据发给Socket Client,并不断收取Socket Client发来的命令,存放到写缓存中。3、编程语言线程/进程通过建立Socket Client连接到Socket Server,既可实现多个线程/进程与物理串口的通信
以下没有实现缓存机制,而是将读取到的串口数据放入队列:
socket server
#!/usr/bin/python# -*- coding: utf-8 -*-import socketimport psutilimport tracebackimport threadingimport SocketServer import jsonimport sysimport Queueimport timefrom serial import Serialfrom SocketServer import StreamRequestHandler as SRHfrom CustomStringIO import CustomStringIOSERIALCOMNUM = {}class MainHandler(SRH): def handle(self): try: print 'Client [%s] is Connected To The Server On Port [%s].' % (self.client_address[0], self.client_address[1]) self.keep_alive = True while self.keep_alive: data = self.request.recv(4096 * 3) if not data: break data_json = json.loads(data) if "RequestType" in data_json: if data_json["RequestType"] == "DevSerialHandle": if "Port" in data_json["Args"]: self.dev_serial_handler(data_json, close_timeout=60) break else: break except Exception as e: traceback.print_exc() finally: print '<------ SerialSocketServer handle request finish ------>' def dev_serial_handler(self, data_json, close_timeout=60): self.read_queue = Queue.Queue() read_id = str(time.time()) if data_json["Args"]['Port'] in SERIALCOMNUM: self.dev_serial = SERIALCOMNUM[data_json["Args"]["Port"]]['serial'] self.dev_serial.client_buffer.update({read_id:self.read_queue}) SERIALCOMNUM[data_json["Args"]["Port"]]['count'] += 1 else: self.dev_serial = SerialHandle(data_json["Args"]['Port']) self.dev_serial.client_buffer.update({read_id:self.read_queue}) SERIALCOMNUM.update({data_json["Args"]['Port']:{'serial':self.dev_serial,'count':1}}) print str(SERIALCOMNUM) th_dev_serial_read = threading.Thread(target=self.read_dev_serial) th_dev_serial_read.start() is_recv_data_none = False while self.keep_alive: try: data = self.request.recv(4096 * 3) print 'your input is %s' % str(data) except socket.error: self.keep_alive = False print "close dut serial" break else: if data: self.dev_serial.write(data) end_time = time.time() + close_timeout # socket client 关闭后,self.request.recv会一直收到空字符串,等待一段时间后,关闭连接 else: if is_recv_data_none == False: is_recv_data_none = True end_time = time.time() + close_timeout if time.time() > end_time: print 'wait for webbroswer connect timeout' print "close dut serial" self.keep_alive = False break if SERIALCOMNUM[data_json["Args"]["Port"]]['count'] > 0: SERIALCOMNUM[data_json["Args"]["Port"]]['count'] -= 1 SERIALCOMNUM[data_json["Args"]["Port"]]['serial'].client_buffer.pop(read_id) print str(SERIALCOMNUM) if SERIALCOMNUM[data_json["Args"]["Port"]]['count'] <= 0: print 'clear serial start' SERIALCOMNUM[data_json["Args"]["Port"]]['serial'].close() if data_json["Args"]['Port'] in SERIALCOMNUM: SERIALCOMNUM.pop(data_json["Args"]["Port"]) def read_dev_serial(self): try: while self.keep_alive: # serial_log = self.dev_serial.read() serial_log = self.read_queue.get() self.request.send(serial_log) except socket.error: passclass ThreadingServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): def _threading_server(self): passclass SerialSocketServer(object): def __init__(self, port=33233): self.server = None self.port = 33233 def start(self): netcard_ips = self.get_netcard() for netcard_ip in netcard_ips: host = netcard_ip[1] try: port = self.port addr = (host, port) self.server = ThreadingServer(addr, MainHandler) self.server.allow_resuse_address = True server_thread = threading.Thread(target=self.server.serve_forever) server_thread.daemon = True server_thread.start() print "Starting Serial Socket Successfully!" while True: try: INPUT = raw_input() except KeyboardInterrupt: sys.exit(0) break except EOFError: print 'Unknown End of file!' continue except Exception, e: print "Starting Serial Socket Server Fail:%s" % e def stop(self): print "Shutdown Slave Socket Server!" if self.server != None: self.server.shutdown() self.server.server_close() def get_netcard(self): """获取网卡信息和ip地址 """ netcard_info = [] info = psutil.net_if_addrs() for k, v in info.items(): for item in v: if item[0] == 2 and not (item[1] == '127.0.0.1' or item[1] == '192.168.2.201'): netcard_info.append((k, item[1])) return netcard_infoclass SerialHandle(): def __init__(self, port=None, baudrate=115200, timeout=30, *args, **kargs): self.serial = Serial(port=port, baudrate=baudrate, timeout=timeout, *args, **kargs) self.is_running = True self.read_buffer = "" self.write_queue = Queue.Queue() self.read_buffer = CustomStringIO(4096) th_wt = threading.Thread(target=self.__write) th_wt.start() th_rd = threading.Thread(target=self.__read) th_rd.start() self.client_buffer = {} def read(self, read_id): return self.read_buffer.getvalue() def __read(self): while self.is_running: serial_log = self.serial.readline() for key, value in self.client_buffer.items(): self.client_buffer[key].put(serial_log) def write(self,write_string): self.write_queue.put(write_string) def __write(self): while self.is_running: write_string = self.write_queue.get() self.serial.write(write_string) def close(self): self.is_running = False self.serial.close() print 'close serial'if __name__ == '__main__': SerialSocketServer().start()
启动服务器:python serial_socket_server.py
socket client :
import threadingimport socketimport tracebackimport jsonimport sysimport reimport Queueclass DevSerialLoadClient(threading.Thread): def __init__(self, node_ip, server_port=33233, serial_port="COM19"): threading.Thread.__init__(self) self.slave_serial_serial_server = node_ip self.server_port = server_port self.serial_port = serial_port self.bufsize = 4096 * 4 self.setDaemon(True) self._is_running = True self._is_establish_connection = False self.client = None def connect(self): try: addr = (self.slave_serial_serial_server, self.server_port) self.client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.client.connect(addr) self._is_establish_connection = True except Exception as e: self._is_establish_connection = False print "Create Socket Connect Fail: %s" % e def run(self): self.connect() if self.is_establish_connection: request_msg = json.dumps({"RequestType":"DevSerialHandle","Args":{"Port":self.serial_port}}) self.client.send(request_msg) while self._is_running: try: response = self.client.recv(self.bufsize) if not response: continue handle_response = re.compile('[\\x00-\\x08\\x0b-\\x0c\\x0e-\\x1f|\\xff]').sub(' ', response.decode('unicode-escape')) print '%s' % str(handle_response) except socket.error: print 'socket error' self.connect() except: traceback.print_exc() print "------stop dev serial communication------" self.close() def close(self): try: if self.client: self.client.shutdown(socket.SHUT_RDWR) self.client.close() except Exception, e: print "close socket client Error[%s]" % str(e) @property def is_establish_connection(self): return self._is_establish_connection def stop(self): self._is_running = Falseif __name__ == '__main__': import getopt opts, args = getopt.getopt(sys.argv[1:], "h:s:") server_ip = "localhost" server_port = 33233 serial_port = None for op, value in opts: if op == "-h": server_ip = value if op == '-s': serial_port = value if not serial_port: print 'should provide serial port args: like -p COM19' dev_serial = DevSerialLoadClient(node_ip=server_ip, server_port=server_port, serial_port=serial_port) dev_serial.start() while True: try: INPUT = raw_input() dev_serial.client.send(INPUT+'\n') except KeyboardInterrupt: sys.exit(0) break except EOFError: print 'Unknown End of file!' continue
启动socket client:python serial_socket_client.py -h 对端ip -s 串口号
在命令行可向要连接的串口发送指令。
可建立多个client读写同一串口,所有client都可向串口发送数据;当一个client向串口输入数据后,其他client都可以收到串口的打印