前提:物理串口链接到PC上,经过串口号被PC惟一识别。node
此时,物理串口经过该串口号仅能被单一线程或进程实例并占用,
其余线程或进程不能再经过该串口号与物理串口通讯。这个暂称为串口独占性。python
解决思路:
核心思想:利用计算机软件中的socket编程,一个socket server 能够链接多个socket client,由socket server 完成多个socket client与物理串口的通讯。web
实现过程:
一、编程语言根据物理串口的串口号实例化一个串口操做类,串口操做类负责与物理串口通讯。创建串口写线程和串口读线程。其中,串口读线程不断收取物理串口输出,并存放到读缓存。串口写线程不断从写缓存取命令,由其不断发往物理串口。
二、创建一个可靠的Socket Server,当有Socket Client链接时,由其将读缓存中的数据发给Socket Client,并不断收取Socket Client发来的命令,存放到写缓存中。
三、编程语言线程/进程经过创建Socket Client链接到Socket Server,既可实现多个线程/进程
与物理串口的通讯编程
如下没有实现缓存机制,而是将读取到的串口数据放入队列:json
socket server缓存
#!/usr/bin/python # -*- coding: utf-8 -*- import socket import psutil import traceback import threading import SocketServer import json import sys import Queue import time from serial import Serial from SocketServer import StreamRequestHandler as SRH from CustomStringIO import CustomStringIO SERIALCOMNUM = {} 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: pass class ThreadingServer(SocketServer.ThreadingMixIn, SocketServer.TCPServer): def _threading_server(self): pass class 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_info class 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 :app
import threading import socket import traceback import json import sys import re import Queue class 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 = False if __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 串口号socket
在命令行可向要链接的串口发送指令。编程语言
可创建多个client读写同一串口,全部client均可向串口发送数据;当一个client向串口输入数据后,其余client均可以收到串口的打印