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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM