zoukankan      html  css  js  c++  java
  • socket 实现单一串口共享读写操作

    前提:物理串口连接到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 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 :

    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+'
    ')
            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都可以收到串口的打印

  • 相关阅读:
    java设计模式-建造者模式
    java设计模式-外观模式
    java设计模式-模板方法模式
    java设计模式-原型模式
    java设计模式-代理模式
    java设计模式-装饰模式
    webpack-PWA概念、typeScript打包、webpackDevServer实现请求转发
    webpack七探-库打包
    webpack六探-打包分析、懒加载、浏览器缓存、shimming、环境变量
    webpack五探-tree shaking、模式、代码分割
  • 原文地址:https://www.cnblogs.com/linyihai/p/7071538.html
Copyright © 2011-2022 走看看