前提:物理串口鏈接到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均可以收到串口的打印